接触过单片机的,肯定都用过延时函数,从while循环,到定时器延时,到systick延时,再到DWT延时等等。延时含义即从某一刻开始,到下一个某刻结束。
从用途上来说,延时有功能性和非功能性之分。功能性延时即某些功能实现必须需要延时,如模拟IIC等;非功能性延时,是为了方便观察或记录现象而进行的延时,一般来说正式发布的时候可以去掉,如加入延时为了printf打印慢一点,非功能性延时通常不用很精确。
根据单片机延时时CPU的动作,分为阻塞延时和非阻塞延时。通俗理解就是阻塞延时就是死等,CPU进入延时程序后,除了响应中断外啥也不干,就等延时时间到达。非阻塞延时是指,进入延时后,CPU可以执行除中断意外的其他功能。
根据延时时是否是靠中断计时,分为中断延时(定时器溢出中断)和非中断延时。
主控:STM32F407ZGT6(HSE:8MHz)
库:STD标准库V1.8.0
工具:RIGOL DS1104Z PLUS数字示波器
各种延时函数测试标准:无论那种方式,mian函数中功能有RTC自动唤醒事件(1s唤醒),通过这些来模拟日常开发,每种延时方式开启后主要测试微秒us延时,毫秒ms延时因为有中断的存在会不准确,当然也有毫秒延时本身误差在。微妙延时会测试50微秒us级(《100us)3组,500微秒us级(《1000us)3组。毫秒延时会测试50毫秒ms级(《100ms)5组,500ms级(《1000us)3组。延时后翻转PB4脚电平,示波器测量电平翻转时间。
提示:如果不想看数据测试客户以直接阅读结论(黄色字体)。
一、阻塞延时函数
延时方式从阻塞方式到非阻塞方式,中断从无中断到有中断,准确性从粗略延时到精确延时。(本文持续更新,因此有些函数暂无)。
延时函数版本号说明
主版本号:1阻塞延时,2非阻塞延时
次版本号:0非中断,1中断,
修订号:
开源协议、版权和免责声明:
本文为原创,代码开源使用开源协议Apache-2.0,可以随意使用。请署名版权信息,格式:
Copyright (c) 20021-2021, Logan(wangzhaoyangly@Foxmail.com)。本文数据只做参考,
本人不对数据正确性和准确性做保证,由此产生问题本人概不负责。
1. 循环延时(V1.0.0)
循环延时即在一个循环中让CPU做一些没有意义的工作来完成延时的目的。由于NOP空指令在不同架构的MCU上执行时间不一定,因此不使用NOP进行延时。循环延时和NOP指令延时本质是一样的。不过这个含税延时时间需要摸索,下面这样设置,实际扩大5倍。
代码(while形式)
void delay_while_us(uint16_t time)
{
uint16_t i = 0;
while(time--)
{
i=168;//168MHz下
while(i--);
}
}
测试数据
5us延时,理论周期为10us,实际测试周期为50.8us,数据偏差+408%。
50us延时,理论周期为100us,实际测试周期为500.8us,数据偏差+408%。
这个数据已经说明问题了,微秒级延时很不准确。
上面已经说明了,这个延时函数只适合粗略延时,其他量级数据也没有测试必要。
特点总结
优点:实现简单
缺点:延时不准确,针对不同单片机需要调整
准确性:粗略定时
OS:理论可用,实际上由于OS多线程的原因,偏差更大。
2. SYSTICK非中断延时(V1.0.1)
正点原子的systick非中断延时,使用硬件定时器SYSTICK嘀嗒定时器,采用往LOAD寄存器里写值倒计时结束即延时结束,不占用额外定时器资源,不靠中断计时,但是还是阻塞方式延时,微秒级延时可以用于中断内,但是不建议在中断中使用毫秒级延时。可用于OS,但是需要改动,大多数OS的时钟节拍是靠SYSTICK的,采用时间摘取法,延时前后进行开中断和关中断,避免中断干扰。ticks 是延时 nus 需要等待的 SysTick 计数次数(也就是延时时间), told 用于记录最近一次的 SysTick-》VAL 值,然后 tnow 则是当前的SysTick-》VAL 值,通过他们的对比累加,实现 SysTick 计数次数的统计,统计值存放在 tcnt 里面,然后通过对比 tcnt 和 ticks,来判断延时是否到达,从而达到不修改 SysTick 实现 nus 的延时,从而可以和 OS 共用一个 SysTick(用于OS请参考正点原子代码)。
代码
/*注意systick的时钟来自AHB时钟(HCLK)8分频。一般配置系统时钟SYSCLK=AHB时钟HCLK。假设外部晶振为8MHz,然后倍频到168MHz,那么systick的时钟为21MHz,也就是systick的计数器VALMeizu减一,就代表时间过了1/21us=46.62ns(如果systick时钟源选择AHB时钟,则systick周期为1/168us=5.95ns)。所以fac_us=SystemCoreClock/8000000,意识是极端在系统时钟频率下1us需要多少个systick的周期,fac_ms同理,fac_ms=fac_us*1000。
nus为要延时的us数,nus的值《2^24/fac_us@fac_us=21 = 798915us
mus为延时的ms数,范围为0-798ms*/
#define fac_us SystemCoreClock/8000000 //us延时基数
#define fac_ms fac_us*1000
void delay_us(u32 nus)
{
u32 temp;
SysTick-》LOAD = fac_us*nus;
SysTick-》VAL=0X00;//清空计数器
SysTick-》CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
do
{
temp=SysTick-》CTRL;//读取当前倒计数值
}while((temp&0x01)&&(!(temp&(1《《16))));//等待时间到达
SysTick-》CTRL=0x00; //关闭计数器
SysTick-》VAL =0X00; //清空计数器
}
void delay_ms(u16 nms) //168MHz下nms《=798ms
{
u32 temp;
SysTick-》LOAD = fac_ms*nms;
SysTick-》VAL=0X00;//清空计数器
SysTick-》CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
do
{
temp=SysTick-》CTRL;//读取当前倒计数值
}while((temp&0x01)&&(!(temp&(1《《16))));//等待时间到达
SysTick-》CTRL=0x00; //关闭计数器
SysTick-》VAL =0X00; //清空计数器
}
//延时nms
//nms:0~65535
void delay_ms(u16 nms)
{
u8 repeat=nms/540; //这里用540,是考虑到某些客户可能超频使用,
//比如超频到248M的时候,delay_xms最大只能延时541ms左右了
u16 remain=nms%540;
while(repeat)
{
delay_xms(540);
repeat--;
}
if(remain)delay_xms(remain);
}
测试数据
50us级延时
延时5us,理论周期10us,实际周期10.8us,数据偏差8%。
延时50us,理论周期100us,实际周期100.0us,数据偏差0%。
延时80us,理论周期160us,实际周期162.0us,数据偏差1.25%。
结论:50us级延时偏差在0%-8%。
500us级延时
延时200us,理论周期400us,实际周期400.0us,数据偏差0%。
延时500us,理论周期1000us,实际周期1000.0us,数据偏差0%。
延时800us,理论周期1600us,实际周期1600.0us,数据偏差0%。
结论:500us级延时偏差在0%是十分准确的,因为毫秒级延时以微秒延时为基础,因此毫秒级延时也是十分准确。
特点总结
优点:非中断,硬件计时,非常准确。
缺点:占用硬件资源,不可嵌套(主函数使用毫秒延时,中断中使用微秒延时,如果在微秒延时时中断产生,进行微秒延时再次修改SYSTICK寄存器的值,因此会导致主函数毫秒延时不准确)。
准确性:精确延时。
OS:理可用于OS,OS时需要开启SYSTICK中断。
3. DWT延时(V1.0.2)
DWT外设用于系统调试及跟踪,DWT 中有剩余的计数器,它们典型地用于程序代码的“性能速写”(profiling)。通过编程它们,就可以让它们在计数器溢出时发出事件(以跟踪数据包的形式)。最典型地,就是使用 CYCCNT寄存器来测量执行某个任务所花的周期数,这也可以用作时间基准相关的目的(操作系统中统计 CPU使用率可以用到它)。
适用范围:m3、m4、m7实测可用(m0不可用)。
精度:1/内核频率(s),1/168MHz=5.95ns。
代码
//.h
#include “stdio.h”
#include “stm32f4xx_conf.h”
#include “sys.h”
#include “core_cm4.h”
#define DWT_CR *(__IO uint32_t *)0xE0001000 //DWT控制寄存器
#define DWT_CYCCNT *(__IO uint32_t *)0xE0001004 //时钟周期寄存器
#define DEM_CR *(__IO uint32_t *)0xE000EDFC //内核调试控制寄存器,使能DWT外设,DEMCR的位24控制,写1使能
#define DWT_CYCCNT_VAL (*(__IO uint32_t *)0xE0001004)
#define DWT_CTRL (*(__IO uint32_t *)0xE0001000)
#define DEM_CR_TRCENA (uint32_t)(1 《《 24) //使能DWT外设
#define DWT_CR_CYCCNTENA (uint32_t)(1 《《 0) //启动CYCCNT计数器
#define SYSCLK 168000000 //系统时钟频率
void DWT_Init(uint32_t sys_clk_c);
void delay_dwt_us(uint32_t nus);
#define delay_dwt_ms(nms) delay_dwt_us(nms*1000)
//.c
static uint32_t sysclk = 0;
void DWT_Init(uint32_t sys_clk_c)
{
//使能SWT外设
DEM_CR |= DEM_CR_TRCENA;
//CYCCNT寄存器清0
DWT_CYCCNT = (uint32_t)0;
//使能CYCCNT
DWT_CR |= DWT_CR_CYCCNTENA;
sysclk = sys_clk_c;
}
void delay_dwt_us(uint32_t nus)
{
uint32_t ticks_start = 0, ticks_end = 0, tcnt = 0;
DWT_CYCCNT = (uint32_t)0;
ticks_start = DWT_CYCCNT_VAL;
if (!sysclk)
{
DWT_Init(SYSCLK);
}
tcnt = (nus * (sysclk / 1000000));
ticks_end = ticks_start + tcnt;
if (ticks_end 》 ticks_start)
{
while (DWT_CYCCNT_VAL 《 ticks_end)
{
};
}
else
{
while (DWT_CYCCNT_VAL 》= ticks_end)
{
};
while (DWT_CYCCNT_VAL 《 ticks_end)
{
};
}
}
测试数据
50us级延时
延时5us,理论周期10us,实际周期10.6us,数据偏差6%。
延时50us,理论周期100us,实际周期101.0us,数据偏差1%。
延时80us,理论周期160us,实际周期160.0us,数据偏差0%。
结论:50us级延时偏差在0%-6%。
500us级延时
延时200us,理论周期400us,实际周期400.0us,数据偏差0%。
延时500us,理论周期1000us,实际周期1000.0us,数据偏差0%。
延时800us,理论周期1600us,实际周期1600.0us,数据偏差0%。
结论:500us级延时偏差在0%是十分准确的,因为毫秒级延时以微秒延时为基础,因此毫秒级延时也是十分准确。
注:50s级误差是基本一致的,无论是SYSTICK定时器还是DWT计时,误差都是一定的,在》100us时是没有误差的,推测《100us计时可能是示波器采样的因素。不过这个100us下不到10%的误差可以忽略不计,并且100us下,计时时间越接近于100us,误差越小,80us时误差就为0了,第一组测试数据5us延时有较大误差,我认为情有可原,是可以接收的。总的来说,硬件计时是十分准确的计时方式。
特点总结
优点:非中断,硬件计时,非常准确,延时时间长(168MHZ下可达25s),可嵌套(最终延时时间大于等于需要延时时间),可用于任何中断。
缺点:占用硬件资源(但是这个资源是不用白不用的资源)
准确性:精确延时。
OS:可用于OS,不占用OS心跳时钟。
二、非阻塞延时
本文主要讨论无OS下非阻塞延时函数实现,OS下有系统调度,线程可以挂起执行效率较高,如果有需要定时轮训的话还是需要用到非阻塞延时功能,思想是一样的。虽然这章是研究无OS下的非阻塞延时函数的,但在研究过程发现,在无OS下实现非阻塞延时,跟做个OS差不多,无OS下实现非阻塞延时照样会用到任务的概念,任务切换,任务状态等,跟OS下是一样的,但是OS下也会用到非阻塞延时。无OS下实现非阻塞延时实际上跟OS下的硬实时概念挺像的,不过应该称为硬延时,比如从系统运行初始化完成后开始,每50ms点亮一次LED灯,每100ms上传一次数据灯。本章就无OS下非阻塞延时就行研究讨论,后续可能会更新OS下的非阻塞延时。
非阻塞延时思想:使用定时器周期性中断产生定时时基,如10ms产生一次中断,然后在定时器中断函数中记录时间,while循环中根据时间执行相关任务,在任务A中判断是否到达执行任务时间,如果到达则执行,没有则调出判断任务B。实际上是将时间作为状态机状态来工作,何为有限状态机还请百度学习,不在本文讨论范围。
1. 无OS下非阻塞延时(待更新)
2. OS下非阻塞延时(暂无更新)
结语:由于水平有限,文中难免有错误之处,欢迎指正,也欢迎探讨交流。
接触过单片机的,肯定都用过延时函数,从while循环,到定时器延时,到systick延时,再到DWT延时等等。延时含义即从某一刻开始,到下一个某刻结束。
从用途上来说,延时有功能性和非功能性之分。功能性延时即某些功能实现必须需要延时,如模拟IIC等;非功能性延时,是为了方便观察或记录现象而进行的延时,一般来说正式发布的时候可以去掉,如加入延时为了printf打印慢一点,非功能性延时通常不用很精确。
根据单片机延时时CPU的动作,分为阻塞延时和非阻塞延时。通俗理解就是阻塞延时就是死等,CPU进入延时程序后,除了响应中断外啥也不干,就等延时时间到达。非阻塞延时是指,进入延时后,CPU可以执行除中断意外的其他功能。
根据延时时是否是靠中断计时,分为中断延时(定时器溢出中断)和非中断延时。
主控:STM32F407ZGT6(HSE:8MHz)
库:STD标准库V1.8.0
工具:RIGOL DS1104Z PLUS数字示波器
各种延时函数测试标准:无论那种方式,mian函数中功能有RTC自动唤醒事件(1s唤醒),通过这些来模拟日常开发,每种延时方式开启后主要测试微秒us延时,毫秒ms延时因为有中断的存在会不准确,当然也有毫秒延时本身误差在。微妙延时会测试50微秒us级(《100us)3组,500微秒us级(《1000us)3组。毫秒延时会测试50毫秒ms级(《100ms)5组,500ms级(《1000us)3组。延时后翻转PB4脚电平,示波器测量电平翻转时间。
提示:如果不想看数据测试客户以直接阅读结论(黄色字体)。
一、阻塞延时函数
延时方式从阻塞方式到非阻塞方式,中断从无中断到有中断,准确性从粗略延时到精确延时。(本文持续更新,因此有些函数暂无)。
延时函数版本号说明
主版本号:1阻塞延时,2非阻塞延时
次版本号:0非中断,1中断,
修订号:
开源协议、版权和免责声明:
本文为原创,代码开源使用开源协议Apache-2.0,可以随意使用。请署名版权信息,格式:
Copyright (c) 20021-2021, Logan(wangzhaoyangly@Foxmail.com)。本文数据只做参考,
本人不对数据正确性和准确性做保证,由此产生问题本人概不负责。
1. 循环延时(V1.0.0)
循环延时即在一个循环中让CPU做一些没有意义的工作来完成延时的目的。由于NOP空指令在不同架构的MCU上执行时间不一定,因此不使用NOP进行延时。循环延时和NOP指令延时本质是一样的。不过这个含税延时时间需要摸索,下面这样设置,实际扩大5倍。
代码(while形式)
void delay_while_us(uint16_t time)
{
uint16_t i = 0;
while(time--)
{
i=168;//168MHz下
while(i--);
}
}
测试数据
5us延时,理论周期为10us,实际测试周期为50.8us,数据偏差+408%。
50us延时,理论周期为100us,实际测试周期为500.8us,数据偏差+408%。
这个数据已经说明问题了,微秒级延时很不准确。
上面已经说明了,这个延时函数只适合粗略延时,其他量级数据也没有测试必要。
特点总结
优点:实现简单
缺点:延时不准确,针对不同单片机需要调整
准确性:粗略定时
OS:理论可用,实际上由于OS多线程的原因,偏差更大。
2. SYSTICK非中断延时(V1.0.1)
正点原子的systick非中断延时,使用硬件定时器SYSTICK嘀嗒定时器,采用往LOAD寄存器里写值倒计时结束即延时结束,不占用额外定时器资源,不靠中断计时,但是还是阻塞方式延时,微秒级延时可以用于中断内,但是不建议在中断中使用毫秒级延时。可用于OS,但是需要改动,大多数OS的时钟节拍是靠SYSTICK的,采用时间摘取法,延时前后进行开中断和关中断,避免中断干扰。ticks 是延时 nus 需要等待的 SysTick 计数次数(也就是延时时间), told 用于记录最近一次的 SysTick-》VAL 值,然后 tnow 则是当前的SysTick-》VAL 值,通过他们的对比累加,实现 SysTick 计数次数的统计,统计值存放在 tcnt 里面,然后通过对比 tcnt 和 ticks,来判断延时是否到达,从而达到不修改 SysTick 实现 nus 的延时,从而可以和 OS 共用一个 SysTick(用于OS请参考正点原子代码)。
代码
/*注意systick的时钟来自AHB时钟(HCLK)8分频。一般配置系统时钟SYSCLK=AHB时钟HCLK。假设外部晶振为8MHz,然后倍频到168MHz,那么systick的时钟为21MHz,也就是systick的计数器VALMeizu减一,就代表时间过了1/21us=46.62ns(如果systick时钟源选择AHB时钟,则systick周期为1/168us=5.95ns)。所以fac_us=SystemCoreClock/8000000,意识是极端在系统时钟频率下1us需要多少个systick的周期,fac_ms同理,fac_ms=fac_us*1000。
nus为要延时的us数,nus的值《2^24/fac_us@fac_us=21 = 798915us
mus为延时的ms数,范围为0-798ms*/
#define fac_us SystemCoreClock/8000000 //us延时基数
#define fac_ms fac_us*1000
void delay_us(u32 nus)
{
u32 temp;
SysTick-》LOAD = fac_us*nus;
SysTick-》VAL=0X00;//清空计数器
SysTick-》CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
do
{
temp=SysTick-》CTRL;//读取当前倒计数值
}while((temp&0x01)&&(!(temp&(1《《16))));//等待时间到达
SysTick-》CTRL=0x00; //关闭计数器
SysTick-》VAL =0X00; //清空计数器
}
void delay_ms(u16 nms) //168MHz下nms《=798ms
{
u32 temp;
SysTick-》LOAD = fac_ms*nms;
SysTick-》VAL=0X00;//清空计数器
SysTick-》CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
do
{
temp=SysTick-》CTRL;//读取当前倒计数值
}while((temp&0x01)&&(!(temp&(1《《16))));//等待时间到达
SysTick-》CTRL=0x00; //关闭计数器
SysTick-》VAL =0X00; //清空计数器
}
//延时nms
//nms:0~65535
void delay_ms(u16 nms)
{
u8 repeat=nms/540; //这里用540,是考虑到某些客户可能超频使用,
//比如超频到248M的时候,delay_xms最大只能延时541ms左右了
u16 remain=nms%540;
while(repeat)
{
delay_xms(540);
repeat--;
}
if(remain)delay_xms(remain);
}
测试数据
50us级延时
延时5us,理论周期10us,实际周期10.8us,数据偏差8%。
延时50us,理论周期100us,实际周期100.0us,数据偏差0%。
延时80us,理论周期160us,实际周期162.0us,数据偏差1.25%。
结论:50us级延时偏差在0%-8%。
500us级延时
延时200us,理论周期400us,实际周期400.0us,数据偏差0%。
延时500us,理论周期1000us,实际周期1000.0us,数据偏差0%。
延时800us,理论周期1600us,实际周期1600.0us,数据偏差0%。
结论:500us级延时偏差在0%是十分准确的,因为毫秒级延时以微秒延时为基础,因此毫秒级延时也是十分准确。
特点总结
优点:非中断,硬件计时,非常准确。
缺点:占用硬件资源,不可嵌套(主函数使用毫秒延时,中断中使用微秒延时,如果在微秒延时时中断产生,进行微秒延时再次修改SYSTICK寄存器的值,因此会导致主函数毫秒延时不准确)。
准确性:精确延时。
OS:理可用于OS,OS时需要开启SYSTICK中断。
3. DWT延时(V1.0.2)
DWT外设用于系统调试及跟踪,DWT 中有剩余的计数器,它们典型地用于程序代码的“性能速写”(profiling)。通过编程它们,就可以让它们在计数器溢出时发出事件(以跟踪数据包的形式)。最典型地,就是使用 CYCCNT寄存器来测量执行某个任务所花的周期数,这也可以用作时间基准相关的目的(操作系统中统计 CPU使用率可以用到它)。
适用范围:m3、m4、m7实测可用(m0不可用)。
精度:1/内核频率(s),1/168MHz=5.95ns。
代码
//.h
#include “stdio.h”
#include “stm32f4xx_conf.h”
#include “sys.h”
#include “core_cm4.h”
#define DWT_CR *(__IO uint32_t *)0xE0001000 //DWT控制寄存器
#define DWT_CYCCNT *(__IO uint32_t *)0xE0001004 //时钟周期寄存器
#define DEM_CR *(__IO uint32_t *)0xE000EDFC //内核调试控制寄存器,使能DWT外设,DEMCR的位24控制,写1使能
#define DWT_CYCCNT_VAL (*(__IO uint32_t *)0xE0001004)
#define DWT_CTRL (*(__IO uint32_t *)0xE0001000)
#define DEM_CR_TRCENA (uint32_t)(1 《《 24) //使能DWT外设
#define DWT_CR_CYCCNTENA (uint32_t)(1 《《 0) //启动CYCCNT计数器
#define SYSCLK 168000000 //系统时钟频率
void DWT_Init(uint32_t sys_clk_c);
void delay_dwt_us(uint32_t nus);
#define delay_dwt_ms(nms) delay_dwt_us(nms*1000)
//.c
static uint32_t sysclk = 0;
void DWT_Init(uint32_t sys_clk_c)
{
//使能SWT外设
DEM_CR |= DEM_CR_TRCENA;
//CYCCNT寄存器清0
DWT_CYCCNT = (uint32_t)0;
//使能CYCCNT
DWT_CR |= DWT_CR_CYCCNTENA;
sysclk = sys_clk_c;
}
void delay_dwt_us(uint32_t nus)
{
uint32_t ticks_start = 0, ticks_end = 0, tcnt = 0;
DWT_CYCCNT = (uint32_t)0;
ticks_start = DWT_CYCCNT_VAL;
if (!sysclk)
{
DWT_Init(SYSCLK);
}
tcnt = (nus * (sysclk / 1000000));
ticks_end = ticks_start + tcnt;
if (ticks_end 》 ticks_start)
{
while (DWT_CYCCNT_VAL 《 ticks_end)
{
};
}
else
{
while (DWT_CYCCNT_VAL 》= ticks_end)
{
};
while (DWT_CYCCNT_VAL 《 ticks_end)
{
};
}
}
测试数据
50us级延时
延时5us,理论周期10us,实际周期10.6us,数据偏差6%。
延时50us,理论周期100us,实际周期101.0us,数据偏差1%。
延时80us,理论周期160us,实际周期160.0us,数据偏差0%。
结论:50us级延时偏差在0%-6%。
500us级延时
延时200us,理论周期400us,实际周期400.0us,数据偏差0%。
延时500us,理论周期1000us,实际周期1000.0us,数据偏差0%。
延时800us,理论周期1600us,实际周期1600.0us,数据偏差0%。
结论:500us级延时偏差在0%是十分准确的,因为毫秒级延时以微秒延时为基础,因此毫秒级延时也是十分准确。
注:50s级误差是基本一致的,无论是SYSTICK定时器还是DWT计时,误差都是一定的,在》100us时是没有误差的,推测《100us计时可能是示波器采样的因素。不过这个100us下不到10%的误差可以忽略不计,并且100us下,计时时间越接近于100us,误差越小,80us时误差就为0了,第一组测试数据5us延时有较大误差,我认为情有可原,是可以接收的。总的来说,硬件计时是十分准确的计时方式。
特点总结
优点:非中断,硬件计时,非常准确,延时时间长(168MHZ下可达25s),可嵌套(最终延时时间大于等于需要延时时间),可用于任何中断。
缺点:占用硬件资源(但是这个资源是不用白不用的资源)
准确性:精确延时。
OS:可用于OS,不占用OS心跳时钟。
二、非阻塞延时
本文主要讨论无OS下非阻塞延时函数实现,OS下有系统调度,线程可以挂起执行效率较高,如果有需要定时轮训的话还是需要用到非阻塞延时功能,思想是一样的。虽然这章是研究无OS下的非阻塞延时函数的,但在研究过程发现,在无OS下实现非阻塞延时,跟做个OS差不多,无OS下实现非阻塞延时照样会用到任务的概念,任务切换,任务状态等,跟OS下是一样的,但是OS下也会用到非阻塞延时。无OS下实现非阻塞延时实际上跟OS下的硬实时概念挺像的,不过应该称为硬延时,比如从系统运行初始化完成后开始,每50ms点亮一次LED灯,每100ms上传一次数据灯。本章就无OS下非阻塞延时就行研究讨论,后续可能会更新OS下的非阻塞延时。
非阻塞延时思想:使用定时器周期性中断产生定时时基,如10ms产生一次中断,然后在定时器中断函数中记录时间,while循环中根据时间执行相关任务,在任务A中判断是否到达执行任务时间,如果到达则执行,没有则调出判断任务B。实际上是将时间作为状态机状态来工作,何为有限状态机还请百度学习,不在本文讨论范围。
1. 无OS下非阻塞延时(待更新)
2. OS下非阻塞延时(暂无更新)
结语:由于水平有限,文中难免有错误之处,欢迎指正,也欢迎探讨交流。
举报