同时处理Alarm A和Alarm B中断
当同时启用Alarm A和Alarm B时,需要在中断处理程序中准确判断是哪个闹钟触发了中断。以下是完整的解决方案:
中断判断与处理流程
1. 在RTC中断服务程序中判断中断源
// stm32l0xx_it.c
void RTC_IRQHandler(void)
{
/* USER CODE BEGIN RTC_IRQn 0 */
// 检查Alarm A中断标志
if (__HAL_RTC_ALARM_GET_FLAG(&hrtc, RTC_ISR_ALRAF) != RESET)
{
// 清除Alarm A中断标志
__HAL_RTC_ALARM_CLEAR_FLAG(&hrtc, RTC_FLAG_ALRAF);
// 处理Alarm A中断
AlarmA_Handler();
printf("Alarm A Triggered\n");
}
// 检查Alarm B中断标志
if (__HAL_RTC_ALARM_GET_FLAG(&hrtc, RTC_ISR_ALRBF) != RESET)
{
// 清除Alarm B中断标志
__HAL_RTC_ALARM_CLEAR_FLAG(&hrtc, RTC_FLAG_ALRBF);
// 处理Alarm B中断
AlarmB_Handler();
printf("Alarm B Triggered\n");
}
// 检查Wakeup定时器中断标志
if (__HAL_RTC_WAKEUPTIMER_GET_FLAG(&hrtc, RTC_ISR_WUTF) != RESET)
{
// 清除Wakeup中断标志
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);
// 处理Wakeup中断
WakeUp_Handler();
printf("WakeUp Timer Triggered\n");
}
/* USER CODE END RTC_IRQn 0 */
// 调用HAL库的中断处理函数
HAL_RTC_AlarmIRQHandler(&hrtc);
HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
/* USER CODE BEGIN RTC_IRQn 1 */
/* USER CODE END RTC_IRQn 1 */
}
2. 实现各中断的处理函数
// alarm_handlers.c
#include "alarm_handlers.h"
// Alarm A处理函数
void AlarmA_Handler(void)
{
// 执行Alarm A特定的任务
HAL_GPIO_TogglePin(LED_A_GPIO_Port, LED_A_Pin);
// 可以在此重新设置Alarm A
// Reset_AlarmA();
}
// Alarm B处理函数
void AlarmB_Handler(void)
{
// 执行Alarm B特定的任务
HAL_GPIO_TogglePin(LED_B_GPIO_Port, LED_B_Pin);
// 可以在此重新设置Alarm B
// Reset_AlarmB();
}
// Wakeup处理函数
void WakeUp_Handler(void)
{
// 执行唤醒后的任务
printf("System Woke Up from Stop Mode\n");
// 禁用Wakeup定时器(除非需要再次使用)
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
}
完整的中断处理流程
关键配置说明
1. 中断标志位
- Alarm A:
RTC_ISR_ALRAF
(寄存器ISR的位0) - Alarm B:
RTC_ISR_ALRBF
(寄存器ISR的位1) - Wakeup定时器:
RTC_ISR_WUTF
(寄存器ISR的位2)
2. 清除中断标志
- 使用
__HAL_RTC_ALARM_CLEAR_FLAG()
宏清除Alarm标志 - 清除标志后,中断挂起位也会自动清除
3. 中断优先级处理
当多个中断同时发生时,处理顺序取决于检查顺序:
- 先检查Alarm A
- 然后检查Alarm B
- 最后检查Wakeup定时器
如果需要改变优先级,可以调整检查顺序。
设置双闹钟的示例代码
1. 配置Alarm A和Alarm B
// rtc.c
void MX_RTC_Init(void)
{
// ... 其他初始化代码
// 配置Alarm A
RTC_AlarmTypeDef sAlarmA = {0};
sAlarmA.AlarmTime.Hours = 0x0;
sAlarmA.AlarmTime.Minutes = 0x0;
sAlarmA.AlarmTime.Seconds = 0x20; // 32秒触发
sAlarmA.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY | RTC_ALARMMASK_HOURS | RTC_ALARMMASK_MINUTES;
sAlarmA.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarmA.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
sAlarmA.AlarmDateWeekDay = 0x1;
sAlarmA.Alarm = RTC_ALARM_A;
if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarmA, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
// 配置Alarm B
RTC_AlarmTypeDef sAlarmB = {0};
sAlarmB.AlarmTime.Hours = 0x0;
sAlarmB.AlarmTime.Minutes = 0x1; // 1分钟触发
sAlarmB.AlarmTime.Seconds = 0x0;
sAlarmB.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY | RTC_ALARMMASK_HOURS | RTC_ALARMMASK_SECONDS;
sAlarmB.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarmB.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
sAlarmB.AlarmDateWeekDay = 0x1;
sAlarmB.Alarm = RTC_ALARM_B;
if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarmB, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
}
2. 中断处理优化建议
避免在中断中执行耗时操作:
void AlarmA_Handler(void) { // 仅设置标志,在主循环中处理 alarmA_triggered = true; }
处理同时触发的情况:
if (__HAL_RTC_ALARM_GET_FLAG(&hrtc, RTC_ISR_ALRAF) && __HAL_RTC_ALARM_GET_FLAG(&hrtc, RTC_ISR_ALRBF)) { // 处理双闹钟同时触发的情况 Handle_Dual_Alarm(); }
添加错误处理:
if (__HAL_RTC_ALARM_GET_FLAG(&hrtc, RTC_ISR_ALRAWF)) { // Alarm A写入正在进行中 printf("Alarm A register is being written\n"); }
调试技巧
使用GPIO调试:
void AlarmA_Handler(void) { HAL_GPIO_WritePin(DEBUG_A_GPIO_Port, DEBUG_A_Pin, GPIO_PIN_SET); // ... 处理代码 HAL_GPIO_WritePin(DEBUG_A_GPIO_Port, DEBUG_A_Pin, GPIO_PIN_RESET); }
记录中断时间:
void AlarmA_Handler(void) { HAL_RTC_GetTime(&hrtc, &lastAlarmATime, RTC_FORMAT_BIN); // ... 其他处理 }
检查中断频率:
static uint32_t alarmA_count = 0; void AlarmA_Handler(void) { alarmA_count++; if (alarmA_count % 10 == 0) { printf("Alarm A triggered %lu times\n", alarmA_count); } }
通过以上方案,您可以准确区分和处理Alarm A、Alarm B以及Wakeup定时器的中断,实现复杂的定时任务调度。