systick 的配置總結

好吧,最近幾天一直都在看stm32的systick,其實不是有多難調好板子,只是,我看過的版本太多,一直很迷茫,直到現在纔有了清晰的概念,這裏和大家分享下。

其實,簡單點說,systick就是stm32內置的一個系統中斷,爲的就是給用戶提供一個系統節拍吧,你當然可以用來精確定時。

我想討論的其實是怎樣用固件庫裏面的函數進行操作。

這是官方例程裏面給的關於systick的操作程序。(我給了註釋。)

#include "main.h"
static __IO uint32_t TimingDelay;/*定義一個靜態全局變量*/
void Delay(__IO uint32_t nTime);/*函數聲明*/
int main(void)
{
	if (SysTick_Config(SystemCoreClock / 1000))
	{ 
		/* Capture error */ 
		while (1);
	}

while (1)
{
	STM_EVAL_LEDToggle(LED2);
	STM_EVAL_LEDToggle(LED4);
	Delay(50);/*用戶要求的延時時間*/

	STM_EVAL_LEDToggle(LED1);
	STM_EVAL_LEDToggle(LED3);

	Delay(100);
}
}

void Delay(__IO uint32_t nTime)/*函數定義*/
{ 
TimingDelay = nTime;/*把用戶要求的延時賦給全局變量*/


while(TimingDelay != 0);/*等待計數完成*/
}

void TimingDelay_Decrement(void)/*定義中斷中的執行函數*/
{
	if (TimingDelay != 0x00)/*等待計數完成*/
	{ 
		TimingDelay--;
	}
}
中斷入口函數是這樣的
void SysTick_Handler(void)
{
	TimingDelay_Decrement();
}


這裏重點研究的是systick的配置是怎樣實現的,也就是這句話if (SysTick_Config(SystemCoreClock / 1000)){ while (1);}是什麼意思。我們從core_cm3.h中可以查到函數SysTick_Config()的原碼。


static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
	if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* 如果計數值大於最大填充值,則返回未成功 */
                                                               
	SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* 置位寄存器值 */
	NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* 設置中斷優先級,這裏設置爲15 */
	SysTick->VAL   = 0;                                          /* 裝載寄存器值 */
	SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    /* 使能中斷和計數器 */
	return (0);                                                  /* 成功配置*/
}


這樣只需要給他一個用戶要的填充值,這個函數就可以讓計數器工作,同時,定義了中斷優先級,所以中斷也可以工作,從而就完成了定時。所以,SystemCoreClock / 1000這個是幹什麼的呢,毫無疑問是填充值,那你也許要問爲什麼不是數字,我說,你寫數字肯定沒錯,但是爲嘛這麼寫呢?我如果告訴你SystemCoreClock被系統宏定義了,默認爲72000000,也就是系統時鐘的頻率,那麼你是不是就懂了呢!所以,如果你要定時1ms就是時鐘頻率除以1000得到的值進行填充,那1us就是除以1000000得到的值進行填充。因爲是24位計數器,你懂的,有上限的,那是不是意味只能定時他的上限呢?of course not!你可以定時1s,然後,當你要定時10s的時候,你循環10次1s不就行了,這也就是爲嘛給出的是Delay()函數,而非直接就讓用戶計數填充到函數SysTick_Config()中啦。

上面是官方的例程中的方法,一看就是很簡單的,操作性和可讀性都很好,但是初學者,尤其是剛看了幾個例程的童靴就要問了,固件庫裏面給的systick的函數怎麼一個都沒用到,以前程序不是這樣寫的啊,怎麼現在又在頭文件裏定義一個函數,還是一半操作寄存器的呢,如果我就是不喜歡寄存器怎麼用固件庫的寫呢?

其實我也遇到同樣問題,我查資料之後,發現固件庫的寫起來也很簡單。

void SysTick_Configuration(void)
{

	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);/* 設置AHB時鐘爲SysTick時鐘*/
	NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick, 3, 0);/* 設置SysTicks中斷搶佔優先級 3, 從優先級0*/
	SysTick_SetReload(72000);/* 每1ms發生一次SysTick中斷*/
	SysTick_ITConfig(ENABLE);/*使能中斷t */
}


這就代替了剛纔的配置函數SysTick_Config()啦,其他的沒什麼兩樣。是不是覺得親切多了呢,我是覺得有熟悉的感覺。好吧,你有沒有思考過爲嘛他要又弄一個這個SysTick_Config()函數呢?好吧,其實不是爲了systick特意寫的這個函數,他是爲中斷優先級寫的NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)!這個函數就是用來配置中斷優先級的,當然你會問這個配置中斷優先級不是有 搶先式優先級和子優先級麼,你只有2個參數怎麼配置呢?其實這就是所謂的隱式的優先級,你寫入的優先級要通過優先級組的配置來選擇:

NVIC_SetPriority(SysTick_IRQn, n);
n=0x00~0x03 ;設置Systick爲搶佔優先級0
n=0x04~0x07 ;設置Systick爲搶佔優先級1
n=0x08~0x0B ;設置Systick爲搶佔優先級2
n=0x0C~0x0F ;設置Systick爲搶佔優先級3

NVIC_SetPriority函數指定中斷優先級的寄存器位(STM32只用4位來表示優先級)的數據,所以中斷優先級組設置爲了2,即高2位用於指定搶佔式優先級,低2位用於指定響應優先級,0x00~0x03高2位爲0,所以搶佔優先級爲0;0x04~0x07高2位爲1,所以搶佔優先級爲1,以此類推。

之所以要設置隱式優先級是因爲中斷中有些優先級特別高,他們的中斷在頭文件定義中,SysTick_IRQn被定義爲-1。

typedef enum IRQn
{
	/****** Cortex-M3 Processor Exceptions Numbers ***************************************************/
	NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
	MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */
	BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */
	UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */
	SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */
	DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */
	PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */
	SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */
....
}


同樣被定義爲負值的都只能通過調用函數NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)來設置優先級,當然也可以用NVIC_SystemHandlerPriorityConfig(u32 SystemHandler, u8 SystemHandlerPreemptionPriority, u8 SystemHandlerSubPriority)來設置,我比較傾向於調用給的函數。

說了這麼多,問大家一個問題,如果我這樣設置可以麼?

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = SysTick_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

如果你在猶豫,請重頭看一遍,因爲你沒懂。。。

哎呀,回頭一看,我寫這麼多了,不過這幾天看的資料真的也頗多,之前一直調不出想要的結果,終於弄好了,這也算整理好了吧,嗯嗯,go on,下一站!!!

PS:這裏面寄存器怎麼操作的,我就不想說了,我給出網上別人的操作程序,(我沒實驗,不知道正確與否。)強調一點,systick是一個24位的自動重裝的遞減計數器。

這是從網友哪裏摘錄的。(尊重原創啊。 http://home.eeworld.com.cn/my/space.php?uid=116357&do=blog&id=31714

//*****************************************************************

//*                               SystemTick-Register                                 

//*******************************************************************

#define SYSTICK_TENMS    (*((volatile unsigned long *)0xE000E01C))

#define SYSTICK_CURRENT  (*((volatile unsigned long *)0xE000E018))

#define SYSTICK_RELOAD   (*((volatile unsigned long *)0xE000E014))

#define SYSTICK_CSR       (*((volatile unsigned long *)0xE000E010))

 

配置systick寄存器:

void SysTick_Configuration(void)

{

   SYSTICK_CURRENT=0; //當前值寄存器

   SYSTICK_RELOAD=20000; //重裝載寄存器,系統時鐘20M中斷一次1mS

   SYSTICK_CSR|=0x06;// HCLK作爲Systick時鐘,Systick中斷使能位

 }

中斷處理:

void SysTick_Handler(void) //中斷函數

{

extern unsigned long TimingDelay; // 延時時間,注意定義爲全局變量

 

SYSTICK_CURRENT=0;

if (TimingDelay != 0x00)

TimingDelay--;

}

利用systick的延時函數:

 

unsigned long TimingDelay;  // 延時時間,注意定義爲全局變量

void Delay(unsigned long nTime)  //延時函數

{

SYSTICK_CSR|=0x07;   // 使能SysTick計數器

TimingDelay = nTime; // 讀取延時時間

while(TimingDelay != 0); // 判斷延時是否結束

SYSTICK_CSR|=0x06;// 關閉SysTick計數器

}

 

int main()

 {

  SystemInit0();    //系統(時鐘)初始化

 stm32_GpioSetup (); //GPIO初始化

  SysTick_Configuration(); //配置systick定時器

 while(1)

 {

  GPIO_PORTB_ODR|=(1<<5);

Delay(1000); //1S

 GPIO_PORTB_ODR&=~(1<<5);

 Delay(1000); //1S                                                                              

  }

}

完成!Delay(1000);實現了1S的精確延時,利用Delay(unsigned long nTime);配合systick定時器可以實現任意時間的精確延時,當然通過定時器TIMx也是可以這樣做的,我只是用它來說明systick定時器的用法。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章