嵌入式裸机计划思想——时间片轮裸机开发架构+状态机+定时器调理机制 ...

打印 上一主题 下一主题

主题 576|帖子 576|积分 1728

前言

   (1)假如有嵌入式企业需要雇用校园大使,湖南地区的日常实习,任何地区的暑假Linux驱动实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)在MCU开发的时间,很多入门者会固执的认为,做项目一定要上及时操作体系。但是真的是这样的吗?
(3)我曾经阅读过一位10年嵌入式开发经验的大佬分享的公众号,这位大佬感叹到,实在对于绝大多数时间,MCU开发不需要上操作体系。只要任务分配的合理,百分之九十的项目不上操作体系都是可以大概跑的。
(4)本日我就分享一下我之前备赛期间所搭建基于TM4C123的工程模板。不过这个模板题目还是有很多,好比模块之间耦合很严峻,当时没有这个意识,如今才发现写的有多垃圾。虽说云云,但本人认为还是有优点的地方。
(5)留意:本文需要一点函数指针,布局体,枚举的知识,不相识的同学请先去增补好底子再来看
  正文

状态机

   (1)状态机实在很好理解,说白了就是一个switch()语句。根据情况将一个任务拆分成多种。例如,在我的代码中,有一个OLED体现步伐。因为OLED体现黑白常浪费时间的,为了不因为一个这样的步伐,而影响到其他任务的实行,我们可以将OLED体现任务分成多个,依次体现。
  1. static void Display(void)
  2. {
  3.         static uint8_t gray_display_state=2;
  4.         switch(gray_display_state)
  5.         {
  6.                 case 2:gray_display_state++;break;
  7.                 case 3:gray_display_state++;break;
  8.                 case 4:gray_display_state++;break;
  9.                 case 5:gray_display_state++;break;
  10.                 case 6:gray_display_state++;break;
  11.                 default:gray_display_state = 2;
  12.         }
  13. }
复制代码
  (2)上面那种实在是最简单的形式,整个状态是呈现圆圈型状的状态机。但是,我们有时间会碰到一些情况,他的状态有很多种任务流程,例如洗衣机,有待机状态,运行状态,运行结束之后的主动停止进入待机状态,手动关停状态,因一些非常情况引起的停止状态。因此,状态机也是一个很好的思路。
  

  1. enum Washer_State_List
  2. {
  3.         off,            //待机状态
  4.         run,            //运行机状态
  5.         automatic_stop, //自动停止
  6.         hand_stop,      //手动关停状态
  7.         malfunction     //故障
  8. };
  9. static void washer_control(void)
  10. {
  11.         static uint8_t washer_state=2;
  12.         switch(washer_state)
  13.         {
  14.                 case off:
  15.                         LCD_show("stop");
  16.                         if(operation_key == true) washer_state = run;
  17.                         break;
  18.                 case run:
  19.                         LCD_show("runing");
  20.                         //如果手动关停
  21.                         if(hand_stop_key == true) washer_state = hand_stop;
  22.                         //如果计时结束
  23.                         if(time-- == over) washer_state = automatic_stop;
  24.                         //识别到了异常
  25.                         if(err == true) goto err;
  26.                         break;
  27.                 case automatic_stop:
  28.                         //显示洗衣机已经关闭
  29.                         LCD_show("Finished washing");
  30.                         washer_state = off;
  31.                         break;
  32.                 case hand_stop:
  33.                         //关闭洗衣机
  34.                         washer_stop();
  35.                         //显示洗衣机已经手动关闭
  36.                         LCD_show("Manually closed");
  37.                         washer_state = off;
  38.                         break;
  39.                 case malfunction:
  40.                         err :
  41.                                 //关闭洗衣机
  42.                                 washer_stop();
  43.                                 //报警
  44.                                 alarm_system(on);
  45.                                 //显示机器故障
  46.                                 LCD_show("Machine failure");
  47.                                 washer_state = off;
  48.                         break;
  49.                 default:gray_display_state = 2;
  50.         }
  51. }
复制代码
时间片轮裸机开发架构

   (1)这个机制名字很多,有些人叫做软定时器,有些叫做Easy51RTOS,具体叫做什么,我们就不穷究了。
