赞
踩
本次我们认识一下基于Cortex M3架构的系统滴答定时器(systick),以及如何使用系统滴答定时器实现精准的 ms 和 μs 延时,大部分是自己收集和整理,如有侵权请联系我删除。
交流群:717237739
如果觉得有用点赞关注收藏三连,多谢支持
本博客内容原创,创作不易,转载请注明
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)- - - 不常用
在使用系统滴答定时器的时候,我们首先需要对时钟源进行选择,通过对SysTick控制及状态寄存器(地址:0xE000_E010)第【2】位来控制选择时钟源。
当寄存器的CLKSOURCE这个位:
为1的时候选择内部时钟源,时钟频率为72M不分频。
为0的时候选择外部时钟源,时钟频率为72M/8=9M。
选择外部时钟和内部时钟的区别?
外部时钟相对来说精准度更高,稳定性更好,内部时钟使用的时RC振荡器,稳定性较差,相信了解过时钟树方面的知识应该能清楚。
计数时间的计算,通常我们计时时间为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
通过系统异常优先级寄存器来设置系统滴答定时器的优先级
在设置优先级之前得确定优先级分组,几位是抢占式优先级,几位是子优先级。
这个时候有人就有疑问了,为什么要开中断,开不开中断的差别在哪里?
1. 其实就是排队和插队的区别
2. 如果不进行中断配置,那么在函数的执行过程中,就是只能排队执行,就是轮到你才能执行。
3. 如果对定时器进行了中断配置,那么优先级高的中断会打断掉延时,但是如果没有其他中断,那么它的优先级是最高的,可以插队执行。
这里的 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时钟数
-
- }
宏定义的代码:
μ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响应。
可以直接复制使用,不过如果移植为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; //关闭系统滴答定时器
- }
系统滴答定时器的代码并不是很多,知识相对来说也比较少,主要就是看时钟源的选择,和中断的使用,然后就是驱动响应的寄存器位来控制定时器的开启和关闭,等待计数为0就代表计数时间到了。
交流群:717237739
如果觉得有用点赞关注收藏三连,多谢支持
本博客内容原创,创作不易,转载请注明
点赞收藏关注双击博主,不定期分享单片机知识,互相学习交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。