当前位置:网站首页>潘多拉 IOT 开发板学习(HAL 库)—— 实验12 RTC实时时钟实验(学习笔记)
潘多拉 IOT 开发板学习(HAL 库)—— 实验12 RTC实时时钟实验(学习笔记)
2022-07-07 12:35:00 【小辉_Super】
本文代码参考正点原子例程
文章目录
实验功能
LCD 和 USARMT 的代码不分析
例程源码:(main.c)
本实验实现的功能:设置 RTC 时间( RTC 初始化函数中),RTC 周期唤醒中断里实现 LED_B 的闪烁,RTC 闹钟实现蜂鸣器的响灭( main()函数中没有设置闹钟时间,所以这个功能应该没有完全实现)。LCD 每 10ms 刷新一次时间日期的显示(LCD 代码本文不分析)。
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "lcd.h"
#include "usmart.h"
#include "rtc.h"
/********************************************************************************* ___ _ _____ _____ _ _ _____ _____ _ __ / _ \ | | |_ _|| ___|| \ | ||_ _|| ___|| | / / / /_\ \| | | | | |__ | \| | | | | |__ | |/ / | _ || | | | | __| | . ` | | | | __| | \ | | | || |_____| |_ | |___ | |\ | | | | |___ | |\ \ \_| |_/\_____/\___/ \____/ \_| \_/ \_/ \____/ \_| \_/ * ****************************************************************************** * 正点原子 Pandora STM32L475 IoT开发板 实验12 * RTC实时时钟实验 HAL库版本 * 技术支持:www.openedv.com * 淘宝店铺:http://openedv.taobao.com * 关注微信公众平台微信号:"正点原子",免费获取STM32资料。 * 广州市星翼电子科技有限公司 * 作者:正点原子 @ALIENTEK * ******************************************************************************/
int main(void)
{
u8 t = 0;
u8 cnt = 0;
char tbuf[40];
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
HAL_Init();
SystemClock_Config(); //初始化系统时钟为80M
delay_init(80); //初始化延时函数 80M系统时钟
uart_init(115200); //初始化串口,波特率为115200
usmart_dev.init(80); //初始化USMART 80M系统时钟
LED_Init(); //初始化LED
BEEP_Init(); //初始化BEEP
LCD_Init(); //初始化LCD
RTC_Init(); //初始化RTC
RTC_Set_WakeUp(RTC_WAKEUPCLOCK_CK_SPRE_16BITS, 0); //配置WAKE UP中断,1秒钟中断一次
POINT_COLOR = RED;
Display_ALIENTEK_LOGO(0, 0);
LCD_ShowString(30, 95, 200, 16, 16, "Pandora STM32L4 IOT");
LCD_ShowString(30, 115, 200, 16, 16, "RTC TEST");
LCD_ShowString(30, 135, 200, 16, 16, "[email protected]");
LCD_ShowString(30, 155, 200, 16, 16, "2018/10/27");
POINT_COLOR = BLUE;
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, 175, 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, 195, 210, 16, 16, tbuf);
sprintf((char*)tbuf, "Week:%d", RTC_DateStruct.WeekDay);
LCD_ShowString(30, 215, 210, 16, 16, tbuf);
}
if( BEEP_Read ) //闹钟到时,蜂鸣器响1S后停止
{
cnt++;
if(cnt>=100) {
BEEP(0);cnt=0;}
}
if((t % 20) == 0)LED_R_TogglePin; //每200ms,翻转一次LED_R
delay_ms(10);
}
}
代码剖析
HAL_Init()
HAL_Init()
定义如下:(具体实现的功能见注释)
HAL_StatusTypeDef HAL_Init(void)
{
HAL_StatusTypeDef status = HAL_OK;
/* 配置 Flash 预取,指令缓存,数据缓存 */
/* 默认配置为:预存取关闭 指令缓存和数据缓存开启 */
#if (INSTRUCTION_CACHE_ENABLE == 0) // Flash开启预存取配置,能加速CPU代码的执行
__HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */
#if (DATA_CACHE_ENABLE == 0)
__HAL_FLASH_DATA_CACHE_DISABLE();
#endif /* DATA_CACHE_ENABLE */
#if (PREFETCH_ENABLE != 0)
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); // 配置 NVIC 优先级分组
/* Use SysTick as time base source and configure 1ms tick (default clock after Reset is MSI) */
if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) //初始化滴答定时器,时钟节拍设置为 1ms
{
status = HAL_ERROR;
}
else
{
/* Init the low level hardware */
HAL_MspInit(); // 低速的外设初始化,比如 GPIO、中断等的设置(使用 STM32CubeMx 生成代码时会将低速外设初始
// 代码当这类函数里,其他情况下可以忽略这个函数
}
/* Return function status */
return status;
}
HAL_InitTick()
滴答定时器时钟节拍初始化函数
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
HAL_StatusTypeDef status = HAL_OK;
/*Configure the SysTick to have interrupt in 1ms time basis*/
if (HAL_SYSTICK_Config(SystemCoreClock/1000UL) != 0U) // 系统时钟/1000,中断周期为 1ms
{
status = HAL_ERROR;
}
else
{
/*Configure the SysTick IRQ priority */
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0); // 将滴答定时器的中断优先级设置为最高
}
/* Return function status */
return status;
}
SystemClock_Config()
SystemClock_Config()
函数定义如下:(具体实现的功能见注释,仅供参考)
void SystemClock_Config(void)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStruct; // 定义振荡器初始化结构体变量
RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义时钟初始化结构体变量
__HAL_RCC_PWR_CLK_ENABLE(); // 使能电源控制时钟
/*Initializes the CPU, AHB and APB busses clocks*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 将 HSE(外部高速时钟)作为时钟源
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 开启 HSE
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启 PLL(锁相环)
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 将 HSE 作为 PLL 的时钟源
RCC_OscInitStruct.PLL.PLLM = 1; // PLL-VCO 输入时钟分频系数,1 表示 2 分频(8 / 2 = 4M,本开发板外部晶振频率为 8MHz)
RCC_OscInitStruct.PLL.PLLN = 20; // PLL-VCO 输出时钟倍频系数,4 * 20 = 80M,即输出时钟频率为 80MHz
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI 时钟的分频系数
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC1, RNG 和 USB 的时钟分频系数
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 主系统时钟的分频系数
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); //初始化时钟配置
if(ret != HAL_OK) while(1);
/*Initializes the CPU, AHB and APB busses clocks*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; // 将所有时钟同时进行配置
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 将 PLL 作为系统时钟源
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB 不分频
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1 不分频
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 不分频
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); // 配置时钟初始结构体变量,
//使用 Flash 延迟4,等待状态(延迟)的数量需要根据CPU时钟(HCLK)的频率和内部电压范围来选择,具体怎么
//选需要参考芯片手册
if(ret != HAL_OK) while(1);
/*Configure the main internal regulator output voltage*/
ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); //内部寄存器输出电压配置
// 下面是 HAL_PWREx_ControlVoltageScaling() 函数说明的部分内容:
//PWR_REGULATOR_VOLTAGE_SCALE1 Regulator voltage output range 1 mode, typical output voltage
// at 1.2 V, system frequency up to 80 MHz.
if(ret != HAL_OK) while(1);
}
delay_init()
滴答定时器已经在 HAL_Init()
中进行了初始化,下面这个函数实际上就是给 fac_us
赋了一个值(目前暂不涉及操作系统,其他代码暂时不去研究)。
static u32 fac_us = 0; //us延时倍乘数
/** * @brief 初始化延迟函数,SYSTICK的时钟固定为AHB时钟 * * @param SYSCLK 系统时钟频率 * * @return void */
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us = SYSCLK; //不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload = SYSCLK; //每秒钟的计数次数 单位为K
reload *= 1000000 / delay_ostickspersec; //根据delay_ostickspersec设定溢出时间
//reload为24位寄存器,最大值:16777216,在80M下,约209.7ms左右
fac_ms = 1000 / delay_ostickspersec; //代表OS可以延时的最少单位
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD = reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
#endif
}
LED_Init()
/** * @brief LED IO初始化函数 * * @param void * * @return void */
void LED_Init(void)
{
/* LED-B PE9 LED-G PE8 LED-R PE7 */
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
}
BEEP_Init()
/** * @brief 蜂鸣器 IO初始化函数 * * @param void * * @return void */
void BEEP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct; // 定义一个GPIO初始化结构体变量
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOE的时钟
//PB2
GPIO_InitStruct.Pin = GPIO_PIN_2; // 设置对应的引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 默认下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 速度设为高速(25 MHz to 50 MHz)
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化结构体变量
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET); //将 IO 拉低
}
RTC_Init()
/** * @brief RTC初始化 * * @param void * * @return u8 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_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if(HAL_RTC_Init(&RTC_Handler) != HAL_OK) return 2;
if(HAL_RTCEx_BKUPRead(&RTC_Handler, RTC_BKP_DR0) != 0x32F2) //是否第一次配置
{
RTC_Set_Time(23, 59, 56, RTC_HOURFORMAT12_PM); //设置时间 ,根据实际时间修改
RTC_Set_Date(15, 12, 27, 7); //设置日期
HAL_RTCEx_BKUPWrite(&RTC_Handler, RTC_BKP_DR0, 0x32F2); //标记已经初始化过了
}
return 0;
}
HAL_RTC_MspInit()
底层驱动函数,主要进行了电源时钟配置,取消备份去写保护,晶振时钟选择,使能 RTC 时钟。
/** * @brief RTC底层驱动,时钟配置,此函数会被HAL_RTC_Init()调用 * * @param hrtc RTC句柄 * * @return void */
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;
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);
__HAL_RCC_RTC_ENABLE();//RTC时钟使能
}
HAL_RTCExBKURead()
下面是该函数的原型,作用是向 RTC 备份寄存器读数据。
/** * @brief Read data from the specified RTC Backup data Register. * @param hrtc RTC handle * @param BackupRegister RTC Backup data Register number. * This parameter can be: RTC_BKP_DRx where x can be from 0 to 31 to * specify the register. * @retval Read value */
uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister)
RTC_Set_Time()
/** * @brief RTC时间设置函数 * * @param hour 小时 * @param min 分钟 * @param sec 秒钟 * @param ampm @RTC_AM_PM_Definitions:RTC_HOURFORMAT12_AM/RTC_HOURFORMAT12_PM * * @return HAL_StatusTypeDef 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);
}
HAL_RTC_SetTime()
HAL 库的 RTC 时间设置函数原型:
/** * @brief Set RTC current time. * @param hrtc RTC handle * @param sTime Pointer to Time structure * @param Format Specifies the format of the entered parameters. * This parameter can be one of the following values: * @arg RTC_FORMAT_BIN: Binary data format * @arg RTC_FORMAT_BCD: BCD data format * @retval HAL status */
HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
RTC_Set_Date()
/** * @brief RTC日期设置函数 * * @param year 年 * @param month 月 * @param date 日 * @param week 星期(1~7,0,非法!) * * @return HAL_StatusTypeDef 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);
}
HAL_RTC_SetDate()
HAL 库 RTC 日期设置函数原型:
/** * @brief Set RTC current date. * @param hrtc RTC handle * @param sDate Pointer to date structure * @param Format specifies the format of the entered parameters. * This parameter can be one of the following values: * @arg RTC_FORMAT_BIN: Binary data format * @arg RTC_FORMAT_BCD: BCD data format * @retval HAL status */
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
HAL_RTCEx_BKUPWrite()
下面是该函数的原型,作用是向 RTC 备份寄存器写数据。
/** * @brief Write a data in a specified RTC Backup data register. * @param hrtc RTC handle * @param BackupRegister RTC Backup data Register number. * This parameter can be: RTC_BKP_DRx where x can be from 0 to 31 to * specify the register. * @param Data Data to be written in the specified Backup data register. * @retval None */
void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data)
RTC_Set_WakeUp()
RTC 周期唤醒设置,本应在 HAL_RTC_MspInit()
中设置,但现在单独定义了,main() 选择了 RTC_WAKEUPCLOCK_CK_SPRE_16BITS 来实现 1s 中断。
/** * @brief 周期性唤醒定时器设置 * * @param 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) * @param cnt 自动重装载值.减到0,产生中断 * * @return void */
//
void RTC_Set_WakeUp(u32 wksel, u16 cnt)
{
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&RTC_Handler, RTC_FLAG_WUTF);//清除RTC WAKE UP的标志
/* 特殊说明:由于本例程使用的是最新HAL库(V1.9.0 / 27-July-2018),该版本中此函数底层没有开启WakeUpTimer中断 自己手动移植程序的时候需要注意修改HAL底层函数,此处已经进行了修改!!! */
HAL_RTCEx_SetWakeUpTimer_IT(&RTC_Handler, cnt, wksel); //设置重装载值和时钟
HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 0x02, 0x02); //抢占优先级1,子优先级2
HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
}
周期唤醒回调函数
HAL_RTCEx_WakeUpTimerEventCallback()
周期唤醒定时器时间回调函数,main() 函数中设置了 1s 的中断周期,所以该函数每秒调用一次。
/** * @brief RTC WAKE UP中断处理 * * @param hrtc RTC句柄 * * @return void */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
LED_B_TogglePin; //每1s,翻转一次LED_B
}
RTC_Set_AlarmA()
main() 函数中并没有调用这个函数,所以没有开启闹钟功能,该函数可以指定闹钟时间。
/** * @brief 设置闹钟时间(按星期闹铃,24小时制) * * @param week 星期几(1~7) @ref RTC_WeekDay_Definitions * @param hour 小时 * @param min 分钟 * @param sec 秒钟 * * @return void */
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;
RTC_AlarmSturuct.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM;
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);
}
闹钟中断回调函数
当 RTC 闹钟中断触发时,系统会自动调用 RTC_Alarm_IRQHandler()
,然后接着运行 HAL 库的闹钟中断处理函数,
/** * @brief RTC闹钟中断服务函数 * * @param void * * @return void */
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&RTC_Handler);
}
HAL 库的闹钟中断处理函数中会调用 HAL_RTC_AlarmAEventCallback()
回调函数。
/** * @brief RTC闹钟A中断处理回调函数 * * @param hrtc RTC句柄 * * @return void */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
printf("ALARM A!\r\n");
BEEP(1);
}
HAL_RTC_GetTime()
HAL 库的 RTC 时间获取函数,从相关寄存器中获取数据然后再进行相关格式转换。
/** * @brief Get RTC current time. * @note You can use SubSeconds and SecondFraction (sTime structure fields returned) to convert SubSeconds * value in second fraction ratio with time unit following generic formula: * Second fraction ratio * time_unit= [(SecondFraction-SubSeconds)/(SecondFraction+1)] * time_unit * This conversion can be performed only if no shift operation is pending (ie. SHFP=0) when PREDIV_S >= SS * @note You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values * in the higher-order calendar shadow registers to ensure consistency between the time and date values. * Reading RTC current time locks the values in calendar shadow registers until Current date is read * to ensure consistency between the time and date values. * @param hrtc RTC handle * @param sTime Pointer to Time structure with Hours, Minutes and Seconds fields returned * with input format (BIN or BCD), also SubSeconds field returning the * RTC_SSR register content and SecondFraction field the Synchronous pre-scaler * factor to be used for second fraction ratio computation. * @param Format Specifies the format of the entered parameters. * This parameter can be one of the following values: * @arg RTC_FORMAT_BIN: Binary data format * @arg RTC_FORMAT_BCD: BCD data format * @retval HAL status */
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
{
uint32_t tmpreg;
/* Check the parameters */
assert_param(IS_RTC_FORMAT(Format));
/* Get subseconds structure field from the corresponding register*/
sTime->SubSeconds = (uint32_t)(hrtc->Instance->SSR);
/* Get SecondFraction structure field from the corresponding register field*/
sTime->SecondFraction = (uint32_t)(hrtc->Instance->PRER & RTC_PRER_PREDIV_S);
/* Get the TR register */
tmpreg = (uint32_t)(hrtc->Instance->TR & RTC_TR_RESERVED_MASK);
/* Fill the structure fields with the read parameters */
sTime->Hours = (uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> RTC_TR_HU_Pos);
sTime->Minutes = (uint8_t)((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >> RTC_TR_MNU_Pos);
sTime->Seconds = (uint8_t)((tmpreg & (RTC_TR_ST | RTC_TR_SU)) >> RTC_TR_SU_Pos);
sTime->TimeFormat = (uint8_t)((tmpreg & (RTC_TR_PM)) >> RTC_TR_PM_Pos);
/* Check the input parameters format */
if(Format == RTC_FORMAT_BIN)
{
/* Convert the time structure parameters to Binary format */
sTime->Hours = (uint8_t)RTC_Bcd2ToByte(sTime->Hours);
sTime->Minutes = (uint8_t)RTC_Bcd2ToByte(sTime->Minutes);
sTime->Seconds = (uint8_t)RTC_Bcd2ToByte(sTime->Seconds);
}
return HAL_OK;
}
HAL_RTC_GetDate()
HAL 库日期获取函数,和时间获取函数类似,
/** * @brief Set RTC current date. * @param hrtc RTC handle * @param sDate Pointer to date structure * @param Format specifies the format of the entered parameters. * This parameter can be one of the following values: * @arg RTC_FORMAT_BIN: Binary data format * @arg RTC_FORMAT_BCD: BCD data format * @retval HAL status */
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
{
uint32_t datetmpreg;
/* Check the parameters */
assert_param(IS_RTC_FORMAT(Format));
/* Process Locked */
__HAL_LOCK(hrtc);
hrtc->State = HAL_RTC_STATE_BUSY;
if((Format == RTC_FORMAT_BIN) && ((sDate->Month & 0x10U) == 0x10U))
{
sDate->Month = (uint8_t)((sDate->Month & (uint8_t)~(0x10U)) + (uint8_t)0x0AU);
}
assert_param(IS_RTC_WEEKDAY(sDate->WeekDay));
if(Format == RTC_FORMAT_BIN)
{
assert_param(IS_RTC_YEAR(sDate->Year));
assert_param(IS_RTC_MONTH(sDate->Month));
assert_param(IS_RTC_DATE(sDate->Date));
datetmpreg = (((uint32_t)RTC_ByteToBcd2(sDate->Year) << RTC_DR_YU_Pos) | \
((uint32_t)RTC_ByteToBcd2(sDate->Month) << RTC_DR_MU_Pos) | \
((uint32_t)RTC_ByteToBcd2(sDate->Date) << RTC_DR_DU_Pos) | \
((uint32_t)sDate->WeekDay << RTC_DR_WDU_Pos));
}
else
{
assert_param(IS_RTC_YEAR(RTC_Bcd2ToByte(sDate->Year)));
assert_param(IS_RTC_MONTH(RTC_Bcd2ToByte(sDate->Month)));
assert_param(IS_RTC_DATE(RTC_Bcd2ToByte(sDate->Date)));
datetmpreg = ((((uint32_t)sDate->Year) << RTC_DR_YU_Pos) | \
(((uint32_t)sDate->Month) << RTC_DR_MU_Pos) | \
(((uint32_t)sDate->Date) << RTC_DR_DU_Pos) | \
(((uint32_t)sDate->WeekDay) << RTC_DR_WDU_Pos));
}
/* Disable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_DISABLE(hrtc);
/* Set Initialization mode */
if(RTC_EnterInitMode(hrtc) != HAL_OK)
{
/* Enable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);
/* Set RTC state*/
hrtc->State = HAL_RTC_STATE_ERROR;
/* Process Unlocked */
__HAL_UNLOCK(hrtc);
return HAL_ERROR;
}
else
{
/* Set the RTC_DR register */
hrtc->Instance->DR = (uint32_t)(datetmpreg & RTC_DR_RESERVED_MASK);
/* Exit Initialization mode */
#if defined(STM32L412xx) || defined(STM32L422xx)
CLEAR_BIT(hrtc->Instance->ICSR, RTC_ICSR_INIT);
#else
CLEAR_BIT(hrtc->Instance->ISR, RTC_ISR_INIT);
#endif
/* If CR_BYPSHAD bit = 0, wait for synchro else this check is not needed */
if((hrtc->Instance->CR & RTC_CR_BYPSHAD) == 0U)
{
if(HAL_RTC_WaitForSynchro(hrtc) != HAL_OK)
{
/* Enable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);
hrtc->State = HAL_RTC_STATE_ERROR;
/* Process Unlocked */
__HAL_UNLOCK(hrtc);
return HAL_ERROR;
}
}
/* Enable the write protection for RTC registers */
__HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);
hrtc->State = HAL_RTC_STATE_READY ;
/* Process Unlocked */
__HAL_UNLOCK(hrtc);
return HAL_OK;
}
}
delay_ms()
delay_ms()
里运行的是 delay_us()
, delay_us()
通过滴答定时器实现延时。上面的 delay_init()
已经将 fac_us 设置为了 80,滴答定时器计数 80 次需要用 10-6 秒(系统时钟为 80MHz),即 1us。
/** * @brief 延时毫秒(ms)函数 * * @param nms 需要延时多少毫秒 * * @return void */
void delay_ms(u16 nms)
{
u32 i;
for(i = 0; i < nms; i++) delay_us(1000);
}
/** * @brief 延时微秒(us)函数 * * @remark nus:0~190887435(最大值即2^32/[email protected]_us=22.5) * * @param nus 需要延时多少微秒 * * @return void */
void delay_us(u32 nus)
{
u32 ticks;
u32 told, tnow, tcnt = 0;
u32 reload = SysTick->LOAD; //LOAD的值
ticks = nus * fac_us; //需要的节拍数
told = SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow = SysTick->VAL;
if(tnow != told)
{
if(tnow < told)tcnt += told - tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt += reload - tnow + told;
told = tnow;
if(tcnt >= ticks)break; //时间超过/等于要延迟的时间,则退出.
}
}
}
LED BEEP 操作函数
LED 和蜂鸣器的控制函数是宏函数,分别用到了 HAL_GPIO_WritePin()
和 HAL_GPIO_TogglePin()
两个库函数。
#define LED_R(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_RESET))
#define LED_R_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_7)
#define LED_G(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_RESET))
#define LED_G_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_8)
#define LED_B(n) (n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_RESET))
#define LED_B_TogglePin HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_9)
#define BEEP(n) (n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET))
#define BEEP_TogglePin HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_2)
#define BEEP_Read HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
边栏推荐
- 【AI实战】应用xgboost.XGBRegressor搭建空气质量预测模型(二)
- 低代码平台中的数据连接方式(下)
- Common response status codes
- WPF DataGrid realizes the UI interface to respond to a data change in a single line
- Reading and understanding of eventbus source code
- The difference between memory overflow and memory leak
- Codes de non - retour à zéro inversés, codes Manchester et codes Manchester différentiels couramment utilisés pour le codage des signaux numériques
- Pert diagram (engineering network diagram)
- 最长上升子序列模型 AcWing 1012. 友好城市
- GVIM [III] [u vimrc configuration]
猜你喜欢
docker部署oracle
[Reading stereo matching papers] [III] ints
Hands on Teaching: XML modeling
Details of redis core data structure & new features of redis 6
js 获取当前时间 年月日,uniapp定位 小程序打开地图选择地点
OAuth 2.0 + JWT protect API security
UML 状态图
LeetCode 648. Word replacement
常用數字信號編碼之反向不歸零碼碼、曼徹斯特編碼、差分曼徹斯特編碼
【愚公系列】2022年7月 Go教学课程 005-变量
随机推荐
Selenium库
一个程序员的水平能差到什么程度?尼玛,都是人才呀...
内部排序——插入排序
Flask session forged hctf admin
今日睡眠质量记录78分
请问,redis没有消费消息,都在redis里堆着是怎么回事?用的是cerely 。
NDK beginner's study (1)
Details of redis core data structure & new features of redis 6
Multi merchant mall system function disassembly lecture 01 - Product Architecture
Navigation — 这么好用的导航框架你确定不来看看?
The longest ascending subsequence model acwing 482 Chorus formation
SAKT方法部分介绍
请问,如图,pyhon云函数提示使用了 pymysql模块,这个是怎么回事?
The longest ascending subsequence model acwing 1014 Mountaineering
搜索引擎接口
libSGM的horizontal_path_aggregation程序解读
Verilog implementation of a simple legv8 processor [4] [explanation of basic knowledge and module design of single cycle implementation]
Leetcode - Sword finger offer 05 Replace spaces
IP and long integer interchange
GAN发明者Ian Goodfellow正式加入DeepMind,任Research Scientist