1. 程式人生 > >第18章 SysTick—系統定時器

第18章 SysTick—系統定時器

capture 3.1 通過 其他 記錄 hal irq 3.2 處理

本章參考資料《Cortex?-M7內核編程手冊》-4.4 章節SysTick Timer(STK)4.38章節SHPRx,其中STK這個章節有SysTick的簡介和寄存器的詳細描述。因為SysTick是屬於CM7內核的外設,有關寄存器的定義和部分庫函數都在core_cm7.h這個頭文件中實現。所以學習SysTick的時候可以參考這兩個資料,一個是文檔,一個是源碼。

18.1 SysTick簡介

SysTick—系統定時器是屬於CM7內核中的一個外設,內嵌在NVIC中。系統定時器是一個24bit的向下遞減的計數器,計數器每計數一次的時間為1/SYSCLK,一般我們設置系統時鐘SYSCLK等於

216MHz。當重裝載數值寄存器的值遞減到0的時候,系統定時器就產生一次中斷,以此循環往復。

因為SysTick是屬於CM7內核的外設,所以所有基於CM7內核的單片機都具有這個系統定時器,使得軟件在CM7單片機中可以很容易的移植。系統定時器一般用於操作系統,用於產生時基,維持操作系統的心跳。

18.2 SysTick寄存器介紹

SysTick—系統定時有4個寄存器,簡要介紹如下。在使用SysTick產生定時的時候,只需要配置前三個寄存器,最後一個校準寄存器不需要使用。

18-1 SysTick寄存器匯總

寄存器名稱

寄存器描述

CTRL

SysTick控制及狀態寄存器

LOAD

SysTick重裝載數值寄存器

VAL

SysTick當前數值寄存器

CALIB

SysTick校準數值寄存器

18-2 SysTick控制及狀態寄存器

位段

名稱

類型

復位值

描述

16

COUNTFLAG

R/W

0

如果在上次讀取本寄存器後, SysTick 已經計到
了 0,則該位為 1。

2

CLKSOURCE

R/W

0

時鐘源選擇位,0=外部時鐘,1=處理器時鐘AHB

1

TICKINT

R/W

0

1=SysTick倒數計數到 0時產生 SysTick異常請
求,0=數到 0 時無動作。也可以通過讀取COUNTFLAG標誌位來確定計數器是否遞減到0

0

ENABLE

R/W

0

SysTick 定時器的使能位

18-3 SysTick 重裝載數值寄存器

位段

名稱

類型

復位值

描述

23:0

RELOAD

R/W

0

當倒數計數至零時,將被重裝載的值

18-4 SysTick當前數值寄存器

位段

名稱

類型

復位值

描述

23:0

CURRENT

R/W

0

讀取時返回當前倒計數的值,寫它則使之清零,同時還會清除在SysTick控制及狀態寄存器中的COUNTFLAG 標誌

18-5 SysTick校準數值寄存器

位段

名稱

類型

復位值

描述

31

NOREF

R

0

指示是否有參考時鐘提供給處理器

0:提供參考時鐘

1:不提供參考時鐘

如果器件不提供參考時鐘,SYST_CSR.CLKSOURCE標誌位為1,不可改寫。

30

SKEW

R

1

S指示TENMS的值是否精確

0TENMS是精確值

1TENMS不是精確值或者不提供

不精確的TENMS值可以影響作為軟件實時時鐘節拍器的適用性。

23:0

TENMS

R

0

重新加載 10ms (100Hz) 計時的值, 受系統時鐘偏差的錯誤。如果值讀取為零, 校準值未知。

系統定時器的校準數值寄存器在定時實驗中不需要用到。有研究過的朋友可以交流,起個拋磚引玉的作用。

18.3 SysTick定時實驗

利用SysTick產生1s的時基,LED1s的頻率閃爍。

18.3.1 硬件設計

SysTick屬於單片機內部的外設,不需要額外的硬件電路,剩下的只需一個LED燈即可。

18.3.2 軟件設計

這裏只講解核心的部分代碼,有些變量的設置,頭文件的包含等並沒有涉及到,完整的代碼請參考本章配套的工程。我們創建了兩個文件:bsp_SysTick.cbsp_ SysTick.h文件用來存放SysTick驅動程序及相關宏定義,中斷服務函數放在stm32f7xx_it.c文件中。

