sujingliang 发表于 2025-6-13 16:07

【灵动微电子MM32F0121测评】4、RTC实时时钟

本文实现RTC实时时钟,以点阵屏形式显示。

关于RTC实时时钟,SDK中有完整的例子,可以不用修改就直接使用:
LibSamples_MM32F0120_V1.13.4\Samples\LibSamples\RTC\RTC_Calendar

在此基础上加上显示部分就可以完成本实验。


1、RTC配置
RTC_Configure完成:
RTC模块的初始化配置(使用LSE作为时钟源)
通过备份寄存器判断是否需要重新配置RTC
配置RTC秒中断
设置中断优先级和使能
加载默认RTC值

void RTC_Configure(void)
{
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_RTC | RCC_APB1Periph_BKP, ENABLE);

    PWR_BackupAccessCmd(ENABLE);

    BKP_DeInit();

    if (BKP_ReadBackupRegister(BKP_DR1) != 0x5B5B)
    {
      RCC_LSEConfig(RCC_LSE_ON);

      while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
      {
      }

      RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

      RCC_RTCCLKCmd(ENABLE);

      RTC_WaitForSynchro();
      RTC_WaitForLastTask();

      RTC_ITConfig(RTC_IT_SEC, ENABLE);
      RTC_WaitForLastTask();

      RTC_SetPrescaler(32767);
      RTC_WaitForLastTask();

      printf("\r\n%s", __FUNCTION__);

      BKP_WriteBackupRegister(BKP_DR1, 0x5B5B);
    }
    else
    {
      printf("\r\nNeed't to configure RTC.");

      RTC_WaitForSynchro();

      RTC_ITConfig(RTC_IT_SEC, ENABLE);
      RTC_WaitForLastTask();
    }

    NVIC_InitStruct.NVIC_IRQChannel = RTC_BKP_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    RTC_LoadDefault();
}


2、RTC_BKP_IRQHandler中断处理
RTC(实时时钟)和 BKP(备份寄存器)共用的中断源
实际上包含多个可能的中断事件,主要分:
秒中断(RTC_IT_SEC)
闹钟中断(RTC_IT_ALR)
溢出中断(RTC_IT_OW)

本例中,用于更新时间显示RTC_UpdateCalendar
打印时间RTC_PrintDateTime
void RTC_BKP_IRQHandler(void)
{
    if (RESET != RTC_GetITStatus(RTC_IT_SEC))
    {
      RTC_UpdateCalendar();
      RTC_PrintDateTime();

      RTC_ClearITPendingBit(RTC_IT_SEC);
      RTC_WaitForLastTask();
    }
}3、RTC_UpdateCalendar()更新时间
void RTC_UpdateCalendar(void)
{
    static uint32_t PreTotalDay = 0;
    uint32_t TotalSecond = 0;
    uint32_t TotalDay    = 0;
    uint16_t Year= 1970;
    uint8_tMonth = 0;

    TotalSecond = RTC_GetCounter();
    TotalDay    = TotalSecond / 86400;

    if (PreTotalDay != TotalDay)
    {
      PreTotalDay = TotalDay;

      while (TotalDay >= 365)
      {
            if (RTC_LeapYear(Year) == 1)
            {
                if (TotalDay >= 366)
                {
                  TotalDay -= 366;
                }
                else
                {
                  break;
                }
            }
            else
            {
                TotalDay -= 365;
            }

            Year++;
      }

      RTC_Calendar.year = Year;

      while (TotalDay >= 28)
      {
            if ((Month == 1) && (RTC_LeapYear(RTC_Calendar.year) == 1))
            {
                if (TotalDay >= 29)
                {
                  TotalDay -= 29;
                }
                else
                {
                  break;
                }
            }
            else
            {
                if (TotalDay >= RTC_DayOfMonth)
                {
                  TotalDay -= RTC_DayOfMonth;
                }
                else
                {
                  break;
                }
            }

            Month++;
      }

      RTC_Calendar.month = Month + 1;
      RTC_Calendar.day   = TotalDay + 1;

      RTC_Calendar.week= RTC_GetWeek(RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day);
    }

    RTC_Calendar.hour   = (TotalSecond % 86400) / 3600;
    RTC_Calendar.minute = ((TotalSecond % 86400) % 3600) / 60;
    RTC_Calendar.second = ((TotalSecond % 86400) % 3600) % 60;
}

