前言
在前面的篇章中,我們已經學會使用 Timer 來精確定時了,而在使用 MCU 的過程中最常會需要精確定時的莫過於 delay()
函式,在此之前我都是單純的讓 MCU 空跑一定的次數,但這樣很難知道它實際上到底 delay 了多久的時間,而已同樣的數值在不同的 Clock Tree 設定下 delay 的長度也不同,因此我們可以使用 Timer 來做出一個更好的 delay()
。
但是如果只是要實現 delay()
功能的話,並不用像之前的 Timer 那樣計算並設定一大堆數值,因為 ARM Cortex M3 有一個特殊的計時器——SysTick,我們可以使用它來完成 delay()
函式。
這次的目標是使用 SysTick 來實現一個 delay_ms()
函式,它可以以毫秒為單位進行 delay,並且用來寫 LED 閃爍的程式。
正文
首先一樣以 Nucleo-F446RE 做示範。
首先建立一個 PIO 的專案,選擇 Framework 為「libopencm3」,並在 src/
資料夾中新增並開啓 main.c
檔案。
完整程式
分段說明
Include
重點在於要記得引入 systick.h
。值得注意的是如果不引入 nvic.h
的話,程式應該也可以完成編譯甚至執行,但 SysTick 的 ISR 函式原型其實是宣告在這裡面的,所以我還是把它加入。
設定 SysTick
SysTick(System tick timer)是 ARM Cortex M3 系列內建的功能,這是一個 24 位元的下數計數器,擁有自動裝載與中斷功能。
從 Clock Tree 中可以看到 SysTick 在 AHB 底下,並且前面有一個可程式設定的預分頻器(圖上雖然看起來是固定 /8
,但根據我實測的結果與 STM32CubeMX 中顯示的設定,這應該是可以選擇 /1
或 /8
)。
這裡使用 systick_set_clocksource()
來指定 SysTick 時鐘源為 AHB 並啓用 /8
預分頻器,因此目前的 SysTick 頻率為 rcc_ahb_frequency / 8
。
然後要設定 SysTick 的 RVR(Reload Value Register)暫存器,這個數值決定了 SysTick 發生中斷的頻率,SysTick 每次下數到 0 時都會自動載入 RVR 的值到 CVR(Current Value Register)中,所以 SysTick 會計數 RVR~0 共 RVR + 1 次。
所以 SysTick 中斷發生的頻率為:
f_int = f_systick / (RVR + 1)
因此
RVR = f_systick / f_int - 1
最後只要套用上面的公式,並用 systick_set_reload()
設定 RVR 的值就好。因為這裡預計要實現 ms 等級的 delay,所以我希望 SysTick 可以每 1 ms 就中斷一次,也就是中斷頻率為 1 kHz,故設定 RVR 的值為 rcc_ahb_frequency / 8 / 1000 - 1
。
RVR 是一個 24 位元的暫存器,它的容許範圍為
0x000001
~0xFFFFFF
,實際在設定時要注意一下。官方說明
Delay 與 SysTick ISR
首先宣告一個全域變數 systick_delay
,並加上 volatile
以防止編譯器優化它。
delay_ms()
要做的就是把其參數 ms
傳遞給 systick_delay
,然後等待 sys_tick_handler()
將 systick_delay
的值減到 0。
而 sys_tick_handler()
是 SysTick 的 ISR,它只要負責每次都把 systick_delay
減 1 即可。
多環境程式(F446RE + F103RB)
由於 STM32F1 的部分函式不同,所以 F103RB 沒辦法直接使用上面的 F446RE 的程式。
以下列出主要的差異部分。完整的程式請看 GitHub repo。
成果
這裡使用兩個 STM32,並分別設定 LED 開關的 delay 為 500ms 和 5ms,結果也是滿精準的。
請注意 LED 要切換 2 次才是一個完整的波形。
小結
delay_ms()
是在用 MCU 時非常常用到的功能,而這次介紹如何使用 SysTick 來實現它,這樣就可以得到一個相對精準的 delay,也不用大費周章去設定一般的 Timer。
參考資料
- Cortex-M3 Devices Generic User Guide
- libopencm3/libopencm3-examples
- platformio/platform-ststm32
- STM32F446RE datasheet (DS10693)
- STM32F446xx reference manual (RM0390)
- STM32F103RB datasheet (DS5319)
- STM32 Nucleo-64 board user manual (UM1724)
本文的程式也有放在 GitHub 上。 本文同步發表於 iT 邦幫忙-2022 iThome 鐵人賽。
留言可能不會立即顯示。若過了幾天仍未出現,請 Email 聯繫:)