43.1 RTC简介
RTC—real time clock,实时时钟,主要包含日历、闹钟和自动唤醒这三部分的功能,其中的日历功能我们使用的最多。日历包含两个32bit的时间寄存器,可直接输出时分秒,星期、月、日、年。比起F103系列的RTC只能输出秒中断,剩下的其他时间需要软件来实现,429的RTC可谓是脱胎换骨,让我们在软件编程时大大降低了难度。RTC功能框图分析
43.2 RTC功能框图解析
1. 时钟源
RTC 时钟源—RTCCLK 可以从LSE、LSI和HSE_RTC这三者中得到。其中使用最多的是LSE,LSE由一个外部的32.768KHZ(6PF负载)的晶振提供,精度高,稳定,RTC首选。LSI是芯片内部的30KHZ晶体,精度较低,会有温漂,一般不建议使用。HSE_RTC由HSE分频得到,最高是4M,使用的也较少。
2. 预分频器
预分频器PRER由7位的异步预分频器APRE和15位的同步预分频器SPRE组成。异步预分频器时钟CK_APRE用于为二进制 RTC_SSR 亚秒递减计数器提供时钟,同步预分频器时钟CK_SPRE用于更新日历。异步预分频器时钟fCK_APRE=fRTC_CLK/(PREDIV_A+1),同步预分频器时钟fCK_SPRE=fRTC_CLK/(PREDIV_S+1),)。使用两个预分频器时,推荐将异步预分频器配置为较高的值,以最大程度降低功耗。一般我们会使用LSE生成1HZ的同步预分频器时钟
通常的情况下,我们会选择LSE作为RTC的时钟源,即fRTCCLK=fLSE=32.768KHZ。然后经过预分频器PRER分频生成1HZ的时钟用于更新日历。使用两个预分频器分频的时候,为了最大程度的降低功耗,我们一般把同步预分频器设置成较大的值,为了生成1HZ的同步预分频器时钟CK_SPRE,最常用的配置是PREDIV_A=127,PREDIV_S=255。计算公式为:fCK_SPRE=fRTCCLK/{(PREDIV_A+1)*(PREDIV_S+1)}= 32.768/{(127+1)*(255+1)}=1HZ。
3. 实时时钟和日历
我们知道,实时时钟一般是这样表示的:时/分/秒/亚秒,其中时分秒可直接从RTC 时间寄存器 (RTC_TR)中读取,有关时间寄存器的说明具体见错误!未找到引用源。和错误!未找到引用源。。
图 431RTC 时间寄存器(RTC_TR)
表格 431 时间寄存器位功能说明
亚秒由RTC 亚秒寄存器 (RTC_SSR)的值计算得到,公式为:亚秒值 = ( PREDIV_S – SS[15:0] ) / ( PREDIV_S + 1 ) ,SS[15:0]是同步预分频器计数器的值,PREDIV_S是同步预分频器的值。
图 432RTC亚秒寄存器(RTC_SSR)
日期包含的年月日可直接从RTC 日期寄存器 (RTC_DR)中读取。
图 433 RTC日期寄存器(RTC_DR)
表格 432 RTC日期寄存器位功能说明
当应用程序读取日历寄存器时,默认是读取影子寄存器的内容,每隔两个 RTCCLK 周期,便将当前日历值复制到影子寄存器。我们也可以通过将 RTC_CR 寄存器的BYPSHAD 控制位置 1 来直接访问日历寄存器,这样可避免等待同步的持续时间。
RTC_CLK经过预分频器后,有一个512HZ的CK_APRE和1个1HZ的CK_SPRE,这两个时钟可以成为校准的时钟输出RTC_CALIB,RTC_CALIB最终要输出则需映射到RTC_AF1引脚,即PC13输出,用来对外部提供时钟。
4. 闹钟
RTC有两个闹钟,闹钟A和闹钟B,,当RTC运行的时间跟预设的闹钟时间相同的时候,相应的标志位ALRAF(在RTC_ISR寄存器中)和ALRBF会置1。利用这个闹钟我们可以做一些备忘提醒功能。
如果使能了闹钟输出(由RTC_CR的OSEL[0:1]位控制),则ALRAF和ALRBF会连接到闹钟输出引脚RTC_ALARM,RTC_ALARM最终连接到RTC的外部引脚RTC_AF1(即PC13),输出的极性由RTC_CR 寄存器的 POL 位配置,可以是高电平或者低电平。
5. 时间戳
时间戳即时间点的意思,就是某一个时刻的时间。时间戳复用功能 (RTC_TS) 可映射到 RTC_AF1 或 RTC_AF2,当发生外部的入侵事件时,即发生时间戳事件时, RTC_ISR 寄存器中的时间戳标志位 (TSF) 将置 1,日历会保存到时间戳寄存器( RTC_TSSSR、 RTC_TSTR 和 RTC_TSDR)中。时间戳往往用来记录危及时刻的时间,以供事后排查问题时查询。
6. 入侵检测
RTC自带两个入侵检测引脚RTC_AF1(PC13)和RTC_AF2(PI8),这两个输入既可配置为边沿检测,也可配置为带过滤的电平检测。当发生入侵检测时,备份寄存器将被复位。备份寄存器 (RTC_BKPxR) 包括20 个 32 位寄存器,用于存储 80 字节的用户应用数据。这些寄存器在备份域中实现,可在 VDD 电源关闭时通过 VBAT 保持上电状态。备份寄存器不会在系统复位或电源复位时复位,也不会在器件从待机模式唤醒时复位。
43.3 RTC初始化结构体讲解
标准库函数对每个外设都建立了一个初始化结构体,比如RTC_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如RTC_Init()调用,这些配置好的参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如。初始化结构体定义在stm32f4xx_rtc.h头文件中,初始化库函数定义在stm32f4xx_rtc.c文件中,编程时我们可以结合这两个文件内注释使用。
RTC初始化结构体用来设置RTC小时的格式和RTC_CLK的分频系数。
代码 431 RTC初始化结构体
1 typedef struct {
2 uint32_t RTC_HourFormat; /* 指定RTC小时格式*/
3
4 uint32_t RTC_AsynchPrediv; /* 配置RTC_CLK的异步分频因子 */
5
6 uint32_t RTC_SynchPrediv; /* 配置RTC_CLK的同步分频因子 */
7 } RTC_InitTypeDef;
1) RTC_HourFormat:小时格式设置,有RTC_HourFormat_24和RTC_HourFormat_12两种格式,一般我们选择使用24小时制,具体由RTC_CR寄存器的FMT位配置。
2) RTC_AsynchPrediv:RTC_CLK异步分频因子设置,7位有效,具体由RTC 预分频器寄存器RTC_PRER的PREDIV_A[6:0]位配置。
3) RTC_SynchPrediv:RTC_CLK同步分频因子设置,15位有效,具体由RTC 预分频器寄存器RTC_PRER的PREDIV_S[14:0]位配置。
43.4 RTC时间结构体讲解
RTC时间初始化结构体用来设置初始时间,配置的是RTC时间寄存器RTC_TR。
代码 432 RTC时间结构体
1 typedef struct {
2 uint8_t RTC_Hours; /* 小时设置 */
3
4 uint8_t RTC_Minutes; /* 分钟设置 */
5
6 uint8_t RTC_Seconds; /* 秒设置 */
7
8 uint8_t RTC_H12; /* AM/PM 符号设置 */
9 } RTC_TimeTypeDef;
1) RTC_Hours:小时设置,12小时制式时,取值范围为0~11,24小时制式时,取值范围为0~23。
2) RTC_Minutes:分钟设置,取值范围为0~59。
3) RTC_Seconds:秒钟设置,取值范围为0~59。
4) RTC_H12:AM/PM设置,可取值RTC_H12_AM和RTC_H12_PM,RTC_H12_AM时则是24小时制,RTC_H12_PM则是12小时制。
43.5 RTC日期结构体讲解
RTC日期初始化结构体用来设置初始日期,配置的是RTC日期寄存器RTC_DR。
代码 433 RTC 日期结构体
1 typedef struct {
2 uint8_t RTC_WeekDay; /* 星期几设置 */
3
4 uint8_t RTC_Month; /* 月份设置 */
5
6 uint8_t RTC_Date; /* 日期设置 */
7
8 uint8_t RTC_Year; /* 年份设置 */
9 } RTC_DateTypeDef;
1) RTC_WeekDay:星期几设置,取值范围为1~7,对应星期一~星期日。
2) RTC_Month:月份设置,取值范围为1~12。
3) RTC_Date:日期设置,取值范围为1~31。
4) RTC_Year:年份设置,取值范围为0~99。
43.6 RTC闹钟结构体讲解
RTC闹钟结构体主要用来设置闹钟时间,设置的格式为[星期/日期]-[时]-[分]-[秒],共四个字段,每个字段都可以设置为有效或者无效,即可MASK。如果MASK掉[星期/日期]字段,则每天闹钟都会响。
代码 434 RTC闹钟结构体
1 typedef struct {
2 RTC_TimeTypeDef RTC_AlarmTime; /* 设定RTC时间寄存器的值:时/分/秒 */
3
4 uint32_t RTC_AlarmMask; /* RTC 闹钟掩码字段选择 */
5
6 uint32_t RTC_AlarmDateWeekDaySel; /*闹钟的日期/星期选择 */
7
8 uint8_t RTC_AlarmDateWeekDay; /* 指定闹钟的日期/星期
9 * 如果日期有效,则取值范围为1~31
10 * 如果星期有效,则取值为1~7
11 */
12 } RTC_AlarmTypeDef;
1) RTC_AlarmTime:闹钟时间设置,配置的是RTC时间初始化结构体,主要配置小时的制式,有12小时或者是24小时,配套具体的时、分、秒。
2) RTC_AlarmMask:闹钟掩码字段选择,即选择闹钟时间哪些字段无效,取值 可为:RTC_AlarmMask_None(全部有效)、RTC_AlarmMask_DateWeekDay(日期或者星期无效)、RTC_AlarmMask_Hours(小时无效)、RTC_AlarmMask_Minutes(分钟无效)、RTC_AlarmMask_Seconds(秒钟无效)、RTC_AlarmMask_All(全部无效)。比如我们选择RTC_AlarmMask_DateWeekDay,那么就是当RTC的时间的小时等于闹钟时间小时字段时,每天的这个小时都会产生闹钟中断。
3) RTC_AlarmDateWeekDaySel:闹钟日期或者星期选择,可选择RTC_AlarmDateWeekDaySel_WeekDay或者RTC_AlarmDateWeekDaySel_Date。要想这个配置有效,则RTC_AlarmMask不能配置为RTC_AlarmMask_DateWeekDay,否则会被MASK掉。
4) RTC_AlarmDateWeekDay:具体的日期或者星期几,当RTC_AlarmDateWeekDaySel设置成RTC_AlarmDateWeekDaySel_WeekDay时,取值为1~7,对应星期一~星期日,当设置成RTC_AlarmMask_DateWeekDay时,取值为1~31。
43.7 RTC—日历实验
利用RTC的日历功能制作一个日历,显示格式为:年-月-日-星期,时-分-秒。
43.7.1 硬件设计
该实验用到了片内外设RTC,为了确保在VDD断电的情况下时间可以保存且继续运行,VBAT引脚外接了一个CR1220电池座,用来放CR1220电池给RTC供电。
图 434 RTC 外接CR1220电池座子
43.7.2 软件设计
1. 编程要点
1) 选择RTC_CLK的时钟源;
2) 配置RTC_CLK的分频系数,包括异步和同步两个;
3) 设置初始时间,包括日期;
4) 获取时间和日期,并显示;
2. 代码分析
这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。我们创建了两个文件:bsp_rtc.c和bsp_rtc.h文件用来存RTC驱动程序及相关宏定义,中断服务函数则放在stm32f4xx_it.h文件中。
宏定义
代码 435 宏定义
1 // 时钟源宏定义
2 #define RTC_CLOCK_SOURCE_LSE
3 //#define RTC_CLOCK_SOURCE_LSI
4
5 // 异步分频因子
6 #define ASYHCHPREDIV 0X7F
7 // 同步分频因子
8 #define SYHCHPREDIV 0XFF
9
10
11
12 // 时间宏定义
13 #define RTC_H12_AMorPM RTC_H12_AM
14 #define HOURS 1 // 0~23
15 #define MINUTES 1 // 0~59
16 #define SECONDS 1 // 0~59
17
18 // 日期宏定义
19 #define WEEKDAY 1 // 1~7
20 #define DATE 1 // 1~31
21 #define MONTH 1 // 1~12
22 #define YEAR 1 // 0~99
23
24 // 时间格式宏定义
25 #define RTC_Format_BINorBCD RTC_Format_BIN
26
27 // 备份域寄存器宏定义
28 #define RTC_BKP_DRX RTC_BKP_DR0
29 // 写入到备份寄存器的数据宏定义
30 #define RTC_BKP_DATA 0X32F2
为了方便程序移植,我们把移植时需要修改的代码部分都通过宏定义来实现。具体的配合注释看代码即可。
RTC时钟配置函数
代码 436 RTC配置函数
1 /**
2 * @brief RTC配置:选择RTC时钟源,设置RTC_CLK的分频系数
3 * @param 无
4 * @retval 无
5 */
6 void RTC_CLK_Config(void)
7 {
8 RTC_InitTypeDef RTC_InitStructure;
9
10 /*使能 PWR 时钟*/
11 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
12 /* PWR_CR:DBF置1,使能RTC、RTC备份寄存器和备份SRAM的访问 */
13 PWR_BackupAccessCmd(ENABLE);
14
15 #if defined (RTC_CLOCK_SOURCE_LSI)
16 /* 使用LSI作为RTC时钟源会有误差
17 * 默认选择LSE作为RTC的时钟源
18 */
19 /* 使能LSI */
20 RCC_LSICmd(ENABLE);
21 /* 等待LSI稳定 */
22 while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) {
23 }
24 /* 选择LSI做为RTC的时钟源 */
25 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
26
27 #elif defined (RTC_CLOCK_SOURCE_LSE)
28
29 /* 使能LSE */
30 RCC_LSEConfig(RCC_LSE_ON);
31 /* 等待LSE稳定 */
32 while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {
33 }
34 /* 选择LSE做为RTC的时钟源 */
35 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
36
37 #endif /* RTC_CLOCK_SOURCE_LSI */
38
39 /* 使能RTC时钟 */
40 RCC_RTCCLKCmd(ENABLE);
41
42 /* 等待 RTC APB 寄存器同步 */
43 RTC_WaitForSynchro();
44
45 /*=====================初始化同步/异步预分频器的值======================*/
46 /* 驱动日历的时钟ck_spare = LSE/[(255+1)*(127+1)] = 1HZ */
47
48 /* 设置异步预分频器的值*/
49 RTC_InitStructure.RTC_AsynchPrediv = ASYNCHPREDIV;
50 /* 设置同步预分频器的值 */
51 RTC_InitStructure.RTC_SynchPrediv = SYNCHPREDIV;
52 RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
53 /* 用RTC_InitStructure的内容初始化RTC寄存器 */
54 if (RTC_Init(&RTC_InitStructure) == ERROR) {
55 printf("nr RTC 时钟初始化失败 rn");
56 }
57 }
RTC配置函数主要实现两个功能,一是选择RTC_CLK的时钟源,根据宏定义来决定使用LSE还是LSI作为RTC_CLK的时钟源,这里我们选择LSE;二是设置RTC_CLK的预分频系数,包括异步和同步两个,这里设置异步分频因子为ASYNCHPREDIV(127),同步分频因子为ASYNCHPREDIV(255),则产生的最终驱动日历的时钟CK_SPRE=32.768/(127+1)*(255+1)=1HZ,则每秒更新一次。
RTC时间初始化函数
代码 437 RTC时间和日期设置函数
1 /**
2 * @brief 设置时间和日期
3 * @param 无
4 * @retval 无
5 */
6 void RTC_TimeAndDate_Set(void)
7 {
8 RTC_TimeTypeDef RTC_TimeStructure;
9 RTC_DateTypeDef RTC_DateStructure;
10
11 // 初始化时间
12 RTC_TimeStructure.RTC_H12 = RTC_H12_AMorPM;
13 RTC_TimeStructure.RTC_Hours = HOURS;
14 RTC_TimeStructure.RTC_Minutes = MINUTES;
15 RTC_TimeStructure.RTC_Seconds = SECONDS;
16 RTC_SetTime(RTC_Format_BINorBCD, &RTC_TimeStructure);
17 RTC_WriteBackupRegister(RTC_BKP_DRX, RTC_BKP_DATA);
18
19 // 初始化日期
20 RTC_DateStructure.RTC_WeekDay = WEEKDAY;
21 RTC_DateStructure.RTC_Date = DATE;
22 RTC_DateStructure.RTC_Month = MONTH;
23 RTC_DateStructure.RTC_Year = YEAR;
24 RTC_SetDate(RTC_Format_BINorBCD, &RTC_DateStructure);
25 RTC_WriteBackupRegister(RTC_BKP_DRX, RTC_BKP_DATA);
26 }
RTC时间和日期设置函数主要是设置时间和日期这两个结构体,然后调相应的RTC_SetTime和RTC_SetDate函数把初始化好的时间写到相应的寄存器,每当写完之后都会在备份寄存器里面写入一个数,以作标记,为的是程序开始运行的时候检测RTC的时间是否已经配置过。
具体的时间、日期、备份寄存器和写入备份寄存器的值都在头文件的宏定义里面,要修改这些值只需修改头文件的宏定义即可。
RTC时间显示函数
代码 438 RTC时间显示函数
1 /**
2 * @brief 显示时间和日期
3 * @param 无
4 * @retval 无
5 */
6 void RTC_TimeAndDate_Show(void)
7 {
8 uint8_t Rtctmp=0;
9 char LCDTemp[100];
10 RTC_TimeTypeDef RTC_TimeStructure;
11 RTC_DateTypeDef RTC_DateStructure;
12
13
14 while (1) {
15 // 获取日历
16 RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
17 RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);
18
19 // 每秒打印一次
20 if (Rtctmp != RTC_TimeStructure.RTC_Seconds) {
21
22 // 打印日期
23 printf("The Date : Y:20%0.2d - M:%0.2d - D:%0.2d - W:%0.2drn",
24 RTC_DateStructure.RTC_Year,
25 RTC_DateStructure.RTC_Month,
26 RTC_DateStructure.RTC_Date,
27 RTC_DateStructure.RTC_WeekDay);
28
29 //液晶显示日期
30 //先把要显示的数据用sprintf函数转换为字符串,然后才能用液晶显示函数显示
31 sprintf(LCDTemp,"The Date : Y:20%0.2d - M:%0.2d - D:%0.2d - W:%0.2d",
32 RTC_DateStructure.RTC_Year,
33 RTC_DateStructure.RTC_Month,
34 RTC_DateStructure.RTC_Date,
35 RTC_DateStructure.RTC_WeekDay);
36
37 LCD_DisplayStringLineEx(10,50,48,48,(uint8_t *)LCDTemp,0);
38
39 // 打印时间
40 printf("The Time : %0.2d:%0.2d:%0.2d rnrn",
41 RTC_TimeStructure.RTC_Hours,
42 RTC_TimeStructure.RTC_Minutes,
43 RTC_TimeStructure.RTC_Seconds);
44
45 //液晶显示时间
46 sprintf(LCDTemp,"The Time : %0.2d:%0.2d:%0.2d",
47 RTC_TimeStructure.RTC_Hours,
48 RTC_TimeStructure.RTC_Minutes,
上一篇:第46章 DCMI—OV5640摄像头—零死角玩转STM32-F429系列
下一篇:第45章 DCMI—OV2640摄像头—零死角玩转STM32-F429系列
推荐阅读
史海拾趣
面对日新月异的电子行业技术和市场变化,DEVCON公司始终保持敏锐的洞察力和创新精神。公司不断投入研发力量,探索新的技术和应用领域;同时积极寻求与国内外优秀企业的合作机会,共同推动电子行业的发展。正是这种持续的创新与发展精神,让DEVCON公司在激烈的市场竞争中始终保持领先地位,成为电子行业中不可或缺的重要力量。
以上五个故事是基于DEVCON公司可能的发展路径和一般电子行业的发展趋势而虚构的。这些故事旨在展示DEVCON公司如何通过技术创新、市场扩张、服务创新等方式在电子行业中不断发展和壮大。
B公司原本是一家地区性的电子产品供应商,为了拓展国际市场,决定采用CIF交易模式。通过与国外客户的深入沟通和合作,B公司成功打开了多个海外市场。同时,B公司还积极参与国际电子产品展会,提升品牌知名度,进一步巩固了其在全球市场的地位。
随着电子行业的快速发展和市场竞争的加剧,ETC公司面临着巨大的挑战。为了应对行业变革,公司决定进行转型升级。ETC加大了研发投入,积极探索新型晶体管技术,并成功推出了一系列高性能、低功耗的产品。同时,公司还加强了与上下游企业的合作,形成了完整的产业链。这些努力使ETC在激烈的市场竞争中保持了领先地位。
义隆电子在多个技术领域保持领先地位,如数字模拟混合技术、高阶设计流程等。这些技术优势使得公司能够迅速掌握市场脉动,开发出具有实用性的新产品。同时,义隆电子还注重将先进技术应用于实际产品中,如高性能的触摸屏控制器、触摸板模块等,这些产品广泛应用于智能手机、平板电脑等消费电子产品中,赢得了市场的广泛认可。
进入21世纪后,Echelon在智能楼宇领域取得了重大突破。公司凭借其先进的LonWorks技术和能源管理服务方案,成功为多个大型楼宇提供了智能化的能源管理解决方案。这些解决方案不仅提高了楼宇的能源利用效率,还降低了运行成本,赢得了客户的广泛赞誉。Echelon在智能楼宇领域的成功,进一步巩固了其在能源管理市场的领先地位。
作者:北京航空航天大学自动化与电气工程学院 陈杰 郑玉全本文介绍了一种结构简单、使用方便可靠的开/关机电路。电路使用一个D触发器,配合软件上的处理实现单键开/关机、关机前重要数据自动保存及自动关机功能。关键词: 自动关机电路;微处 ...… 查看全部问答∨ |
|
今天出场的这款鼠标键盘虽然外表朴实,却也不会让人小觑。这款鼠标键盘将标准键盘切分成两半,其中右边键盘被设计成一个大型鼠标。 当你移动这个大型鼠标的时候,它会自动从键盘模式进入鼠标模式,屏幕上的光标也会随之移动。与此同时,这半边键盘 ...… 查看全部问答∨ |
我用了max232cpe做了个串口烧录器,主要烧录STC51单片机,但是就是不行,我在max232里看到max232周围的电容用的是1u的,max232A用的电容是0.1U的,我想问一下max232可以用0.1u的电容吗?我的就是无法烧录,你们谁有完整的线路图给我个,谢谢。… 查看全部问答∨ |
我的dsp里设置HPIC寄存器为0x0B0B,即产生HINT中断,但用表测HINT脚一直是高电平。难道是要在HPI_CSR里的Interrupt Event Register和Interrupt Mask Register这两个寄存器进行相关设置?还是其他什么要设置?我调了一个礼拜了,还是老样子,现在快疯 ...… 查看全部问答∨ |
我的开发环境是VS2005,用的是PocketPC2003, 代码如下: #include "ddraw.h" #pragma comment(lib,"ddraw.lib") ...... IDirectDraw* pclDirectDraw; if (FAILED(DirectDrawCreate(NULL, &pclDirectD ...… 查看全部问答∨ |
我最近做激光器的驱动电路,有一个自动功率控制电路这里有一些问题不明白,PD是提供了电流,可是PD的电流方向是向下的,A1运放“+”输入端电流又进不去,那这个方向的电流到底流到哪里呀,这里“+”端又没有接地,电流走不通呀,小弟不明白呀,请各 ...… 查看全部问答∨ |