当前位置:文档之家› 基于STM32-RTC实时时钟

基于STM32-RTC实时时钟

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 秒。

相关主题