前言
本次我们认识一下基于Cortex M3架构的系统滴答定时器(systick),以及如何使用系统滴答定时器实现精准的 ms 和 μs 延时,大部分是自己收集和整理,如有侵权请联系我删除。
交流群:717237739
如果觉得有用点赞关注收藏三连,多谢支持
本博客内容原创,创作不易,转载请注明
1.认识系统滴答定时器
SysTick定时器被捆绑在NVIC中,是一个简单的定时器,对于CM3、CM4内核芯片,都有Systick定时器。Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。
系统滴答定时器有4个寄存器:
CTRL | SysTick控制及状态寄存器(地址:0xE000_E010) |
---|---|
LOAD | SysTick重装载数值寄存器(地址:0xE000_E014) |
VAL | SysTick当前数值寄存器(地址:0xE000_E018) |
CALIB | SysTick校准数值寄存器(地址:0xE000_E01C) |
SysTick当前数值寄存器(地址:0xE000_E018)
SysTick校准数值寄存器(地址:0xE000_E01C)- - - 不常用
2.系统滴答定时器的使用流程
1)时钟源的选择:
在使用系统滴答定时器的时候,我们首先需要对时钟源进行选择,通过对SysTick控制及状态寄存器(地址:0xE000_E010)第【2】位来控制选择时钟源。
当寄存器的CLKSOURCE这个位:
为1的时候选择内部时钟源,时钟频率为72M不分频。
为0的时候选择外部时钟源,时钟频率为72M/8=9M。
选择外部时钟和内部时钟的区别?
外部时钟相对来说精准度更高,稳定性更好,内部时钟使用的时RC振荡器,稳定性较差,相信了解过时钟树方面的知识应该能清楚。
2)系统滴答定时器的计时说明
计数时间的计算,通常我们计时时间为1S = 72M = 72 000 000。
如果我们选择了外部时钟源,经过8分频之后时钟频率为 9M,相当于1 S = 9 M;
所以 1s = 9 000 000 1ms = 9000 1μs = 9;计数9次为1微秒。
因为系统滴答定时器为24位的定时器,正常来说8位的范围在 0 -255之间
所以 24 位的 范围为 256*256*256 = 16 777 215
16 777 215 / 9 = 1864135μs = 1864.135ms = 1.864s
所以这个系统滴答定时器的最大延时为 1.864s
3)系统滴答定时器中断优先级设置
通过系统异常优先级寄存器来设置系统滴答定时器的优先级
在设置优先级之前得确定优先级分组,几位是抢占式优先级,几位是子优先级。
这个时候有人就有疑问了,为什么要开中断,开不开中断的差别在哪里?
1. 其实就是排队和插队的区别
2. 如果不进行中断配置,那么在函数的执行过程中,就是只能排队执行,就是轮到你才能执行。
3. 如果对定时器进行了中断配置,那么优先级高的中断会打断掉延时,但是如果没有其他中断,那么它的优先级是最高的,可以插队执行。
3.程序执行代码过程(附源码)
1)初始化延时函数,配置选择时钟源为外部时钟源,预分频为8
这里的 fac_us=SystemCoreClock/8 000 000; //为系统时钟的1/8
表示fac_us = 9; 计数9次为1微秒
#include "delay.h"
static u8 fac_us=0; //us延时倍乘数
static u16 fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数
//初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8
fac_us=SystemCoreClock/8000000; //为系统时钟的1/8
fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
}
2)不进中断的延时函数源码
宏定义的代码:
μs延时函数代码:
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ; //开始倒数 1<<0
//SysTick->CTRL|= 0x01 ;
//do while 判断就是systick使能(bit0)位为1且(bit16)位为1的时候等待结束
//(temp&0x01)说明已经开始开始使能CTRL寄存器并且开始计数了,这个时候为真:1
//!(temp&(1<<16))位&是为了判断里面第16位是否为1,为1则temp&(1<<16)为真,
// 再取反为假,逻辑与&&判断while里面为假:0,则程序结束,表明计数结束
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
ms延时函数代码:
//延时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|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
通过以上对系统滴答定时器的代码配置,我们就可以在日常使用的时候能够精准使用延时。不占用定时器的资源,直接和cpu响应。
3)进中断的延时函数代码(基于基于Cortex M4)- - - 时钟为168M
可以直接复制使用,不过如果移植为M3架构的芯片,就是需要找到对应的时钟宏定义,把时钟频率改为你当前芯片默认时钟频率就能正常使用了。
#include "delay.h"
#include "stdio.h"
u32 delayTime ;
//延时初始化
void delay_Init(void)
{
//1us进一次中断
if(SysTick_Config(SystemCoreClock/1000000) == 1)
printf("系统滴答初始化失败\r\n");
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭系统滴答定时器
}
void SysTick_Handler(void)
{
if(delayTime > 0 )
delayTime--;
}
void delay_us(u32 us)
{
delayTime = us;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//开启系统滴答定时器
while(delayTime);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭系统滴答定时器
}
void delay_ms(u32 ms)
{
delayTime = ms * 1000;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//开启系统滴答定时器
while(delayTime);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭系统滴答定时器
}
4)代码使用实例
总结 :
系统滴答定时器的代码并不是很多,知识相对来说也比较少,主要就是看时钟源的选择,和中断的使用,然后就是驱动响应的寄存器位来控制定时器的开启和关闭,等待计数为0就代表计数时间到了。
交流群:717237739
如果觉得有用点赞关注收藏三连,多谢支持
本博客内容原创,创作不易,转载请注明
点赞收藏关注双击博主,不定期分享单片机知识,互相学习交流。