同時處理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 ARTC_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 BRTC_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定時器的中斷,實現復雜的定時任務調度。