STM32 RTC时钟日历

发布者:dandan666最新更新时间:2016-10-11 来源: eefocus关键字:STM32  RTC  时钟日历 手机看文章 扫描二维码
随时随地手机看文章
STM32内部有一个RTC实时时钟模块。RTC模块拥有一组连续技术的计数器。在相应软件配置下,可提供时钟日历的功能,修改计数器的值就可以重新设置系统当前的时间和日期。RTC模块和时钟配置系统是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变。系统复位后,静止访问后备寄存器和RTC,防止对后备区域(BKP)的意外写操作。
下面就讲讲怎么来配置STM32的时钟日历。还是基于我自己的规范工程。
1、工程的修改
1)首先当然要添加stm32f10x_rtc.c文件了,除此之外还要用到备份寄存器,所以要添加stm32f10x_bkp.c文件。由于使用了备份寄存器,那么还要添加stm32f10x_pwr.c 到STM32F10x_StdPeriod_Driver工程组中。
2)打开stm32f10x_conf.h文件将其中被屏蔽的下面语句:#include "stm32f10x_rtc.h"  #include "stm32f10x_bkp.h"  #include "stm32f10x_pwr.h" 的屏蔽去掉。
3)新建Calendar.c与Calendar.h两个文件分别保存到BSP文件夹下的src与inc两个文件中。并将Calendar.c文件添加到BSP工程组中。
 
2、Calendar.c与Calendar.h文件的修改
首先是RTc的初始化函数,在这个函数中最主要的是初始化RTC的时钟。代码如下:

/*************************************************************
Function : Calendar_RTC_Init
Description: RTC初始化
Input : none
return : none
*************************************************************/
static void Calendar_RTC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

PWR_BackupAccessCmd(ENABLE);//允许访问BKP数据
BKP_DeInit(); /复位BPK
RCC_LSEConfig(RCC_LSE_ON); //打开LSE低速时钟
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//等待LSE稳定
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//选择LSE作为RTC的时钟源
RCC_RTCCLKCmd(ENABLE); /打开RTC时钟
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_WaitForLastTask(); //等待最后一个写操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE);//打开RTC中断
RTC_WaitForLastTask(); //等待最后写操作完成
RTC_SetPrescaler(32767);//设置于分频,周期 period = RTCCLK/RTC_PR = (32.768kHz)/(32767 + 1) = 1s
RTC_WaitForLastTask();//等待最后的写操作完成
}

最开始的时候介绍过,RTC模块和时钟配置系统都在后备区域,所以要在最开始配置下备份寄存器,让允许范文备份寄存器。接下去是配置RTC的时钟频率。RTC的时钟源可以选择低速时钟LSI,它有内部RC振荡获得,频率在40kHz左右;RTC时钟源也可以选择低速外部晶振LSE,它的频率一般外32.768kHz。在这里,我选择LSE作为RTC的时钟源。对于STM32来说,它会接一个频率为32.768KHz的外部晶振,作为STM32 的低速时钟LSE。所以,RTC的时钟要选择低速时钟LSE,时钟频率为32768Hz。然后在将LSE时钟进行32768分频,这样的话,就可以产生RTC需要的秒信号了。
上面已经将RTC的时钟都设置完了,下面就要开始讲讲如何校准当前的时间,换句话说是输入当前准确的时间,包括小时、分钟、秒,代码如下:

/*************************************************************
Function : Calendar_Time_Regulate
Description: 校准RTC时间
Input : none
return : none
*************************************************************/
static u32 Calendar_Time_Regulate(void)
{
u32 Tmp_HH = 0xff, Tmp_MM = 0xff, Tmp_SS = 0xff;

PRINTF("==================Time Settings================\r\n");
PRINTF("Please set hours:");
while(Tmp_HH == 0xff)
{
Tmp_HH = Calendar_Adjust_Scanf(23);//输入小时数
}
PRINTF("%d\r\n", Tmp_HH);
PRINTF("Please set minites:");
while(Tmp_MM == 0xff)
{
Tmp_MM = Calendar_Adjust_Scanf(59);//输入分钟数
}
PRINTF("%d\r\n", Tmp_MM);
PRINTF("Please set seconds:");
while(Tmp_SS == 0xff)
{
Tmp_SS = Calendar_Adjust_Scanf(59);//输入秒数
}
PRINTF("%d\r\n", Tmp_SS);

return (Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS);//返回秒数
}

