打印
[研电赛技术支持]

GD32F10X ----RTC

[复制链接]
34|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1. RTC的简介
STM32 的实时时钟(RTC)是一个独立的定时器。STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
        RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要取消备份区域(BKP)写保护。


2. RTC的框图
这里用的是STM32其实与GD都是差不多。



RTC 由两个主要部分组成 第一部分(APB1 接口),第二部分是后备区域。

与RT_DIV寄存器设置可编程产生 1 秒的 RTC 时间基准 TR_CLK。每一秒到来RTC_CNT

寄存器的值就会加1。RTC_CNT是32位的寄存器。1秒到还可以产生中断。以及溢出中断。以及闹钟中断。(当RTC_ALR寄存器与RTC_CNT一样)。

我们通过读取RTC_CNT的大小(多少秒)然后转换成实时时钟(年,月,日,时,分,秒)

如果我要设置一个实时时钟转成秒然后设置到RTC_CNT。

不管是设置与获取都是操作RTC_CNT并且单位是秒。具体怎么转成实时时钟要自己写逻辑。

实时时钟都是以1970年1月1日00 :00:00为开始。(红色字是重点)

由于RTC_CNT是32位的,可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记录 4294967296 秒,约合 136 年左右。所以最大:1970 + 136。如果大于这个年就会溢出。

3. 代码实现
RTC.h

#ifndef _RTC_H
#define _RTC_H

#include "gd32f10x.h"
#include <stdio.h>

//日期时间结构体
typedef struct{
        //时间
        uint8_t hour;
        uint8_t min;
        uint8_t sec;
        //日期
        uint16_t w_year;
        uint8_t w_month;
        uint8_t w_day;
}_calender_obj;

extern _calender_obj calender;  //日期、时间结构体变量
extern uint8_t const month_table[12];

void RTC_Config(void);  //RTC配置
uint8_t RTC_Init(void);    //RTC初始化
void RTC_NVIC_Config(void); //配置RTC中断

uint8_t RTC_Set(uint16_t syear, uint8_t smonth, uint8_t sday, uint8_t shour, uint8_t smin, uint8_t ssec); //将设置时间转化为秒数,给到RTC_CNT
uint8_t RTC_Get(void);  //得到RTC_CNT的值并转换为日期时间
uint8_t Is_Leap_Year(uint16_t year); //判断year是否闰年
uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day);
#endif

RTC.c

#include "RTC.h"

_calender_obj calender;


uint8_t const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
uint8_t const month_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年月份的天数

uint32_t timecount = 0;

//RTC配置
void RTC_Config(void){
        rcu_periph_clock_enable(RCU_BKPI); //备份区域的时钟要先使能
        rcu_periph_clock_enable(RCU_PMU);  //电源管理时钟使能
        pmu_backup_write_enable();         //使能备份域访问允许
        bkp_deinit();                      //备份域复位
       
        rcu_osci_on(RCU_LXTAL);            //使能外部低速时钟
        rcu_osci_stab_wait(RCU_LXTAL);     //等待外部低速时钟稳定
        rcu_rtc_clock_config(RCU_RTCSRC_LXTAL); //时钟源选择
       
        rcu_periph_clock_enable(RCU_RTC); //使能RTC时钟
        rtc_register_sync_wait();         //等待寄存器与APB1时钟同步
        rtc_lwoff_wait();   //等待RTC的最后一次操作完成
        rtc_interrupt_enable(RTC_INT_SECOND);//使能RTC的秒中断
        rtc_lwoff_wait();   //等待RTC的最后一次操作完成

        rtc_prescaler_set(32767); /* 配置RTC_PRL的值(时钟分频) */
        rtc_lwoff_wait();   //等待RTC的最后一次操作完成
}

//RTC初始化
uint8_t RTC_Init(void){
        RTC_Config();
        RTC_Set(2023, 8, 21, 23, 13, 15);
        RTC_NVIC_Config();//配置中断的优先级
        return 0;
}

// 配置RTC的中断优先级
void RTC_NVIC_Config(void){
        nvic_irq_enable(RTC_IRQn, 2, 0);
}

//RTC的中断服务函数
void RTC_IRQHandler(void){
        if(rtc_flag_get(RTC_FLAG_SECOND) != RESET){ //判断是否为秒中断
                rtc_flag_clear(RTC_FLAG_SECOND);
                RTC_Get();
                printf("Now time is: %d-%d-%d %d:%d:%d\r\n", calender.w_year, calender.w_month, calender.w_day, calender.hour, calender.min, calender.sec);
        }
}

