STM32-RTC实时时钟

目录

RTC实时时钟

功能框图

UNIX时间戳

初始化结构体

RTC时间结构体

RTC日期结构体

RTC闹钟结构体

进入和退出配置函数

实验环节1:显示日历

常规配置

RTC配置

测试环节

实验现象

实验环节2:闹钟

常规配置

RTC配置

测试环节

实验现象


RTC实时时钟

STM32的RTC外设,实质上是一个掉电后还继续运行的定时器。类似于通用定时器TIM外设,可以计时和触发中断。

掉电指的是电源VDD断开时为了RTC外设掉电继续运行,必须接上锂电池给STM32的RTC、备份发卡通过Vbat引脚供电。当主电源VDD有效时由VDD给RTC外设供电,当CDD掉电后,由Vbat给RTC外设供电。但无论由什么电源供电,RTC的数据都保存在属于RTC的备份域中,若主电源VDD和Vbat都掉电,则备份域保存的所有数据将丢失。备份域除了RTC模块的寄存器,还有42个16位的寄存器可以在VDD掉电时保存用户程序的数据,系统复位或电源复位时,数据也不会清空。

从RTC的定时器特性来说,是一个32位的递增计数器。时钟源有三种:HSE/128、LSI和LSE。

使用LSI或HSE/8时钟源,在主电源VDD掉电时,这两个时钟来源都会受到影响,因此无法保证RTC正常工作。因此RTC一般使用LSE。在设计中,频率通常为32.768kHz

在主电源VDD有效并处于待机模式时,RTC还可以配置闹钟事件时STM32退出待机模式

功能框图

浅灰色部分属于备份域,VDD掉电时可在Vbat的驱动下继续运行。包括了RTC分频器、计数器、闹钟控制器。

若VDD电源有效,RTC可以触发RTC_Second(秒中断)、RTC_Overflow(溢出事件)和RTC_Alarm(闹钟中断)。从结构图可以分析出,定时器溢出中断无法被配置为中断。

若STM32处于待机模式,可由闹钟事件或WKUP事件(外部唤醒事件,属于EXTI模块,不属于RTC)退出待机模式。

闹钟事件在计数器RTC_CNT的值等于闹钟寄存器RTC_ALR的值时触发。

在备份域中所有寄存器都是16位的,RTC控制相关的寄存器也不例外。它的计数器RTC_CNT的32位由RTC_CNTL和RTC_CNTH这两个寄存器组成。

在配置RTC模块的时钟时,通常把输入的32768Hz的RTCCLK进行32768分频得到实际驱动计数器的时钟TR_CLK = RTCCLK / 32768 =1Hz,计数周期为1s,计数器在TR_CLK的驱动下计数,即每秒计数器RTC_CNT的值+1。

由于备份域的存在,使得RTC核具有了完全独立于APB1接口的特性,也因此对RTC寄存器的访问要遵守一定的规则。

系统复位后,默认禁止访问后备寄存器和RTC,防止对后备区域(BKP)的意外写操作。执行以下操作使能对后备寄存器和RTC的访问:

        设置RCC_APB1ENR:PWREN、BKPEN位来使能电源和后备接口时钟。

        设置PWR_CR:DBP位使能对后备寄存器和RTC的访问。

设置后备寄存器为可写访问后,在第一次通过APB1接口访问RTC时,因为时钟频率的差异,所以必须等待APB1和RTC外设同步,确保被读取出来的RTC寄存器值是正确的。若在同步后,一直没有关闭APB1的RTC外设接口,就不需要再次同步了。

如果内核要对RTC寄存器进行任何写操作,在内核写出写指令后,RTC模块在3个RTCCLK时钟后才开始正式的写RTC寄存器操作。由于RTCCLK的频率比内核主频低得多,所以每次操作后都必须检查RTC关闭操作标志位RTOFF,当这个标志位被置1,写操作才正式完成。

当然,以上操作都具有对应的库函数,不需要具体的查阅寄存器。 

UNIX时间戳

RTC_CNT是32位寄存器,可存储的最大值为2^32-1,即约等于136年。

如某个时刻读取计数器的值为2天的秒数,以2011.1.1 0:0:0时间置0计数器的,则可以算出是2011.1.3 0:0:0时间,计数器会在2011+136年左右溢出。定时器被置0的时间为计数元年,相对计时元年的秒数为时间戳(计数器的值)。

大多数操作系统都是利用时间戳和计时元年来计算当前时间的,有个标准:UNIX时间戳和UNIX计时元年。