上面代码来调整准确的当前时间。它会在串口上提示你输入小时数、分钟数、秒数。在这个函数中,定义了3个局部变量,分别用来保存要设置的小时、分钟、秒钟,它们的默认值都设0xff。最后在将他们全部转换成秒数值返回。输入的媒介也是串口,通过串口来输出这些要设定的时间,在这里调用Calendar_Adjust_Scanf()函数来作为输入的接口。下面就讲讲这个函数。
Calendar_Adjust_Scanf()函数的代码如下:

/*************************************************************
Function : Calendar_Adjust_Scanf
Description: 输入要设计的时间
Input : none
return : none
*************************************************************/
static u8 Calendar_Adjust_Scanf(u32 value)
{
u32 index = 0;
u32 tmp[2] = {0, 0};

while(index < 2)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);//等待直到,串口接收到数据
tmp[index++] = USART_ReceiveData(USART1);//存输入的数据
if((tmp[index - 1] < '0') || (tmp[index - 1] > '9'))//输入有效数字0~9
{
PRINTF("Please enter valid number between 0~9\r\n");
index--;
}
}
index = (tmp[0] - '0')*10 + (tmp[1] - '0');//将字符转换成数字
if(index > value)//数字要小于value
{
PRINTF("Please enter valid number between 0 ~%d\r\n", value);
return 0xff;
}

return index;//返回设置值
}

这个函数带有一个参数:value,用这个参数来限定输入数值的上限,以便防止错误输入。因为是串口输入的,它输入的是字符,需要将字符转换成十进制数字,然后在检测它是否有效,如果转换后的不是数字0~9,则需要提示输入有效的值,否则保存,等待输入第2个字符。如果输入的两个字符都是有效的,然后将他们分别作为个位与十位转换一个2位的整数,如果这个整数超过输入的上限值value,则提示重新输入。如果都正确,则返回这个歌值。
接下去就要将上面输入并转换好的描述写进RTC了,代码如下:

/*************************************************************
Function : Calendar_Time_Adjust
Description: 设置当前时间:时、分、秒
Input : none
return : none
*************************************************************/
static void Calendar_Time_Adjust(void)
{
RTC_WaitForLastTask(); //等待操作完成
RTC_SetCounter(Calendar_Time_Regulate());//修改当前时间
RTC_WaitForLastTask(); //等待操作完成
}

RTC通过RTC_SetCounter()这个函数将要设定的秒数写进RTC的计数寄存器中去。
为了方便调试,可以打开RTC的时钟中断。上面的代码中,已经RTC的节拍设置为1s,所以如果打开RTC的中断,就会1s进入一次这个中断,我们则可以在这个中断函数中做些文章方便调试。所以这里还要配置下RTC的中断,代码如下:

/*************************************************************
Function : Calendar_Int_Config
Description: 设置RTC中断配置
Input : none
return : none
*************************************************************/
static void Calendar_Int_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //设置RTC中断
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

还要编写一个总函数Calendar_init(),将上面的配置函数全部包含进来,代码如下:

/*************************************************************
Function : Calendar_init
Description: 日历初始化
Input : none
return : none
*************************************************************/
void Calendar_init(void)
{
Calendar_Int_Config();//配置RTC秒中断
if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)//查看BKP,判断RTC是否配置
{
PRINTF("RTC not yet configured!\r\n");
Calendar_RTC_Init();//RTC初始化
PRINTF("RTC configured!\r\n");
Calendar_Time_Adjust();//通过串口设置时间
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
}
else
{
if(RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)//上电复位标志
{
PRINTF("Power on reset!\r\n");
}
else if(RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)//引脚复位标志
{
PRINTF("External reset occured!\r\n");
}
PRINTF("No need to configure RTC\r\n");
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE);//打开秒中断
RTC_WaitForLastTask(); //等待对吼一个寄存器操作完成
}
RCC_ClearFlag();//清除复位标志
}

这段代码,先是打开秒中断,然后再读取备份寄存器的数据寄存器1,看看是否等于0xA5A5。这里用备份寄存器的数据寄存器1来判断否需要配置的重新设置RTC。如果它的值不为0xA5A5,则重新配置RTC,输入初始时间,同时将值0xA5A5写入备份寄存器的数据寄存器1。如果数据寄存器的值为0xA5A5,则只需要打开中断就可以了。一般情况下,STM32有一个后备电源,单独为RTC模块提供电源,那么即使开发板上电或者案件复位,都不会影响RTC的正常工作。
最后,当然要给出一个显示当前时间的函数,代码如下:

