? ? ? ? 定時器中斷功能主要是兩點:
????????1.怎么配置的定時器中斷時間間隔;
????????2.中斷里長什么樣
一、定時器中斷配置函數
? ? ? ? 直接在bsp_basic_timer.c里找到下面函數:
void basic_timer_config(uint16_t pre,uint16_t per)
{/* ò????ü?úμ?ê±??T = 1/f, ?¨ê±ê±??time = T * ?ü?úéè?¤·??μ?μ??pre,?ü?ú??pertime = (pre + 1) * (per + 1) / psc_clk*/timer_parameter_struct timere_initpara; // ?¨ò??¨ê±?÷?á11ì?/* ?a??ê±?ó */rcu_periph_clock_enable(BSP_TIMER_RCU); // ?a???¨ê±?÷ê±?ó/* CK_TIMERx = 4 x CK_APB1 = 4x50M = 200MHZ */rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); // ?????¨ê±?÷ê±?ótimer_deinit(BSP_TIMER); // ?′???¨ê±?÷/* ?????¨ê±?÷2?êy */timere_initpara.prescaler = pre-1; // ê±?ó?¤·??μ?μ 0-65535 psc_clk = CK_TIMER / pretimere_initpara.alignedmode = TIMER_COUNTER_EDGE; // ±??μ???? timere_initpara.counterdirection = TIMER_COUNTER_UP; // ?òé???êy timere_initpara.period = per-1; // ?ü?ú /* ?úê?è?2???μ?ê±oòê1ó? êy×???2¨?÷ê1ó?μ?2é?ù?μ?ê????μ?·??μ±èày */timere_initpara.clockdivision = TIMER_CKDIV_DIV1; // ·??μòò×ó /* ??óD?????¨ê±?÷2?óD ?????ax£??í???′x+1′???è??D?? */ timere_initpara.repetitioncounter = 0; // ???′??êy?÷ 0-255 timer_init(BSP_TIMER,&timere_initpara); // 3?ê??ˉ?¨ê±?÷/* ?????D??ó??è?? */nvic_irq_enable(BSP_TIMER_IRQ,3,2); // éè???D??ó??è???a 3,2/* ê1?ü?D?? */timer_interrupt_enable(BSP_TIMER,TIMER_INT_UP); // ê1?ü?üD?ê??t?D?? /* ê1?ü?¨ê±?÷ */timer_enable(BSP_TIMER);
}
????????定時器中斷初始化流程:
啟用定時器時鐘
- 代碼:
rcu_periph_clock_enable(BSP_TIMER_RCU);
- 功能:開啟定時器模塊的時鐘(
BSP_TIMER_RCU
?定義了具體定時器外設,如 TIMER0)。 - 說明:通過 RCU(Reset and Clock Unit)模塊為定時器提供時鐘信號。
- 代碼:
配置定時器時鐘分頻
- 代碼:
rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
- 功能:設置定時器時鐘為 APB1 時鐘的 4 倍(4 × 50MHz = 200MHz)。
- 說明:定時器時鐘頻率(CK_TIMER)決定計數速度,影響定時精度。
- 代碼:
復位定時器
- 代碼:
timer_deinit(BSP_TIMER);
- 功能:將定時器寄存器復位到默認狀態,確保無殘留配置。
- 說明:清除之前的配置,為新設置做準備。
- 代碼:
配置定時器參數
- 代碼:
timer_parameter_struct timere_initpara; timere_initpara.prescaler = pre-1; // 預分頻值
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?timere_initpara.alignedmode = TIMER_COUNTER_EDGE; // 邊沿對齊 timere_initpara.counterdirection = TIMER_COUNTER_UP; // 向上計數 timere_initpara.period = per-1; // 周期? ? ? ? ? ? ? ? ? ? timere_initpara.clockdivision = TIMER_CKDIV_DIV1; // 時鐘分頻因子 timere_initpara.repetitioncounter = 0; // 重復計數器? ? ? ? ? ? timer_init(BSP_TIMER, &timere_initpara);
- 功能:
- 定義并初始化定時器參數結構體?
timer_parameter_struct
。 - 設置預分頻值 (
pre-1
),定時器計數頻率 = CK_TIMER / (pre)。 - 配置為邊沿對齊模式(
TIMER_COUNTER_EDGE
)和向上計數(TIMER_COUNTER_UP
)。 - 設置周期值 (
per-1
),決定定時器溢出周期,定時時間 = (pre × per) / CK_TIMER。 - 時鐘分頻因子為 1(
TIMER_CKDIV_DIV1
),不額外分頻。 - 重復計數器為 0(
repetitioncounter = 0
),無重復計數,適用于基本定時器。 - 調用?
timer_init
?初始化定時器寄存器。
- 定義并初始化定時器參數結構體?
- 說明:定時時間計算公式為?
time = (pre × per) / CK_TIMER
,例如,若?pre=20000
,?per=10000
, CK_TIMER=200MHz,則?time = (20000 × 10000) / 200,000,000 = 1秒
。
- 代碼:
配置中斷優先級
- 代碼:
nvic_irq_enable(BSP_TIMER_IRQ, 3, 2);
- 功能:啟用定時器中斷(
BSP_TIMER_IRQ
),設置 NVIC 中斷優先級為搶占優先級 3,子優先級 2。 - 說明:確保定時器中斷能夠被 CPU 響應,優先級決定中斷處理順序。
- 代碼:
使能定時器更新中斷
- 代碼:
timer_interrupt_enable(BSP_TIMER, TIMER_INT_UP);
- 功能:啟用定時器更新事件中斷(
TIMER_INT_UP
),當計數器溢出時觸發中斷。 - 說明:更新事件發生在計數器達到?
per-1
?并溢出時,觸發中斷處理程序。
- 代碼:
啟動定時器
- 代碼:
timer_enable(BSP_TIMER);
- 功能:使能定時器,開始計數。
- 說明:定時器按照配置的頻率和周期運行,定期觸發中斷。
- 代碼:
二、中斷函數
? ? ? ? 同樣在bsp_basic_timer.c中的中斷函數如下:
void BSP_TIMER_IRQHANDLER(void)
{/* ?aà?ê??¨ê±?÷?D?? */if(timer_interrupt_flag_get(BSP_TIMER,TIMER_INT_FLAG_UP) == SET){timer_interrupt_flag_clear(BSP_TIMER,TIMER_INT_FLAG_UP); // ??3y?D??±ê???? /* ?′DD1|?ü */gpio_bit_toggle(PORT_LED2,PIN_LED2); // ·-×aledprintf("BSP_TIMER_IRQHANDLER!\r\n"); // ′??ú′òó?BSP_TIMER_IRQHANDLER!}
}
? ? ? ? 定時器中斷函數在執行功能前主要進行了兩個操作:
檢查中斷標志
- 代碼:
if(timer_interrupt_flag_get(BSP_TIMER, TIMER_INT_FLAG_UP) == SET)
- 功能:檢查定時器?
BSP_TIMER
?的更新中斷標志位(TIMER_INT_FLAG_UP
)是否置位(SET
)。 - 說明:
- 更新中斷標志在定時器計數器溢出時自動置位,表示一次定時周期完成。
- 條件判斷確保只處理更新中斷,避免誤處理其他中斷類型。
BSP_TIMER
?是預定義的定時器,此處對應的是TIMER5,由歷史會話中的?basic_timer_config
?配置。
- 代碼:
清除中斷標志
- 代碼:
timer_interrupt_flag_clear(BSP_TIMER, TIMER_INT_FLAG_UP);
- 功能:清除定時器的更新中斷標志位。
- 說明:
- 清除標志位以確保下一次中斷能夠正確觸發。
- 如果不清除,中斷可能持續觸發,導致程序異常或死鎖。
- 這是中斷服務函數的標準操作,防止重復進入中斷。
- 代碼:
三、如何調用
? ? ? ? 在main.c里面只需要配置一下?basic_timer_config函數就行,調用代碼如下:
basic_timer_config(20000,10000); // ?¨ê±?÷3?ê??ˉ
- 定時時間公式:
time = (pre × per) / CK_TIMER
- 代入參數
pre = 20000 (實際預分頻系數 19999 + 1)
per = 10000 (實際周期 9999 + 1)
CK_TIMER = 200,000,000 Hz
time = (20000 × 10000) / 200,000,000 = 200,000,000 / 200,000,000 = 1 秒
? ? ? ? 運行后可以看到串口一直在輸出:
? ? ? ? 并且開發板上對應的LED2在閃爍
四、思考
為什么常常使用ADC時不直接放在?while(1)
?主循環中使用?delay
?控制采樣間隔?
? ? ? ? 不是不行,也可以,但是:
延時不精確,受主循環影響
- 在主循環中使用?
delay_ms(500)
?等軟件延時函數時,延時基于 CPU 空閑循環(如空指令循環)實現,但實際時間會因編譯優化、系統時鐘偏差或中斷干擾而抖動(例如,串口中斷或 GPIO 操作可能打斷延時,導致采樣間隔不均勻)。 - 問題:采樣間隔(如每 500ms 采樣一次)無法嚴格控制,可能導致數據采集不均勻,影響信號處理精度(如歷史會話中提到的 ADC 采樣)。
- 示例:若主循環中插入其他任務(如 LED 控制),延時函數會阻塞整個 CPU,造成采樣時機漂移。
- 在主循環中使用?
阻塞主循環,降低系統響應性
delay
?是“忙等待”(busy-waiting),CPU 在延時期間完全閑置,無法處理其他任務(如用戶輸入、通信或外設事件)。- 問題:在多任務環境中(如實時控制系統),主循環被阻塞會導致系統響應遲鈍,甚至錯過關鍵事件。例如,定時器中斷每 1 秒觸發 LED 翻轉,若也需要ADC 采樣用?
delay
?阻塞 500ms,主循環就無法及時響應其他中斷。 - 效率低:CPU 利用率低下,浪費資源,尤其在低功耗應用中不友好。
不適合實時性和周期性要求
- ADC 采樣往往需要固定間隔(如傳感器數據采集),主循環的軟件延時無法保證硬件級精度,受溫度、電壓等因素影響。
- 問題:在高速采樣場景(如音頻或電機控制),間隔抖動可能導致數據失真或系統不穩定。若 ADC 采樣直接用?
delay
,與定時器中斷(1 秒間隔)沖突,可能干擾整體流程。
為什么ADC采樣常放到定時器中斷中?
精確的硬件定時
- 定時器使用硬件計數器(如?
basic_timer_config(20000, 10000)
?配置 1 秒間隔),基于系統時鐘(CK_TIMER = 200MHz)精確控制中斷觸發時機,間隔誤差極小(納秒級)。 - 優勢:不受主循環影響,確保 ADC 采樣嚴格周期性(如每 500ms 觸發一次),提高數據一致性和精度。定時器可自動重載,無需軟件干預。
- 定時器使用硬件計數器(如?
非阻塞執行,提高系統效率
- 中斷服務函數(如?
BSP_TIMER_IRQHANDLER
)在定時器溢出時自動調用,執行 ADC 采樣后快速返回,主循環繼續運行其他任務。 - 優勢:避免阻塞,支持多任務并行。例如,歷史會話中定時器中斷可用于 ADC 觸發采樣,同時主循環處理串口輸出或 LED 控制,CPU 利用率高。結合 DMA(直接內存訪問)可進一步自動化數據傳輸,減少 CPU 負擔。
- 中斷服務函數(如?
增強實時性和可擴展性
- 中斷優先級機制(如 NVIC 設置搶占優先級 3)確保 ADC 采樣及時響應,適用于實時系統(如工業控制)。
- 優勢:易于調整間隔(修改?
pre
?和?per
?參數),并支持多通道采樣(如定時器觸發 ADC 多通道)。1 秒中斷可擴展為 500ms ADC 采樣,結合?timer_interrupt_enable(BSP_TIMER, TIMER_INT_UP)
?實現周期觸發。