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定时器的用法。


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