第15章 RTC实时时钟实验

第十五章 RTC实时时钟实验

1. 硬件设计

本实验用到的硬件资源有:

  • 指示灯 DS0

  • 串口

  • TFTLCD 模块

  • RTC

前面 3 个都介绍过了,而 RTC 属于 STM32F4 内部资源,其配置也是通过软件设置好就可以了。不过 RTC 不能断电,否则数据就丢失了, 我们如果想让时间在断电后还可以继续走,那么必须确保开发板的电池有电(ALIENTEK 探索者 STM32F4 开发板标配是有电池的)。

2. 软件设计

2.1 RTC时间设置

//RTC时间设置
//返回值:SUCEE(1),成功
//      ERROR(0),进入初始化模式失败 
HAL_StatusTypeDef RTC_Set_Time(u8 hour,u8 min,u8 sec,u8 ampm)
{
	RTC_TimeTypeDef RTC_TimeStructure;
	
	RTC_TimeStructure.Hours = hour;
	RTC_TimeStructure.Minutes = min;
	RTC_TimeStructure.Seconds = sec;
	RTC_TimeStructure.TimeFormat = ampm;
	RTC_TimeStructure.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    RTC_TimeStructure.StoreOperation = RTC_STOREOPERATION_RESET;
	return HAL_RTC_SetTime(&RTC_Handler, &RTC_TimeStructure,RTC_FORMAT_BIN);	
}

2.2 RTC日期设置

//RTC日期设置
//返回值:SUCEE(1),成功
//       ERROR(0),进入初始化模式失败 
HAL_StatusTypeDef RTC_Set_Date(u8 year,u8 month,u8 date,u8 week)
{
	RTC_DateTypeDef RTC_DateStructure;
    
	RTC_DateStructure.Date = date;
	RTC_DateStructure.Month = month;
	RTC_DateStructure.WeekDay = week;
	RTC_DateStructure.Year = year;
	return HAL_RTC_SetDate(&RTC_Handler, &RTC_DateStructure,RTC_FORMAT_BIN);
}

2.3 RTC初始化

//RTC初始化
//返回值:0,初始化成功;
//       2,进入初始化模式失败;
u8 RTC_Init(void)
{      

	RTC_Handler.Instance=RTC;
    RTC_Handler.Init.HourFormat=RTC_HOURFORMAT_24;// RTC设置为24小时格式 
    RTC_Handler.Init.AsynchPrediv=0X7F;           // RTC异步分频系数(1~0X7F)
    RTC_Handler.Init.SynchPrediv=0XFF;            // RTC同步分频系数(0~7FFF)   
    RTC_Handler.Init.OutPut=RTC_OUTPUT_DISABLE;   // RTC输出禁止
    RTC_Handler.Init.OutPutPolarity=RTC_OUTPUT_POLARITY_HIGH; // RTC输出极性高
    RTC_Handler.Init.OutPutType=RTC_OUTPUT_TYPE_OPENDRAIN; // RTC输出类型开漏
    if(HAL_RTC_Init(&RTC_Handler)!=HAL_OK) return 2;
      
    if(HAL_RTCEx_BKUPRead(&RTC_Handler,RTC_BKP_DR0)!=0X5050)//是否第一次配置
    { 
        RTC_Set_Time(17,41,0,RTC_HOURFORMAT12_PM);	        //设置时间 ,根据实际时间修改
		RTC_Set_Date(2024,10,11,2);		                    //设置日期
        HAL_RTCEx_BKUPWrite(&RTC_Handler,RTC_BKP_DR0,0X5050);//标记已经初始化过了
    }
    return 0;
}

2.4 RTC时钟配置

//RTC底层驱动,时钟配置
//此函数会被HAL_RTC_Init()调用
//hrtc:RTC句柄
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
    __HAL_RCC_PWR_CLK_ENABLE(); // 使能电源时钟PWR
	HAL_PWR_EnableBkUpAccess(); // 取消备份区域写保护
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; // LSE配置
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; // LSE不作为PLL来源
    RCC_OscInitStruct.LSEState = RCC_LSE_ON;       //RTC使用LSE
    HAL_RCC_OscConfig(&RCC_OscInitStruct); // 配置外部时钟
    PeriphClkInitStruct.PeriphClockSelection=RCC_PERIPHCLK_RTC; // 外设为RTC
    PeriphClkInitStruct.RTCClockSelection=RCC_RTCCLKSOURCE_LSE; // RTC时钟源为LSE
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); // 配置RTC时钟
    __HAL_RCC_RTC_ENABLE();//RTC时钟使能
}

2.5 设置闹钟时间

