本实验使用到硬件资源如下: (1)D1 指示灯 (2)串口 1 (3)RTC D1 指示灯、串口 1 电路在前面章节都介绍过,这里就不多说,至于 RTC 它属于 STM32F1 芯片内部的资源,只要通过软件配置好即可使用。D1 指示灯用来提示系统运行状态。串口1将读取的 RTC 时间日期信息打印出来。 这里需要注意 RTC 不能断电,否则时间数据将会丢失,如果想让时间在断电后还可以继续走,那么必须确保开发板上的纽扣电池有电。 实验所要实现的功能是:设置 RTC 时间日期初值,在 RTC 秒中断内使用串口打印出RTC 日期和时间,D1指示灯闪烁提示系统运行。程序框架如下: (1)初始化 RTC,设置RTC 时间日期初值 (2)开启 RTC 的秒中断,编写 RTC 中断函数, (3)在 RTC 中断内更新时间并打印输出 (4)编写主函数 前面介绍 RTC 配置步骤时, 就已经讲解如何初始化 RTC。下面我们打开 “RTC 实时时钟实验”工程,在 APP 工程组中添加 rtc.c 文件(里面包含了 RTC 驱动程序),在 StdPeriph_Driver 工程组中添加 stm32f10x_rtc.c、stm32f10x_pwr.c 和 stm32f10x_bkp.c 库文件。RTC操作的库函数都放在 stm32f10x_rtc.c、stm32f10x_pwr.c 和 stm32f10x_bkp.c及对应的头文件中,所以使用到 RTC 就必须加入 stm32f10x_rtc.c、stm32f10x_pwr.c 和 stm32f10x_bkp.c 文件,同时还要包含对应的头文件路径。 这里我们分析几个重要函数,其他部分程序大家可以打开工程查看。 RTC初始化函数 要使用 RTC,我们必须先对它进行配置。初始化代码如下: - /****************************************************************
- * 函 数 名 : RTC_Init
- * 函数功能 : RTC 初始化
- * 输 入 : 无
- * 输 出 : 0,初始化成功
- 1,LSE开启失败
- *****************************************************************/
- u8 RTC_Init(void)
- {
- //检查是不是第一次配置时钟
- u8 temp=0;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,
- ENABLE); //使能 PWR 和BKP外设时钟
- PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
- if (BKP_ReadBackupRegister(BKP_DR1) != 0xA0A0) // 从 指 定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
- {
- BKP_DeInit(); //复位备份区域
- RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
- while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)
- //检查指定的 RCC 标志位设置与否,等待低速晶振就绪
- {
- temp++;
- delay_ms(10);
- }
- if(temp>=250)return 1;//初始化时钟失败,晶振有问题
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置 RTC 时钟(RTCCLK),选择 LSE 作为RTC时钟
- RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
- RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
- RTC_WaitForSynchro(); //等待 RTC 寄存器同步
- RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断
- RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
- RTC_EnterConfigMode();// 允许配置
- RTC_SetPrescaler(32767); //设置 RTC 预分频的值
- RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
- RTC_Set(2017,3,22,17,34,55); //设置时间
- RTC_ExitConfigMode(); //退出配置模式
- BKP_WriteBackupRegister(BKP_DR1, 0XA0A0); //向指定的后备寄存器中写入用户程序数据
- }
- else//系统继续计时
- {
- RTC_WaitForSynchro(); //等待最近一次对 RTC 寄存器的写操作完成
- RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断
- RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
- }
- RTC_NVIC_Config();//RCT 中断分组设置
- RTC_Get();//更新时间
- return 0; //ok
- }
复制代码 在 RTC_Init()函数中,首先使能电源 PWR 和后备域时钟,打开后备寄存器 写访问,因为 RTC 初始化和初值的设置只需执行一次即可,第一次初始化 RTC,我们只需按照前面介绍的RTC配置步骤完成, 在设置 RTC 时间和日期初值时是通过 RTC_Set 函数完成,其实里面还是调用 RTC_SetCounter 完成的,这里单独写这个函数是为了方便设置RTC时间和日期。默认我们将RTC初值日期设置为2017年 3月22 日,初值时间设置为17 点 34 分 55 秒。 设置好时间后我们调用函数 RTC_WriteBackupRegister 向 RTC 的 BKR 寄存器(地址 0)写入标志字 0XA0A0,用于 标 记 时 间 已 经 被 设 置 了 。 这 样 ,再 次 发 生 复 位 的 时 候 , 通 过 调 用 函 数RTC_ReadBackupRegister 判断 RTC 对应 BKR 地址的值,来决定是不是需要重新设置时间,如果不需要设置,则跳过时间设置,这样就不会重复设置时间,使得我们设置的时间不会因复位或者断电而丢失。 写备份域寄存器函数RTC_WriteBackupRegister 原形是: - void RTC_WriteBackupRegister(uint32_t RTC_BKP_DR, uint32_t Data);
复制代码 参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。 读取备份域寄存器函数RTC_ReadBackupRegister 原形是: - uint32_t RTC_ReadBackupRegister(uint32_t RTC_BKP_DR);
复制代码 参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。 最后我们的 RTC 初始化函数 RTC_Init 带有一个返回值,如果返回值为 1 表示 RTC初始化失败,否则成功。 RTC设置日期时间函数 在RTC 初始化函数内调用了一个 RTC_Set 函数设置日期和时间, 具体代码如下 - /****************************************************************
- * 函 数 名 : RTC_Set
- * 函数功能 : RTC 设置日期时间函数(以 1970 年 1 月 1 日为基准,把输入的时钟转换为秒钟)1970~2099 年为合法年份
- * 输 入 : syear:年 smon:月 sday:日hour:时 min:分 sec:秒
- * 输 出 : 0,成功1,失败
- *****************************************************************/
- 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<syear;t++) //把所有年份的秒钟相加
- {
- if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
- else seccount+=31536000; //平年的秒钟数
- }
- smon-=1;
- for(t=0;t<smon;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;
- }
复制代码 该函数用于设置日期时间,把我们输入的时间,转换为以 1970 年 1 月 1 日 0 时 0 分 0 秒当做起始时间的秒钟信号,后续的计算都以这个时间为基准 的,由于 STM32 的秒钟计数器可以保存 136 年的秒钟数据,这样我们可以计时到 2106 年。 Rtc.c 文件内还有一个闹钟设置函数 RTC_Alarm_Set,此函数与设置时间函数完全一样,只是函数名不同而已。 |