第18章 SysTick—系統定時器—零死角玩轉STM32-F429系列

第18章     SysTick—系統定時器

全套200集視頻教程和1000PDF教程請到秉火論壇下載:www.firebbs.cn 編程

野火視頻教程優酷觀看網址:http://i.youku.com/firege ide

 

 

本章參考資料《 ARM Cortex-M4F 技術參考手冊》-4.5 章節SysTick Timer(STK)4.48章節SHPRx,其中STK這個章節有SysTick的簡介和寄存器的詳細描述。由於SysTick是屬於CM4內核的外設,有關寄存器的定義和部分庫函數都在core_cm4.h這個頭文件中實現。因此學習SysTick的時候能夠參考這兩個資料,一個是文檔,一個是源碼。函數

18.1 SysTick簡介

SysTick—系統定時器是屬於CM4內核中的一個外設,內嵌在NVIC中。系統定時器是一個24bit的向下遞減的計數器,計數器每計數一次的時間爲1/SYSCLK,通常咱們設置系統時鐘SYSCLK等於180M。當重裝載數值寄存器的值遞減到0的時候,系統定時器就產生一次中斷,以此循環往復。學習

由於SysTick是屬於CM4內核的外設,因此全部基於CM4內核的單片機都具備這個系統定時器,使得軟件在CM4單片機中能夠很容易的移植。系統定時器通常用於操做系統,用於產生時基,維持操做系統的心跳。ui

18.2 SysTick寄存器介紹

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

181 SysTick寄存器彙總spa

寄存器名稱操作系統

寄存器描述設計

CTRL3d

SysTick控制及狀態寄存器

LOAD

SysTick重裝載數值寄存器

VAL

SysTick當前數值寄存器

CALIB

SysTick校準數值寄存器

表 182 SysTick控制及狀態寄存器

位段

名稱

類型

復位值

描述

16

COUNTFLAG

R/W

0

若是在上次讀取本寄存器後, SysTick 已經計到
0,則該位爲 1

2

CLKSOURCE

R/W

0

時鐘源選擇位,0=AHB/81=處理器時鐘AHB

1

TICKINT

R/W

0

1=SysTick倒數計數到 0時產生 SysTick異常請
求,0=數到 0 時無動做。也能夠經過讀取COUNTFLAG標誌位來肯定計數器是否遞減到0

0

ENABLE

R/W

0

SysTick 定時器的使能位

表 183 SysTick 重裝載數值寄存器

位段

名稱

類型

復位值

描述

23:0

RELOAD

R/W

0

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

184 SysTick當前數值寄存器

位段

名稱

類型

復位值

描述

23:0

CURRENT

R/W

0

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

185 SysTick校準數值寄存器

位段

名稱

類型

復位值

描述

31

NOREF

R

0

NOREF flag. Reads as zero. Indicates that a separate reference clock is provided.
The frequency of this clock is HCLK/8

30

SKEW

R

1

SKEW flag: Indicates whether the TENMS value is exact. Reads as one. Calibration
value for the 1 ms inexact timing is not known because TENMS is not known. This can affect
the suitability of SysTick as a software real time clock

23:0

TENMS

R

0

Calibration value. Indicates the calibration value when the SysTick counterruns on HCLK max/8 as external clock. The value is product dependent, please refer to theProduct Reference Manual, SysTick Calibration Value section. When HCLK is programmed atthe maximum frequency, the SysTick period is 1ms.
If calibration information is not known, calculate the calibration value required from thefrequency of the processor clock or external clock

系統定時器的校準數值寄存器在定時實驗中不須要用到。有關各個位的描述這裏引用手冊裏面的英文版本,比較晦澀難懂,暫時不知道這個寄存器用來幹什麼。有研究過的朋友能夠交流,起個拋磚引玉的做用。

18.3 SysTick定時實驗

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

18.3.1 硬件設計

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

18.3.2 軟件設計

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

1.    編程要點

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

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

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

2.    代碼分析

SysTick 屬於內核的外設,有關的寄存器定義和庫函數都在內核相關的庫文件core_cm4.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()便可,形參ticks用來設置重裝載寄存器的值,最大不能超太重裝載寄存器的值2^24,當重裝載寄存器的值遞減到0的時候產生中斷,而後重裝載寄存器的值又從新裝載往下遞減計數,以此循環往復。緊隨其後設置好中斷優先級,最後配置系統定時器的時鐘爲180M,使能定時器和定時器中斷,這樣系統定時器就配置好了,一個庫函數搞定。

SysTick_Config()庫函數主要配置了SysTick中的三個寄存器:LOADVALCTRL,有關具體的部分看代碼註釋便可。

配置SysTick中斷優先級

SysTick_Config()庫函數還調用了固件庫函數NVIC_SetPriority()來配置系統定時器的中斷優先級,該庫函數也在core_m4.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屬於內核外設,跟普通外設的中斷優先級有些區別,並無搶佔優先級和子優先級的說法。在STM32F429中,內核外設的中斷優先級由內核SCB這個外設的寄存器:SHPRxx=1.2.3)來配置。有關SHPRx寄存器的詳細描述可參考《Cortex-M4內核編程手冊》4.4.8章節。下面咱們簡單介紹下這個寄存器。

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

186 系統異常優先級字段

異常

字段

寄存器描述

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

若是要修改內核外設的優先級,只須要修改下面三個寄存器對應的某個字段便可。