/*************************************************************
Function : Calendar_Time_Display
Description: 显示当前时间
Input : none
return : none
*************************************************************/
void Calendar_Time_Display(void)
{
u32 THH = 0, TMM = 0, TSS = 0;
u32 timeValue = 0;

timeValue = RTC_GetCounter();
if(timeValue == 0x0001517F) //0x0001515F对应的时间是23:59:59
{
RTC_SetCounter(0x0);//RTC时钟定时时间清零
RTC_WaitForLastTask();//等待操作完成
}

THH = timeValue / 3600;//计算小时
TMM = (timeValue % 3600) / 60;//计算分钟
TSS = (timeValue % 3600) % 60;//计算秒
PRINTF("Time: %0.2d:%0.2d:%0.2d\r\n", THH, TMM, TSS);//输出当前时间
}

怎么获取当前的时间。首先,要获取RTC的计数值。然后查看下这个值是否等于0x0001517F,即23:59:59所对应的秒数,如果是等于0x0001517F,则RTC计数值清零。如果不等0x0001517F,则计算出计数值对应的小时数、分钟数以及秒数,然后用串口打印出来。
下面的是Calendar.h文件,仅仅将RTC的初始化函数以及时间的显示函数在这里声明下,代码如下:

#ifndef __CALENDAR_H__
#define __CALENDAR_H__
#include "stm32f10x.h"

void Calendar_init(void);
void Calendar_Time_Display(void);

#endif


3、stm32f10x_it.c的修改
stm32f10x_it.c文件只要添加一个RTC的中断服务函数就可以了,代码如下:

/*************************************************************
Function : RTC_IRQHandler
Description: RTC秒中断
Input : none
return : none
*************************************************************/
void RTC_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_SEC) != RESET)
{
RTC_ClearITPendingBit(RTC_IT_SEC); //清除秒中断标志
LED1_Toggle(); //翻转LED灯
RTC_WaitForLastTask(); //等待RTC操作完成
}
}

在中断函数,没有做说什么具体的操作,仅仅让一盏LED等闪灭而已,以便观察。
 
4、main函数的编写
main()函数首先要初始化下RTC的相关代码,然后没个一秒就将当前的时间显示出来。代码如下:

/*************************************************************
Function : main
Description: main入口
Input : none
return : none
*************************************************************/
int main(void)
{
BSP_Init();
PRINTF("\nmain() is running!\r\n");
Calendar_init();
while(1)
{
Calendar_Time_Display();
Delay_ms(1000);
}
}

5、测试
下载好程序后,用串口线连接电脑与开发板,打开电脑上的串口调试软件。然后给开发板上电,可以看到串口调试软件上有消息提示输入小时、分钟与秒钟,输入好当前的时间后,就可以看到,每个1s就显示一下当前的时间,现象如下图所示:
STM32 RTC时钟日历 - ziye334 - ziye334的博客
关键字:STM32  RTC  时钟日历 引用地址:STM32 RTC时钟日历

上一篇:STM32 备份寄存器操作
下一篇:STM32 串口DMA发送

推荐阅读最新更新时间:2024-03-16 15:14