//将设置时间转化为秒数,给到RTC_CNT
uint8_t RTC_Set(uint16_t syear, uint8_t smonth, uint8_t sday, uint8_t shour, uint8_t smin, uint8_t ssec){
        uint32_t seccounts = 0;
        uint16_t temp_year = 1970;
        uint8_t temp_month;
        if(syear<1970 || syear>2099){  //设置的时间不合理
                return 1;
        }
       
        //整年的秒数
        while(temp_year < syear){
                if(Is_Leap_Year(temp_year))seccounts += 31622400; //闰年,一年的秒数  366* 24 * 60 *60
                else seccounts += 31536000;  //平年,一年的秒数  365* 24 * 60 *60
                temp_year++;
        }

        //整月的秒数
        smonth--;
        for(temp_month = 0; temp_month<smonth; temp_month++){
                seccounts += (uint32_t)month_table[temp_month]*86400;
                if(Is_Leap_Year(syear)&&temp_month==1)seccounts += 86400; //如果设置的年份是闰年,在二月这个月份要加多一天
        }
       
        //日、时、分、秒的处理
        seccounts += (uint32_t)(sday-1)*86400; //整日的秒数  24 * 60 * 60
        seccounts += (uint32_t)shour*3600;//小时
        seccounts += (uint32_t)smin*60;   //分
        seccounts += ssec;      //秒
       
        rtc_lwoff_wait();
        rtc_counter_set(seccounts);
        return 0;
}

//得到RTC_CNT的值并转换为日期时间
uint8_t RTC_Get(void){
        //把timecount转换为日期时间,并赋给calender
        uint32_t temp_days = timecount/86400;
        uint16_t temp_year = 1970;
        uint16_t temp_month;
  uint32_t temp_seconds;
       
        timecount = rtc_counter_get();//读取RTC_CNT寄存器的值

        //处理天数中的整年,
        if(temp_days>0){
                while(temp_days>=365){
                        if(Is_Leap_Year(temp_year)){//如果是闰年
                                if(temp_days>365){
                                        temp_days -= 366;
                                }
                                else{
                                        break;
                                }
                        }else{
                                temp_days -= 365;
                        }
                        temp_year++;
                }
                calender.w_year = temp_year;
               
                //剩下不足一年的,再处理整月
                temp_month = 1; //用来临时存放月份
                while(temp_days >= 28){ //超过了一个月
                        if(Is_Leap_Year(calender.w_year) && temp_month == 2){
                                if(temp_days>=29){ //闰年的2月是29天
                                        temp_days -= 29;
                                }else{
                                        break;
                                }
                        }else{
                                if(temp_days >= month_table[temp_month-1]){//剩余的天数是不是大于temp_month这个月整月的天数
                                        temp_days -= month_table[temp_month-1];
                                }else{
                                        break;
                                }
                        }
                        temp_month++;
                }
        }

        calender.w_month = temp_month;
        calender.w_day = temp_days+1;
       
        //处理剩下的不足一天的秒数,时:分:秒
        temp_seconds = timecount%86400; //不足一天的秒数
        calender.hour = temp_seconds/3600;
        calender.min = (temp_seconds%3600)/60;
        calender.sec = temp_seconds%60;
       
        return 0;
}


uint8_t Is_Leap_Year(uint16_t year){ //判断year是否闰年
        if(year%4 == 0){
                if(year%100 == 0){
                        if(year%400 == 0)
                                return 1;
                        else
                                return 0;
                }else{
                        return 1;
                }
        }else{
                return 0;
        }
}


//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//year,month,day:公历年月日
//返回值:星期号                                                                                                                                                                                 
uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day)
{       
        uint16_t temp2;
        uint8_t yearH,yearL;
       
        yearH=year/100;        yearL=year%100;
        // 如果为21世纪,年份数加100  
        if (yearH>19)yearL+=100;
        // 所过闰年数只算1900年之后的  
        temp2=yearL+yearL/4;
        temp2=temp2%7;
        temp2=temp2+day+table_week[month-1];
        if (yearL%4==0&&month<3)temp2--;
        return(temp2%7);
}

main.c

#include "gd32f10x_eval.h"

#include "LED.h"
#include "SYSTICK_DELAY.h"
#include "RTC.h"

int main(){
        gd_eval_com_init(EVAL_COM0);    // 初始化USART0
        LED_Init();
        my_systick_config();
        printf("This is a RTC DEMO test.\r\n");
       
        RTC_Init();
       
        while(1){
                LED1_Toggle();
                my_systick_delay_ms(1000);        //delay 1000 ms
        }
}

/*重写fputc*/
int fputc(int ch, FILE *f)
{
        usart_data_transmit(EVAL_COM0,ch);  //通过串口把ch给发送出去
        while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TBE));
        return ch;
}


       

通过串口工具来显示实时时钟。先设置然后在读取通过串口显示出来。



————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_41328470/article/details/133468315

使用特权

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

本版积分规则

20

主题

44

帖子

0

粉丝