181 SHPR1寄存器

182 SHPR2寄存器

183 SHPR3寄存器

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

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

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

可是,問題來了,剛剛咱們只是學習了內核的外設的優先級配置。若是我同時使用了systick和片上外設呢?並且片上外設也恰好須要使用中斷,那systick的中斷優先級跟外設的中斷優先級怎麼設置?會不會由於systick是內核裏面的外設,因此它的中斷優先級就必定比內核以外的外設的優先級高?

從《STM32中斷應用概覽》這章咱們知道,外設在設置中斷優先級的時候,首先要分組,而後設置搶佔優先級和子優先級。而systick這類內核的外設在配置的時候,只須要配置一個寄存器便可,取值範圍爲0~15。既然配置方法不一樣,那如何區分二者的優先級?下面舉例說明。

好比配置一個外設的中斷優先級分組爲2,搶佔優先級爲1,子優先級也爲1systick的優先級爲固件庫默認配置的15。當咱們比較內核外設和片上外設的中斷優先級的時候,咱們只須要抓住NVIC的中斷優先級分組不只對片上外設有效,一樣對內核的外設也有效。咱們把systick的優先級15轉換成二進制值就是1111(0b),又由於NVIC的優先級分組2,那麼前兩位的11(0b)就是3,後兩位的11(0b)也是3。不管從搶佔仍是子優先級都比咱們設定的外設的優先級低。若是當兩個的軟件優先級都配置成同樣,那麼就比較他們在中斷向量表中的硬件編號,編號越小,優先級越高。

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 (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 =180MHZ。若是設置爲180,那中斷一次的時間TINT=180/180M=1us。不過1us的中斷沒啥意義,整個程序的重心都花在進出中斷上了,根本沒有時間處理其餘的任務。

SysTick_Config(SystemCoreClock / 100000))

SysTick_Config()的形咱們配置爲SystemCoreClock / 100000=180M/100000=1800,從剛剛分析咱們知道這個形參的值最終是寫到重裝載寄存器LOAD中的,從而可知咱們如今把SysTick定時器中斷一次的時間TINT=1800/180M=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 /* LED 端口初始化 */

4 LED_GPIO_Config();

5

6 /* 配置SysTick 10us中斷一次,時間到後觸發定時中斷,

7 *進入stm32fxx_it.c文件的SysTick_Handler處理,經過數中斷次數計時

8 */

9 SysTick_Init();

10

11 while (1) {

12

13 LED_RED;

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

15

16 LED_GREEN;

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

18

19 LED_BLUE;

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

21 }

22 }

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

上面的實驗,咱們是使用了中斷,並且通過多個函數的調用,還使用了全局變量,理解起來挺費勁的,其實還有另一種更簡潔的寫法。咱們知道,systickcounterreload值往下遞減到0的時候,CTRL寄存器的位16:countflag會置1,且讀取該位的值可清0,全部咱們可使用軟件查詢的方法來實現延時。具體代碼見代碼 183和代碼 184,我敢確定這樣的寫法,初學者確定會更喜歡,由於它直接,套路淺。

代碼 183 systick 微秒級延時

1 void SysTick_Delay_Us( __IO uint32_t us)

2 {

3 uint32_t i;

4 SysTick_Config(SystemCoreClock/1000000);

5

6 for (i=0; i<us; i++) {

7 // 當計數器的值減少到0的時候,CRTL寄存器的位16會置1

8 while ( !((SysTick->CTRL)&(1<<16)) );

9 }

10 // 關閉SysTick定時器

11 SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;

12 }

 

代碼 184 systick 毫秒級延時

1 void SysTick_Delay_Ms( __IO uint32_t ms)

2 {

3 uint32_t i;

4 SysTick_Config(SystemCoreClock/1000);

5

6 for (i=0; i<ms; i++) {

7 // 當計數器的值減少到0的時候,CRTL寄存器的位16會置1

8 // 當置1時,讀取該位會清0

9 while ( !((SysTick->CTRL)&(1<<16)) );

10 }

11 // 關閉SysTick定時器

12 SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;

13 }

另一種更簡潔的定時編程

在這兩個微秒和毫秒級別的延時函數中,咱們仍是調用了SysTick_Config這個固件庫函數,有關這個函數的說明具體見代碼 185。配套代碼註釋理解便可。其中SystemCoreClock是一個宏,大小爲72000000,若是不想使用這個宏,也能夠直接改爲數字。

代碼 185 systick 配置函數

1 // 這個固件庫函數 core_cm3.h

2 static __INLINE uint32_t SysTick_Config(uint32_t ticks)

3 {

4 // reload 寄存器爲24bit,最大值爲2^24

5 if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);

6

7 // 配置 reload 寄存器的初始值

8 SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;

9

10 // 配置中斷優先級爲 1<<4 -1 = 15,優先級爲最低

11 NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);

12

13 // 配置 counter 計數器的值

14 SysTick->VAL = 0;

15

16 // 配置systick 的時鐘爲 72M

17 // 使能中斷

18 // 使能systick

19 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |

20 SysTick_CTRL_TICKINT_Msk |

21 SysTick_CTRL_ENABLE_Msk;

22 return (0);

23 }

18.4 每課一問

一、若是修改SysTick的中斷優先級?

二、如何計算SysTick進入一次中斷的時間?

三、如何利用SysTick實現一個1ms的延時?

相關文章
相關標籤/搜索