0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于单片机的非阻塞式延时设计

我快闭嘴 来源:玩转单片机与嵌入式 作者:玩转单片机与嵌入 2022-09-02 09:15 次阅读

对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马灯的经历。从看到那一排排小灯按着我们的想法在跳动时激动心情。到随着经验越多,越来又会感觉到这个小灯是个好东西,尤其是在调试资源有限的环境中,有时会帮上大忙。

一:阻塞式延时

但对于绝大多数人,我们在最最初让灯闪烁起来时大约都会用到阻塞延时实现,会像如下代码的样子:
    while(1){LED=OFF;Delay_ms(500);LED=ON;Delay_ms(500);}
这时我们就会慢慢意识到,第一种(阻塞延时)方法效率很低,让芯片在那儿空运行几百毫米,什么也不做,真是莫大的浪费,尤其在芯片频率较高,任务又很多时,这样做就像在平坦宽阔的高速公路上挖了一大坑,出现事故可想而知。

二:定时器延时

然后,在我们接触到定时器,我们会发现,原来用定时中断来处理会更好。比如我们可以500ms中断一次,让灯亮或灭,其余的时间系统还可以做非常之多的事情,效率一下提升了很多。
但一个单片机中的定时器毕竟有限,如果我需要几十个或者更多不同时间的定时中断,每一个时间到都完成不同的处理动作,如何去做呢。一般我们会想到在一个定时中断函数中再定义static 变量继续定时,到了所需时间,做不同的动作。而这样又会导致在一个中断里做了很多不同的事情,会抢占主轮询更多时间,有时甚至喧宾夺主,并也不是很如的思维逻辑。

那么有没有更好的方法来实现呢,答案是肯定的。下面介绍我在一个项目中偶遇,一个精妙设计的非阻塞定时延时软件的设计(此设计主要针对于无操作系统的裸机程序)。

三:非阻塞式延时设计

在之前的文章中有对systick的介绍,比如我要设置其10ms中断一次,如何实现呢?

也很简单,只需调用core_cm3.h文件中 SysTick_Config函数 ,当系统时钟为72MHZ,则设置成如下即可SysTick_Config(720000); (递减计数720000次后中断一次) 。此时SysTick_Handler中断函数就会10ms进入一次;
任务定时用软件是如何设计的呢 ?
且先看其数据结构,这也是精妙所在之处,在此作自顶向下的介绍:

其定义结构体类型如:

typedef struct{        uint8_t Tick10Msec;    Char_Field Status;} Timer_Struct;

其中Char_Field 为一联合体,设计如下:

typedefunion{    unsigned char byte;    Timer_Bit field;} Char_Field{

而它内部的Timer_Bit是一个可按位访问的结构体:

typedef struct{    unsigned char bit0: 1;    unsigned char bit1: 1;    unsigned char bit2: 1;    unsigned char bit3: 1;    unsigned char bit4: 1;    unsigned char bit5: 1;    unsigned char bit6: 1;    unsigned char bit7: 1;} Timer_Bit

此联合体的这样设计的目的将在后面的代码中体现出来。

如此结构体的设计就完成了。

然后我们定义的一全局变量,Timer_Struct gTimer;

并在头文件中宏定义如下:

#define bSystem10Msec        gTimer.Status.field.bit0#define bSystem50Msec        gTimer.Status.field.bit1#define bSystem100Msec       gTimer.Status.field.bit2#define bSystem1Sec          gTimer.Status.field.bit3#define bTemp10Msec          gTimer.Status.field.bit4#define bTemp50Msec          gTimer.Status.field.bit5#define bTemp100Msec         gTimer.Status.field.bit6#define bTemp1Sec            gTimer.Status.field.bit7

另外为了后面程序清晰,再定义一状态指示:

typedef enum{    TIMER_RESET = 0,    TIMER_SET = 1,} TimerStatus;

至此,准备工作就完成了。下面我们就开始大显神通了!

首先,10ms定时中断处理函数如,可以看出,每到达10ms 将把bTemp10Msec置1,每50ms 将把bTemp50Msec置1,每100ms 将把bTemp100Msec置1,每1s 将把bTemp1Sec置1。

void SysTick_Handler(void){        bTemp10Msec = TIMER_SET;                ++gTimer.Tick10Msec;        if (0 == (gTimer.Tick10Msec % 5))        {            bTemp50Msec = TIMER_SET;        }                if (0 == (gTimer.Tick10Msec % 10))        {            bTemp100Msec = TIMER_SET;        }                if (100 == gTimer.Tick10Msec)        {            gTimer.Tick10Msec = 0;            bTemp1Sec = TIMER_SET;        }}

而这又有什么用呢 ?

这时,我们需在主轮询while(1)内最开始调用一个定时处理函数如下:

void SysTimer _Process(void){    gTimer.Status.byte &= 0xF0;        if (bTemp10Msec)    {        bSystem10Msec = TIMER_SET;    }        if (bTemp50Msec)    {        bSystem50Msec = TIMER_SET;    }        if (bTemp100Msec)    {        bSystem100Msec = TIMER_SET;    }        if (bTemp1Sec)    {        bSystem1Sec = TIMER_SET;    }        gTimer.Status.byte &= 0x0F;}

此函数开头与结尾两句:

gTimer.Status.byte &= 0xF0;gTimer.Status.byte &= 0x0F

就分别巧妙的实现了bSystemXXX (低4位) 和 bTempXXX(高4位)的清零工作,不用再等定时到达后还需手动把计数值清零。此处清零工作用到了联合体中的变量共用一个起始存储空间的特性。

但要保证while(1)轮询时间要远小于10ms,否则将导致定时延时不准确。这样,在每轮询一次,就先把bSystemXXX ,再根据bTempXXX判断是否时间到达,并把对应的bSystemXXX 置1,而后面所有的任务就都可以通过bSystemXXX来进行定时延时,在最后函数退出时,又会把bTempXXX清零,为下一次时间到达后查询判断作好了准备。

说了这么多,举例说明一下如何应用:

void Task_A_Processing(void){    if(TIMER_SET == bSystem50Msec){        //do something    }}
void Task_B_Processing(void){    if(TIMER_SET == bSystem100Msec){        //do something    }}
void Task_C_Processing(void){    static uint8_t ticks = 0;    if(TIMER_SET == bSystem100Msec){       ticks ++ ;    }
    if(5 == ticks){        ticks = 0;         //do something    }
}
void Task_D_Processing(void){    if(TIMER_SET == bSystem1Sec){        //do something    }}

以上示例四个任务进程,

在主轮询里可进行如下处理:

int main(void){    while(1)    {        SysTimer _Process();
        Task_A_Processing();        Task_B_Processing();        Task_C_Processing();Task_D_Processing();    }}

这样,就可以轻松且清晰实现了多个任务,不同时间内处理不同事件。(但注意,每个任务处理中不要有阻塞延时,也不要处理过多的事情,以致处理时间较长。可设计成状态机来处理不同任务。

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6030

    文章

    44489

    浏览量

    631881
  • 延时
    +关注

    关注

    0

    文章

    107

    浏览量

    25491
  • STM32
    +关注

    关注

    2264

    文章

    10854

    浏览量

    354243
  • 定时器
    +关注

    关注

    23

    文章

    3231

    浏览量

    114314

原文标题:【软件】精妙的用STM32单片机设计的非阻塞延时程序

文章出处:【微信号:玩转单片机与嵌入式,微信公众号:玩转单片机与嵌入式】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    STM32单片机延时原理和延时函数方法

    当涉及到单片机编程时,延时是一项常见但关键的任务。在许多应用中,我们需要控制程序暂停一段时间,以实现精确的时间控制或协调不同设备之间的操作。本文将以STM32为例,介绍关于单片机延时
    发表于 08-09 09:13 1.7w次阅读

    精妙的单片机阻塞延时程序设计

    [导读]对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马灯的经历。从看到那一排排小灯按着我们的想法在跳动时激动心情。到随着经验越多,越来又会感觉到这个小灯
    发表于 05-23 15:23

    精妙的单片机阻塞延时程序设计

    对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马灯的经历。从看到那一排排小灯按着我们的想法在跳动时激动心情。到随着经验越多,越来又会感觉到这个小灯是个
    发表于 07-09 15:12

    精妙的单片机阻塞延时程序设计

    对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马灯的经历。从看到那一排排小灯按着我们的想法在跳动时激动心情。到随着经验越多,越来又会感觉到这个小灯是个好东西
    发表于 03-26 16:53

    PIC单片机精确延时函数的延时时间怎么计算?

    PIC单片机精确延时函数的延时时间怎么计算?
    发表于 04-25 12:52

    基于单片机+CPLD的多路精确延时控制系统设计

    根据多路精确延时控制系统的设计参数指标,利用单片机和CPLD的功能特性与片上资源,设计了基于CPLD与多个单片机的多路精确延时控制系统,实现了多路独立调整、显示,并能输出
    发表于 12-16 16:15 43次下载

    单片机延时计算小程序

    电子发烧友网站提供《单片机延时计算小程序.zip》资料免费下载
    发表于 09-17 17:24 6次下载

    51单片机汇编语言教程_单片机延时程序分析

    51单片机汇编语言教程:5课单片机延时程序分析
    发表于 01-19 15:26 0次下载

    单片机延时计算小程序

    单片机延时计算小程序
    发表于 12-01 16:44 2次下载

    单片机延时计算小程序

    单片机延时计算小程序
    发表于 12-28 11:07 6次下载

    单片机延时函数的资料合集免费下载

    本文档的主要内容详细介绍的是单片机延时函数的资料合集免费下载包括了:1.51单片机延时,晶振为11.0592MHz,(粗略延时,定时器
    发表于 08-02 17:34 8次下载
    <b class='flag-5'>单片机</b><b class='flag-5'>延时</b>函数的资料合集免费下载

    STM8S_002_TIM精确延时阻塞

    STM8S_002_TIM精确延时阻塞
    的头像 发表于 03-20 10:51 3278次阅读
    STM8S_002_TIM精确<b class='flag-5'>延时</b> (<b class='flag-5'>阻塞</b><b class='flag-5'>式</b>)

    单片机延时问题20问

    简介:延时与中断出错,是单片机新手在单片机开发应用过程中,经常会遇到的问题,本文汇总整理了包含了MCS-51系列单片机、MSP430单片机
    发表于 11-13 21:06 6次下载
    <b class='flag-5'>单片机</b><b class='flag-5'>延时</b>问题20问

    51单片机 利用定时中断做“阻塞”点灯

    。 今天的自学笔记记录的例程是如何利用定时中断,来做一个“阻塞”的闪灯程序。 这节内容,看似简单,实际项目中却有很多地方可以应用,比如模拟PWM波形,还有接下来要写的 按键消抖清零 应用一、 “
    发表于 11-21 21:06 15次下载
    51<b class='flag-5'>单片机</b> 利用定时中断做“<b class='flag-5'>非</b><b class='flag-5'>阻塞</b><b class='flag-5'>式</b>”点灯

    什么是阻塞阻塞

    什么是阻塞阻塞?我们就用管道的读写来举例子。
    的头像 发表于 03-25 10:04 443次阅读