1. 編程要點

1、設置重裝載寄存器的值

2、清除當前數值寄存器的值

3、配置控制與狀態寄存器

2. 代碼分析

SysTick 屬於內核的外設,有關的寄存器定義和庫函數都在內核相關的庫文件core_cm7.h中。

SysTick配置庫函數

代碼 181SysTick配置庫函數

1 __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)

2 {

3 // 不可能的重裝載值,超出範圍

4 if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) {

5 return (1UL);

6 }

7

8 // 設置重裝載寄存器

9 SysTick->LOAD = (uint32_t)(ticks - 1UL);

10

11 // 設置中斷優先級

12 NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);

13

14 // 設置當前數值寄存器

15 SysTick->VAL = 0UL;

16

17 // 設置系統定時器的時鐘源為AHBCLK=180M

18 // 使能系統定時器中斷

19 // 使能定時器

20 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |

21 SysTick_CTRL_TICKINT_Msk |

22 SysTick_CTRL_ENABLE_Msk;

23 return (0UL);

24 }

用固件庫編程的時候我們只需要調用將SysTick_Config函數封裝好的庫函數HAL_SYSTICK_Config ()即可,形參ticks用來設置重裝載寄存器的值,最大不能超過重裝載寄存器的值224,當重裝載寄存器的值遞減到0的時候產生中斷,然後重裝載寄存器的值又重新裝載往下遞減計數,以此循環往復。緊隨其後設置好中斷優先級,最後配置系統定時器的時鐘為216MHz,使能定時器和定時器中斷,這樣系統定時器就配置好了,一個庫函數搞定。

SysTick_Config()庫函數主要配置了SysTick中的三個寄存器:LOADVALCTRL,有關具體的部分看代碼註釋即可。其中還調用了固件庫函數NVIC_SetPriority()來配置系統定時器的中斷優先級,該庫函數也在core_m7.h中定義,原型如下:

1 __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

2 {

3 if ((int32_t)IRQn < 0) {

4 SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] =

5 (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);

6 } else {

7 NVIC->IP[((uint32_t)(int32_t)IRQn)] =

8 (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);

9 }

10 }

因為SysTick屬於內核外設,跟普通外設的中斷優先級有些區別,並沒有搶占優先級和子優先級的說法。在STM32F767中,內核外設的中斷優先級由內核SCB這個外設的寄存器:SHPRxx=1.2.3)來配置。有關SHPRx寄存器的詳細描述可參考《Cortex-M7內核編程手冊》4.3.8章節。下面我們簡單介紹下這個寄存器。

SPRH1-SPRH3是一個32位的寄存器,但是只能通過字節訪問,每8個字段控制著一個內核外設的中斷優先級的配置。在STM32F767中,只有位7:3這高四位有效,低四位沒有用到,所以內核外設的中斷優先級可編程為:0~15,只有16個可編程優先級,數值越小,優先級越高。如果軟件優先級配置相同,那就根據他們在中斷向量表裏面的位置編號來決定優先級大小,編號越小,優先級越高。

18-6 系統異常優先級字段

異常

字段

寄存器描述

Memory management fault

PRI_4

SHPR1

Bus fault

PRI_5

Usage fault

PRI_6

SVCall

PRI_11

SHPR2

PendSV

PRI_14

SHPR3

SysTick

PRI_15

如果要修改內核外設的優先級,只需要修改下面三個寄存器對應的某個字段即可。

技術分享圖片

18-1 SHPR1寄存器

技術分享圖片

18-2 SHPR2寄存器

技術分享圖片

18-3 SHPR3寄存器

在系統定時器中,配置優先級為(1UL << __NVIC_PRIO_BITS) - 1UL),其中宏__NVIC_PRIO_BITS4,那計算結果就等於15,可以看出系統定時器此時設置的優先級在內核外設中是最低的。

1 // 設置系統定時器中斷優先級

2 NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);

SysTick初始化函數

代碼 182 SysTick初始化函數

1 /**

2 * @brief 啟動系統滴答定時器 SysTick

3 * @param

4 * @retval

5 */