4、RTC_PrintDateTime打印时间
void RTC_PrintDateTime(void)
{
    printf("\r\n%04d-%02d-%02d", RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day);

    switch (RTC_Calendar.week)
    {
      case 0:
            printf(" SUN ");
            break;

      case 1:
            printf(" MON ");
            break;

      case 2:
            printf(" TUE ");
            break;

      case 3:
            printf(" WED ");
            break;

      case 4:
            printf(" THU ");
            break;

      case 5:
            printf(" FRI ");
            break;

      case 6:
            printf(" SAT ");
            break;

      default:
            break;
    }

    printf("%02d:%02d:%02d\r\n", RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second);
               
               
       
}

5、增加自己的时间显示
因为用到74hc595驱动点阵屏,74hc595没有输出锁定功能,需要将相关的点阵屏驱动放在while(1)中不断刷新。
void RTC_Calendar_Sample(void)
{
    printf("\r\nTest %s", __FUNCTION__);

                //初始化LED点阵屏
                config_led_matrix_gpio();
       
                //配置RTC
    RTC_Configure();

    while (1)
    {
      //PLATFORM_LED_Toggle(LED1);
      //PLATFORM_DelayMS(100);
                       
                                //在LED点阵屏上显示
                        clear_screen();
                        vs_clear_screen();
                       
                        //填充小时
                        fill_num_to_buffer(0,0,RTC_Calendar.hour/10);
                        fill_num_to_buffer(4,0,RTC_Calendar.hour%10);
                        //填充:
                        fill_num_to_buffer(8,0,10);
                        //填充小时
                        fill_num_to_buffer(12,0,RTC_Calendar.minute/10);
                        fill_num_to_buffer(16,0,RTC_Calendar.minute%10);
                        //填充:
                        fill_num_to_buffer(20,0,10);
                        //填充秒钟
                        fill_num_to_buffer(24,0,RTC_Calendar.second/10);
                        fill_num_to_buffer(28,0,RTC_Calendar.second%10);
                       
                        vs_render_screen();
                        render_screen_to_matrix8x24();
    }
}6、点阵屏的驱动

#include "hc595.h"
#include "hal_conf.h"

//位置,595扫描的行/列
//COM
const uint8_t pos[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
//显示缓存 7*11*4=7*44,使用8*48缓存
//SEG,实际屏幕,节约空间
uint8_t matrix3={0x00};

// 虚拟屏幕
uint8_t virtual_screen={0};

unsigned char bigNUM[]=
{
/* 3*7数字字模,上下顶到头,空左边一列*/
0x07,0x05,0x05,0x05,0x05,0x05,0x07, /*0*/
0x02,0x02,0x02,0x02,0x02,0x02,0x02, /*1*/
0x07,0x01,0x01,0x07,0x04,0x04,0x07, /*2*/
0x07,0x01,0x01,0x07,0x01,0x01,0x07, /*3*/
0x05,0x05,0x05,0x07,0x01,0x01,0x01, /*4*/
0x07,0x04,0x04,0x07,0x01,0x01,0x07, /*5*/
0x07,0x04,0x04,0x07,0x05,0x05,0x07, /*6*/
0x07,0x01,0x01,0x01,0x01,0x01,0x01, /*7*/
0x07,0x05,0x05,0x07,0x05,0x05,0x07, /*8*/
0x07,0x05,0x05,0x07,0x01,0x01,0x07, /*9*/
0x00,0x02,0x00,0x00,0x00,0x02,0x00, /*:*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*空,补位*/
};

unsigned char smallNUM[]=
{
/* 4*7数字字模,上下各空一行,空左边一列*/
0x00,0x07,0x05,0x05,0x05,0x07,0x00, /*0*/
0x00,0x02,0x02,0x02,0x02,0x02,0x00, /*1*/
0x00,0x07,0x01,0x07,0x04,0x07,0x00, /*2*/
0x00,0x07,0x01,0x07,0x01,0x07,0x00, /*3*/
0x00,0x05,0x05,0x07,0x01,0x01,0x00, /*4*/
0x00,0x07,0x04,0x07,0x01,0x07,0x00, /*5*/
0x00,0x07,0x04,0x07,0x05,0x07,0x00, /*6*/
0x00,0x07,0x01,0x01,0x01,0x01,0x00, /*7*/
0x00,0x07,0x05,0x07,0x05,0x07,0x00, /*8*/
0x00,0x07,0x05,0x07,0x01,0x07,0x00, /*9*/
0x00,0x00,0x02,0x00,0x02,0x00,0x00, /*:*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*空,补位*/
};



void config_led_matrix_gpio(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
                RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOD, ENABLE);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_15;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
       

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
    GPIO_Init(GPIOD, &GPIO_InitStruct);       
       
                LED_OE_CLR;
}
/*
*PN:反显
* */
void HC595(uint8_t c,uint8_t PN)
{
    uint8_t i;
    for(i=0;i<8;i++){

      if(PN==0){                     //正常显示
            if((c>>i) & 0x01) LED_SER_SET;
            else LED_SER_CLR;
      }
      else{                              //反显
            if((c>>i) & 0x01) LED_SER_CLR;
            else LED_SER_SET;
      }

      LED_SRCK_CLR;
      LED_SRCK_SET;   // 上升沿进行一次数据移入
    }
}