//设置闹钟时间(按星期闹铃,24小时制)
//week:星期几(1~7) @ref  RTC_WeekDay_Definitions
//hour,min,sec:小时,分钟,秒钟
void RTC_Set_AlarmA(u8 week,u8 hour,u8 min,u8 sec)
{ 
    RTC_AlarmTypeDef RTC_AlarmSturuct;
    
    RTC_AlarmSturuct.AlarmTime.Hours=hour;  // 小时
    RTC_AlarmSturuct.AlarmTime.Minutes=min; // 分钟
    RTC_AlarmSturuct.AlarmTime.Seconds=sec; // 秒
    RTC_AlarmSturuct.AlarmTime.SubSeconds=0; // 设置亚秒为0
    RTC_AlarmSturuct.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM; // 24小时制
    RTC_AlarmSturuct.AlarmMask=RTC_ALARMMASK_NONE; // 精确匹配星期,时分秒
    RTC_AlarmSturuct.AlarmSubSecondMask=RTC_ALARMSUBSECONDMASK_NONE; // 无亚秒匹配
    RTC_AlarmSturuct.AlarmDateWeekDaySel=RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;//按星期
    RTC_AlarmSturuct.AlarmDateWeekDay=week; // 星期
    RTC_AlarmSturuct.Alarm=RTC_ALARM_A;     // 闹钟A
    HAL_RTC_SetAlarm_IT(&RTC_Handler,&RTC_AlarmSturuct,RTC_FORMAT_BIN);
    HAL_NVIC_SetPriority(RTC_Alarm_IRQn,0x01,0x02); //抢占优先级1,子优先级2
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}

2.6 RTC周期唤醒函数

//周期性唤醒定时器设置  
/*wksel:  @ref RTCEx_Wakeup_Timer_Definitions
#define RTC_WAKEUPCLOCK_RTCCLK_DIV16        ((uint32_t)0x00000000)
#define RTC_WAKEUPCLOCK_RTCCLK_DIV8         ((uint32_t)0x00000001)
#define RTC_WAKEUPCLOCK_RTCCLK_DIV4         ((uint32_t)0x00000002)
#define RTC_WAKEUPCLOCK_RTCCLK_DIV2         ((uint32_t)0x00000003)
#define RTC_WAKEUPCLOCK_CK_SPRE_16BITS      ((uint32_t)0x00000004)
#define RTC_WAKEUPCLOCK_CK_SPRE_17BITS      ((uint32_t)0x00000006)
*/
//cnt:自动重装载值.减到0,产生中断.
void RTC_Set_WakeUp(u32 wksel, u16 cnt)
{ 
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&RTC_Handler, RTC_FLAG_WUTF);//清除RTC WAKE UP的标志
	HAL_RTCEx_SetWakeUpTimer_IT(&RTC_Handler,cnt,wksel);          //设置重装载值和时钟 
    HAL_NVIC_SetPriority(RTC_WKUP_IRQn,0x02,0x02); // 抢占优先级1,子优先级2
    HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
}

2.7 RTC中断相关函数

//RTC闹钟中断服务函数
void RTC_Alarm_IRQHandler(void)
{
    HAL_RTC_AlarmIRQHandler(&RTC_Handler);
}
    
//RTC闹钟A中断处理回调函数
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    printf("ALARM A!\r\n");
}

//RTC WAKE UP中断服务函数
void RTC_WKUP_IRQHandler(void)
{
    HAL_RTCEx_WakeUpTimerIRQHandler(&RTC_Handler); 
}

//RTC WAKE UP中断处理
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    LED1=!LED1;
}

2.8 主函数

int main(void)
{
	RTC_TimeTypeDef RTC_TimeStruct;
    RTC_DateTypeDef RTC_DateStruct;
    u8 tbuf[40];
	u8 t=0;
	
    HAL_Init();                  // 初始化HAL库    
    Stm32_Clock_Init(336,8,2,7); // 设置时钟,168Mhz
	delay_init(168);             // 初始化延时函数
	uart_init(115200);           // 初始化USART
	LED_Init();				     // 初始化LED	
 	LCD_Init();           	     // 初始化LCD
	usmart_dev.init(84); 		 // 初始化USMART	
    RTC_Init();                  // 初始化RTC 
    RTC_Set_WakeUp(RTC_WAKEUPCLOCK_CK_SPRE_16BITS, 0); // 配置WAKE UP中断,1秒钟中断一次  
    POINT_COLOR=RED; // 设置背景色为红色
	LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");	
	LCD_ShowString(30,70,200,16,16,"RTC TEST");	
	LCD_ShowString(30,90,200,16,16,"by hazy1k");
	LCD_ShowString(30,110,200,16,16,"2024/10/11");		   
    while(1)
    {
		t++;
		if((t%10)==0) // 每100ms更新一次显示数据
		{
            HAL_RTC_GetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN); // 获取当前时间
			sprintf((char*)tbuf,"Time:%02d:%02d:%02d",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds); // 打印时间
			LCD_ShowString(30,140,210,16,16,tbuf); // 显示时间
            HAL_RTC_GetDate(&RTC_Handler,&RTC_DateStruct,RTC_FORMAT_BIN); // 获取当前日期
			sprintf((char*)tbuf,"Date:20%02d-%02d-%02d",RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date); // 打印日期 
			LCD_ShowString(30,160,210,16,16,tbuf); // 显示日期
			sprintf((char*)tbuf,"Week:%d",RTC_DateStruct.WeekDay); // 打印星期
			LCD_ShowString(30,180,210,16,16,tbuf); // 显示星期
		} 
		if((t%20)==0)LED0=!LED0; // 每200ms,翻转一次LED0 
        delay_ms(10);
	}
}

