• STM32F4X RTC


    什么是RTC

    RTC全程叫Real-Time Clock实时时钟,是MCU中一个用来计时的模块。RTC的一个主要作用是用来显示实时时间,就像日常生活中的时钟一样,RTC除了可以显示时间之外,还有闹钟功能,唤醒功能等。很多的MCU里面都会有RTC模块,当然也可以外接RTC芯片使用。

    STM32F4X RTC

    STM32F4X RTC框图

    在这里插入图片描述

    1. RTC时钟源:RTC的时钟源有3个,分别是外部低频晶振(LSE)、内部低频晶振(LSI)和外部高频晶振(HSE)。一般来说我们都会选择32768Hz的LSE作为RTC的时钟源。
    2. RTC分频器:时钟源选择好之后,就需要对时钟源进行分频。STM32F4X的RTC分频器有两个,分别是异步分频器7位的PREDIV_A和15位的同步分频器 PREDIV_A。RTC的分频由这两个分频器共同决定。
    3. RTC闹钟:STM32F4X的RTC有两个闹钟,用户可以通过设置闹钟的触发条件,当闹钟设置的触发条件跟当前RTC的计数时间一致时就会产生一个信号,如果使能了闹钟中断,就会触发闹钟中断
    4. RTC唤醒:当系统进入低功耗模式时,可以通过RTC唤醒系统。RTC有一个唤醒预分频器,可以设置2/4/6/8分频,还有一个16位的唤醒自动重装载寄存器,用来设置唤醒时间。
    5. RTC影子寄存器:影子寄存器的作用是提供给用户读取日历使用,用户不能直接访问影子寄存器,由STM32F4X每隔两个TRCCLK周期进行复制。

    STM32F4X RTC计数频率

    对于RTC来说,我们需要得到一个1秒的计数频率,这样才符合日常生活的习惯。下面就以RTC的时钟源为32768Hz来计算,看如何得到1Hz的频率。
    STM32F4X给出了RTC时钟频率的计算公式
    在这里插入图片描述
    STM32F4X的RTC分频需要PREDIV_A和PREDIV_S两个分频器共同决定。其中PREDIV_A为7位,PREDIV_S为15位。我们可以设置PREDIV_A为0x7F,PREDIV_S为0xFF。

    F = 32768 / (0x7F + 1) * (0xFF + 1) = 32768 / 32768 = 1Hz
    刚好可以得到1Hz的频率,这样就可以让RTC以1秒的频率进行计数。

    STM32F4X RTC日历

    用户可以设置RTC的日历功能,通过RTC_TR和RTC_DR寄存器可以设置日历。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    STM32F4X RTC闹钟

    STM32F4X的RTC有两个闹钟,分别是ALARM A和ALARM B。可以通过RTC_ALRMAR和RTC_ALRMBR进行设置。可以设置闹钟的触发条件和触发时间。
    在这里插入图片描述
    在这里插入图片描述

    STM32F4X RTC例程

    #include "rtc.h"
    #define ALARM_HOUR_MIN_SEC  1 // 闹钟匹配 时分秒
    #define ALARM_HOUR_MIN      2 // 闹钟匹配 时分
    #define ALARM_MIN_SEC       3 // 闹钟匹配 分秒
    #define ALARM_HOUR          4 // 闹钟匹配 时
    #define ALARM_MIN           5 // 闹钟匹配 分
    #define ALARM_SEC           6 // 闹钟匹配 秒
    ErrorStatus rtc_set_time(u8 hour,u8 min,u8 sec,u8 ampm)
    {
    	RTC_TimeTypeDef RTC_TimeStruct;
    	RTC_TimeStruct.RTC_H12 = ampm;
    	RTC_TimeStruct.RTC_Hours = hour; // 设置小时
    	RTC_TimeStruct.RTC_Minutes = min; // 设置分钟
    	RTC_TimeStruct.RTC_Seconds = sec;// 设置秒
    	return RTC_SetTime(RTC_Format_BIN,&RTC_TimeStruct);
    }
    ErrorStatus rtc_set_date(u8 year,u8 mon,u8 day,u8 week)
    {
    	RTC_DateTypeDef RTC_DateStruct;
    	
    	RTC_DateStruct.RTC_Date = day; // 设置日
    	RTC_DateStruct.RTC_Month = mon; // 设置月
    	RTC_DateStruct.RTC_WeekDay = week;// 设置周
    	RTC_DateStruct.RTC_Year = year;//设置年
    	return RTC_SetDate(RTC_Format_BIN,&RTC_DateStruct);
    }
    
    void set_alarm_time(u32 alarmx,u8 hour,u8 min,u8 sec,u8 week,u8 alarm_type)
    {
    	RTC_AlarmTypeDef RTC_AlarmStruct;
    	RTC_TimeTypeDef RTC_AlarmTime; 
    	NVIC_InitTypeDef NVIC_InitStruct;
    	EXTI_InitTypeDef   EXTI_InitStructure;
    	
    	
    	RTC_AlarmCmd(alarmx,DISABLE); // 关闭闹钟
    	
    	RTC_AlarmTime.RTC_H12 = RTC_H12_AM;
    	RTC_AlarmTime.RTC_Hours = hour; // 闹钟小时
    	RTC_AlarmTime.RTC_Minutes = min; // 闹钟分
    	RTC_AlarmTime.RTC_Seconds = sec;// 闹钟秒
    	
    	
    	RTC_AlarmStruct.RTC_AlarmDateWeekDay = week; // 闹钟周
    	RTC_AlarmStruct.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_Date;
    	
    	// 闹钟类型选择
    	if(alarm_type == ALARM_HOUR_MIN_SEC)
    		RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay ;
    	else if(alarm_type == ALARM_HOUR_MIN)
    		RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay | RTC_AlarmMask_Seconds;
    	else if(alarm_type == ALARM_MIN_SEC)
    		RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay | RTC_AlarmMask_Hours;
    	else if(alarm_type == ALARM_HOUR)
    		RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay | RTC_AlarmMask_Minutes | RTC_AlarmMask_Seconds;
    	else if(alarm_type == ALARM_MIN)
    		RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay | RTC_AlarmMask_Hours | RTC_AlarmMask_Seconds;
    	else if(alarm_type == ALARM_SEC)
    		RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay | RTC_AlarmMask_Hours | RTC_AlarmMask_Minutes;
    	else
    		RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay | RTC_AlarmMask_Hours | RTC_AlarmMask_Minutes;
    		
    	RTC_AlarmStruct.RTC_AlarmTime = RTC_AlarmTime;
    	RTC_SetAlarm(RTC_Format_BIN,alarmx,&RTC_AlarmStruct);
    	
    	NVIC_InitStruct.NVIC_IRQChannel = RTC_Alarm_IRQn; // RTC 闹钟中断号
    	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority  = 2;
    	NVIC_Init(&NVIC_InitStruct);
    	
    	EXTI_InitStructure.EXTI_Line = EXTI_Line17;//LINE17
    	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
    	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发 
    	EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE17
    	EXTI_Init(&EXTI_InitStructure);//配置
    	
    	if (alarmx == RTC_Alarm_A)
    	{
    		RTC_ClearITPendingBit(RTC_IT_ALRA);
    		RTC_ITConfig( RTC_IT_ALRA,ENABLE);
    	}
    	else if(alarmx == RTC_Alarm_B)
    	{
    		RTC_ClearITPendingBit(RTC_IT_ALRB);
    		RTC_ITConfig( RTC_IT_ALRB,ENABLE);
    	}
    		
    	RTC_AlarmCmd(alarmx,ENABLE);
    }
    	
    u8 bsp_rtc_init(void)
    {
    	u32 lse_rdy_count = 0xFFFF;
    	RTC_InitTypeDef RTC_InitStruct;
    	
    	
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能PWR时钟
    	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问 
    	RCC_LSEConfig(RCC_LSE_ON); // 使能LSE时钟
    	
    	if(RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x525443)  // 判断是不是第一次配置
    	{
    		while(1)
    		{
    			if(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == SET) // 等待LSE时钟稳定
    				break;
    			
    			lse_rdy_count--;
    			delay_ms(10);
    			if(lse_rdy_count == 0)
    				return 1;
    		}
    		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // RTC时钟源选择LSE时钟
    		RCC_RTCCLKCmd(ENABLE);                  // RTC使能
    		
    		
    		// RTC最终的计数频率 = 32768 / (0x7F + 1) * (0xFF + 1) 
    		RTC_InitStruct.RTC_AsynchPrediv = 0x7F; // 设置异步分频器
    		RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24; // 24小时制
    		RTC_InitStruct.RTC_SynchPrediv = 0xFF;  // 设置同步分频器
    		RTC_Init(&RTC_InitStruct);
    
    		
    		rtc_set_time(4,7,4,RTC_H12_AM); // 设置时间 4:7:4
    		rtc_set_date(23,9,8,5);         // 设置日期 2023-9-8 
    		
    		RTC_WriteBackupRegister(RTC_BKP_DR0,0x525443); // 如果是第一次就往备份寄存器写一个值
    	}
    	
    	return 0;
    	
    }
    
    void RTC_Alarm_IRQHandler(void)
    {
    	if(RTC_GetITStatus(RTC_IT_ALRA) == SET)
    	{
    		printf("Alarm A\r\n");
    		RTC_ClearITPendingBit(RTC_IT_ALRA);
    	}
    	if(RTC_GetITStatus(RTC_IT_ALRB) == SET)
    	{
    		printf("Alarm B\r\n");
    		RTC_ClearITPendingBit(RTC_IT_ALRB);
    	}
    	EXTI_ClearITPendingBit(EXTI_Line17);	//清除中断线17的中断标志 	
    }
    int main(void)
    {
    	RTC_TimeTypeDef RTC_TimeStruct;
    	RTC_DateTypeDef RTC_DateStruct;
    	uint32_t crc_value = 0;
    	NVIC_PriorityGroupConfig(2);
    	system_tick_init();
    	
    	bsp_usart_init(115200);
    	bsp_rtc_init();
    	set_alarm_time(RTC_Alarm_A,10,0,55,2,ALARM_SEC); // 设置闹钟A 按秒闹 
    	set_alarm_time(RTC_Alarm_B,10,1,45,2,ALARM_SEC); // 设置闹钟B 按秒闹
    	
      while(1){
    
    	  delay_ms(1000);
    	  RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct);	// 获取时间
    	  RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct); // 获取日期
    	  printf("20%02d-%02d-%02d %02d:%02d:%02d\r\n",RTC_DateStruct.RTC_Year,RTC_DateStruct.RTC_Month,RTC_DateStruct.RTC_Date,RTC_TimeStruct.RTC_Hours,RTC_TimeStruct.RTC_Minutes,RTC_TimeStruct.RTC_Seconds); 
    		
    	}
      
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171

    在这里插入图片描述

  • 相关阅读:
    java基于ssm人体健康体检信息管理系统-springboot
    深入解析HashMap
    【广州华锐互动】VR溺水预防教育:在虚拟世界中学会自救!
    大模型+人形机器人,用AI唤起钢筋铁骨
    【2024-04-24】华为春招笔试三道编程题解
    RegNet架构复现--CVPR2020
    WebGL 初始化着色器
    为何电脑从 C 盘开始? A、B 盘去哪了
    【论文不精读】Reinforced Path Reasoning for Counterfactual Explainable Recommendation
    [激光原理与应用-20]:《激光原理与技术》-6- 谐振腔的结构、作用、工作原理
  • 原文地址:https://blog.csdn.net/hwx1546/article/details/132761664