void clear_screen(void)
{
        uint8_t i;
    for ( i = 0; i < 48; i++) {
      matrix3=0x00;
    }
}

//紧凑缓存画点
void draw_pixel(uint8_t x,uint8_t y)
{
    uint8_t mx,my;

    if (x>21) x=x+2;

    mx=5-x/8;
    my=y+1;

    matrix3|=1<<(7-x%8);
}




//控制闪烁的措施,如果点灯少就多循环显示,否则少循环显示
uint8_t count_ones(uint8_t i) {
    uint8_t count = 0;
    while (i != 0) {
      i &= (i - 1); // 清除最低位的1
      count++;
    }
    return count;
}

//将紧凑缓存内容显示在LED矩阵上,没有虚拟屏直接显示的函数
//pBuff通常是matrix3,
void render_screen_to_matrix8x24(void)
{
                uint8_t *pBuff=matrix3;
    uint8_t k;
    uint16_t m,n;

   for(m=0;m<32*2;m++)    //为移动预留
   {

         for(k=0;k<7;k++)   //行扫描
         {
             for(n=0;n<(count_ones(pBuff)+count_ones(pBuff)+count_ones(pBuff)+count_ones(pBuff)
             +count_ones(pBuff)+count_ones(pBuff))*2;n++)//控制显示速度,防止闪烁
                                               
             {

               HC595(~pos,0);
               HC595(pBuff,0);
               HC595(pBuff,0);
               HC595(pBuff,0);

               HC595(~pos,0);
               HC595(pBuff,0);
               HC595(pBuff,0);
               HC595(pBuff,0);
               LED_RCK_CLR;
               LED_RCK_SET;    //并行输出
                                                                __nop();__nop();
               
             }


          }
   }
    //传COM -

    //传SEG +
}



//虚拟屏清屏
void vs_clear_screen(void)
{
        uint8_t x,y;
    for ( y = 0; y < SCREEN_HEIGHT; y++) {
      for ( x = 0; x < SCREEN_WIDTH; x++) {
            virtual_screen = 0;
      }
    }
}

//虚拟屏画点
void vs_drawpixel(uint8_t x,uint8_t y)
{
    virtual_screen=1;
}

void vs_clearpixel(uint8_t x,uint8_t y)
{
        virtual_screen=0;
}

//虚拟屏复制到紧凑缓存
void vs_render_screen(void)
{
        uint8_t x,y;
    //clear_screen();
    for ( y = 0; y < SCREEN_HEIGHT; y++) {
      for ( x = 0; x < SCREEN_WIDTH; x++) {
            if (virtual_screen) {
                draw_pixel(x, y); // 画点
            } else {
                // 如果需要清除点,可以调用清屏函数或设置背景色
            }
      }
    }
}

/*
* 填充数字到屏幕位置,写物理和虚拟缓存
* (x,y)是显示位置坐标;NUM是要显示的数字
*/
void fill_num_to_buffer(uint8_t x,uint8_t y,uint8_t num)
{
    uint8_t i,j;

    for(i=0;i<7;i++)
    {
      for(j=0;j<4;j++)
      {
            if(((smallNUM>>j)&0x01)==0x01)
            {
                //draw_pixel(x+4-j, y+i);
                vs_drawpixel(x+4-j, y+i);
            }
                                                else
                                                {
                                                        vs_clearpixel(x+4-j, y+i);
                                                }
      }
    }
}


void hc595_test(void)
{
                config_led_matrix_gpio();
       

       
                while(1)
                {
                                        vs_clear_screen();
    fill_num_to_buffer(0,0,0);
    fill_num_to_buffer(4,0,1);
    fill_num_to_buffer(8,0,2);
    fill_num_to_buffer(12,0,3);
    fill_num_to_buffer(16,0,4);
    fill_num_to_buffer(20,0,5);
    fill_num_to_buffer(24,0,6);
    fill_num_to_buffer(28,0,7);
    fill_num_to_buffer(32,0,8);
    fill_num_to_buffer(36,0,9);
       
    vs_render_screen();
                        render_screen_to_matrix8x24();
                }
}
6、效果






页: [1]
查看完整版本: 【灵动微电子MM32F0121测评】4、RTC实时时钟