6 void SysTick_Init(void)

7 {

8 /* SystemFrequency / 1000 1ms中斷一次

9 * SystemFrequency / 100000 10us中斷一次

10 * SystemFrequency / 1000000 1us中斷一次

11 */

12 if (HAL_SYSTICK_Config(SystemCoreClock / 100000)) {

13 /* Capture error */

14 while (1);

15 }

16 }

SysTick初始化函數由用戶編寫,裏面調用了SysTick_Config()這個固件庫函數,通過設置該固件庫函數的形參,就決定了系統定時器經過多少時間就產生一次中斷。

SysTick中斷時間的計算

SysTick定時器的計數器是向下遞減計數的,計數一次的時間TDEC=1/CLKAHB,當重裝載寄存器中的值VALUELOAD減到0的時候,產生中斷,可知中斷一次的時間TINT=VALUELOAD * TDEC中斷= VALUELOAD/CLKAHB,其中CLKAHB =216MHz。如果設置為216,那中斷一次的時間TINT=216/216MHz =1us。不過1us的中斷沒啥意義,整個程序的重心都花在進出中斷上了,根本沒有時間處理其他的任務。

SysTick_Config(SystemCoreClock / 100000))

SysTick_Config()的形我們配置為SystemCoreClock / 100000=216MHz /100000=2160,從剛剛分析我們知道這個形參的值最終是寫到重裝載寄存器LOAD中的,從而可知我們現在把SysTick定時器中斷一次的時間TINT=2160/216MHz =10us

SysTick定時時間的計算

當設置好中斷時間TINT後,我們可以設置一個變量t,用來記錄進入中斷的次數,那麽變量t乘以中斷的時間TINT就可以計算出需要定時的時間。

SysTick定時函數

現在我們定義一個微秒級別的延時函數,形參為nTime,當用這個形參乘以中斷時間TINT就得出我們需要的延時時間,其中TINT我們已經設置好為10us。關於這個函數的具體調用看註釋即可。

1 /**

2 * @brief us延時程序,10us為一個單位

3 * @param

4 * @arg nTime: Delay_us( 1 ) 則實現的延時為 1 * 10us = 10us

5 * @retval

6 */

7 void Delay_us(__IO u32 nTime)

8 {

9 TimingDelay = nTime;

10

11 while (TimingDelay != 0);

12 }

函數Delay_us()中我們等待TimingDelay0,當TimingDelay0的時候表示延時時間到。變量TimingDelay在中斷函數中遞減,即SysTick每進一次中斷即10us的時間TimingDelay遞減一次。

SysTick中斷服務函數

1 void SysTick_Handler(void)

2 {

3 TimingDelay_Decrement();

4 }

中斷復位函數調用了另外一個函數TimingDelay_Decrement(),原型如下:

1 /**

2 * @brief 獲取節拍程序

3 * @param

4 * @retval

5 * @attention SysTick 中斷函數 SysTick_Handler()調用

6 */

7 void TimingDelay_Decrement(void)

8 {

9 if (TimingDelay != 0x00) {

10 TimingDelay--;

11 }

12 }

TimingDelay的值等於延時函數中傳進去的nTime的值,比如nTime=100000,則延時的時間等於100000*10us=1s

主函數

1 int main(void)

2 {

3 /* 系統時鐘初始化成216 MHz */

4 SystemClock_Config();

5 /* LED 端口初始化 */

6 LED_GPIO_Config();

7 /* 配置SysTick 10us中斷一次,

8 時間到後觸發定時中斷,

9 *進入stm32f7xx_it.

10 c文件的SysTick_Handler處理,通過數中斷次數計時

11 */

12 SysTick_Init();

13

14 while (1) {

15

16 LED_RED;

17 Delay_us(100000); // 10000 * 10us = 1000ms

18

19 LED_GREEN;

20 Delay_us(100000); // 10000 * 10us = 1000ms

21

22 LED_BLUE;

23 Delay_us(100000); // 10000 * 10us = 1000ms

24

25 }

26 }

主函數中初始化了LEDSysTick,然後在一個while循環中以1s的頻率讓LED閃爍。

第18章 SysTick—系統定時器