目录

工作原理上一篇博客有详细讲解。
必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、 RTC_ALR寄存器。另外,对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是'1'时,才可以写入RTC寄存器。
1.查询RTOFF位,直到RTOFF的值变为'1”
2.置CNF值为1,进入配置模式
3.对一个或多个RTC寄存器进行写操作
4.清除CNF标志位,退出配置模式
5.查询RTOFF,直至RTOFF位变为'1'以确认写操作已经完成。
仅当CNF标志位被清除时,写操作才能进行,这个过程至少需要3个RTCCLK周期。
RTC核完全独立于RTC APB1接口。
软件通过APB1接口访问RTC的预分频值、计数器值和闹钟值。但是,相关的可读寄存器只在与RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。RTC标志也是如此的。"这意味着,如果APB1接门曾经被关闭,而读操作又是在刚刚重新开启APB1之后,则在第一次的内部寄存器更新之前,从APB1上读出的RTC寄存器数值可能被破坏了(通常读到0)。下述几种情况下能够发生这种情形:
发生系统复位或电源复位系统刚从待机模式唤醒。
系统刚从停机模式唤醒。
所有以上情况中APB1接口被禁止时(复位、无时钟或断电)RTC核仍保持运行状态。因此,若在读取RTC寄存器时, RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置'1'。
RTC时钟源和时钟操作函数:
void RCC_RTCCLKConfig(uint32_t CLKSource): //时钟源选择
void RCC_RTCCLKCmd(FunctionalState NewState)//时钟使能
RTC配置函数(预分频,计数值:
void RTC_SetPrescaler(uint32_t PrescalerValue);//预分频配置: PRLH/PRLL
void RTC_SetCounter(uint32_t CounterValue): //设置计数器值: CNTH/CNTL
void RTC SetAlarm(uint32 tAlarmValue); //闹钟设置: ALRH/ALRL
RTC中断设置函数:
void RTC_ITConfig(uint16_tRTC_IT, FunctionalState NewState);//CRH
RTC允许配置和退出配置函数:
void RTC_EnterConfigMode(void);//允许RTC配置:CRL位 CNF
void RTC_ExitConfigMode(void);//退出配置模式:CRL位 CNF
同步函数:
void RTC_WaitForLastTask(void); //等待上次操作完成: CRL位RTOFF
void RTC_WaitForSynchro(void); //等待时钟同步: CRL位RSF
相关状态位获取清除函数:
FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);
void RTC_ClearFlag(uint16_t RTC_FLAG);
ITStatus RTC_GetITStatus(uint16_t RTC_IT);
void RTC_ClearlTPendingBit(uint16_t RTC_IT);
其他相关函数(BKP等)
PWR_BackupAccessCmd();//BKP后备区域访问使能
RCC_APB1 PeriphClockCmd();//使能PWR和BKP时钟
RCC LSEConfig();//开启LSE, RTC选择LSE作为时钟源
使能PWR和BKP时钟: RCC_APB1PeriphClockCmd();
使能后备寄存器访问: PWR_BackupAccessCmd();
配置RTC时钟源,使能RTC时钟:
RCC_RTCCLKConfig();
RCC_RTCCLKCmd();
如果使用LSE,要打开LSE: RCC_LSEConfig(RCC_LSE_ON);
设置RTC预分频系数: RTC_SetPrescaler();
设置时间: RTC_SetCounter();
开启相关中断(如果需要) RTC_ITConfig();
编写中断服务函数: RTC_IRQHandler();
部分操作要等待写操作完成和同步。
RTC_WaltForLastTask();//等待最近一次对RTC寄存器的写操作完成
RTC WaitForSynchro(); //等待RTC寄存器同步
- /**
- ******************************************************************************
- * @file : user_rcc_config.c
- * @brief : V1.00
- ******************************************************************************
- * @attention
- *
- ******************************************************************************
- */
-
- /* Include 包含---------------------------------------------------------------*/
- #include "user_rcc_config.h"
- /* Typedef 类型----------------------------------------------------------------*/
- /* Define 定义----------------------------------------------------------------*/
- /* Macro 宏------------------------------------------------------------------*/
- /* Variables 变量--------------------------------------------------------------*/
- /* Constants 常量--------------------------------------------------------------*/
- /* Function 函数--------------------------------------------------------------*/
-
- /*!
- \brief RCC配置
- \param[in] none
- \param[out] none
- \retval none
- */
- void Rcc_config(void)
- {
- /*使能PWR时钟*/
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
- /*使能BKP外设时钟*/
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
- /*使能后备寄存器才可以访问*/
- PWR_BackupAccessCmd(ENABLE);
-
- /*使能GPIOA时钟*/
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
- /*使能UART1时钟*/
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
-
- }
-
- /************************************************************** END OF FILE ****/
-
- /**
- ******************************************************************************
- * @file : user_gpio.c
- * @brief : V1.00
- ******************************************************************************
- * @attention
- *
- ******************************************************************************
- */
-
- /* Include 包含---------------------------------------------------------------*/
- #include "user_gpio.h"
- /* Typedef 类型----------------------------------------------------------------*/
- /* Define 定义----------------------------------------------------------------*/
- /* Macro 宏------------------------------------------------------------------*/
- /* Variables 变量--------------------------------------------------------------*/
- /* Constants 常量--------------------------------------------------------------*/
- /* Function 函数--------------------------------------------------------------*/
-
- /*!
- \brief GPIO初始化函数
- \param[in] none
- \param[out] none
- \retval none
- */
- void Gpio_Init(void)
- {
- /*GPIO结构体*/
- GPIO_InitTypeDef GPIO_InitTypeDefstruct;
-
- /*UART1发送引脚配置*/
- GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_AF_PP;//推挽复用输出
- GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_9;
- GPIO_InitTypeDefstruct.GPIO_Speed = GPIO_Speed_10MHz;
- /*写入结构体到GPIOA*/
- GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
-
- /*UART1接收引脚配置*/
- GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
- GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_10;
- GPIO_InitTypeDefstruct.GPIO_Speed = GPIO_Speed_10MHz;
- /*写入结构体到GPIOA*/
- GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
-
- }
-
- /************************************************************** END OF FILE ****/
-
- /**
- ******************************************************************************
- * @file : user_uart.c
- * @brief : V1.00
- ******************************************************************************
- * @attention
- *
- ******************************************************************************
- */
-
- /* Include 包含---------------------------------------------------------------*/
- #include "user_uart.h"
- /* Typedef 类型----------------------------------------------------------------*/
- /* Define 定义----------------------------------------------------------------*/
- /* Macro 宏------------------------------------------------------------------*/
- /* Variables 变量--------------------------------------------------------------*/
-
- extern uint16_t USART_RX_STA;
- extern uint8_t USART_RX_BUF[200];
-
-
- /* Constants 常量--------------------------------------------------------------*/
- /* Function 函数--------------------------------------------------------------*/
- #if 1
- #pragma import(__use_no_semihosting)
- /*实现Printf代码*/
- struct __FILE
- {
- int handle;
-
- };
- FILE __stdout;
-
- void _sys_exit(int x)
- {
- x = x;
- }
- //重定义fputc函数
- int fputc(int ch, FILE *f)
- {
- while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
- USART1->DR = (u8) ch;
- return ch;
- }
- #endif
-
-
-
-
- /*!
- \brief UART1初始化
- \param[in] none
- \param[out] none
- \retval none
- */
-
- void Uart1_Init(u32 bound)
- {
- /*UART结构体*/
- USART_InitTypeDef USART_InitTypeDefstruct;
-
- /*UART结构体配置*/
- USART_InitTypeDefstruct.USART_BaudRate = bound; //波特率
- USART_InitTypeDefstruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //不使用硬件流
- USART_InitTypeDefstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送接收使能
- USART_InitTypeDefstruct.USART_Parity = USART_Parity_No; //不使用奇偶校验
- USART_InitTypeDefstruct.USART_StopBits = USART_StopBits_1; //1个停止位
- USART_InitTypeDefstruct.USART_WordLength = USART_WordLength_8b; //8个数据位
- /*写入USART1*/
- USART_Init(USART1,&USART_InitTypeDefstruct);
-
- /*使能串口1*/
- USART_Cmd(USART1,ENABLE);
-
- }
-
-
- /*!
- \brief UART1中断服务函数
- \param[in] none
- \param[out] none
- \retval none
- */
-
- void USART1_IRQHandler(void)
- {
-
- }
-
-
- /************************************************************** END OF FILE ****/
-
- /**
- ******************************************************************************
- * @file : user_rtc.h
- * @brief : V1.00
- ******************************************************************************
- * @attention
- *
- ******************************************************************************
- */
-
- /* Define to prevent recursive incluson---------------------------------------*/
- #ifndef _USER_RTC_H__
- #define _USER_RTC_H__
-
- /* Include 包含---------------------------------------------------------------*/
- #include "stm32f10x.h"
- #include "user_delay.h"
- #include "user_uart.h"
- /* Typedef 类型----------------------------------------------------------------*/
- typedef struct
- {
- uint8_t hour; //时
- uint8_t min; //分
- uint8_t sec; //秒
-
- uint16_t year; //年
- uint8_t month;//月
- uint8_t date; //日
- uint8_t week; //周
- }Calendar;
-
- /* Define 定义----------------------------------------------------------------*/
- /* Macro 宏------------------------------------------------------------------*/
- /* Variables 变量--------------------------------------------------------------*/
- /* Constants 常量--------------------------------------------------------------*/
- /* Function 函数--------------------------------------------------------------*/
-
-
- uint8_t Rtc_Init(void);
-
- #endif
-
- /************************************************************** END OF FILE ****/
- /**
- ******************************************************************************
- * @file : user_rtc.c
- * @brief : V1.00
- ******************************************************************************
- * @attention
- *
- ******************************************************************************
- */
-
- /* Include 包含---------------------------------------------------------------*/
- #include "user_rtc.h"
- /* Typedef 类型----------------------------------------------------------------*/
- /* Define 定义----------------------------------------------------------------*/
- /* Macro 宏------------------------------------------------------------------*/
- /* Variables 变量--------------------------------------------------------------*/
-
- /*时间结构体*/
- Calendar timer;
-
- /* Constants 常量--------------------------------------------------------------*/
- /*月修正数据表*/
- uint8_t const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5};
- /*平年的月份日期表*/
- const uint8_t mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
- /* Function 函数--------------------------------------------------------------*/
-
-
- /*!
- \brief 判断是否是闰年函数
- \param[in] 年份
- \param[in] none
- \retval 该年份是不是闰年.1,是.0,不是
- */
- uint8_t Is_Leap_Year(u16 year)
- {
- if(year%4==0) //必须能被4整除
- {
- if(year%100==0)
- {
- if(year%400==0)return 1;//如果以00结尾,还要能被400整除
- else return 0;
- }else return 1;
- }else return 0;
- }
- /*!
- \brief 获得现在是星期几函数
- \param[in] 输入公历日期得到星期(只允许1901-2099年)年月日
- \param[in] none
- \retval 星期号
- */
- uint8_t RTC_Get_Week(u16 year,u8 month,u8 day)
- {
- u16 temp2;
- u8 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);
- }
-
-
- /*!
- \brief 得到当前的时间函数
- \param[in] none
- \param[in] none
- \retval 0,成功;其他:错误代码.
- */
- u8 Rtc_Get(void)
- {
- static u16 daycnt=0;
- u32 timecount=0;
- u32 temp=0;
- u16 temp1=0;
- timecount=RTC_GetCounter();
- temp=timecount/86400; //得到天数(秒钟数对应的)
- if(daycnt!=temp)//超过一天了
- {
- daycnt=temp;
- temp1=1970; //从1970年开始
- while(temp>=365)
- {
- if(Is_Leap_Year(temp1))//是闰年
- {
- if(temp>=366)temp-=366;//闰年的秒钟数
- else {temp1++;break;}
- }
- else temp-=365; //平年
- temp1++;
- }
- timer.year=temp1;//得到年份
- temp1=0;
- while(temp>=28)//超过了一个月
- {
- if(Is_Leap_Year(timer.year)&&temp1==1)//当年是不是闰年/2月份
- {
- if(temp>=29)temp-=29;//闰年的秒钟数
- else break;
- }
- else
- {
- if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
- else break;
- }
- temp1++;
- }
- timer.month=temp1+1; //得到月份
- timer.date=temp+1; //得到日期
- }
- temp=timecount%86400; //得到秒钟数
- timer.hour=temp/3600; //小时
- timer.min=(temp%3600)/60; //分钟
- timer.sec=(temp%3600)%60; //秒钟
- timer.week=RTC_Get_Week(timer.year,timer.month,timer.date);//获取星期
- return 0;
- }
-
-
-
-
- /*!
- \brief 设置时钟以1970年1月1日为基准
- \param[in] 年月日时分秒
- \param[in] none
- \retval 1是大于2099错误,0设置成功
- */
- u8 Rtc_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
- {
- u16 t;
- u32 seccount=0;
- if(syear<1970||syear>2099)return 1;
- for(t=1970;t
//把所有年份的秒钟相加 - {
- if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
- else seccount+=31536000; //平年的秒钟数
- }
- smon-=1;
- for(t=0;t
//把前面月份的秒钟数相加 - {
- seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
- if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
- }
- seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
- seccount+=(u32)hour*3600;//小时秒钟数
- seccount+=(u32)min*60; //分钟秒钟数
- seccount+=sec;//最后的秒钟加上去
-
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
- PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
- RTC_SetCounter(seccount); //设置RTC计数器的值
-
- RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
- return 0;
- }
-
-
- /*!
- \brief RTC初始化
- \param[in] none
- \param[in] none
- \retval 1失败,0成功
- */
- uint8_t Rtc_Init(void)
- {
- /*中断结构体*/
- NVIC_InitTypeDef NVIC_InitStructure;
-
- uint8_t Value = 0;
-
- /*RTC只需要初初始化一次,因为断电后备份寄存器还是会保存,
- 这里的4040可以自己定义,根据上次初始化成功后写入的值判断
- 是否还需要初始化*/
- if (BKP_ReadBackupRegister(BKP_DR1) != 0x4040)
- {
- /*复位备份区域 */
- BKP_DeInit();
- /*设置外部低速晶振(LSE),使用外设低速晶振*/
- RCC_LSEConfig(RCC_LSE_ON);
- /*检查指定的RCC标志位设置与否,等待低速晶振就绪*/
- while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&Value<250)
- {
- /*等待2秒*/
- Value++;
- delay_ms(10);
- }
- if(Value >= 250)
- {
- /*初始化失败,晶振异常返回1*/
- return 1;
- }
- /*设置RTC时钟(RTCCLK),选择LSE作为RTC时钟32.768kHz*/
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
- /*使能RTC时钟*/
- RCC_RTCCLKCmd(ENABLE);
- /*等待最近一次对RTC寄存器的写操作完成*/
- RTC_WaitForLastTask();
- /*等待RTC寄存器同步*/
- RTC_WaitForSynchro();
- /*使能RTC秒中断*/
- RTC_ITConfig(RTC_IT_SEC, ENABLE);
- /*使能RTC秒中断*/
- RTC_ITConfig(RTC_IT_SEC, ENABLE);
- /*等待最近一次对RTC寄存器的写操作完成*/
- RTC_WaitForLastTask();
- /*允许配置*/
- RTC_EnterConfigMode();
- /*设置RTC预分频重装载值
- 32.768Khz/32767+1 = 1Hz*/
- RTC_SetPrescaler(32767);
- /*等待最近一次对RTC寄存器的写操作完成*/
- RTC_WaitForLastTask();
- /*设置时间*/
- Rtc_Set(2022,12,5,21,25,10);
- /*退出配置模式*/
- RTC_ExitConfigMode();
- /*备份寄存器1里写入0x4040*/
- BKP_WriteBackupRegister(BKP_DR1, 0x4040);
- }
- /*不需要重新配置*/
- else
- {
- /*等待最近一次对RTC寄存器的写操作完成*/
- RTC_WaitForSynchro();
- /*使能RTC秒中断*/
- RTC_ITConfig(RTC_IT_SEC, ENABLE);
- /*等待最近一次对RTC寄存器的写操作完成 */
- RTC_WaitForLastTask();
-
- }
- /*中断NVIC配置*/
- NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//先占优先级1位,从优先级3位
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//先占优先级0位,从优先级4位
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能该通道中断
- /*写入结构体*/
- NVIC_Init(&NVIC_InitStructure);
-
- /*初始化*/
- return 0;
-
- }
-
-
- /*!
- \brief RTC时钟中断
- \param[in] none
- \param[in] none
- \retval none
- */
- void RTC_IRQHandler(void)
- {
- if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
- { /*时间写入结构体*/
- Rtc_Get();//更新时间
- /*打印当前时间*/
- printf("当前时间:%d-%d-%d %d:%d:%d\r\n",timer.year,timer.month,timer.date,timer.hour,timer.min,timer.sec);
- }
- /*清1s中断*/
- RTC_ClearITPendingBit(RTC_IT_SEC);
- /*等待最近一次对RTC寄存器的写操作完成*/
- RTC_WaitForLastTask();
- }
-
- /************************************************************** END OF FILE ****/
-
- /**
- ******************************************************************************
- * @file : user_mian.h
- * @brief : V1.00
- ******************************************************************************
- * @attention
- *
- ******************************************************************************
- */
-
- /* Include 包含---------------------------------------------------------------*/
- #include "stm32f10x.h"
- #include
- #include "user_gpio.h"
- #include "user_delay.h"
- #include "user_rcc_config.h"
- #include "user_uart.h"
- #include "user_rtc.h"
-
-
- /* Typedef 类型----------------------------------------------------------------*/
- /* Define 定义----------------------------------------------------------------*/
- /* Macro 宏------------------------------------------------------------------*/
- /* Variables 变量--------------------------------------------------------------*/
- //最多一次接收200个字节
- uint8_t USART_RX_BUF[200];
- //接收状态
- //bit15, 接收完成标志
- //bit14, 接收到0x0d
- //bit13~0, 接收到的有效字节数目
- uint16_t USART_RX_STA=0; //接收状态标记
- /* Constants 常量--------------------------------------------------------------*/
- /* Function 函数--------------------------------------------------------------*/
-
-
- int main(void)
- {
-
- /*配置系统中断分组为2位抢占2位响应*/
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
- /*延时函数初始化*/
- delay_init();
- /*RCC配置*/
- Rcc_config();
- /*GPIO初始化*/
- Gpio_Init();
- /*USART1初始化*/
- Uart1_Init(9600);
- /*初始化RTC*/
- if(Rtc_Init())
- {
- printf("初始化Rtc失败\r\n");
- }
-
- /*死循环*/
- while(1){
- }
-
-
- }
-
- /************************************************************** END OF FILE ****/