UNIX计时元年被设置为格林威治时间1970.1.1 0:0:0时间。

在这个计时系统上,使用的是有符号的32位整形变量来保存UNIX时间戳,因此最高位表示符号,时间戳能显示的范围更小了,会在2038.1.19 3:14:07时间溢出。

网页上可搜:UNIX时间戳。可实时查看。

初始化结构体

STM32 HAL库对RTC控制提供了完善的函数。

typedef struct {uint32_t AsynchPrediv;    /* 配置RTC_CLK的异步分频因子(0x00~0x7F ) ,具体由RTC_PRER:PREDIV_A[6:0]配置 */uint32_t OutPut;          /* RTCEx输出通道设置,指定哪一路信号作为RTC的输出,禁止输出/闹钟A输出/闹钟B输出/唤醒输出 */} RTC_InitTypeDef;

RTC时间结构体

用来设置初始时间,配置的是RTC时间寄存器RTC_TR。

 typedef struct {uint8_t Hours;    /* 小时设置。12小时制式时,0~11;24小时制式时,0~23 */uint8_t Minutes;  /* 分钟设置,0~59 */uint8_t Seconds;  /* 秒设置,0~59 */} RTC_TimeTypeDef;

RTC日期结构体

用来设置初始日期,配置的是RTC日期寄存器RTC_DR。

typedef struct {uint8_t WeekDay; /* 星期几设置,1~7 */uint8_t Month;   /* 月份设置,1~12 */uint8_t Date;    /* 日期设置,1~31 */uint8_t Year;    /* 年份设置,0~99 */} RTC_DateTypeDef;

RTC闹钟结构体

用来设置闹钟时间,设置的格式为[星期/日期]-[时]-[分]-[秒],4个字段,每个字段可以设置为有效或无效(MASK)。如果MASK掉[星期/日期]字段,则每天闹钟都会响。

typedef struct {RTC_TimeTypeDef AlarmTime;     /* 设定RTC时间寄存器的值:时/分/秒 */uint32_t Alarm;                /* RTC 闹钟选择:闹钟A、闹钟B */} RTC_AlarmTypeDef;

进入和退出配置函数

/*** @brief  进入 RTC 配置模式 .* @param  None* @retval None*/void RTC_EnterConfigMode(void){/* 设置 CNF 位进入配置模式 */RTC->CRL |= RTC_CRL_CNF;}/** @brief  退出 RTC 配置模式 .* @param  None* @retval None*/void RTC_ExitConfigMode(void){/* 清空  CNF 位退出配置模式 */RTC->CRL &= (uint16_t)~((uint16_t)RTC_CRL_CNF);}

实验环节1:显示日历

常规配置

USART1:带中断,支持printf输出。

RTC配置

RTC_HandleTypeDef hrtc;/* RTC init function */
void MX_RTC_Init(void)
{RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef DateToUpdate = {0};/* USER CODE BEGIN RTC_Init 1 *//* 判断是否首次上电 */if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x5050){HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5050);	// 插入BKP数值判断/* USER CODE END RTC_Init 1 *//** Initialize RTC Only*/hrtc.Instance = RTC;hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;if (HAL_RTC_Init(&hrtc) != HAL_OK){Error_Handler();}/** Initialize RTC and set the Time and Date*/sTime.Hours = 0x0;sTime.Minutes = 0x0;sTime.Seconds = 0x0;if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;DateToUpdate.Month = RTC_MONTH_JANUARY;DateToUpdate.Date = 0x1;DateToUpdate.Year = 0x0;if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}/* USER CODE BEGIN RTC_Init 2 */}/* USER CODE END RTC_Init 2 */
}void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){HAL_PWR_EnableBkUpAccess();	// 取消BKP区域写保护,才能进行时间保存和计时/* Enable BKP CLK enable for backup registers */__HAL_RCC_BKP_CLK_ENABLE();	// 开启BKP时钟/* RTC clock enable */__HAL_RCC_RTC_ENABLE();}
}void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){__HAL_RCC_RTC_DISABLE();}
}

测试环节

