目錄
概述
1 SysTick 定時器
1.1?SysTick 定時器功能介紹
1.2?SysTick 定時器功能實現
1.3?SysTick在系統中的作用
2 SysTick應用的實例
2.1?建立異常服務例程
2.2 使能異常
2.3?鬧鐘功能
2.4??重定位向量表
2.5?消滅二次觸發
3?SysTick在FreeRTOS中的應用
3.1 STM32Cube配置SysTick
3.2 STM32HAL中sysTick接口
概述
本文主要介紹Cortex-M3的SysTick 定時器的相關知識,還介紹了一個使用匯編語言編寫的Cortex-M3的SysTick的應用實例。還介紹基于Cortex-M3內核的經典MCU STM32F103中SysTick在實際項目應用的方法。
1 SysTick 定時器
1.1?SysTick 定時器功能介紹
SysTick 是一個 24 位的倒計數定時器,當計到 0 時,將從 RELOAD 寄存器中自動重裝載定時初值。只要SysTick 控制及狀態寄存器中的使能位被置位, 就永不停息。下圖是SysTick的相關寄存器:
CM3 允許為 SysTick 提供兩個時鐘源以供選擇:
1)是內核的“自由運行時鐘” FCLK。“自由” 表現在它不來自系統時鐘 HCLK, 因此在系統時鐘停止時 FCLK 也繼續運行。
2)一個外部的參考時鐘。但是使用外部時鐘時,因為它在內部是通過 FCLK 來采樣的,因此其周期必須至少是 FCLK 的兩倍(采樣定理)。
很多情況下芯片廠商都會忽略此外部參考時鐘,因此通常不可用。通過檢查校準寄存器的位[31](NOREF), 可以判定是否有可用的外部時鐘源,而芯片廠商則必須把該引線連接至正確的電平。
1.2?SysTick 定時器功能實現
當 SysTick 定時器從 1 計到 0 時,它將把 COUNTFLAG 位置位;
下述方法可以清零之:
1)讀取 SysTick 控制及狀態寄存器(STCSR)
2)往 SysTick 當前值寄存器(STCVR)中寫任何數據
1.3?SysTick在系統中的作用
1)SysTick 的最大使命,就是定期地產生異常請求,作為系統的時基。 OS 都需要這種“滴答” 來推動任務和時間的管理。
2)如欲使能 SysTick 異常, 則把 STCSR.TICKINT 置位。 另外, 如果向量表被重定位到 SRAM 中,還需要為 SysTick 異常建立向量,提供其服務例程的入口地址。
2 SysTick應用的實例
2.1?建立異常服務例程
建立SysTick異常服務例程
MOV R0, #0xF ; 異常號: 15
LDR R1, =systick_handler ; 加載服務例程的入口地址
LDR R2, =0xE000ED08 ; 加載向量表偏移量寄存器的地址
LDR R2, [R2] ; 讀取向量表的首地址
STR R1, [R2, R0, LSL #2] ; 寫入向量
2.2 使能異常
LDR R0, =0xE000E010 ; 加載STCSR的地址
MOV R1, #0
STR R1, [R0] ; 先停止SysTick,以防意外產生異常請求
LDR R1, =0x3FF ; 讓SysTick每1024周期計完一次。因為是從1023數到; 0,總共數了1024個周期,所以加載值為0x3FF
STR R1, [R0,#4] ; 寫入重裝載的值
STR R1, [R0,#8] ; 往STCVR中寫任意的數,以確保清除COUNTFLAG標志
MOV R1, #0x7 ; 選擇FCLK作為時鐘源,并使能SysTick及其異常請求
STR R1, [R0] ; 寫入數值,開啟定時器
除此之外, SysTick 定時器還提供了走完 10ms 所需要的格數(TENMS 位段),作為時間校準的參考信息。在 CM3 處理器的頂層有一個 24 位的輸入,芯片廠商可以寫入一個 10ms 的加載值,寫程序時就可以讀取 STCR 寄存器中的 TENMS 位段來獲取此信息。不一定每個芯片都實現了此功能,因此在使用時還需查閱芯片的數據手冊。
2.3?鬧鐘功能
SysTick 定時器還可以用作鬧鐘,作為啟動一個特定任務的時間依據。例如,如果需要在 300 周期后執行一段代碼,就可以在 SysTick 異常服務例程中設置執行那段代碼的軟件標志。使用 SysTick 時,清零 CURRENT 再編程 RELOAD 寄存器,以使它在 300 周期后產生異常,如下述代碼所演示:
LDR r0, =15LDR r1, =SysTickAlarm ; SysTick異常服務例程為SetupExcpHanlerBL SetupExcpHandler ; 調用前面章節講到的子程來建立向量LDR R0, =0xE000E010 ; SysTick寄存器組的基地址MOV R1, #0 ; 編程前先除能SysTickSTR R1, [R0]STR R1, [R0,#0x8] ; 清零CURRENTLDR R1, =(300-12) ; 設置裝載值。減去12是為了補償中延遲STR R1, [R0,#0x4] ; 寫入RELOADLDR R4, =SysTickFired ; 在RAM中的一個變量,指示是計時到期MOV R5, #0 ; 初始為0STR R5, [R4]MOV R1, #0x7 ; 使用FCLK,使能SysTick,使能SysTick異常STR R1, [R0] ; 啟動計時LDR R4, =SysTickFired
WaitLoopLDR R5, [R4] ; 循環查詢軟件標志CMP R5, #0BEQ WaitLoop... ; SysTickFired在服務例程中被置位,主程序可以繼續執行
2.4??重定位向量表
因為計數器是從 0 開始計數的,所以它會立即把 300‐12 加載入 CURRENT。 12 是中斷響應的最短延時,因此減去它用以補償。但是如果有更高優先級的異常搶占或者阻塞了它,則中斷延遲還是會有的。
SetupExcpHandler 來建立向量表,但注意:必須重定位向量表到RAM 中才行。
SetupExcpHandler; 入口條件: R0 = 異常號; 入口條件: R1 = 異常服務例程PUSH {R0, R2, LR}LDR R2, =NVIC_VECTTBLLDR R2, [R2] ; 讀取向量表的地址STR.W R1, [R2, R0, LSL #2] ; 表中[R2+R0<<2]的位置就是為該向量的POP {R0, R2, PC} ; 快速返回
2.5?消滅二次觸發
在2.4節?重定位向量表時,減去12 只適用于一次性(one shot)的鬧鐘操作,必須在 SysTick 服務例程中按停這個 SysTick。進一步地,如果其它異常把它延遲得太久,就有可能會使 SysTick異常被懸起兩次。因此,對于單次處理時,還需要其它一些步驟來消滅二次觸發:
SysTickAlarm ; SYSTICK exception handlerPUSH {LR}LDR R0, =0xE000E010 ; SYSTICK寄存器組的基地址MOV R1, #0STR R1, [R0] ; 除能SysTick,因為只使用一次LDR R0, =0xE000ED04LDR R1, =0x02000000 ; 手工清除NVIC中的SysTick懸起位STR R1, [R0]... ; 執行所需的處理工作LDR R2, = SysTickFiredLDR R1, [R2]ORR R1, #1STR R1, [R2] ; 設置軟件標志,與主程序同步,以執行任務POP {PC} ; 異常返回
在服務例程的末尾處,通過設置 SysTickFired 標志,通知主程序定時已經到期,可以結束循環等待了。
?
3?SysTick在FreeRTOS中的應用
3.1 STM32Cube配置SysTick
筆者以STM32103RTB芯片作為平臺配置SysTick和FreeRTOS
1) 配置FreeRTOS的系統時鐘為SysTick
2)使能FreeRTOS
3.2 STM32HAL中sysTick接口
在STM32HAL庫中定義在core_cm3.h中,這部分代碼在實際項目應用中,不建議修改,用戶可以重新stm32f1xx_hal.c中和sysTick相關的應用接口。在這個文件中,和sysTick相關的應用接口被定義為__weak類型,意味著其可以被重寫。