玩转单片机之裸机多任务操作技巧

"磨棱角,褪优越,沉下心"
"不止于心动,更付诸于行动,执行力!“

前言

今天想分享一个我们在进行裸机开发时,处理多任务的技巧。基于前人的分享,做一个简单记录,还是比较实用。虽然说现在流行许多嵌入式操作系统,但是实际上接触得更多的还是裸机。

裸机程序执行

这个图中我们可以看出,单片机的运行是在ALU的主导下进行的;而定时器指是一个定时装置,它在定时计数期间是无需ALU干预的,完全独立运行;串口的通讯单元对数据的接收与发送也是完全独立完成的,并不需要ALU干预。很显然这三个任务是并行处理,切互不干涉,只有在定时器或串口产生中断时才会到代码中临时运行一段程序,已向单片机的主体运行过程交付一下结果,以便进行汇总处理。

一个任务的线程:假设一个任务的执行代码有50步,通常编程只会一次执行完毕,但是我们现在需要想想,因为我们会嫌这个任务总占用着ALU的时间而影响其他任务的执行效果,所以就可以对任务进行划分,把它分为5份,每份10步,这样我们每次执行其中的一个程序片–每次正在运行的程序片我们称为线程。


核心概念理解

这里引入的思想就是细分思想,就是把需要执行的时间任务进行细分,也就是划分为很小的时间片,根据不同的时间片去执行。这里说到的并行执行其实也只是从整体表现觉得是并行的,本质上并不是并行。
在我工作中,我们也并没有上什么操作系统,也都是跑裸机,基本上也是使用这种时间片的思想,把一个2ms的时间事件细分为4个0.5ms的时间片。

我们可能接触多一点应该大概有两种

  • 在主函数while(1)中添加许多不同的任务进行顺序执行。
  • 配置外部中断、定时中断,结合死循环使用。

之前我自己在学校做智能车的时候,大概差不多使用的就是定时器中断,不同的时间用不同的变量进行计时,也还得行,看着条理清晰一些,但是现在去看看别人写的,自己之前那种方法还是有点low。
附上之前我做车的任务处理:

void TIM2_IRQHandler (void)
{
    uint32 state = TIM2->SR;                                                        // 读取中断状态
    TIM2->SR &= ~state;                                                     // 清空中断状态
///----------------------------
        static uint8 t_2ms = 0;
        static uint8 t_6ms = 0;
        static uint8 t_10ms = 0;
        static uint8 t_100ms = 0;
/*************************中断执行程序********2ms进一次中断***************/
        t_2ms++;
      t_6ms++;
      t_10ms++;           
      t_100ms++;     
//2ms直立控制周期     
        if(t_2ms == 1)       
      {
        t_2ms = 0;
        Flag.T_2ms=1;
      }        
//6ms角度外环控制周期
      if(t_6ms == 3)      
      {
        t_6ms = 0;
        Flag.T_6ms=1;
      }        
//10ms转向环      
            if (t_10ms == 5)     //转向外环10ms
            {
                t_10ms = 0;
                Flag.T_10ms=1;
            }

//100ms速度控制周期      
            if (t_100ms == 50)   //速度 100ms  
            {
                t_100ms = 0;
                Flag.T_100ms=1;
                    
            }
//**********************************************************
     Fuse_result();     
}

下面参考别人的整理了另外一种多任务处理的方法

void TaskDisplayClock(void);
void TaskKeySan(void);
void TaskDispStatus(void);

// 任务结构
typedef unsigned char uint8 ;

typedef struct 
{
    uint8 Run;                 // 程序运行标记:0-不运行,1运行
    uint8 Timer;              // 计时器,用于运行起来变化的量
    uint8 ItvTime;              // 任务运行间隔时间
    void (*TaskHook)(void);    // 要运行的任务函数
} TASK_COMPONENTS;       // 别名

static TASK_COMPONENTS TaskComps[] = 
{
    {0, 60, 60, TaskDisplayClock},         // 显示时钟
    {0, 20, 20, TaskKeySan},               // 按键扫描
    {0, 30, 30, TaskDispStatus},            // 显示工作状态

};

// 任务清单
typedef enum _TASK_LIST
{
    TAST_DISP_CLOCK,            // 显示时钟
    TAST_KEY_SAN,             // 按键扫描
    TASK_DISP_WS,             // 工作状态显示
  //...........
     TASKS_MAX         // 总的可供分配的定时任务数目
} TASK_LIST;

/**************************************************************************************
* FunctionName   : TaskRemarks()
* Description    : 任务标志处理
* EntryParameter : None
* ReturnValue    : None
* attention      : ***在定时器中断中调用此函数即可***
**************************************************************************************/
void TaskRemarks(void)
{
    uint8 i;

    for (i=0; i<TASKS_MAX; i++)          // 逐个任务时间处理
    {
         if (TaskComps[i].Timer)          // 时间不为0
        {
           TaskComps[i].Timer--;         // 减去一个节拍
           if (TaskComps[i].Timer == 0)       // 时间减完了
           {
             TaskComps[i].Timer = TaskComps[i].ItvTime; // 恢复计时器值,从新下一次
             TaskComps[i].Run = 1;           // 任务可以运行
           }
        }
   }
}

/**************************************************************************************
* FunctionName   : TaskProcess()
* Description    : 任务处理|判断什么时候该执行那一个任务
* EntryParameter : None
* ReturnValue    : None
* * attention      : ***放在mian的while(1)即可***
**************************************************************************************/
void TaskProcess(void)
{
    uint8 i;

    for (i=0; i<TASKS_MAX; i++)           // 逐个任务时间处理
    {
         if (TaskComps[i].Run)           // 时间不为0
        {
             TaskComps[i].TaskHook();         // 运行任务
             TaskComps[i].Run = 0;          // 标志清0
        }
    }   
}

/**************************************************************************************
* FunctionName   : main()
* Description    : 主函数
* EntryParameter : None
* ReturnValue    : None
**************************************************************************************/
int main(void) 
{ 
   // InitSys();                  // 初始化-打开定时器

    while (1)
    {
        TaskProcess();             // 任务处理
    }
}


/**************************************************************************************
* FunctionName   : TaskDisplayClock()
* Description    : 显示任务

* EntryParameter : None
* ReturnValue    : None
**************************************************************************************/
void TaskDisplayClock(void)
{

}

/**************************************************************************************
* FunctionName   : TaskKeySan()
* Description    : 扫描任务
* EntryParameter : None
* ReturnValue    : None
**************************************************************************************/
void TaskKeySan(void)
{

}

/**************************************************************************************
* FunctionName   : TaskDispStatus()
* Description    : 工作状态显示
* EntryParameter : None
* ReturnValue    : None
**************************************************************************************/
void TaskDispStatus(void)
{

}

实验验证

说明:在keil中对51单片机进行移植上述代码,测试,P10和P20两个IO以50ms,100ms的频率翻转,验证一下代码的可行性,其中配置了单片机1ms的定时中断作为最小时间片。
代码移植如下:

验证结果如下:

参考资料:
网络博文:https://blog.csdn.net/qq_37272520/article/details/88916568
知乎等其他文章内容

在此感谢网络其他佬的分享,上述部分内容参考网络,用于学习记录传播,如有不妥请联系修改

小结

本次主要分享一个简单的逻辑多任务处理的代码demo,这种写更规范,使用起来就更方便一些。当然了,主要还是理解思路方法,代码实现的方式不止上面这种。就先到这里了!

欢迎关注本人微信公众号:那个混子
记录自己学习的过程,分享乐趣、技术、想法、感悟、情感!
单片机类嵌入式交流学习可加企鹅群:120653336
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容