(2)新手入门MCU裸机开发,肯定都是一股脑的采用while(1)死循环,然后一直跑。假如是开发稍微复杂一点点的任务时间,你就会深刻的感受到,这样写毕竟有多垃圾。
(3)一股脑的while(1)死循环,然后内里堆一大堆的任务,这样做有一下几种题目:
<1>在后续的维护中非常复杂。想找到目标任务找起来很费力。
<2>并不是所有的任务都需要频仍实行,例如OLED体现,就是一个非常费时间,但是又没有须要一直保持刷新的任务。他只要可以大概做到50ms刷新一次就可以大概做动画了。因此这种任务放在while(1)无脑实行无疑是对CPU的浪费,导致真正需要CPU实行的任务没有有效的照顾。
(4)为了优化上面这些题目,大佬们于是提出了基于时间片的裸机开发架构,我们可以利用一个定时器提供心跳,不停的进行计数。然后当定时时间一到,那么就可以开始实行相应的任务了。
(5)时间片轮转的裸机架构看起来是不是很完美?NO,不是的,他确实比无脑while(1)优秀很多,但是我们需要知道,每个任务的实行时间不能超出一次时间片。(例如我们上面的滴答定时器是2ms定时,所以时间片是2ms)
(6)因此,我们需要大概估计每个任务的实行时间,这里有很两种做法:
<1>利用滴答定时器,当任务开始记载当前时间,任务结束记载当前时间然后两者相减。
<2>进入函数,让某个引脚为高电平,函数结束让他为低电平,然后用示波器捕捉这个引脚的上升沿和下降沿。(这个方法感觉有点麻烦,我是使用的第一个)
(7)时间片轮裸机开发框架机制确实非常好,但是毕竟还是软件实现的任务调理,及时性只能说相对于只有一个while(1)好,对于其他的机制还是不太行。所以我个人还是发起,只有不紧张的任务,对整个项目影响不大的任务可以放在这个框架内里,例如OLED体现,按键扫描。
  1. /*------------------------------------------------*/
  2. /*------------------- systick.c ------------------*/
  3. /*------------------------------------------------*/
  4. /* 滴答定时器中断,每2毫秒进入一次,count表示过了多少个2毫秒,最多计时2366多个小时*/
  5. static void SycTickHandler(void) {
  6.         counter++;
  7.         task_remarks();    //用于任务调度,此任务执行时间在5us之内
  8. }
  9. /*------------------------------------------------*/
  10. /*-------------------- Task.h --------------------*/
  11. /*------------------------------------------------*/
  12. #ifndef     _Task__H
  13. #define     _Task__H
  14. // 任务结构
  15. typedef struct
  16. {
  17.     uint8_t  run;                          // 程序运行标记:0:不运行,1:运行
  18.     uint16_t timer;                        // 定时器,用于自减 单位:ms
  19.     uint16_t itv_time;                     // 任务运行间隔时间 单位:ms
  20.     void (*hook)(void);                    // 要运行的任务函数
  21. } task_params_t;  
  22. /*****    函数声明    *******/
  23. void task_proc(void);     //任务执行处理
  24. void task_remarks(void);  //任务标志位处理
  25. #endif
  26. /*------------------------------------------------*/
  27. /*-------------------- Task.c --------------------*/
  28. /*------------------------------------------------*/
  29. static void SW_Scan(void);
  30. // 任务清单
  31. enum TASK_LIST {
  32.         TASK1_SW_Scan,                    // 任务2,按键扫描
  33.         TASK2_Display,                    // 任务3,OLED显示
  34.        
  35.         TASKS_NUM                         // 任务总数
  36. };
  37. //任务列表
  38. static task_params_t s_task_params[TASKS_NUM] = {
  39.         {0, Task_interval_ms(20),  Task_interval_ms(20),  SW_Scan },                  //任务2
  40.         {0, Task_interval_ms(10),  Task_interval_ms(10),  Display },                  //任务3
  41. };
  42. /*
  43. *********************************************************************************************************
  44. *        函 数 名: task_proc
  45. *        功能说明: 任务处理
  46. *        形    参:无
  47. *        返 回 值: 无
  48. *********************************************************************************************************
  49. */
  50. void task_proc(void)
  51. {
  52.     uint8_t i = 0;
  53.         for(i = 0; i < TASKS_NUM; i++)
  54.         {
  55.                 if(s_task_params[i].run)
  56.                 {
  57.                         s_task_params[i].run = 0;
  58.                         s_task_params[i].hook();
  59.                         s_task_params[i].timer = s_task_params[i].itv_time;
  60.                         break;
  61.                 }
  62.         }
  63. }
  64. /* 作用 : 任务标志处理,单位是2ms,因为滴答定时器中断设置的是2ms
  65. * 传入参数 : 无
  66. * 返回参数 : 无
  67. * 任务时间 : TASKS_NUM少于10的时候,运行速度在5ns之内
  68. */
  69. void task_remarks(void)
  70. {
  71.         uint8_t i = 0;
  72.         for(i = 0; i < TASKS_NUM; i++)
  73.         {
  74.                 if(s_task_params[i].timer)
  75.                 {
  76.                         s_task_params[i].timer--;
  77.                         if(s_task_params[i].timer == 0)
  78.                         {
  79.                                 s_task_params[i].run = 1;
  80.                         }
  81.                 }
  82.         }
  83. }
  84. /*****************************************************************************************/
  85. /*************************************  按键扫描任务  *************************************/
  86. /*****************************************************************************************/
  87. static void SW_Scan(void)
  88. {
  89.         //...
  90. }
  91. /*****************************************************************************************/
  92. /*********************************  灰度传感器时间显示任务  *******************************/
  93. /*****************************************************************************************/
  94. static void Display(void)
  95. {
  96.         static uint8_t gray_display_state=2;
  97.         switch(gray_display_state)
  98.         {
  99.                 case 2:
  100.                         gray_display_state++;
  101.                         //...
  102.                         break;
  103.                 case 3:
  104.                         gray_display_state++;
  105.                         //...
  106.                         break;
  107.                 case 4:
  108.                         gray_display_state++;
  109.                         //...               
  110.                 break;
  111.                 case 5:
  112.                         gray_display_state++;
  113.                         //...
  114.                         break;
  115.                 case 6:
  116.                         gray_display_state++;
  117.                         //...
  118.                         break;
  119.                 default:
  120.                         gray_display_state = 2;
  121.         }
  122. }
