delay延时函数讲解(Systick应用)
频率定义:单位时间内完成周期性变化次数。
(借鉴正点原子网课)
(一)Systick定时器,是一个简单的定时器(最主要的),对于CM3,CM4内核芯片,都有Systick定时器。
Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。
(二)
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作(这也是为什么要特意开发出Systick定时器的缘由,如果此时用普通定时器在睡眠模式下会大大增加功耗 )。
SysTick定时器被捆绑在NVIC(中断优先级管理)中,用于产生SYSTICK异常。
Systick中断的优先级也可以设置。
(三)4个Systick寄存器
①CTRL SysTick 控制和状态寄存器 LOAD
备注: TICKINT 中断位(1),M3外部时钟源72/8=9MHZ,M4外部时钟源是168/21=21MHZ
②SysTick 自动重装载除值寄存器
③SysTick 当前值寄存器 CALIB
(四)Systick库函数
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
/* Check the parameters */
assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
//!!该段代码的作用是选择以下某段时钟源
if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
{
SysTick->CTRL |= SysTick_CLKSource_HCLK;
}
else
{
SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
}
}
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ //两个中断中间有多少个ticks周期。
if (ticks-1 > SysTick_LOAD_RELOAD_Msk) return (1);
//对tick值进行有效性分析,最后要写到24位的LOAD寄存器,不能大于24位寄存器2的24次方
/* Reload value impossible */
/* set reload register */
SysTick->LOAD = ticks - 1; //设置重装载寄存器值
/* set Priority for Cortex-M0 System Interrupts */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); //设置优先级
SysTick->VAL = 0; //设置Value值为0,会重新去加载。 /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
//设置时钟源,中断,使能
/* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
static __IO uint32_t TimingDelay;
void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
void SysTdler(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
int main(void)
{ …
if (SysTick_Config(SystemCoreClock / 1000)) //systick时钟为HCLK,中断时间间隔1ms
{
while (1);
}
while(1)
{ Delay(200);//2ms
…
}
}
(四)用中断方式实现delay延时(举例)
static __IO uint32_t TimingDelay;
void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
void SysTdler(void) //每等待一毫秒产生一个中断
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
int main(void)
{ …
if (SysTick_Config(SystemCoreClock / 1000)) //systick时钟为HCLK,中断时间间隔1ms
{
while (1);
}
while(1)
{ Delay(200);//200ms
…
}
}
//在Main中先设置好入口参数ticks的值,配置好初始化systick,进入Delay(200)循环,在这个循环体
//中每一次中断,Systick中断服务函数Systick_Handler自动减一,知道最后减到0,while结束循环,此延时函数也就结束了。
(五)delay_init函数介绍
//初始化延迟函数
//当使用 OS 的时候,此函数会初始化 OS 的时钟节拍
//SYSTICK 的时钟固定为 HCLK 时钟的 1/8
void delay_init()
{#if SYSTEM_SUPPORT_OS //如果需要支持 OS.
u32 reload;
#endif
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//选择外部时钟 HCLK/8
fac_us=SystemCoreClock/8000000; //为系统时钟的 1/8
#if SYSTEM_SUPPORT_OS //如果需要支持 OS.
reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为 M
reload*=1000000/delay_ostickspersec; //根据 delay_ostickspersec 设定溢出时间
//reload 为 24 位寄存器,最大值:16777216,在 168M 下,约合 0.7989s 左右
fac_ms=1000/delay_ostickspersec; //代表 OS 可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启 SYSTICK 中断
SysTick->LOAD=reload; //每 1/delay_ostickspersec 秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启 SYSTICK
#else
fac_ms=(u16)fac_us*1000; //非 OS 下,代表每个 ms 需要的 systick 时钟数
#endif
该函数用来初始化 2 个重要参数:fac_us 以及 fac_ms;同时把 SysTick 的时钟源选择为外部时
钟,如果需要支持操作系统(OS),只需要在 sys.h 里面,设置 SYSTEM_SUPPORT_OS 宏的值
为 1 即可,然后,该函数会根据 delay_ostickspersec 宏的设置,来配置 SysTick 的中断时间,并
开启 SysTick 中断。
可以看到,delay_init 函数使用了条件编译,来选择不同的初始化过程,如果不使用 OS 的
时候,只是设置一下 SysTick 的时钟源以及确定 fac_us 和 fac_ms 的值。
(六)Delay_us,Delay_xms,Delay_ms 的延时函数介绍
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载,我们要延时n倍的us, 1us是一个fac_ua周期,所以总共要延时的周期值为二者相乘最后送到Load中。
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开启使能位 开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //用来判断 systick 定时器是否还处于开启状态,然后在等待时间到达,也就是数到0的时候,此时第十六位设置为1
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器使能位
SysTick->VAL =0X00; //清空计数器
}
void delay_xms(u16 nms)
{ //跟上列同理
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //
SysTick->VAL =0x00; //
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //
SysTick->VAL =0X00; //
}
//
void delay_ms(u16 nms)
{
u8 repeat=nms/540; //nms除以540后的整数值赋值给repeat,也就是倍数
u16 remain=nms%540; //nms除以540后余数值赋值给remain
while(repeat) //开始倍数循环 循环条件依次减一直到最后为零
{
delay_xms(540); //延时一次540ms
repeat--;
}
if(remain)delay_xms(remain); //延时n倍的540后,剩余的值在这里延时
}