stm32——RTC&BKP

取经自江科大,RTC实时时钟与BKP备份寄存器、PWR电源控制关联性较强

对于BKP寄存器,与Flash存储(真正的掉电不丢失)不同,需要UBAT端口接3.3V/电池才能存储数据(板子不供电或者复位也能保持)

一、Unix时间戳

1.1Unix Timestamp

实际上就是秒计数器数值1970年1月1日0时0分0秒开始(伦敦的本初子午线),世界上所有时区的秒计数器相同,不同时区通过添加偏移值即可得到当地时间。不同于硬件定时器,它的存在占用较少硬件电路和寄存器、方便计算、存储方便,不好的地方则是像后台软件一样会占用一些软件资源

关于秒计数器,它是32/64位的整型数值,早期Unix操作系统用的是有符号整型(int32_t),最大计数(2^32)/2-1,2038年会溢出;STM32则是用的uint32_t,2106年溢出;如今,不特殊说明,都用的是64位,不用担心溢出问题

1.2GMT/UTC

GMT(Greenwich Mean Time),格林威治标准时间:以地球自转为基准,缺点是地球自转受潮汐力、地球活动影响越转越慢,所以这个时间不固定

UTC(Universal Time Coordinated),协调世界时,以原子钟(铯原子辐射)为基准,属于高精度

闰秒:为消除现实(GMT)与理想(UTC)时间的误差,当原子钟计时一天的时间与地球自转一周时间的误差大于0.9s,执行闰秒操作来协调。上一次是2017年,也就是说有那么一分钟其实是61秒

1.3时间戳转换

C语言的库函数time.h中,有很多便于我们获取系统时间或者秒计数器值的接口,以下截取一些

有关的三种数据类型:time_t 秒计数器数据类型、struct tm 日期时间结构体类型、char*

对于struct tm结构体,其中参数:tm_mon需要+1(0~11),tm_year需要加70(原本是自1900开始),tm_isdst:+1表示使用夏令时,0表示不使用夏令时

这些接口的一些注意事项:

  • time函数,参数和返回值都可得到系统时间,只需要返回值时,参数给NULL即可,这个函数在STM32中用不了,因为是离线的裸机...
  • gmtime和localtime函数,区别只是后者可以获得当地系统时间,结果放在结构体变量中
  • strftime函数,类似于printf的用法,三个参数可以这样赋值(数组名,数组长度,“自定义的格式”)

二、BKP

2.1BKP简介

Backup Registers备份寄存器,可用于存储用户应用程序数据

  • TAMPER引脚对产生的侵入事件处理操作是将所有备份寄存器的内容清除
  • RTC引脚输出RTC校准时钟、RTC闹钟脉冲或秒脉冲
  • 存储RTC时钟校准寄存器
  • 用户数据存储容量:20bytes(中/小容量)或84bytes(大容量/互联型)

对于F103C8T6,PC13、TAMPER、RTC共用一个端口。

有关TAMPER侵入检测:

硬件电路上可以外接上拉电阻,再引出一根线到设备的防拆开关或触点,通过边沿信号来检测

当侵入事件产生,BKP数据自动清零并申请中断,在中断中还可继续保护设备,比如清除其他存储器数据、锁死设备等

此外,主电源断电,侵入检测仍然有效

2.1BKP基本结构

主电源断电时由VBAT备用电池供电;主电源上电时,切换为VDD提供

三、BKP相关库函数及应用

3.1BKP库函数

3.2BKP应用

#include "stm32f10x.h"
#include "bkp.h"
#include "swi2c.h"

void BKP_Init (void)
{
	//开启PWR、BKP时钟,用PWR来使能对BKP的访问
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
	PWR_BackupAccessCmd(ENABLE);	
	
}
#include "stm32f10x.h"
#include "SysTick.h"
#include "swi2c.h"
#include "bkp.h"
#include "key.h"

uint16_t WR_Buffer[1]={0x1234};
uint16_t RD_Buffer[1];
uint8_t Key_Num;

int  main()
{	
	SW_IIC_Init();
	SW_OLED_Init();
	delay_ms(1000);
	OLED_CLEAR();

	OLED_ShowStr(1,1,"WR:",2);
	OLED_ShowStr(1,3,"RD:",2);
	
	Key_Init();
	BKP_Init();
	
	while(1)
	{	
		Key_Num =Get_KeyState();
		if(Key_Num == 1) //按键1按下 写数组执行-
		{	
			WR_Buffer[0]--;
			BKP_WriteBackupRegister(BKP_DR1,WR_Buffer[0]); //写到BKP_DR1
			OLED_ShowHexNums(49,1,WR_Buffer[0],4,2); //OLED显示
		}
		if(Key_Num == 2) //按键2按下 写数组执行+
		{	
			WR_Buffer[0]++;
			BKP_WriteBackupRegister(BKP_DR1,WR_Buffer[0]);//写到BKP_DR1
			OLED_ShowHexNums(49,1,WR_Buffer[0],4,2);	//OLED显示
		}
		
		RD_Buffer[0] = BKP_ReadBackupRegister(BKP_DR1);; //将BKP_DR1数据始终读到读数组中
		OLED_ShowHexNums(49,3,RD_Buffer[0],4,2);//OLED显示读出的数据
			
	}
	
}

注意oled显示不要选择显示BKP_DRx(因为是寄存器的地址宏)

四、RTC

4.1RTC简介

