基于STM32-RTC实时时钟
STM32系列MCU硬件实时时钟(RTC)应用笔记说明书
2017年6月Doc ID 018624 Rev 1 [English Rev 5]1/45AN3371应用笔记在 STM32 F0、F2、F3、F4 和 L1 系列MCU 中使用硬件实时时钟(RTC )前言实时时钟 (RTC) 是记录当前时间的计算机时钟。
RTC 不仅应用于个人计算机、服务器和嵌入式系统,几乎所有需要准确计时的电子设备也都会使用。
支持 RTC 的微控制器可用于精密计时器、闹钟、手表、小型电子记事薄以及其它多种设备。
本应用笔记介绍超低功耗中等容量、超低功耗大容量、F0、F2和 F4 系列器件微控制器中嵌入式实时时钟 (RTC) 控制器的特性,以及将 RTC 用于日历、闹钟、定时唤醒单元、入侵检测、时间戳和校准应用时所需的配置步骤。
本应用笔记提供了含有配置信息的示例,有助于您快速准确地针对日历、闹钟、定时唤醒单元、入侵检测、时间戳和校准应用配置 RTC 。
注:所有示例和说明均基于 STM32L1xx 、STM32F0xx 、STM32F2xx 、STM32F4xx 和STM32F3xx 固件库,以及 STM32L1xx (RM0038)、STM32F0xx (RM0091)、STM32F2xx (RM0033)、STM32F4xx (RM0090)、STM32F37x (RM0313) 和 STM32F30x(RM0316) 的参考手册。
本文提到的STM32 指超低功耗中等容量、超低功耗大容量、F0、F2 和 F4 系列器件。
超低功耗中等 (ULPM) 容量器件包括 STM32L151xx 和 STM32L152xx 微控制器,Flash 容量在 64 KB 到 128 KB 之间。
超低功耗大 (ULPH) 容量器件包括 STM32L151xx 、STM32L152xx 和 STM32L162xx 微控制器,Flash 容量为 384 KB 。
F2 系列器件包括 STM32F205xx 、STM32F207xx 、STM32F215xx 和 STM32F217xx 微控制器。
STM32-RTC实时时钟-毫秒计时实现
STM32-RTC实时时钟-毫秒计时实现OS:Windows 64Development kit:MDK5.14IDE:UV4MCU:STM32F103C8T61、RTC时钟简介 STM32 的实时时钟(RTC)是⼀个独⽴的定时器,在相应软件配置下,可提供时钟⽇历的功能。
详细资料请参考ALIENTEK的官⽅⽂档——《STM32F1开发指南(精英版-库函数版)》,以下为博主摘录要点:RTC 模块和时钟配置系统(RCC_BDCR 寄存器)在后备区域,系统复位后,会⾃动禁⽌访问后备寄存器和 RTC ,所以在要设置时间之前,先要取消备份区域(BKP)的写保护RTC 内核完全独⽴于 RTC APB1 接⼝,⽽软件是通过 APB1 接⼝访问 RTC 的预分频值、计数器值和闹钟值,因此需要等待时钟同步,寄存器同步标志位(RSF)会硬件置1RTC相关寄存器包括:控制寄存器(CRH、CRL)、预分频装载寄存器(PRLH、PRLL)、预分频器余数寄存器(DIVH、DIVL)、计数寄存器(CNTH、CNTL)、闹钟寄存器(ALRH、ALRL)STM32备份寄存器,存RTC校验值和⼀些重要参数,最⼤字节84,可由VBAT供电计数器时钟频率:RTCCLK频率/(预分频装载寄存器值+1)2、软硬件设计 由于RTC是STM32芯⽚⾃带的时钟资源,所以⾃主开发的时候只需要在设计时加上晶振电路和纽扣电池即可。
编程时在HARDWARE⽂件夹新建 rtc.c、rtc.h ⽂件。
3、时钟配置与函数编写 为了使⽤RTC时钟,需要进⾏配置和时间获取,基本上按照例程来写就可以了。
为避免零散,我将附上完整代码。
函数说明如下:rtc.c中需要编写的函数列表RTC_Init(void)配置时钟RTC_NVIC_Config(void)中断分组RTC_IRQHandler(void)秒中断处理RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)设置时间RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8sec)闹钟设置RTC_Get(void)获取时钟RTC_Get_Week(u16 year,u8 month,u8 day)星期计算Is_Leap_Year(u16 year)闰年判断 事实上,以上函数并不都要,闹钟没有⽤到的话就不要,秒中断也可以不作处理,看项⽬需求。
stm32 rtc实时时钟
stm32 rtc实时时钟STM32 RTC实时时钟一、介绍STM32是意法半导体公司(STMicroelectronics)推出的一系列32位ARM Cortex-M微控制器。
其中,RTC(Real-Time Clock)是STM32微控制器中的一个重要组件,用于实时时钟和日历功能。
本文将详细介绍STM32 RTC的实时时钟功能及其应用。
二、RTC概述RTC模块是一种独立的硬件模块,可以在微控制器断电时继续运行。
它提供了一个与时间和日期相关的计数器,通过时钟信号源来驱动计数器,从而实现实时时钟的功能。
RTC模块通常由一个独立的低功耗振荡器来提供时钟源。
STM32微控制器中的RTC模块支持多种工作模式,如年历模式、单位数字模式和二进制模式等。
三、RTC的主要功能1. 实时时钟:RTC模块可以提供精确的实时时钟,可以记录时间、日期和星期等信息。
2. 闹钟功能:RTC可以设置多个闹钟时间,并在闹钟时间到达时触发中断或其他操作。
3. 倒计时功能:RTC模块可以进行倒计时操作,并在倒计时结束时触发中断。
4. 调度功能:RTC可以设置预定的时间点,并在该时间点触发中断。
5. 报警功能:RTC可以设置报警功能,当发生特定事件时触发中断或其他操作。
四、配置RTC模块在使用STM32微控制器的RTC功能之前,需要进行一些配置。
首先,需要选择合适的时钟源。
通常,RTC模块使用低功耗振荡器作为时钟源。
其次,需要配置RTC的预分频器和计数器,以实现所需的时间精度。
还需配置中断和/或事件触发条件,以便在特定事件发生时触发中断或其他操作。
五、RTC的中断与事件RTC模块可以生成多个中断和事件,以满足应用的需求。
常见的中断和事件有:1. 秒中断:每当计数器的秒字段更新时触发中断。
2. 分钟中断:每当计数器的分钟字段更新时触发中断。
3. 小时中断:每当计数器的小时字段更新时触发中断。
4. 日期中断:每当计数器的日期字段更新时触发中断。
STM32编程RTC的时钟(时间无限制)
要想知道某年某月某日是星期几,首先,要知道这一年元旦以公元元年元旦是星期一为起点,有时候,想知道公元某年某月某日是星期几,可以用下面的公式算出来:这里的方括号表示只取商的整数部分。
式中:x:这一年是公元多少年。
y:这一天是这一年的第几天。
s:星期几。
不过要先除以7,再取余数。
没有余数是星期日,余数是1、2、3、4、5、6,分别是星期一、星期二、星期三、星期四、星期五、星期六。
比如,今年国庆节(2010年10月1日)是星期几?x=2010。
y=31+28+31+30+31+30+31+31+30+1=31×5+30×3+28+1=274。
s=2010-1+502-20+5+274=2770,2770÷7余5。
所以,今年国庆节是星期五。
如果,你只想知道这个公式怎样用,到这儿就可以了。
而要想知道这个公式的道理是什么,那可就说来话长了。
“星期制”是公元321年3月7日,古罗马皇帝君士坦丁宣布开始实行的,并且规定这一天为星期一。
实际上,就是把公元元年元旦(公元1年1月1日)规定为星期一。
(相当于公式中的x=1,y=1,所以s=1。
)通常1年有365天,365÷7=52……1,就是说比52个星期多1天。
所以,同一个日期,下一年是星期几,就要比上一年向后推1天。
比如,上一年元旦是星期三,下一年元旦就是星期四。
“通常,每过1年,同一日期是星期几就要向后推1天”,是理解这个公式的关键。
要想知道某年某月某日是星期几,首先,要知道这一年元旦以公元元年元旦是星期一为起点,已经把星期几向后推了多少天,还要知道这一天是这一年的第几天。
而要知道这一年元旦已经把星期几向后推了多少天,可以从公元元年到这一年已经过了多少年算起,先按1年向后推1天计算,再根据闰年的规定进行调整。
闰年的规定是:年份是4的倍数的一般都是闰年,其中,年份是整百数的一般不是闰年,只有年份是400的倍数的才是闰年。
现在,可以解释公式中各部分的含义了。
STM32L4实时时钟模块(RTC)介绍
64
• Alternate function inputs:
• RTC_TAMP1: tamper1 event detection. • RTC_TAMP2: tamper2 event detection. • RTC_TAMP3: tamper3 event detection. • RTC_TS: timestamp event detection. • RTC_REFIN: reference clock input.
Presentation Title 02/07/2015
RTC Features (2/2)
• Alternate function outputs:
• RTC_CALIB: 512 Hz or 1Hz clock output (with an LSE frequency of 32.768 kHz). It is routed to the device RTC_OUT output. • RTC_ALARM: Alarm A, B flag output. It is routed to the device RTC_OUT output.
No VBAT
YES
3 pins/ 3 events
Edge or Level Detection with Configurable filtering External interrupt and NO trigger with filtering 32-bit Backup registers 20 32
• The RTC remains active what ever the low power mode
• Sleep, Stop 1 & 2, Standby, Shutdown
STM32实时时钟RTC按键修改时间
uip_setnetmask(ipaddr);
uip_listen(HTONS(1200));//监听1200端口,用于TCP Server
uip_listen(HTONS(80));//监听80端口,用于Web Server
case 1:LCD_ShowString(1,7,"一");break;
case 2:LCD_ShowString(1,7,"二");break;
case 3:LCD_ShowString(1,7,"三");break;
case 4:LCD_ShowString(1,7,"四");break;
case 5:LCD_ShowString(1,7,"五");break;
{
uip_arp_arpin();
//当上面的函数执行后,如果需要发送数据,则全局变量uip_len>0
//需要发送的数据在uip_buf,长度是uip_len(这是2个全局变量)
if(uip_len>0)tapdev_send();//需要发送数据,则通过tapdev_send发送
}
}else if(timer_expired(&periodic_timer))//0.5秒定时器超时
tcp_client_reconnect();//尝试连接到TCP Server端,用于TCP Client
while (1)
{ Display_Time();
uip_polling();//处理uip事件,必须插入到用户程序的循环体中
//key=KEY_Scan();
基于STM32的RTC实时时钟说明书
RTC实时时钟说明书
一:工作原理
STM32芯片中内置RTC时钟,当对RTC进行初始化后,便可以像电子钟一样运行,通过读取RTC对应的寄存器,可以获知年月日、时分秒信息。
有关RTC的说明可参考《STM32中文参考资料》。
二:实验现象及操作
程序下载后,有以下两种情况。
●如果芯片第一次初始化RTC,则程序将写入HEX生成的时间信息到芯片RTC时钟中,
然后读出时钟信息。
通过数码管显示。
●如果不是第一次初始化RTC,则程序将读取RTC对应寄存器的值,以时间信息显示
在数码管上。
如果开发板上有放入3V的小电池,则即使掉电,RTC时钟依然在走。
程序刚下载,数码管显示的数据为时-分-秒。
K1键,则显示年月日,左边四位为年份,最右边两位为日期。
其它两位为月份。
K2键,数码管继续显示时-分-秒。
STM32单片机RTC时钟的使用方法及步骤
STM32单片机RTC时钟的使用方法及步骤
STM32RTC使用步骤:
打开PWR时钟和Backup区数据访问
若使用外部低速时钟(LSE),打开LSE并等待起振
选择和打开RTC时钟,等待时钟同步
配置时间格式,分频系数等
根据需要配置时钟,日期,闹钟,唤醒,输出,时间戳,备份寄存器等模块
根据需要配置和打开中断,其中
RTC Alarm ——EXTI line 17
RTC tamper and TImestamps——EXTI line 19
RTC wakeup——EXTI line 20
下面的代码配置日期,时间,当前时间设置为15年05月31日,星期日(7),15:50:40,打开闹钟A和唤醒中断,每一秒钟来一次中断,15:50:45秒产生闹钟中断,用串口打印相应的信息。
代码:
void RTC_Config(void)
{
RTC_TImeTypeDef RTC_TimeStructure;
RTC_DateTypeDef RTC_DateStructure;
RTC_InitTypeDef RTC_InitStructure;
RTC_AlarmTypeDef RTC_AlarmStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_BackupResetCmd(ENABLE);
RCC_BackupResetCmd(DISABLE);
RCC_LSEConfig(RCC_LSE_ON);。
STM32单片机RTC时钟的使用方法及步骤
STM32单片机RTC时钟的使用方法及步骤一、配置RTC模块时钟源RTC模块的时钟源可以选择外部低速晶振(LSE)或者低速内部时钟(LSI)。
通过以下步骤配置RTC时钟源:1.使能外部低速晶振(LSE)或者低速内部时钟(LSI)。
例如,如果使用外部低速晶振,则需要使能相应的GPIO端口,并配置为晶振模式。
2.配置RCC时钟控制寄存器(RCC_CR)和时钟配置寄存器(RCC_CSR)。
二、使能RTC模块时钟1.使能PWR模块时钟和备份寄存器访问。
RCC_APB1ENR,=(1<<28);RCC_APB1ENR,=(1<<27);2.校验并关闭RTC模块。
RCC->BDCR,=RCC_BDCR_RTCEN;PWR->CR,=PWR_CR_DBP;if ((RCC->BDCR & RCC_BDCR_RTCEN) == 0)RCC->BDCR,=RCC_BDCR_RTCEN;3.配置RTC时钟预分频器和提供给RTC的时钟源。
RTC->PRER ,= rtc_prescaler_value << RTC_PRER_PREDIV_S_Pos;RTC->PRER ,= 127 << RTC_PRER_PREDIV_A_Pos;RTC->CR&=~RTC_CR_FMT;三、配置RTC模块时间和日期1.关闭RTC时钟写保护功能。
RTC->WPR=0xCA;RTC->WPR=0x53;RTC->ISR,=RTC_ISR_INIT;while((RTC->ISR & RTC_ISR_INITF) == 0);2.配置RTC的时间和日期寄存器。
RTC->TR ,= (uint32_t)((hours / 10) << RTC_TR_Hours10_Pos);RTC->TR ,= (uint32_t)((hours % 10) << RTC_TR_Hours1_Pos);RTC->TR ,= (uint32_t)((minutes / 10) <<RTC_TR_Minutes10_Pos);RTC->TR ,= (uint32_t)((minutes % 10) <<RTC_TR_Minutes1_Pos);RTC->TR ,= (uint32_t)((seconds / 10) <<RTC_TR_Seconds10_Pos);RTC->TR ,= (uint32_t)((seconds % 10) <<RTC_TR_Seconds1_Pos);RTC->DR ,= (uint32_t)((year / 10) << RTC_DR_YT_Pos);RTC->DR ,= (uint32_t)((year % 10) << RTC_DR_YU_Pos);RTC->DR ,= (uint32_t)((month / 10) << RTC_DR_MT_Pos);RTC->DR ,= (uint32_t)((month % 10) << RTC_DR_MU_Pos);RTC->DR ,= (uint32_t)((day / 10) << RTC_DR_DT_Pos);RTC->DR ,= (uint32_t)((day % 10) << RTC_DR_DU_Pos);3.开启RTC时钟写保护功能。
STM32入门系列教程之十二《实时时钟RTC编程》
STM32入门系列教程实时时钟RTC编程Revision0.01(2010-04-27)对于单片机转ARM的同学来说,RTC可能比较少接触。
提到实时时钟,更经常想到的是DS1302。
当然,在STM32里,自己一个CPU已经足够,不需要DS1302。
实际上,RTC就只一个定时器而已,掉电之后所有信息都会丢失,因此我们需要找一个地方来存储这些信息,于是就找到了备份寄存器。
因为它掉电后仍然可以通过纽扣电池供电,所以能时刻保存这些数据。
我们在本期教程中将详细讲述RTC原理及例程,以引导大家顺利进入RTC的世界。
1.STM32的RTC模块RTC模块之所以具有实时时钟功能,是因为它内部维持了一个独立的定时器,通过配置,可以让它准确地每秒钟中断一次。
下面就来看以下它的组成结构。
1.1RTC的组成RTC由两个部分组成:APB1接口部分以及RTC核心部分(感觉说了等于没说,因为任何模块都会有接口部分和它自己的核心部分。
请注意,权威的STM32系列手册是这么说的�)。
笔者猜想原因可能是STM32所有的外设默认时钟无效,使用某个外设时,再开启时钟,用这样的方式来降低功耗。
这里的RTC,APB1接口由APB1总线时钟来驱动。
为了突出时钟吧?不过据说APB1接口部分还包括一组16位寄存器。
RTC核心部分又分为预分频模块和一个32位的可编程计数器。
前者可使每个TR_CLK周期中RTC产生一个秒中断,后者可被初始化为当前系统时间。
此后系统时间会按照TR_CLK周期进行累加,实现时钟功能。
1.2对RTC的操作我们对RTC的访问,是通过APB1接口来进行的。
注意,APB1刚被开启的时候(比如刚上电,或刚复位后),从APB1上读出来的RTC寄存器的第一个值有可能是被破坏了的(通常读到0)。
这个不幸,STM32是如何预防的呢?我们在程序中,会先等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1,然后才开始读操作,这时候读出来的值就是OK的。
stm32RTC实时时钟[操作寄存器+库函数]-ChangingsBlog
stm32RTC实时时钟[操作寄存器+库函数]-ChangingsBlog"RTC"是Real Time Clock 的简称,意为实时时钟。
stm32提供了一个秒中断源和一个闹钟中断源。
RTC的技术器是一个32位的计数器,使用32.768khz的外部晶振。
2038年问题在计算机应用上,2038年问题可能会导致某些软件在2038年无法正常工作。
所有使用UNIX时间表示时间的程序都将受其影响,因为它们以自1970年1月1日经过的秒数(忽略闰秒)来表示时间。
这种时间表示法在类Unix(Unix-like)操作系统上是一个标准,并会影响以其C编程语言开发给其他大部份操作系统使用的软件。
在大部份的32位操作系统上,此“time_t”数据模式使用一个有正负号的32位元整数(signedint32)存储计算的秒数。
也就是说最大可以计数的秒数为 2^31次方可以算得:2^31/3600/24/365 ≈ 68年所以依照此“time_t”标准,在此格式能被表示的最后时间是2038年1月19日03:14:07,星期二(UTC)。
超过此一瞬间,时间将会被掩盖(wrap around)且在内部被表示为一个负数,并造成程序无法工作,因为它们无法将此时间识别为2038年,而可能会依个别实作而跳回1970年或1901年。
对于PC机来说,时间开始于1980年1月1日,并以无正负符号的32位整数的形式按秒递增,这与UNIX时间非常类似。
可以算得:2^32/3600/24/365 ≈ 136年到2116年,这个整数将溢出。
Windows NT使用64位整数来计时。
但是,它使用100纳秒作为增量单位,且时间开始于1601年1月1日,所以NT将遇到2184年问题。
苹果公司声明,Mac在29,940年之前不会出现时间问题!由于RTC是一个32位计数器,同样其计时时间是有限的。
库函数中使用到了C标准时间库,时间库中的计时起始时间是1900年,可以知道时间库中不是用有符号位的32位整数来表示时间的,否则在1968年就已经溢出了。
STM32L15x —— 实时时钟RTC
RTC —— 可编程警报
两个可编程警报模块(ALARM A 和ALARM B) 可用于退出睡眠和低功耗睡眠模式 警报事件可映射到特殊的RTC_AF1引脚,并输出可配置的电平信号 当日历中的日期,小时,分钟和秒与警报寄存器(ALRMAR/
ALRMBR)中的值相同时,警报标志位会被置起 日历中的日期,小时,分钟和秒都可以作为单独的选择与警报寄存器
三种可选择的时钟配置:
第一种 RTC_CR寄存器的WUCKWSaEkLe[-2U:p0] = 0xx
RTCCLK
异步4位分频器
WakeUpCLK
WUCKSEL[2:0] ValueMax = RTCCLK / 16 ValueMin = RTCCLK / 2
16bit autoreload
相配合(屏蔽不需要的时间域)
10
RTC —— RTC输入/输出引脚 RTC_AF1
引脚配置和 功能
OSEL[1:0]位 (RTC_CR寄存器)
警报输出 开漏输出
警报输出 推挽输出
校准输出 推挽输出
时间戳输入 浮空输入
侵入输入 浮空输入
时间戳和 侵入输入 浮空输入 唤醒引脚2
01 / 10 01 / 10
Wake-Up
WakeUpCLK
16bit autoreload Timer
ValueMax = 0xFFFF ValueMin = 0x0000
WakeUpCLKmax = RTCCLK/(1 x (0x10000 + 1)) => 36.4 hours WakeUpCLKmin = RTCCLK/(220 x (0x1FFFF + 1)) => 18 hours
浅谈STM32F10X芯片RTC实时时钟
1、介绍系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。
执行以下操作将使能对后备寄存器和RTC的访问:●设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟(调用:RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR,ENABLE));●设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问(调用:PWR_BackupAccessCmd(ENABLE))。
2、RTC注意事项●RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位;系统复位或电源复位不会影响他们的值;●RTC提供APB1接口通ABP1读取RTC寄存器的值,但必须等待RTC_CRL寄存器中的RSF(同步标志位)位被硬件置“1”之后进行;●RTC的配置必需在前一次写操作结束(判断RTC_CR寄存器中的RTOFF是否为1,为1表示更新完成),并设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器,清除CNF标志位时,写操作才实际有效(说明RTC是动态配置的,即是在RTC 运行起来之后再进行配置);●RTC中的任何标志位都将保持挂起状态(因为OWF、ALRF、SECF和RSF只能由硬件置位由软件清零),直到适当的RTC_CR请求位被软件复位,表示所有请求的中断已经被接受;●若ALRF=1且ALRIE=1,则允许产生RTC全局中断,如果EXTI控制器中允许产生EXTI线17中断,则允许产生RTC全局中断和RTC闹钟中断,在这种情况下,一般设置闹铃中断优先级高于全局中断,如果全局中断优先级高于闹铃中断,则在全局中断中必须清除闹钟中断标志之后,才能进入闹钟中断处理函数进一步处理(因为不清除标志,则会一直引发中断,而全局中断优先级高,就会一直在全局中断中无法跳出来);●若ALRF=1,如果在EXTI控制器中设置了EXTI线17的中断模式,则允许产生RTC闹钟中断;如果在EXTI控制器中设置了EXTI线17的事件模式,则这条线上会产生一个脉冲(不会产生RTC闹钟中断);●当APB1时钟不运行时,OWF、ALRF、SECF和RSF位不被更新;●系统复位时禁止所有中断,无挂起中断请求,可以对RTC寄存器进行写操作;●对RTC的写操作必须使用如下过程之一与RTC秒标志同步:使用RTC闹钟中断,并在中断处理程序中修改RTC闹钟和/或RTC计数器;等待RTC控制寄存器中秒标志SECF置位,再更改RTC闹钟和/或RTC计数器。
rtc实时时钟
// jj = 0;
// }
//得到时间并显示 RTC_GetCounter():获得RTC计数器的值 返回值是u32类型的RTC计数器的值
Time_Display(RTC_GetCounter());
//从指定的后备寄存器中读取数据,参数用来选择后备寄存器,可以是BKP_DR1~BKP_DR10 10个后备寄存器
if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) //???为什么要检测0xA5A5
{
//配置RTC
RTC_Configuration();
// {
RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成,也即等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能或失能指定的RTC中断 第一个参数指定待配置的RTC中断源,可以是RTC_IT_SEC:秒中断
unsigned int LedOut[10]; //变量定义
void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART_Configuration(void);
void RTC_Configuration(void);
GPIOB->BSRR = LedOut[i]<<8 & 0xFF00;
GPIOB->BRR = (~(LedOut[i]<<8)) & 0xFF00; //BRR:端口位复位寄存器
STM32F4 第19讲 RTC实时时钟程序讲解-M4
✓ RTC原理
RTC闹钟配置一般步骤
① RTC已经初始化好相关参数。 ② 关闭闹钟:RTC_AlarmCmd(RTC_Alarm_A,DISABLE); ③ 配置闹钟参数:RTC_SetAlarm(); ④ 开启闹钟:RTC_AlarmCmd(RTC_Alarm_A,EABLE); ⑤ 开启配置闹钟中断:
RTC_SetWakeUpCounter(); ① 使能WakeUp : RTC_WakeUpCmd( ENABLE); ② 开启配置闹钟中断:
RTC_ITConfig(); EXTI_Init(); NVIC_Init(); ⑤编写中断服务函数: RTC_WKUP_IRQHandler();
✓ RTC原理 讲解RTC实时时钟实验程序。
✓ RTC原理
RTC日历配置一般步骤
① 使能PWR时钟:RCC_APB1PeriphClockCmd(); ② 使能后备寄存器访问: PWR_BackupAccessCmd(); ③ 配置RTC时钟源,使能RTC时钟:
RCC_RTCCLKConfig(); RCC_RTCCLKCmd(); 如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);
uint32_t RTC_GetAlarmSubSecond(uint32_t RTC_Alarm);
✓ RTC原理
RTC周期唤醒相关函数
void RTC_WakeUpClockConfig(uint32_t RTC_WakeUpClock); void RTC_SetWakeUpCounter(uint32_t RTC_WakeUpCounter); uint32_t RTC_GetWakeUpCounter(void); RTC_WakeUpCmd(DISABLE);//关闭WAKE UP
STM32-实时时钟-RTC
STM32-实时时钟(RTC)STM32系列基于专为要求高性能、低成本、低功耗的嵌入式应用专门设计的ARM Cortex®-M0,M0+,M3, M4和M7内核(ST's product portfolio contains a comprehensive range of microcontrollers, from robust, low-cost 8-bit MCUs up to 32-bit ARM-based Cortex®-M0 and M0+, Cortex®-M3, Cortex®-M4 Flash microcontrollers with a great choice of peripherals. ST has also extended this range to include an ultra-low-power MCU platform) 。
实时时钟(RTC)是一个专用于保持时间的计时元素。
在许多的应用中,特别是在需要执行精确定时操作的应用,RTC是非常有用的工具。
除了钟表这类应用的例子外还包括洗衣机、医药柜、数据记录仪等。
RTC基本上是一个定时计数器,但和MCU的其他定时器不同的是,它更精确一些。
在此之前文章中,我们探讨了STM32定时器,但他们对PWM生成、时基和其它波形相关任务的应用程序是有用的。
那些都不适合于精确的计时功能。
在大多数的8位MCU中,像普通的PIC和AVR,并有没有内置RTC模块,所以当我们需要一个板载的精确计时器件时,只能使用类似常见的DS1302或PCF8563的专用RTC 芯片。
这些芯片还需要一些额外的电路、布线以及电路板空间。
但是,目前大多数先进的微控制器都集成了设计人员可以想到的每一个可能的硬件。
这仅取决于设计者决定使用现代微控制器的哪个资源,来满足特定的设计目标。
制造用于满足应用特定需求的MCU的时代已经过去了,在设计中使用并涉及多个元件的时代也已经过去了。
STM32实时钟设计
STM32实时钟设计实现功能:1、定时器用于实时钟的月日、时分和秒计时;2、2 个按键用于实时钟的显示切换和设置;3、LCD 用于实时钟的月日、时分和秒显示;4、通过UART 接口在微机上显示和设置实时钟。
按键要求:程序如下:#include "stm32f10x.h"#include "misc.h"//#include "uart_my.h"#include "stdio.h"#include "stm32f10x_exti.h"//#define NVIC_SETENA1 (*(volatile unsigned long *) (0xe000e104))int Rx=0;int receive=0;u8 temp;int time[6];int led = 0x200;intlcddata[5][2]={0};intrunnian=1,flag=2,dian=0,flag2=1,timer=0; charrtc[30];char no=0;char lcd_code[16]= //编码{0xeb,//00x0a,//10xad,//20x8f,//30x4e,//40xc7,//50xe7,//60x8a,//70xef,//80xcf,//90x7e,//A0x37,//B0x71,//C0x1f,//D0x75,//E0x74//F};//private functionvoidLCD_Write(int data, int bit); voidSystick_Proc(void);voidLcd_Proc(void);void lcd_display0(void);void lcd_display1(void);void lcd_display2(void);void lcd_display3(void);void lcd_display4(void);void lcd_display5(void);void lcd_display6(void);voidUart_IO_Config(void);voidLCD_IO_Config(void);voidLed_IO_Config(void);voidUart_IO_Config(void); voidButton_IO_Config(void); voidEXTILineConfig(void); voidNVIC_Configuration(void); voidUart_Init(void);voidLCD_Init(void); voidSystick_Init(void); voidLED_Proc(void);voidLcd_Proc(void);voidUart(void);voiduartPuts( u8 *s);voidUart_r(void); voidUSART_GetS_Time(void); int main(void){//SystemInit();//Systick_Init();SystemInit();SysTick_Config(9000000);LCD_IO_Config();Led_IO_Config();Uart_IO_Config();Button_IO_Config();EXTILineConfig();NVIC_Configuration();Uart_Init();LCD_Init();while(1){Systick_Proc();Lcd_Proc();Uart_r();}}voidSystick_Proc(void){if(NVIC_STCSR&0x10000){dian^=1;if((++time[0])>=60){time[0]=0;if((++time[1])>=60){time[1]=0;if((++time[2])>=24)time[2]=0;switch(time[4]){case 1:case 3:case 5:case 7:case 8:case 10:case 12:if(++time[3] >= 32){if(++time[4] >= 13)time[4]=1;}break;case 2:if(runnian){if(++time[3] >= 30){if(++time[4] >= 13)time[4]=0;}}else {if(++time[3] >= 29){if(++time[4] >= 13)time[4]=0;}}break;case 4:case 6:case 9:case 11:if(++time[3] >= 31){if(++time[4] >= 13)time[4]=0;}break;default:break;//time[4]=0;time[3]=0;}}sprintf(rtc ,"%d-%d %d:%d:%d",time[4],time[3],time[2],time[1],time[0]); //格式化输入出处//sprintf(rtc2 ,"",);}lcddata[0][0] = time[0] % 10;lcddata[0][1] = time[0] / 10;lcddata[1][0] = time[1] % 10;lcddata[1][1] = time[1] / 10;lcddata[2][0] = time[2] % 10;lcddata[2][1] = time[2] / 10;lcddata[3][0] = time[3] % 10;lcddata[3][1] = time[3] / 10;lcddata[4][0] = time[4] % 10;lcddata[4][1] = time[4] / 10;//之前开始以分钟之后才能显示放在前边sprintf(rtc ,"日期与时间是:%d-%d %d:%d:%d",time[4],time[3],time[2],time[1],time[0]); //格式化输入出处Uart();while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) ==RESET);USART_SendData (USART1,0xd);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) ==RESET);USART_SendData (USART1,0xa);LED_Proc();}}voidLcd_Proc(void){ /*inttemp[8];temp[0] = ((lcd_code[min_display_h]&0xf0)>>4); //分钟十位高四位temp[1] = (lcd_code[min_display_h]&0x0f); //分钟十位低四位temp[2] = ((lcd_code[min_display_l]&0xf0)>>4); //分钟个位高四位temp[3] = (lcd_code[min_display_l]&0x0f); //分钟个位低四位temp[4] = ((lcd_code[sec_display_h]&0xf0)>>4); //秒钟十位高四位temp[5] = (lcd_code[sec_display_h]&0x0f); //秒钟十位低四位temp[6] = ((lcd_code[sec_display_l]&0xf0)>>4); //秒钟个位高四位temp[7] = (lcd_code[sec_display_l]&0x0f); //秒钟个位低四位*/switch(flag){case 0:lcd_display0();break;case 1:lcd_display1();break;case 2:lcd_display2();break;case 3:lcd_display3();break;case 4:lcd_display4();break;case 5:lcd_display5();break;case 6:lcd_display6();break;default:break;}void lcd_display0(void) //时分显示{LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[2][1]]+(dian<< 4), 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[2][0]], 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[1][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[1][0]], 17);}void lcd_display1(void) //月日显示{LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[4][1]], 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[4][0]], 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[3][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[3][0]], 17);}void lcd_display2(void) //分秒显示{LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[1][1]]+(dian<< 4), 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[1][0]], 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[0][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[0][0]], 17);}void lcd_display3(void) //月设置显示{if(dian){LCD_Write((5<<14)+(0<<8)+no, 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+no, 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[3][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[3][0]], 17);}elseLCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[4][1]], 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[4][0]], 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[3][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[3][0]], 17);}}void lcd_display4(void) //天设置显示{if(dian){LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[4][1]], 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[4][0]], 17); //移位这一块LCD_Write((5<<14)+(4<<8)+no, 17);LCD_Write((5<<14)+(6<<8)+no, 17);}else{LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[4][1]], 17);LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[4][0]], 17);LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[3][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[3][0]], 17);}}void lcd_display5(void) //时设置显示{if(dian){LCD_Write((5<<14)+(0<<8)+no, 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+no, 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[1][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[1][0]], 17);}else{LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[2][1]], 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[2][0]], 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[1][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[1][0]], 17);}}void lcd_display6(void) //分设置显示{if(dian){LCD_Write((5<<14)+(4<<8)+no, 17);//移位出现问题LCD_Write((5<<14)+(6<<8)+no, 17); //移位这一块LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[2][1]], 17);LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[2][0]], 17);}else{LCD_Write((5<<14)+(0<<8)+lcd_code[lcddata[2][1]], 17);//移位出现问题LCD_Write((5<<14)+(2<<8)+lcd_code[lcddata[2][0]], 17); //移位这一块LCD_Write((5<<14)+(4<<8)+lcd_code[lcddata[1][1]], 17);LCD_Write((5<<14)+(6<<8)+lcd_code[lcddata[1][0]], 17);}}voidLCD_IO_Config(void){GPIO_InitTypeDefGPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);}voidButton_IO_Config(void){GPIO_InitTypeDefGPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);GPIO_InitStructure.GPIO_Pin= GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //配置错误GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);}voidLed_IO_Config(void){GPIO_InitTypeDefGPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);/* Configure PC.06, PC.07, PC.08 and PC.09 as output push-pull */GPIO_InitStructure.GPIO_Pin= GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 |GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);}voidUart_IO_Config(void){GPIO_InitTypeDefGPIO_InitStructure;USART_ClearITPendingBit(USART1,USART_IT_TC|USART_IT_RXNE);//RXRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //接收GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOA, &GPIO_InitStructure);//TXGPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //发送GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOA, &GPIO_InitStructure);}voidEXTILineConfig(void){EXTI_InitTypeDefEXTI_InitStructure;GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource15);EXTI_InitStructure.EXTI_Line = EXTI_Line14|EXTI_Line15;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);}voidNVIC_Configuration(void){NVIC_InitTypeDefNVIC_InitStructure;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//抢占优先级组4//2位抢占优先级2位子优先级/* Enable the EXTI14 Interrupt on PB.14 *///sw3 输入时间NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //对于相同中断号的中断无法设置抢断优先级NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);/* Enable the EXTI15 Interrupt on PB.15 //sw2 led方向NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);*/}voidLED_Proc(void){//if(NVIC_STCSR&0x10000){if(flag2){led <<= 1; // 左环移 led=0x2000if(led == 0x400)led = 0x40;}else{led >>= 1; // 右环移if(led == 0x20)led = 0x200;}GPIOC_BSRR = 0x03C0; // 灭所有灯写1寄存器 BRR写0寄存器GPIOC_ODR &= ~led; // 亮指定}}voidUart(void){uartPuts((u8*) rtc);}voidUart_r(void){if(receive){USART_GetS_Time();receive = 0;}USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); }void USART1_IRQHandler(void){if(USART_ReceiveData(USART1)=='s')receive = 1; USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); }void EXTI15_10_IRQHandler(void){// inti=1000;if(EXTI_GetITStatus(EXTI_Line14)==SET){EXTI_ClearITPendingBit(EXTI_Line14);switch(flag){case 0:case 1:flag++;break;case 2:flag=0;break;case 3:case 4:case 5:flag++;break;case 6:flag=0;break;default:flag=0;}}if(EXTI_GetITStatus(EXTI_Line15)==SET){while(i--);EXTI_ClearITPendingBit(EXTI_Line15);switch(flag){case 0:case 1:case 2:flag=3;break;case 3:if(++time[4]>=13)time[4]=0;break;case 4:if(++time[3]>=32)time[3]=0;break;case 5:if(++time[2]>=25)time[2]=0;break;case 6:if(++time[1]>=60)time[1]=0;break;default:break;}}}/*---------------------------------------------------------** LED_Water** MCU :STM32F103RBT6** Crystal :External Input=8MHz** Author :FHL** Date :2011.11.30** Function :实现数字时钟功能,并通过LM046显示;**** ----------Copyright (c) 2000-2011 FHL Co.,Ltd.** ----------All rights reserved.-------------------------------------------------------------------------------*//* Includes ------------------------------------------------------------------*/#include "uart_my.h"#include "stdio.h"#include "stm32f10x.h"#define NVIC_SETENA1 (*(volatile unsigned long *) (0xe000e104))/* Local includes ------------------------------------------------------------*/ externint time[5];extern u8 temp;externint Rx;/* Private typedef -----------------------------------------------------------*//* Private define ------------------------------------------------------------*//* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------externintsec_display_l,sec_display_h;externintmin_display_l,min_display_h;externinthour_display_l,hour_display_h; *//* Private functions ---------------------------------------------------------*/voidUart_Init(void){USART_InitTypeDefUSART_InitStructure;//USART_ClockInitTypeDefUSART_ClockInitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE);//未加usart1开时钟USART_ART_BaudRate = 115200;USART_ART_WordLength = USART_WordLength_8b;USART_ART_StopBits = USART_StopBits_1;USART_ART_Parity = USART_Parity_No;USART_ART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_ART_HardwareFlowControl =USART_HardwareFlowControl_None;USART_Init(USART1, &USART_InitStructure);NVIC_SETENA1=0x20;//使能USART1接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//使能USART1发送中断//USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//使能串口USART_Cmd(USART1, ENABLE);}voiduartPuts( u8 *s)while (*s != '\0'){while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //未判断导致错误USART_SendData(USART1, *(s++));}}voidUSART_GetS_Time(void){u32 index = 0;u8tmp[15] ;u8 tempp[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};int s;char p[20];while (1){while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET){} //Data is not received 0temp = USART_ReceiveData(USART1); //接收数据if ( temp == 0x8 ){ // 为什么是8 backspaceif ( index != 0 ){index--;}}else{if ( index < 11) tmp[index] = temp;/*while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //未判断导致错误USART_SendData(USART1, temp);*/index++;if ( temp == 0xd ) break; //为什么是13 enter}for(s=0;s<10;s++){tempp[s] = tmp[s]-0x30; //0的ASSIC码是48 即0x30if ( tempp[s] >0 &&tempp[s] < 10); //或导致字母进入&& (tempp[2] == 0xaelse{while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //未判断导致错误sprintf(p,"输入错误!");uartPuts((u8*) p);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) ==RESET);USART_SendData (USART1,0xd);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) ==RESET);USART_SendData (USART1,0xa);break;}}if(tempp[0]<2 &&tempp[2] < 4 &&tempp[4] < 3 &&tempp[6] < 6 &&tempp[8] < 6 )//未判断导致分秒高位出现大于5情况{ //月份天小时没判断time[0] = tempp[8]*10+tempp[9];time[1] = tempp[6]*10+tempp[7];time[2] = tempp[4]*10+tempp[5];time[3] = tempp[2]*10+tempp[3];time[4] = tempp[0]*10+tempp[1];}else{while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //未判断导致错误sprintf(p,"输入错误!");uartPuts((u8*) p);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) ==RESET);USART_SendData (USART1,0xd);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) ==RESET);USART_SendData (USART1,0xa);}}其中硬件电路图如下所示:。
STM32库开发实战指南-M4:43-RTC—实时时钟
26.在PVD电源监控实验的基础上,修改PVD监控的电压阈值等级,进入PVD中断时“3.3V”电源线的临界电压。
第43章RTC—实时时钟43.1RTC简介RTC—real time clock,实时时钟,主要包含日历、闹钟和自动唤醒这三部分的功能,其中的日历功能我们使用的最多。
日历包含两个32bit的时间寄存器,可直接输出时分秒,星期、月、日、年。
比起F103系列的RTC只能输出秒中断,剩下的其他时间需要软件来实现,429的RTC可谓是脱胎换骨,让我们在软件编程时大大降低了难度。
RTC功能框图分析43.2RTC功能框图解析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用于更新日历。
异步预分频器时钟f CK_APRE=f RTC_CLK/(PREDIV_A+1),同步预分频器时钟f CK_SPRE=f RTC_CLK/(PREDIV_S+1),)。
使用两个预分频器时,推荐将异步预分频器配置为较高的值,以最大程度降低功耗。
一般我们会使用LSE生成1HZ的同步预分频器时钟通常的情况下,我们会选择LSE作为RTC的时钟源,即f RTCCLK=f LSE=32.768KHZ。
然后经过预分频器PRER分频生成1HZ的时钟用于更新日历。
使用两个预分频器分频的时候,为了最大程度的降低功耗,我们一般把同步预分频器设置成较大的值,为了生成1HZ的同步预分频器时钟CK_SPRE,最常用的配置是PREDIV_A=127,PREDIV_S=255。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1 课程设计内容本文将利用ALIENTEK 2.8寸TFTLCD模块来显示日期时间,实现一个简单的时钟。
2 STM32芯片简介2006年ARM公司推出了基于ARMv7架构的Cortex系列的标准体系结构,以满足各种技术的不同性能要求,包含A、R、M三个分工明确的系列[1]。
其中,A系列面向复杂的尖端应用程序,用于运行开放式的复杂操作系统;R系列适合实时系统;M系列则专门针对低成本的微控制领域。
Cortex-M3是首款基于ARMv7-M体系结构的32位标准处理器,具有低功耗、少门数、短中断延迟、低调试成本等众多优点。
它是专门为在微控制系统、汽车车身系统、工业控制系统和无线网络等对功耗和成本敏感的嵌入式应用领域实现高系统性能而设计的,它大大简化了编程的复杂性,集高性能、低功耗、低成本于一体[2]。
半导体制造厂商意法半导体ST公司是ARM公司Cortex-M3内核开发项目一个主要合作方,2007年6月11日ST公司率先推出了基于Cortex-M3内核的STM32系列MCU。
本章将简要介绍STM32系列处理器的分类、内部结构及特点,并对本设计中重点应用的通用定时器做进一步分析。
2.1 STM32 RTC时钟简介STM32 的实时时钟(RTC)是一个独立的定时器。
STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。
修改计数器的值可以重新设置系统当前的时间和日期。
RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。
但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。
所以在要设置时间之前,先要取消备份区域(BKP)写保护。
RTC 的简化框图,如图 20.1.1 所示:图 20.1.1 RTC 框图RTC 由两个主要部分组成(参见图 20.1.1),第一部分(APB1 接口)用来和 APB1 总线相连。
此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。
APB1 接口由 APB1 总线时钟驱动,用来与 APB1 总线连接。
另一部分(RTC 核心)由一组可编程计数器组成,分成两个主要模块。
第一个模块是 RTC 的预分频模块,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。
RTC 的预分频模块包含了一个 20位的可编程分频器(RTC 预分频器)。
如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个TR_CLK 周期中 RTC 产生一个中断(秒中断)。
第二个模块是一个 32 位的可编程计数器,可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记录 4294967296 秒,约合 136 年左右,作为一般应用,这已经是足够了的。
RTC 还有一个闹钟寄存器 RTC_ALR,用于产生闹钟。
系统时间按 TR_CLK 周期累加并与存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。
RTC 内核完全独立于 RTC APB1 接口,而软件是通过 APB1 接口访问 RTC 的预分频值、计数器值和闹钟值的。
但是相关可读寄存器只在 RTC APB1 时钟进行重新同步的 RTC 时钟的上升沿被更新,RTC 标志也是如此。
这就意味着,如果 APB1 接口刚刚被开启之后,在第一次的内部寄存器更新之前,从 APB1 上都处的 RTC 寄存器值可能被破坏了(通常读到 0)。
因此,若在读取 RTC 寄存器曾经被禁止的 RTC APB1 接口,软件首先必须等待 RTC_CRL 寄存器的 RSF位(寄存器同步标志位,bit3)被硬件置 1。
2.2 RTC相关配置正常工作的一般配置步骤如下:1)使能电源时钟和备份区域时钟。
前面已经介绍了,我们要访问 RTC 和备份区域就必须先使能电源时钟和备份区域时钟。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP, ENABLE);2)取消备份区写保护。
要向备份区域写入数据,就要先取消备份区域写保护(写保护在每次硬复位之后被使能),否则是无法向备份区域写入数据的。
我们需要用到向备份区域写入一个字节,来标记时钟已经配置过了,这样避免每次复位之后重新配置时钟。
取消备份区域写保护的库函数实现方法是:PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问3)复位备份区域,开启外部低速振荡器。
在取消备份区域写保护之后,我们可以先对这个区域复位,以清除前面的设置,当然这个操作不要每次都执行,因为备份区域的复位将导致之前存在的数据丢失,所以要不要复位,要看情况而定。
然后我们使能外部低速振荡器,注意这里一般要先判断 RCC_BDCR 的 LSERDY位来确定低速振荡器已经就绪了才开始下面的操作。
备份区域复位的函数是:BKP_DeInit();//复位备份区域开启外部低速振荡器的函数是:RCC_LSEConfig(RCC_LSE_ON);// 开启外部低速振荡器4)选择 RTC 时钟,并使能。
这里我们将通过 RCC_BDCR 的 RTCSEL 来选择选择外部 LSI 作为 RTC 的时钟。
然后通过RTCEN 位使能 RTC 时钟。
库函数中,选择 RTC 时钟的函数是:RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择 LSE 作为 RTC 时钟对于RTC 时钟的选择,还有RCC_RTCCLKSource_LSI 和RCC_RTCCLKSource_HSE_Div128两个,顾名思义,前者为 LSI,后者为 HSE 的 128 分频,这在时钟系统章节有讲解过。
使能 RTC 时钟的函数是:RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟5)设置 RTC 的分频,以及配置 RTC 时钟。
在开启了 RTC 时钟之后,我们要做的就是设置 RTC 时钟的分频数,通过RTC_PRLH 和RTC_PRLL 来设置,然后等待 RTC 寄存器操作完成,并同步之后,设置秒钟中断。
然后设置RTC 的允许配置位(RTC_CRH 的 CNF 位),设置时间(其实就是设置 RTC_CNTH 和 RTC_CNTL两个寄存器)。
下面我们一一这些步骤用到的库函数:在进行 RTC 配置之前首先要打开允许配置位(CNF),库函数是:RTC_EnterConfigMode();/// 允许配置在配置完成之后,千万别忘记更新配置同时退出配置模式,函数是:RTC_ExitConfigMode();//退出配置模式,更新配置设置 RTC 时钟分频数,库函数是:void RTC_SetPrescaler(uint32_t PrescalerValue);这个函数只有一个入口参数,就是 RTC 时钟的分频数,很好理解。
然后是设置秒中断允许,RTC 使能中断的函数是:void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);这个函数的第一个参数是设置秒中断类型,这些通过宏定义定义的。
对于使能秒中断方法是:RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断下一步便是设置时间了,设置时间实际上就是设置 RTC 的计数值,时间与计数值之间是需要换算的。
库函数中设置 RTC 计数值的方法是:void RTC_SetCounter(uint32_t CounterValue)最后在配置完成之后通过这个函数直接设置 RTC 计数值。
6)更新配置,设置 RTC 中断分组。
在设置完时钟之后,我们将配置更新同时退出配置模式,这里还是通过 RTC_CRH 的 CNF来实现。
库函数的方法是:RTC_ExitConfigMode();//退出配置模式,更新配置在退出配置模式更新配置之后我们在备份区域 BKP_DR1 中写入 0X5050 代表我们已经初始化过时钟了,下次开机(或复位)的时候,先读取 BKP_DR1 的值,然后判断是否是 0X5050 来决定是不是要配置。
接着我们配置 RTC 的秒钟中断,并进行分组。
往备份区域写用户数据的函数是:void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);这个函数的第一个参数就是寄存器的标号了,这个是通过宏定义定义的。
比如我们要往BKP_DR1 写入 0x5050,方法是:BKP_WriteBackupRegister(BKP_DR1, 0X5050);同时,有写便有读,读取备份区域指定寄存器的用户数据的函数是:uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);这个函数就很好理解了,这里不做过多讲解。
设置中断分组的方法之前已经详细讲解过,调用 NVIC_Init 函数即可,这里不做重复讲解。
7)编写中断服务函数。
最后,我们要编写中断服务函数,在秒钟中断产生的时候,读取当前的时间值,并显示到TFTLCD 模块上。
通过以上几个步骤,我们就完成了对 RTC 的配置,并通过秒钟中断来更新时间。
3单元模块及电路设计3.1 电源模块图1 3.2 复位电路模块图2 3.3 外部时钟模块图3 3.4 外部晶振模块图4 3.5 JTAG下载模块图5 3.6 主控制器模块图83.7 BootLoader配置模块图94 软件设计首先是 RTC_Init,其代码如下://实时时钟配置//初始化 RTC 时钟,同时检测时钟是否工作正常//BKP->DR1 用于保存是否第一次配置的设置//返回 0:正常//其他:错误代码u8 RTC_Init(void){u8 temp=0; //检查是不是第一次配置时钟if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //从指定的后备寄存器中//读出数据:读出了与写入的指定数据不相乎{RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR |RCC_APB1Periph_BKP, ENABLE); //使能 PWR 和 BKP 外设时钟PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问BKP_DeInit(); //③复位备份区域RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE)while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的//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(2009,12,2,10,0,55); //设置时间RTC_ExitConfigMode(); //退出配置模式BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后备寄存器中//写入用户程序数据 0x5050}else//系统继续计时{RTC_WaitForSynchro(); //等待最近一次对 RTC 寄存器的写操作完成RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成}RTC_NVIC_Config(); //RCT 中断分组设置RTC_Get(); //更新时间return 0; //ok}该函数用来初始化 RTC 时钟,但是只在第一次的时候设置时间,以后如果重新上电/复位都不会再进行时间设置了(前提是备份电池有电),在第一次配置的时候,我们是按照上面介绍的 RTC 初始化步骤来做的,这里就不在多说了,这里我们设置时间是通过时间设置函数RTC_Set(2012,9,7,13,16,55);来实现的,这里我们默认将时间设置为 2012 年 9 月 7 日 13 点 16 分55 秒。