打印
[MM32硬件]

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

[复制链接]
334|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本文实现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_t  Month = 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[Month])
                {
                    TotalDay -= RTC_DayOfMonth[Month];
                }
                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[48]={0x00};

// 虚拟屏幕
uint8_t virtual_screen[SCREEN_WIDTH][SCREEN_HEIGHT]={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[i]=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[mx*8+my]|=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[k])+count_ones(pBuff[k+8])+count_ones(pBuff[k+16])+count_ones(pBuff[k+24])
             +count_ones(pBuff[k+32])+count_ones(pBuff[k+40]))*2;n++)//控制显示速度,防止闪烁
                                               
             {

                 HC595(~pos[k],0);
                 HC595(pBuff[k],0);
                 HC595(pBuff[k+8],0);
                 HC595(pBuff[k+16],0);

                 HC595(~pos[k],0);
                 HC595(pBuff[k+24],0);
                 HC595(pBuff[k+32],0);
                 HC595(pBuff[k+40],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[x][y] = 0;
        }
    }
}

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

void vs_clearpixel(uint8_t x,uint8_t y)
{
        virtual_screen[x][y]=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[x][y]) {
                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[num*7+i]>>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、效果






使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

72

主题

133

帖子

3

粉丝