Real Time Clock  实时、独立定时器,提供年、月、日、时、分、秒的信息

  • 32位的可编程计数器RTC_CNT,对应Unix时间戳的秒计数器
  • 20位的可编程预分频器RTC_DIV(将高频时钟降到1Hz)
  • 可选择三种时钟源(一般都用LSE):HSE(8M/128)、LSE(32.768K)、LSI(40K)

4.2RTC框图

主要分为四部分:

  • APB1读写相关
  • 后备区域(备用电池可维持工作)分频、计数,当RTC_CNT == RTC_ALR时,可触发终端或唤醒设备,RTC_DIV余数寄存器,自减一轮RTC_CNT+1
  • 中断相关,'F'中断标志位    'IE'使能中断
  • PWR关联,RTC闹钟可以唤醒设备,退出待机模式

4.3RTC基本结构

(使能的中断才能通往NVIC)

4.4硬件电路

注意VBAT供电3V而不是5V即可

4.5RTC操作注意事项

  • 要使用BKP或RTC,需要进行:开启PWR和BKP的时钟、使用PWR来使能对BKP、RTC的访问(调用API即可)
  • 初始化时,需要RTC等待同步,具体是PCLK和RTC_CLK的同步(调用API即可)
  • RTC_CRL寄存器中的CNF位必须置1——使RTC进入配置模式后,才能写入RTC_PRL、RTC_DIV、RTC_CNT寄存器
  • 对RTC任何寄存器的 写 操作,都必须等待前一次写操作结束,具体通过查询RTC_CR寄存器中的RTOFF标志位,仅当它为1时,才能写入,(调用API即可)

五、RTC相关库函数及应用

5.1RTC库函数

5.2RTC应用

#include "stm32f10x.h"
#include "bkp.h"
#include "rtc.h"
#include <stdio.h>
#include <time.h>

void RTC_Init()
{
	
	BKP_Init(); //开启PWR、BKP时钟,用PWR来使能对BKP的访问
	//RCC时钟配置、等待准备好、选择时钟源、使能 -----基于LSE
	RCC_LSEConfig(RCC_LSE_ON);
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
	RCC_RTCCLKCmd(ENABLE);

	//等待同步、等待写结束(因为等待同步的函数里有对寄存器的写操作)
	RTC_WaitForSynchro();
	RTC_WaitForLastTask();
	
	//设置预分频
	RTC_SetPrescaler(32768-1);
	RTC_WaitForLastTask();	
}

void RTC_SetTime(uint16_t DateToCnt[6])
{
	time_t time_cnt;
	struct tm time_date;
	
	//将所要设置的时间赋值进结构体变量
	time_date.tm_year = DateToCnt[0]-1900;
	time_date.tm_mon = DateToCnt[1]-1;
	time_date.tm_mday = DateToCnt[2];
	time_date.tm_hour = DateToCnt[3];
	time_date.tm_min = DateToCnt[4];
	time_date.tm_sec = DateToCnt[5];
	
	//转换为time_t 设置给RTC   这里不用return是因为需要的时候去调用GetCounter接口即可
	time_cnt = mktime(&time_date);
	RTC_SetCounter(time_cnt);
	RTC_WaitForLastTask();
}

void RTC_ReadTime(time_t *CntToSet,uint16_t CntToDate[6])
{
	struct tm time_date;
	
	//利用所给计数器的值,调用localtime获得伦敦时间 
	time_date = *(localtime(CntToSet));
	
	//赋值到要存放的数组里
	CntToDate[0] = time_date.tm_year +1900;
	CntToDate[1] = time_date.tm_mon +1;
	CntToDate[2] = time_date.tm_mday;
	CntToDate[3] = time_date.tm_hour;
	CntToDate[4] = time_date.tm_min;
	CntToDate[5] = time_date.tm_sec;
}
#include "stm32f10x.h"
#include "SysTick.h"
#include "swi2c.h"
#include "bkp.h"
#include "rtc.h"
#include "time.h"

time_t CntToSet = 1451020492;//要转换为日期的计数器值
uint16_t CntToDate[6]; //由计数器值转换的日期时间值

uint16_t DateToCnt[6]={2015,12,25,13,14,52}; //要转换为计数器的时间

int  main()
{	
	//oled初始化
	SW_IIC_Init();
	SW_OLED_Init();
	delay_ms(1000);
	OLED_CLEAR();

	//RTC初始化
	RTC_Init();
	
	OLED_ShowStr(0,1,"Date:XXXX-XX-XX",2);
	OLED_ShowStr(0,3,"Time:XX:XX:XX",2);
	OLED_ShowStr(0,5,"CNT:",2);
	
	CntToSet = CntToSet+(8*3600); //东八区+8h  直接加cnt的值不容易造成进位风险
	RTC_ReadTime(&CntToSet,CntToDate);
	RTC_SetTime(DateToCnt);
	
	OLED_ShowNums(40,1,CntToDate[0],4,2);
	OLED_ShowNums(80,1,CntToDate[1],2,2);
	OLED_ShowNums(104,1,CntToDate[2],2,2);
	OLED_ShowNums(40,3,CntToDate[3],2,2);
	OLED_ShowNums(64,3,CntToDate[4],2,2);
	OLED_ShowNums(88,3,CntToDate[5],2,2);
	OLED_ShowNums(32,5,RTC_GetCounter()-8*3600,10,2);//所转换时间已为东八区 所以实际伦敦秒计数器的值要减
	
	
	while(1)
	{	

			
	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值