#include "string.h"uint8_t RxBuffer[20];void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){}
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){// A5 5A 00 01 01 00 00 00if(RxBuffer[0]==0xA5 && RxBuffer[1]==0x5A){RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;RtcTime.Hours = RxBuffer[5];RtcTime.Minutes = RxBuffer[6];RtcTime.Seconds = RxBuffer[7];if (HAL_RTC_SetTime(&hrtc, &RtcTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}// 星期内部自动校正RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;RtcDate.Month = RxBuffer[3];RtcDate.Date = RxBuffer[4];RtcDate.Year = RxBuffer[2];if (HAL_RTC_SetDate(&hrtc, &RtcDate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}memset(RxBuffer, 0, sizeof(RxBuffer));HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);}}	
}void test(void)
{RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);while(1){HAL_RTC_GetTime(&hrtc, &RtcTime,  RTC_FORMAT_BIN);	// 读出时间值HAL_RTC_GetDate(&hrtc, &RtcDate,  RTC_FORMAT_BIN);	// 一定要先读时间后读日期,这样才能校正星期参数printf("实时时间:%04d-%02d-%02d  %02d:%02d:%02d 星期:%2d\r\n", 2000+RtcDate.Year, RtcDate.Month, RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//显示日期时间HAL_Delay(1000);}
}

实验现象

实验环节2:闹钟

常规配置

USART1:带中断,支持printf输出。

蜂鸣器配置。

RTC配置

RTC_HandleTypeDef hrtc;void MX_RTC_Init(void)
{RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef DateToUpdate = {0};RTC_AlarmTypeDef sAlarm = {0};/* USER CODE BEGIN RTC_Init 1 *//* 判断是否首次上电 */if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x5050){HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5050);	// 插入BKP数值判断/* USER CODE END RTC_Init 1 *//** Initialize RTC Only*/hrtc.Instance = RTC;hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;if (HAL_RTC_Init(&hrtc) != HAL_OK){Error_Handler();}/** Initialize RTC and set the Time and Date*/sTime.Hours = 0x0;sTime.Minutes = 0x0;sTime.Seconds = 0x0;if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;DateToUpdate.Month = RTC_MONTH_JANUARY;DateToUpdate.Date = 0x1;DateToUpdate.Year = 0x0;if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}/** Enable the Alarm A*/sAlarm.AlarmTime.Hours = 0x0;sAlarm.AlarmTime.Minutes = 0x1;sAlarm.AlarmTime.Seconds = 0x0;sAlarm.Alarm = RTC_ALARM_A;if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}/* USER CODE BEGIN RTC_Init 2 */}/* USER CODE END RTC_Init 2 */
}void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){HAL_PWR_EnableBkUpAccess();/* Enable BKP CLK enable for backup registers */__HAL_RCC_BKP_CLK_ENABLE();/* RTC clock enable */__HAL_RCC_RTC_ENABLE();/* RTC interrupt Init */HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);}
}void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{if (rtcHandle->Instance == RTC){__HAL_RCC_RTC_DISABLE();/* RTC interrupt Deinit */HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);}
}

测试环节

#include "string.h"uint8_t RxBuffer[20];void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){}
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){// A5 5A 00 01 01 00 00 00if(RxBuffer[0]==0xA5 && RxBuffer[1]==0x5A){RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;RTC_AlarmTypeDef RtcAlarm;RtcTime.Hours = RxBuffer[5];RtcTime.Minutes = RxBuffer[6];RtcTime.Seconds = RxBuffer[7];if (HAL_RTC_SetTime(&hrtc, &RtcTime, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}// 星期内部自动校正RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;RtcDate.Month = RxBuffer[3];RtcDate.Date = RxBuffer[4];RtcDate.Year = RxBuffer[2];if (HAL_RTC_SetDate(&hrtc, &RtcDate, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}RtcAlarm.AlarmTime.Hours = RxBuffer[5];RtcAlarm.AlarmTime.Minutes = RxBuffer[6];RtcAlarm.AlarmTime.Seconds = RxBuffer[7] + 0x10;RtcAlarm.Alarm = RTC_ALARM_A;if (HAL_RTC_SetAlarm_IT(&hrtc, &RtcAlarm, RTC_FORMAT_BCD) != HAL_OK){Error_Handler();}memset(RxBuffer, 0, sizeof(RxBuffer));HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);}}	
}void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
}void test(void)
{RTC_DateTypeDef RtcDate;RTC_TimeTypeDef RtcTime;初始化HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);while(1){HAL_RTC_GetTime(&hrtc, &RtcTime,  RTC_FORMAT_BIN);	// 读出时间值HAL_RTC_GetDate(&hrtc, &RtcDate,  RTC_FORMAT_BIN);	// 一定要先读时间后读日期,这样才能校正星期参数printf("实时时间:%04d-%02d-%02d  %02d:%02d:%02d 星期:%2d\r\n", 2000+RtcDate.Year, RtcDate.Month, RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//显示日期时间HAL_Delay(1000);}
}

实验现象

上电运行,LED默认灭。一分钟后LED亮绿灯。

通过串口调试助手发送A55A231101000000,10秒后LED灭。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/158157.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC]

文章目录 致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 致远OA wpsAssistServlet任意文件读取漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用…

lua-web-utils库

lua--导入所需的库local web_utilsrequire("lua-web-utils")--定义要下载的URLlocal url"https://jshk.com.cn/"--定义代理服务器的主机名和端口号local proxy_port8000--使用web_utils的download函数下载URLlocal file_pathweb_utils.download(url,proxy_…

HALCON的综合应用案例【01】: 3D 算法处理在 Visual Studio 2019 C# 环境中的集成实例

前言: HALCON 为一款比较流行的商业视觉处理软件,他提供了多种开发的模式,可以在HALCON中开发,也可以将HALCON的设计通过导出库的形式集成到其他开发环境里面,以方便系统集成。本文为笔者自己的一个3D 视觉检测项目,利用HALCON的3D 库开发算法,然后,将算法集成到 MS-V…

力扣每日一题92:反转链表||

题目描述&#xff1a; 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输…

VS Code 开发Java之代码导航和代码编辑

快速导航 VS Code提供了一些功能可以在Java开发时快速导航。 代码导航 大纲视图&#xff08;Outline view&#xff09;&#xff1a; 方便地导航当前文件中的成员项目视图&#xff08;Projects view&#xff09;&#xff1a; 项目的详细概述Java编辑器&#xff1a; 支持调用层…

你担心spring容器中scope为prototype的bean太大内存溢出吗?

你担心spring容器中scope为prototype的bean太大内存溢出吗&#xff1f; 提出假设 之前一直担心spring的scope为prototype的bean在一些高并发的场景下&#xff0c;吃不消吗&#xff0c;甚至会内存溢出&#xff0c;这样的担心不是没有道理的&#xff0c;&#xff08;以下是假设…

AI智能分析网关高空抛物算法如何实时检测高楼外立面剥落?

高楼外立面剥落是一种十分危险的行为&#xff0c;会造成严重的人身伤害和财产损失。TSINGSEE青犀智能分析网关利用高楼外立面剥落的信息&#xff0c;结合高空抛物算法来进行处理就可很好解决此问题。 1. 数据收集 首先&#xff0c;需要收集关于高楼外立面剥落的数据。这可以通…

C++二分查找算法的应用:俄罗斯套娃信封问题

本文涉及的基础知识点 二分查找 题目 给你一个二维整数数组 envelopes &#xff0c;其中 envelopes[i] [wi, hi] &#xff0c;表示第 i 个信封的宽度和高度。 当另一个信封的宽度和高度都比这个信封大的时候&#xff0c;这个信封就可以放进另一个信封里&#xff0c;如同俄罗…

K8s Error: ImagePullBackOff 故障排除

Error: ImagePullBackOff 故障排除 1. 起因 起因是要在一组k8s环境下做个Prometheus的测试,当时虚拟机用完直接暂停了. 启动完master和node节点后重启了这些节点. 当检查dashboard时候发现Pod处于ImagePullBackOff状态,使用命令查看详细情况 kubectl describe pods -n kuber…

5G物联网关相较有线网关有哪些独特优势

5G为产业物联网应用带来了质的飞跃&#xff0c;5G技术实现更高速率、更低延迟和更大带宽&#xff0c;使得物联网能够接入更多数量的设备&#xff0c;实现更稳定、高效的连接和数据传输&#xff0c;在提高生产效率的同时&#xff0c;也进一步促进了物联网的应用发展和升级。 针对…

视频剪辑技巧:如何使用背景图片增强视频的画中画效果

在视频剪辑中&#xff0c;背景图片的运用可以极大地增强视频的视觉效果&#xff0c;尤其是画中画效果的呈现。本文介绍如何使用云炫AI智剪将背景图片批量制作画中画视频的实用剪辑技巧。未来随着技术的不断发展和创新&#xff0c;我们可以期待更多富有创意的视频作品出现。同时…

关于pytorch张量维度转换及张量运算

关于pytorch张量维度转换大全 1 tensor.view()2 tensor.reshape()3 tensor.squeeze()和tensor.unsqueeze()3.1 tensor.squeeze() 降维3.2 tensor.unsqueeze(idx)升维 4 tensor.permute()5 torch.cat([a,b],dim)6 torch.stack()7 torch.chunk()和torch.split()8 与tensor相乘运算…