复制代码
定时器调理

   (1)假如经验稍微丰富的同学会发现,上面的方法实在还是有题目的。例如有些任务我需要说实行就实行,假如是按照上面的方法来肯定是有一点点痴钝的。
(2)因此,我们可以采用定时器调理机制,例如我们的编码器数值读取和PID运算是优先级最高的任务,他的影响因素非常大,是我们的核心任务,所以他单独分配一个定时器。
(3)然后其他的一些任务,很相对来说,比较紧张,但是却又不是核心任务。因此,我们可以将它存放在一个优先级低一级的定时器中。
(4)假如还有剩余的定时器,你可以自己根据需求进行任务分级,然后实现定时器调理功能。
  1. //用于获取编码器的值
  2. systime Time0_Delta;
  3. void TIMER0A_Handler(void)
  4. {
  5.   get_systime(&Time0_Delta);
  6.         cnt = 1;
  7.         left_motor_speed_cmps =get_left_motor_speed();   //获取左边轮子实际速度值
  8.         right_motor_speed_cmps=get_right_motor_speed();  //获取右边轮子实际速度值
  9.         Motor_Foreward_Right(400);
  10.         Motor_Foreward_Left(400);
  11.   TimerIntClear(TIMER0_BASE,TIMER_TIMA_TIMEOUT);   //清除中断标志位
  12. }
  13. //普通任务
  14. systime Time1_Delta;
  15. void TIMER1A_Handler(void)
  16. {
  17.   get_systime(&Time1_Delta);
  18.         cnt = 2;
  19.         //任务列表
  20.         //超声波数据解析
  21.   US_100_Statemachine(UART5_BASE);  
  22.         //MPU6050数据采集
  23.         MPU6050_Read_Data(&WP_Sensor.gyro_raw,&WP_Sensor.accel_raw,&WP_Sensor.temperature);
  24.   TimerIntClear(TIMER1_BASE,TIMER_TIMA_TIMEOUT);  //清除中断标志位
  25. }
复制代码
总结

   (1)假如我们相识了如上机制,对于后续的操作体系学习,以及任务调理的理解会有一定的帮助。
(2)使用如上机制之后,也会很好的管理项目标各种任务,多裸机开发也很有利。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

西河刘卡车医

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表