STM32串口收数据的几种不同方式
本例程通过PC机的串口调试助手将数据发送至STM32,接收数据后将所接收的数据又发送至PC机,具体下面详谈。。。 实例一: void USART1_IRQHandler(u8 GetData) { u8 BackData; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //中断产生 { USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断标志. GetData = UART1_GetByte(BackData); //也行GetData=USART1- DR; USART1_SendByte(GetData); //
[单片机]
集性能、紧凑、灵活、能效于一身,ST8引脚STM32微控制器问市
意法半导体8引脚STM32微控制器(MCU)现已上市,紧凑、经济的封装让简单的嵌入式开发项目也能利用32位MCU的性能和灵活性。 新推出的四款STM32G0 微控制器是8引脚经济性和32位性能的完美组合,在市场绝无仅有,基于59 DMIPS的 64MHzArm®Cortex®-M0 + CPU,片上高达8KB的RAM和32KB闪存,高性能外设包括2.5Msps ADC、高分辨率定时器和高速SPI接口。灵活的I/O引脚映射和MCU内部功能,让设计人员轻松升级终端产品功能,不会牺牲电路板空间或物料清单成本。高稳定内部振荡器,在宽温度和宽压范围内精度达到±1%,为开发者节省了外部时钟元件。 电池容量极限、生态设计规则或电器
[单片机]
集性能、紧凑、灵活、能效于一身,ST8引脚<font color='red'>STM32</font>微控制器问市
旋转编码器(STM32PWM输入实验)
一、编码器的介绍 编码器拆解视频 ECC11旋转编码器介绍 51的编程以及消抖(没完全消) 二、简单玩一玩 1.简单理解一下: VCC和GND基本上都知道了 然后SW就是按钮,详细的看拆解视频,按下接通GND,为低电平。 CLK和DT说明: //CLK和DT实际上并没有明确定义什么意思,别一拿到就想是IIC的sck个sda,这不是那个!!! 1 SCK和DT更像是通道A和通道B。 你旋转的话,涉及到了谁先转向低电平的问题。 比如我通道A(图中ch1,蓝色的那个)接CLK,通道2(CH2,黄色的那个)接DT,图拍的不好,看视频好一点,左右转时的电平变化情况: 左转: 右转: 2.结论: 触发方式为下拉触发
[单片机]
旋转编码器(STM32PWM输入实验)
stm32编译时报错 ..........ER_IROM1: File exists 的问题
芯片型号是STM32F103CB(128K),在工程中定义了一个常量数组到801F000地址处,编译时出现 ......ER_IROM1: File exists ........错误, 发现原来是芯片的型号之前为了程序方便STM32F103C8型号使用,设置成了STM32F103C8(64K),程序空间大小不一样,所以 编 译时超出了地址范围就报错。
[单片机]
STM32在Ubuntu上编写USB上位机程序实现
libusb 介绍 libusb是开源的C库,使用该库是的用户可以在应用程序中直接访问 USB 设备,无需为 USB 设备编写内核驱动。libusb支持多个平台 (linux, window, ios),所以可以很方便地将应用程序移植到其他平台。 linux libusb 安装 从网上下载libusb的源码,下载地址:http://www.libusb.org/, 下载后编译安装。 # tar jxvf libusb-1.0.20.tar.bz2 # cd libusb-1.0.20 # ./configure # make # sudo make install ubuntu下可以通过以下命令快速安装。
[单片机]
STM32 SPI 收发数据 ---规则 + 问题解析
规则: 1) 高速同步串行口。3~4线接口(CS ,CLK ,MOSI,MISO),收发独立、可同步进行。 2)SPI分为主从模式,主模式提供时钟和片选选择信号. 3) 模式控制:CPOL用来控制时钟信号(clk)在空闲时候的状态;CPHA用来控制采样时刻时CLK的边缘动作。 CPOL CPHA 模式: 0 0 CLK空闲时为低电平,CLK上升沿采样数据。 0 1 CLK空闲为低电平,CLK下降沿采样数据。 1 0 CLK空闲时为高电平,CLK上升沿采样数据。 1 1 CLK空闲时为高电平,CLK下降沿采样数据。 1)SPI配置(3.01库): SPI_InitStructure.SPI_Direction = SPI_D
[单片机]
STM32学习笔记--GPIO的设置
STM32的IO口可以由软件配置成8种模式: 1、输入浮空 2、输入上拉 3、输入下拉 4、模拟输入 5、开漏输出 6、推挽输出 7、推挽式复用功能 8、开漏复用功能 STM32的每个IO端口都有7个寄存器来控制。他们分别是:配置模式的2个32位的端口配置寄存器CRL和CRH;2个32位的数据寄存器IDR和ODR;1个32位的置位/复位寄存器BSRR;一个16位的复位寄存器BRR;1个32位的锁存寄存器LCKR;这里我们仅介绍常用 的几个寄存器,我们常用的IO端口寄存器只有4个:CRL、CRH、IDR、ODR。 STM32的IO口位配置表如表3.1.1.1所示: STM32输出模式配置如表3.1.1.2所示: 该寄存
[单片机]
大神教你如何快速使用DMA处理ADC
ADC: 1.STM32内部的ADC模块有三个ADC1,ADC2,ADC3,他们彼此独立,所以可以进行同步采样。 2ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生,要在RCC_CFGR配置,再ADC自己的寄存器中在没有时钟分频的配置位。 3.ADC转换时间: STM32F103xx增强型产,时钟为56MHz时为1μ s( 时钟为72MHz为1.17 μ s) 4.ADC的转换精度默认设置为12位,输入范围:ADC输入范围:V REF-≤ VIN≤ VREF+ 5.共有18个通道,其中外部16个通道,内部两个通道,内部温度传感器连接在ADC1_IN16,内部参考电压V REFINT连接在ADC1_IN17 6
[单片机]
大神教你如何快速使用DMA处理ADC
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved