当前位置: 首页 > article >正文

STM32G474RE之RTC

STM32G474RE之RTC使用HAL库实现RTC时间配置,以及报警配置,支持双路报警。

1、STM32G474RE的RTC晶振引脚:

OSC32_IN为PC14,OSC32_OUT为PC15;

2、Vbat引脚

Vbat引脚是用来给外部晶振LSE和备份寄存器提供电源。当没有“外部电池”连接到Vbat引脚,RTC会使用VDD供电。因此Vbat也可以不接“外部电池”。1.55V<Vbat<3.6V

通过上图,连接到Vbat的外部电池,需要设计充电电路给它充电

3、RTC_OUT映射

RTC报警输出RTC_OUT1和RTC_OUT2
RTC_OUT1为PC13,RTC_OUT2为PB2;

RTC_CR寄存器的bit22:21(OSEL[1:0])
OSEL[1:0]=00b,输出不使能
OSEL[1:0]=01b,RTC报警器A输出使能
OSEL[1:0]=10b,RTC报警器B输出使能
OSEL[1:0]=11b,RTC唤醒输出使能

RTC_CR寄存器的bit31(OUT2EN)
OUT2EN=0,RTC_OUT2输出不使能;
OUT2EN=1,RTC_OUT2输出使能;

RTC_CR寄存器的bit23(COE)
COE=0校准输出不使能
COE=1校准输出使能

RTC_CR寄存器的bit19(COSEL)
COE=1且COSEL=0,CALIB输出为512Hz
COE=1且COSEL=1,CALIB输出为1Hz

 4、测试程序

RTC.c程序

RTC_HandleTypeDef hrtc;

void RTC_Init(void);
void RTC_Display(void);

void TEST_Hal_RTC_Set_Alarm_A(void);
void TEST_Hal_RTC_Set_Alarm_B(void);

void RTC_Init(void)
{
	RCC_OscInitTypeDef        RCC_OscInitStruct;  //配置LSE/LSI时钟
	RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;//用来为RTC选择时钟源

  __HAL_RCC_PWR_CLK_ENABLE();//Enable write access
	HAL_PWR_EnableBkUpAccess();//Enable the power clock

#ifdef RTC_CLOCK_SOURCE_LSE
  RCC_OscInitStruct.OscillatorType =  RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
  HAL_RCC_OscConfig(&RCC_OscInitStruct);
	//配置LSE时钟,关闭LSI时钟

  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
	//选择LSE时钟为RTC时钟源

#elif defined (RTC_CLOCK_SOURCE_LSI)
  RCC_OscInitStruct.OscillatorType =  RCC_OSCILLATORTYPE_LSI;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
	HAL_RCC_OscConfig(&RCC_OscInitStruct);
	//配置LSI时钟

  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
	HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
	//选择LSI时钟为RTC时钟源
#endif /*RTC_CLOCK_SOURCE_LSE*/

  __HAL_RCC_RTCAPB_CLK_ENABLE();//使能RTC APB外部设备时钟,Enable RTC peripheral Clocks
  __HAL_RCC_RTC_ENABLE();//使能RTC时钟,Enable RTC Clock

  hrtc.Instance = RTC;                       //选择RTC
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;  //指定RTC小时的格式
  hrtc.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;//指定RTC异步预分法器的值。
  hrtc.Init.SynchPrediv = RTC_SYNCH_PREDIV;  //指定RTC同步预分配器的值。
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE;
  HAL_RTC_Init(&hrtc);

  if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != 0x32F2)
  {//读TAMP_BKPxR寄存器,因为RTC_BKP_DR0=0,所以是读TAMP_BKP0R寄存器

		Test_RTC_Set_Date_And_Time();//设置时间为24年2月29日星期四23:59:50:00

		HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x32F2);
		//写TAMP_BKPxR寄存器,因为RTC_BKP_DR0=0,所以是写TAMP_BKPxR寄存器
		//将0x32F2写入TAMP_BKP0R寄存器
		//Writes a data in a RTC Backup data Register0
  }


  TEST_Hal_RTC_Set_Alarm_A();
  TEST_Hal_RTC_Set_Alarm_B();
}

//函数功能:显示“年月日和星期几”以及“时分秒”
void RTC_Display(void)
{
  RTC_DateTypeDef sdatestructureget;//用来保存“年月日和星期几”
  RTC_TimeTypeDef stimestructureget;//用来保存“时分秒”

  printf("RTC_IT_SEC\r\n");
  HAL_RTC_GetTime(NULL, &stimestructureget, RTC_FORMAT_BIN);//读取"RTC时间"
	//读RTC_TR寄存器bit21:20(HT[1:0]),HT[1:0]表示小时的十位数值
	//读RTC_TR寄存器bit19:16(HU[3:0]),HU[3:0]表示小时的个位数值
	//读RTC_TR寄存器bit14:12(MNT[2:0]),MNT[2:0]表示分钟的十位数值
	//读RTC_TR寄存器bit11:8(MNU[3:0]),MNU[3:0]表示分钟的个位数值
	//读RTC_TR寄存器bit6:4(ST[2:0]),ST[2:0]表示秒的十位数值
	//读RTC_TR寄存器bit3:0(SU[3:0]),SU[3:0]表示秒的个位数值
	//HAL库耍牛氓,我也耍牛氓

  HAL_RTC_GetDate(NULL, &sdatestructureget, RTC_FORMAT_BIN);//读取"RTC日期"
	//读RTC_DR寄存器bit23:20(YT[3:0]),YT[3:0]表示年的十位数值
	//读RTC_DR寄存器bit19:16(YU[3:0]),YU[3:0]表示年的个位数值
	//读RTC_DR寄存器bit15:13(WDU[2:0]),WDU[2:0]=001b表示星期1;WDU[2:0]=010b表示星期2......WDU[2:0]=111b表示星期日
	//读RTC_DR寄存器bit12(MT),MT表示月的十位数值
	//读RTC_DR寄存器bit11:8(MU[3:0]),MU[3:0]表示月的个位数值
	//读RTC_DR寄存器bit5:4(DT[1:0]),DT[1:0]表示日的十位数值
	//读RTC_DR寄存器bit3:0(DU[3:0]),DU[3:0]表示日的个位数值
	//HAL库耍牛氓,我也耍牛氓

	printf("%02d-%02d-%02d  day:%02d  ", sdatestructureget.Year,sdatestructureget.Month,sdatestructureget.Date,sdatestructureget.WeekDay);
  printf("%02d:%02d:%02d\r\n", stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);
	//显示时间格式为 : YY-MM-DD hh:mm:ss
}


//设置时间为24年2月29日星期四23:59:50:00
void Test_RTC_Set_Date_And_Time(void)
{
  RTC_TimeTypeDef sTime = {0};  //用来设置时间“时分秒”
  RTC_DateTypeDef sDate = {0};  //用来设置日期“年月日和星期几”

	sTime.Hours = 0x23;      //这里是BCD码,0x23表示23小时
	sTime.Minutes = 0x59;    //这里是BCD码,0x59表示59分钟
	sTime.Seconds = 0x50;    //这里是BCD码,0x50表示50秒种
	sTime.SubSeconds = 0x00;
	//RTC_SSR寄存器bit15:0(SS[15:0])是只读的,这里设置为0x00没有意义
	sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
	//RTC_CR寄存器bit16(ADD1H),ADD1H=0,当前时间不增加1小时
	//若使用“夏令时”,ADD1H=1,表示当前时间增加1小时
	sTime.StoreOperation = RTC_STOREOPERATION_RESET;
	//RTC_CR寄存器bit18(BKP),BKP=0,不使用“夏令时”
	//现在已经废除了“夏令时”,不要去了解。
	HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD);
	//设置“时分秒”

	sDate.WeekDay = RTC_WEEKDAY_THURSDAY;
	//这里是BCD码,RTC_WEEKDAY_THURSDAY表示0x04,星期四
	sDate.Date = 0x29; //这里是BCD码,0x29表示29日
	sDate.Month = RTC_MONTH_FEBRUARY;
	//这里是BCD码,RTC_MONTH_FEBRUARY表示0x02,2月
	sDate.Year = 0x24; //这里是BCD码,0x24表示24年
	HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD);
	//设置“年月日”
}

//使用HAL设置报警,有点呆板
void TEST_Hal_RTC_Set_Alarm_A(void)
{
	RTC_AlarmTypeDef sAlarm = {0};//用来设置报警时间

	HAL_RTC_DeactivateAlarm(&hrtc,RTC_ALARM_A);
	//选择RTC_ALARM_A时,表示停用AlarmA报警:
	//不使能“Alarm A报警”和“Alarm A报警中断”,同时不选择“RTC报警事件”作为触发源
	//必须先停用AlarmA报警,才能配置AlarmA报警时间

	sAlarm.Alarm = RTC_ALARM_A; //指定配置RTC报警A寄存器(RTC_ALRMAR)
	sAlarm.AlarmTime.TimeFormat = RTC_HOURFORMAT_24;  //指定RTC报警小时的格式

  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
	//指定RTC警报是按照日期报警
  sAlarm.AlarmDateWeekDay = 0x01;
	//每月1号报警

//  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
	//指定RTC警报是按照星期报警
//  sAlarm.AlarmDateWeekDay = RTC_WEEKDAY_FRIDAY;
	//每个星期五报警

  sAlarm.AlarmTime.Hours = 0x00;    //这里是BCD码,0x00表示00小时
  sAlarm.AlarmTime.Minutes = 0x00;  //这里是BCD码,0x00表示00分钟
  sAlarm.AlarmTime.Seconds = 0x20;  //这里是BCD码,0x20表示20秒种
  sAlarm.AlarmTime.SubSeconds = 0x30;
	//RTC_ALRMASSR寄存器bit27:24(MASKSS[3:0]),bit14:0(SS[14:0])
	//当选择“RTC_ALARM_A”时,时间写入RTC_ALRMAR
	//当选择“RTC_ALARM_B”时,时间写入RTC_ALRMBR

//  sAlarm.AlarmMask = RTC_ALARMMASK_HOURS|RTC_ALARMMASK_MINUTES|RTC_ALARMMASK_DATEWEEKDAY;
	//RTC_ALARMMASK_HOURS|RTC_ALARMMASK_MINUTES|RTC_ALARMMASK_DATEWEEKDAY不关注日期小时和分钟,则每分钟报警一次
  sAlarm.AlarmMask = RTC_ALARMMASK_ALL;
	//RTC_ALARMMASK_ALL不关注日期,小时,分钟和秒,则每秒种报警一次

  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
	//需要匹配“子秒”
  HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD);

  HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 5, 0);
	//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
	//选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
  HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}

//使用HAL设置报警,有点呆板
void TEST_Hal_RTC_Set_Alarm_B(void)
{
	RTC_AlarmTypeDef sAlarm = {0};//用来设置报警时间

	HAL_RTC_DeactivateAlarm(&hrtc,RTC_ALARM_B);
	//选择RTC_ALARM_A时,表示停用AlarmB报警:
	//不使能“Alarm B报警”和“Alarm B报警中断”,同时不选择“RTC报警事件”作为触发源
	//必须先停用AlarmB报警,才能配置AlarmB报警时间

	sAlarm.Alarm = RTC_ALARM_B; //指定配置RTC报警B寄存器(RTC_ALRMBR)
	sAlarm.AlarmTime.TimeFormat = RTC_HOURFORMAT_24;  //指定RTC报警小时的格式

  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
	//指定RTC警报是按照日期报警
  sAlarm.AlarmDateWeekDay = 0x01;
	//每月1号报警

//  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
	//指定RTC警报是按照星期报警
//  sAlarm.AlarmDateWeekDay = RTC_WEEKDAY_FRIDAY;
	//每个星期五报警

  sAlarm.AlarmTime.Hours = 0x00;    //这里是BCD码,0x00表示00小时
  sAlarm.AlarmTime.Minutes = 0x00;  //这里是BCD码,0x00表示00分钟
  sAlarm.AlarmTime.Seconds = 0x20;  //这里是BCD码,0x20表示20秒种
  sAlarm.AlarmTime.SubSeconds = 0x30;
	//RTC_ALRMASSR寄存器bit27:24(MASKSS[3:0]),bit14:0(SS[14:0])
	//当选择“RTC_ALARM_A”时,时间写入RTC_ALRMAR
	//当选择“RTC_ALARM_B”时,时间写入RTC_ALRMBR

//  sAlarm.AlarmMask = RTC_ALARMMASK_HOURS|RTC_ALARMMASK_MINUTES|RTC_ALARMMASK_DATEWEEKDAY;
	//RTC_ALARMMASK_HOURS|RTC_ALARMMASK_MINUTES|RTC_ALARMMASK_DATEWEEKDAY不关注日期小时和分钟,则每分钟报警一次
  sAlarm.AlarmMask = RTC_ALARMMASK_ALL;
	//RTC_ALARMMASK_ALL不关注日期,小时,分钟和秒,则每秒种报警一次

  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
	//需要匹配“子秒”
  HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD);

  HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 5, 0);
	//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
	//选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
  HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}

void RTC_Alarm_IRQHandler(void)
{
//  HAL_RTC_AlarmIRQHandler(&hrtc);
  uint32_t tmp = READ_REG(RTC->MISR);//Get interrupt status

  if ((tmp & RTC_MISR_ALRAMF) != 0U)//RTC ALARM A报警
  {//当选择“RTC_ALARM_A”时,时间与RTC_ALRMAR相同时报警
    WRITE_REG(RTC->SCR, RTC_SCR_CALRAF);
		//Clear the AlarmA interrupt pending bit
//    __HAL_RTC_ALARM_EXTI_CLEAR_IT();
//		LED1_On();//报警时,LED亮
		LED1_Toggle();
  }

  if ((tmp & RTC_MISR_ALRBMF) != 0U)//RTC ALARM A报警
  {
		//当选择“RTC_ALARM_B”时,时间与RTC_ALRMBR相同时报警
    WRITE_REG(RTC->SCR, RTC_SCR_CALRBF);
		//Clear the AlarmB interrupt pending bit
//    __HAL_RTC_ALARM_EXTI_CLEAR_IT();
		LED2_Toggle();
  }
	__HAL_RTC_ALARM_EXTI_CLEAR_IT();
}

RTC.h程序

#ifndef __RTC_H__
#define __RTC_H__

#include "stm32g4xx_hal.h"
//使能int8_t,int16_t,int32_t,int64_t
//使能uint8_t,uint16_t,uint32_t,uint64_t

//#define RTC_CLOCK_SOURCE_LSI    //RTC使用CPU内部LSI作为时钟源
#define RTC_CLOCK_SOURCE_LSE

#ifdef RTC_CLOCK_SOURCE_LSI
#define RTC_ASYNCH_PREDIV    0x7C
#define RTC_SYNCH_PREDIV     0xF9
#endif

#ifdef RTC_CLOCK_SOURCE_LSE
#define RTC_ASYNCH_PREDIV  0x7F
#define RTC_SYNCH_PREDIV   0xFF
#endif

extern void RTC_Init(void);
extern void RTC_Display(void);

#endif /*__ RTC_H__ */

5、测试结果

6、配置RTC_OUT引脚输出

//在调用HAL_RTC_Init()之前调用RTC_OUTx_Init()
//RTC_OUTx_Init(&hrtc,RTC_OUTPUT_ALARMA,1);//RTC报警器A从RTC_OUT1引脚输出
//RTC_OUTx_Init(&hrtc,RTC_OUTPUT_ALARMB,1);//RTC报警器B从RTC_OUT1引脚输出
//RTC_OUTx_Init(&hrtc,RTC_OUTPUT_ALARMA,2);//RTC报警器A从RTC_OUT2引脚输出
//RTC_OUTx_Init(&hrtc,RTC_OUTPUT_ALARMB,2);//RTC报警器B从RTC_OUT2引脚输出

注意:
配置RTC_OUT1输出“RTC报警器A的报警标志”,RTC_OUT1输出的电平时间和ALRAF的保持时间有关;
配置RTC_OUT2输出“RTC报警器A的报警标志”,RTC_OUT2输出的电平时间和ALRAF的保持时间有关;

配置RTC_OUT1输出“RTC报警器B的报警标志”,RTC_OUT1输出的电平时间和ALRBF的保持时间有关;
配置RTC_OUT2输出“RTC报警器B的报警标志”,RTC_OUT2输出的电平时间和ALRBF的保持时间有关

 

 调用举例:

 中断函数添加一个延时,否则,示波器差,会看不到波形

 

这里是为了演示RTC_OUT1和RTC_OUT2输出。 


http://www.kler.cn/news/303867.html

相关文章:

  • C++——内存管理
  • 【软考】设计模式之责任链模式
  • springboot对数据库进行备份+对一个文件夹内的文件按时间排序,只保留最近的8个文件
  • 基于鸿蒙API10的RTSP播放器(四:沉浸式播放窗口)
  • 五星级可视化页面(23):污水处理、防汛可视化大屏
  • 自闭症摘帽流程解析:从诊断到摘帽的完整指南
  • graphQL 参数使用报错问题
  • Node.js学习记录(二)
  • 鸿蒙(API 12 Beta6版)GPU加速引擎服务【自适应VRS】
  • C语言 条件编译
  • 【2024】前端学习笔记5-表单标签使用
  • leaflet【十】实时增加轨迹点轨迹回放效果实现
  • 2024/9/11学校教的响应式前端能学到什么?
  • 【路径规划】APF算法、Vortex APF算法、Safe APF算法和动态Windows方法的比较
  • AI教你学Python 第3天:函数和模块
  • ai智能语电销机器人有哪些功能?
  • 初识软件测试
  • 数据结构——单链表基本操作的实现
  • 我与Linux的爱恋:自动化构建工具-make/Makefile
  • 测试-Gatling 与性能测试
  • 98、RS485全自动收发电路入坑笔记
  • Gmtracker_深度学习驱动的图匹配多目标跟踪项目启动与算法流程
  • ES机制原理
  • linux ubuntu编译 openjdk11
  • 中国科技统计年鉴1991-2020年
  • JDBC客户端连接Starrocks 2.5
  • python-回文数(一)
  • 4G MQTT网关在物联网应用中的优势-天拓四方
  • 组播 2024 9 11
  • 为什么mac打不开rar文件 苹果电脑打不开rar压缩文件怎么办