3. 小结

下面我们简单回顾一下:

#include "stm32f4xx_hal.h"  // 引入 HAL 库
// RTC 句柄
RTC_HandleTypeDef hrtc;
// USART 句柄(用于串口通信)
UART_HandleTypeDef huart2;
// 错误处理函数
void Error_Handler(void) {
    // 用户可以添加自己的错误处理代码
    while(1) {
        // 进入死循环,等待调试
    }
}
// RTC 初始化函数
void RTC_Init(void) {
    // 启用 RTC 时钟
    __HAL_RCC_RTC_ENABLE();
    // 配置 RTC
    hrtc.Instance = RTC;  // 设置 RTC 实例
    hrtc.Init.HourFormat = RTC_HOURFORMAT_24;  // 设置为24小时制
    hrtc.Init.AsynchPrediv = 127;               // 设置异步预分频器
    hrtc.Init.SynchPrediv = 255;                 // 设置同步预分频器
    hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;       // 禁用输
    // 初始化 RTC,检查返回值以确保成功
    if (HAL_RTC_Init(&hrtc) != HAL_OK) {
        Error_Handler();  // 初始化失败,调用错误处理函数
    }
    // 设置初始时间和日期
    RTC_TimeTypeDef sTime;
    RTC_DateTypeDef sDate;
    sTime.Hours = 12;  // 初始小时
    sTime.Minutes = 0; // 初始分钟
    sTime.Seconds = 0; // 初始秒钟
    HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);  // 设置时间
    sDate.Year = 24;   // 设置年份 (2024)
    sDate.Month = RTC_MONTH_JANUARY; // 设置月份 (1月)
    sDate.Date = 1;    // 设置日期 (1号)
    HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);  // 设置日期
}

// USART2 初始化函数
void USART2_Init(void) {
    __HAL_RCC_USART2_CLK_ENABLE();  // 启用 USART2 时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();    // 启用 GPIOA 时钟
    // 配置 USART2 引脚 (PA2 为 TX, PA3 为 RX)
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; // TX 和 RX 引脚
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;        // 复用推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;            // 无上下拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 高速
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2;   // 复用功能
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);         // 初始化引脚
    // 配置 USART 参数
    huart2.Instance = USART2;
    huart2.Init.BaudRate = 115200;                  // 波特率
    huart2.Init.WordLength = UART_WORDLENGTH_8B;   // 数据位长度
    huart2.Init.StopBits = UART_STOPBITS_1;        // 停止位
    huart2.Init.Parity = UART_PARITY_NONE;          // 无奇偶校验
    huart2.Init.Mode = UART_MODE_TX_RX;             // 收发模式
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;    // 无硬件流控制
    huart2.Init.OverSampling = UART_OVERSAMPLING_16; // 过采样
    if (HAL_UART_Init(&huart2) != HAL_OK) {
        Error_Handler();  // 初始化失败,调用错误处理函数
    }
}
// 主函数
int main(void) {
    HAL_Init();          // 初始化 HAL 库
    SystemClock_Config(); // 配置系统时钟
    USART2_Init();       // 初始化 USART2
    RTC_Init();          // 初始化 RTC
    // 主循环
    while (1) {
        RTC_TimeTypeDef sTime;
        char buffer[50];
        // 获取当前时间
        HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
        // 格式化时间字符串
        snprintf(buffer, sizeof(buffer), "Current Time: %02d:%02d:%02d\r\n",
                 sTime.Hours, sTime.Minutes, sTime.Seconds);
   
        // 通过串口发送时间
        HAL_UART_Transmit(&huart2, (uint8_t*)buffer, strlen(buffer), HAL_MAX_DELAY);
        // 延时 1 秒
        HAL_Delay(1000);
    }
}
// 系统时钟配置函数 (根据具体需要实现)
void SystemClock_Config(void) {
    // 这里需要配置系统时钟,具体实现取决于你的硬件设计
}
posted @ 2024-10-10 09:57  hazy1k  阅读(5)  评论(0编辑  收藏  举报