1、系统滴答定时器
参考资料《Cortex M3与M4权威指南.pdf》中的9.5 The SysTick timer小节,里面详细讲解了
1、为什么要用SysTick计时器;
2、SysTick定时器的操作情况;
3、如何使用SysTick定时器;
4、使用SysTick定时器。
2、简介
系统定时器【SysTick】它挂载在AHB时钟线上,是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SystemCoreClock,当前我们的开发板选用的是25M的外部晶振,倍频至200M,且当前提供的标准库设置为不分频,则系统时钟SystemCoreClock等于200M。当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复。
3、SysTick寄存器
SysTick寄存器:
CTRL:SysTick控制及状态寄存器
LOAD:SysTick重装载数值寄存器
VAL:SysTick当前数值寄存器
CALIB:SysTick校准数值寄存器
SysTick控制及状态寄存器:
COUNTFLAG:如果在上次读取本寄存器后 SysTick已经计到了0,则该位为 1;
CLKSOURCE:时钟源选择位 ,0=AHB/8,1=处理器时钟AHB
TICKINT:1=SysTick倒数计数到 0时产生 SysTick异常请求,0=数到 0 时无动作。也可以通过读取COUNTFLAG标志位来确定计数器是否递减到0
ENABLE:SysTick 定时器的使能位
思路
SysTick->VAL寄存器的值每一个时钟周期就会递减1,当它递减到0时候,SysTick->LOAD的值将会进入SysTick->VAL中,并且SysTick->CTRL的COUNTFLAG位将会置1,如果还使能了中断,将还会进入中断。
延时原理就是通过使能中断,并设定SysTick->LOAD的值以及时钟周期的数值(通常设定为1kHz = 1ms)来实现的。
4、SysTick代码如下:
/** \brief System Tick Configuration The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
Counter is in free running mode to generate periodic interrupts.
\param [in] ticks Number of ticks between two interrupts.
\return 0 Function succeeded.
\return 1 Function failed.
\note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
must contain a vendor-specific implementation of this function.
*/
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = ticks - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */
SysTick->VAL = 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 */
}
M4手册解说
如果你只想产生一个周期性的SysTick中断,最简单的方法是使用CMSIS-Core函数“SysTick_Config”:
SysTick_Config(uint32_t ticks);
该函数将SysTick中断间隔设置为“ticks”,启用使用处理器时钟的计数器,并启用异常优先级最低的SysTick异常。
例如,如果你有一个200MHz的时钟频率,你想要触发一个1KHz的SysTick异常,你可以使用:
(SystemCoreClock / 1000);
变量“SystemCoreClock”应该保持正确的时钟频率值200M。 “SysTick_Handler(void)”将以1khz的速率被触发。
如果SysTick_Config函数的输入参数不能装进24位重新加载值寄存器(大于OxFFFFFF), SysTick_Config函数返回1;否则,它返回0。
个人深入理解
或者可以这么理解AHB为200M,则计数周期为0.02us(1/200M)1ms/0.02us=10^3/0.02=200000,也就是计数1ms要递减200000次,也就是SysTick->VAL递减200000次,所以SysTick->LODA是200000,也可以说ticks参数必须是200000
5、SysTick库函数添加延时函数
先初始化SysTick配置函数:将SysTick函数配置成ms级中断,如果配置成us级中断,中断就会频繁的进入,将会占用cpu的使用资源,
\brief configure systick
\param[in] none
\param[out] none
\retval none
*/
#define sys_1ms 1000U
#define sys_1us 1000000U
void systick_config(void)
{
/* setup systick timer for 1000Hz interrupts */
if(SysTick_Config(SystemCoreClock / sys_1ms))
{
/* capture error */
while(1)
{
}
}
/* configure the systick handler priority */
NVIC_SetPriority(SysTick_IRQn,0x00U);
usTicks=SystemCoreClock/sys_1us;
}
定义一个全局变量,在每次触发SysTick定时器中断让它加1;
volatile uint32_t sysTickUptime;
/*!
\brief this function handles SysTick exception
\param[in] none
\param[out] none
\retval none
*/
void SysTick_Handler(void)
{
sysTickUptime++;
}
/*!
\brief 获取运行时间
\param[in] none
\param[out] none
\retval none
*/
uint32_t get_system_run_time(void)
{
return sysTickUptime; //ms
}
/*
\brief delay a time in milliseconds
\param[in] count: count in milliseconds
\param[out] none
\retval none
*/
void delay_ms(uint32_t millis_)
{
uint32_t target;
target = sysTickUptime + millis_;
while (sysTickUptime < target);
}
us计数器原理:由于SysTick->VAL 的计数范围20 0000~0就是1ms,递减计数器,因此可以根据它的递减计数器,推出us延时。
//微妙计数器
uint32_t systick_get_micros(void)
{
//register请求编译器尽可能的将变量存在CPU内部寄存器,而不是通过内存寻址访问。
register uint32_t ms, cycle_cnt;
do
{
ms = sysTickUptime;
cycle_cnt = SysTick->VAL;//SysTick->VAL 的计数范围20 0000~0就是1ms,递减计数器
}while (ms != sysTickUptime);
return (ms*1000)+ (usTicks * 1000 - cycle_cnt)/usTicks;
}
/********************************************************************************************************
**函数信息 :void delay_us(u16 nus)
**功能描述 :延时微秒
**输入参数 :nms微秒数
**输出参数 :无
********************************************************************************************************/
void delay_us(uint32_t micros_)
{
uint32_t tcnt=systick_get_micros();
while(systick_get_micros() - tcnt < micros_);
}
6、函数测试
《灯语测试》
默认离线模式:led1 100ms亮,100ms灭 共两次,周期为1s;
KEY1按下:在线模式,led1 200ms亮,200ms灭 共两次,周期为2s;