一、什么是Systick系统定时器?
CM3 内核的处理器,内部包含了一个 SysTick 定时器,SysTick 是一个 24 位的倒计数定时器,当计数到 0 时,将从RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
有三个函数是用到这个Systick系统定时器的:
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);
二、四个寄存器控制Systick定时器工作
1.SysTick控制及状态寄存器(地址:0xE000_E010)
位段 | 名称 | 描述 |
---|---|---|
16 | COUNTFLAG | 如果Systick计数到0,这个标志就是1,如果直接读取该位,就会将该位为0 |
2 | CLKSOURCE | 如果是外部时钟则为0,内部时钟为1 |
1 | TICKINT | 1为Systick数到0的时候,中断;0为数到0时不产生中断 |
0 | ENABLE | 1为使能Systick定时器 |
16 | 15 | 14 | … | 2 | 1 | 0 |
---|---|---|---|---|---|---|
COUTNFLAG | TICKSOURCE | TICKINT | ENABLE |
代码分析:
#define SysTick_CTRL_ENABLE ((uint32_t)0x00000001) //控制寄存器第0位为1,默认是使能Systick时钟
#define SysTick_CTRL_TICKINT ((uint32_t)0x00000002) //控制寄存器第1位为1,默认是当systick计数到0的时候,就中断
#define SysTick_CTRL_CLKSOURCE ((uint32_t)0x00000004) //控制寄存器第2位为1,默认是计数时钟来源是内部时钟
#define SysTick_CTRL_COUNTFLAG ((uint32_t)0x00010000) //控制寄存器第16位为1,开始初始计数器当前值初始化为零
注:
【1】COUNTFLAG其实就是是否数到0的一个标志,如果Systick数到了0,就是为1;
【2】CLKSOURCE其实就是时钟来源标志,来源为0的时候,表示外部时钟,来源为1表示的是内部时钟。
【3】TICKINT其实就是Systick数到0时,是否是直接产生中断还是无响应,一般来说,当Systick数到0的时候,是要产生中断的,也就是一般情况下为1,如果不想让它产生响应,就设置为0就可以了。
2.SysTick重装载数值寄存器(地址:0xE000_E014)
位段 | 名称 | 描述 |
---|---|---|
23:0 | RELOAD | 当Systick计数到0的时候,就会把RELOAD寄存器中的值重新装载到CURRENT寄存器中 |
3.SysTick当前数值寄存器(地址:0xE000_E018)
位段 | 名称 | 描述 |
---|---|---|
23:0 | CURRENT | 该寄存器存储了当前的Systick计数器的计数数值 |
4.SysTick校准数值寄存器(地址:0xE000_E01C)
/***************** Bit definition for SysTick_CTRL register *****************/
#define SysTick_CTRL_ENABLE ((uint32_t)0x00000001) /*!< Counter enable */
#define SysTick_CTRL_TICKINT ((uint32_t)0x00000002) /*!< Counting down to 0 pends the SysTick handler */
#define SysTick_CTRL_CLKSOURCE ((uint32_t)0x00000004) /*!< Clock source */
#define SysTick_CTRL_COUNTFLAG ((uint32_t)0x00010000) /*!< Count Flag */
/***************** Bit definition for SysTick_LOAD register *****************/
#define SysTick_LOAD_RELOAD ((uint32_t)0x00FFFFFF) /*!< Value to load into the SysTick Current Value Register when the counter reaches 0 */
/***************** Bit definition for SysTick_VAL register ******************/
#define SysTick_VAL_CURRENT ((uint32_t)0x00FFFFFF) /*!< Current value at the time the register is accessed */
/***************** Bit definition for SysTick_CALIB register ****************/
#define SysTick_CALIB_TENMS ((uint32_t)0x00FFFFFF) /*!< Reload value to use for 10ms timing */
#define SysTick_CALIB_SKEW ((uint32_t)0x40000000) /*!< Calibration value is not exactly 10 ms */
#define SysTick_CALIB_NOREF ((uint32_t)0x80000000) /*!< The reference clock is not provided */
三、手撕代码
1.delay_ms()代码分析
//延时nus
//nus为要延时的us数.
fac_us=SYSCLK/8;//时基,表示每us计数次数,知道就可以了
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //fac_us表示每us计数的次数,LOAD寄存器中装载的是计数该时间需要的总共次数
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数,使能ENABLE计数开始
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //计数使能且COUNTFLAG标志不为1,也就是没有计数到零,一直循环,当计数到0之后,就可以停止了
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
思路总结:
获取传入的参数计数时间,计算对应的次数,将其转入LOAD寄存器中,VAL寄存器初始计数值清零,启动CRTL控制寄存器中计数器使能ENABLE,开始计数,当控制寄存器中的COUNTFLAG位为1时表示计数到0了,也就是计数结束了,此时,延时功能完成,处理后事,关闭计数器,计数器当前值清零。
2.delay_us()代码分析
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
类似delay_us(u32 nus),留个作业,可以自己试着分析一下。