文章目录

  • 前言
  • 裸机开发模式
    • 轮询方式
    • 事件驱动方式
    • 改进的事件驱动方式
    • 常用时间驱动方式:定时器
    • 使用状态机进行改进
    • 总结
  • RTOS的引入
  • RTOS编程要注意的问题
    • 临界资源的访问
    • 任务的休眠唤醒
  • 总结

前言

感谢韦东山老师的直播教学,在今天的这次教学中学到了很多实用的知识。对逻辑开发和RTOS开发有了本质的理解。
博主也在这里向大家推荐一个嵌入式学习网站百问网官网。在这里有所有嵌入式学习的视频,更有韦东山老师的亲自授课为你解答疑惑。


裸机开发模式

所谓裸机开发,指的就是没有操作系统,就是单片机开发。程序的运行,完全取决于代码的逻辑设计,硬件设备的固定设定。不需要操作系统的参与和调度。
这里将韦老师上课举得例子拿来进行分析
讲的是一位宝妈,需要一编进行喂孩子吃饭,一边需要回复同事的消息。
那么我们首先想到的方式就是进行轮询

轮询方式

void main{}{while(1){eat();//喂孩子吃饭函数message();//回复同事消息
}
}

这是一个非常经典的单片机程序,是不是就是你的跑马灯程序。那么我们来分析这个程序:
在执行喂孩子这个函数的时候,回复同事消息这个函数是无法执行的,在执行回复同事消息这个函数的时候,程序是无法执行喂孩子这个函数,那么对于同事而言,宝妈总是在一段时间消失,无法回复消息。对于孩子而言,妈妈总是在一段时间无法来喂我吃饭。
双方(同事和孩子)似乎都没有得到满足。显然这个程序是不太好的,那么我们如何来进行优化呢?
相信已经有朋友类比到了我们最初的单片机实验,我们可以使用中断呀!没错中断就是下面我们的优化方式,也叫做事件驱动方式

事件驱动方式

事件是一个宽泛的概念,什么是事件?可以是:按下了按键、串口接收到了数据、模块产生了中断、某个全局变量被设置了。

什么叫事件驱动?当某个事件发生时,才调用对应函数,这就叫事件驱动。

我们将上面的例子进行改进:

  • 当孩子哭的时候宝妈就给他喂饭
  • 当同事发送了消息,电脑提示了才去回复同事
void crying_isr(){//检测孩子是否在哭的中断函数eating();//哭了就执行喂孩子吃饭的函数
}
void message_isr(){//检测同事是否发消息函数.message();//执行回消息函数
}
void main(){while(1){}
}

这种编程方式就使得这两个中断函数执行的都很快,不用像轮询一样再去等待上一个函数的执行完毕。

但是如果两个中断同时发生,就会相互影响:

  • 两个中断,同一时间只能处理一个
  • 如果当前中断处理时间比较长,就会影响到另一个中断的处理。
    下面继续优化

改进的事件驱动方式

对于上面的程序,我们出现的问题是,当两个中断同时产生的时候,同一时间只能处理一个,如果一个中断处理时间比较长,就会影响另一个中断的处理。

下面我们针对这些问题来进行改进我们的程序。

对于中断的处理,原则上是“尽快”。否则就会影响其他中断,导致其他中断的处理延迟,甚至丢失。

下面我们通过设置标志位来改进程序。

void crying_isr(){//检测孩子是否哭了
is_crying=1;//如果哭了就将标志位置1
}
void message_isr(){is_message=1;//将有消息标志位置1。
}
void main(){while(1){if(is_crying==1)eating();if(is_message==1)message();
}
}

设置了标志位以后,我们的中断处理函数就会很快执行,那么就不会影响到其他中断的处理,不会导致中断的延迟,丢失。

相信大家已经想到了,中断持续触发后的后续处理就退回轮询了。那岂不是我们这也没啥改进?别急,下面我们继续改进!

常用时间驱动方式:定时器

这里我先用韦老师的例子来给大家介绍这种方法,然后再来分析上面的例子的改进方法。

例子:宝妈喂饭这个例子只有两个任务,如果有多个任务,一些有经验的工程师会使用定时器来驱动

  • 设置一个定时器,比如每1ms产生一次中断
  • 对于函数A,可以设置它的执行周期,比如每1ms执行一次
  • 对于函数B,可以设置它的执行周期,比如每2ms执行一次
  • 对于函数C,可以设置它的执行周期,比如每3ms执行一次
  • 注意:1ms、2ms、3ms只是假设,你可根据实际情况调整。

那么我们编写代码可以如下

typedef struct soft_timer{int remain;//表示剩余多少时间,就需要调用下面的函数int period;//表示周期void (*function)(void);//处理函数
}soft_timer,*p_soft_timer;static soft_timer timers[]={{1,1,A},{2,2,B},{3,3,C}
};//符合题目要求void main(){while(1){}
}
void timer_isr(){int i;//是每个timers数组成员的remain都减1.for(i=0;i<3;i++){timers[i].remain--;
}
//当remain减到0,就表示要调用对应结构体中的函数了for(i=0;i<3;i++){if(timers[i].remain==0){timers[i].function();//调用函数timers[i].remain=timers[i].period;//重置remain.
}
}
}

经过这样设置以后,我们很好的解决了每个人数的处理时间。但是对于当某一个程序执行时间很长,就会出现下面的后果:

  • 影响其他函数的调用
  • 延误整个时间基准

那么我们怎么改进呢?针对这个问题,这里我以上面第二个例子进行分析,对于宝妈问题同理

typedef struct soft_timer{int remain;int period;void (*function)(void);
}soft_timer,*p_soft_timer;static soft_timer timers[]={{1,1,A},{2,2,B},{3,3,C}
};void main(){while(1){for(int j=0;j<3;j++){if(flag[i]){timers[i].function();//调用函数}}}
}
void timer_isr(){for(i=0;i<3;i++){timers[i].remain--;
}for(i=0;i<3;i++){if(timers[i].remain==0){flag[i]=1;//设置标志位timers[i].remain=timers[i].period;
}
}
}

通过上面设置标志位,来解决因为某个函数执行时间过长导致影响整个过程的时间基准。

使用状态机进行改进

问题,如果当任务处理函数执行时间都很长的时候,我们的裸机该怎么办呢?
这里我们可以使用状态机的思想来解决这个问题(其实思路就是操作系统的时间片)

void crying_isr(void)
{static int state = 0;switch (state){case 0: /* 开始 */{/* 盛饭 */state++;return;}case 1: /* 盛菜 */{/* 盛菜 */state++;return;}case 2: {/* 拿勺子 */state++;return;}}
}void mesage_isr(void)
{static int state = 0;switch (state){case 0: /* 开始 */{/* 打开电脑 */state++;return;}case 1: {/* 观看信息 */state++;return;}case 2: {/* 打字 */state++;return;}}
}void main()
{while (1){crying_isr();message_isr();//其实就是将这个执行时间很长的函数,拆分为短时间来处理。}
}

显然这里使用状态机拆分程序:

  • 比较麻烦
  • 有些复杂的程序无法拆分为状态机。

总结

总的来说,裸机程序难以解决的问题就是,控制每个任务的运行时间。难以消除任务与任务之间的相互影响。

RTOS的引入

假设要调用两个函数AB,AB执行的时间都很长,使用裸机程序时可以把AB函数改造为"状态机",还可以使用RTOS。这两种方法的核心都是"分时复用":

  • 分时:函数A运行一小段时间,函数B再运行一小段时间
  • 复用:复用谁?就是CPU

这里还是以宝妈的例子进行分析:
将宝妈比作CPU,喂孩子比作函数A,回消息比作函数B

宝妈一会儿喂孩子饭,一会儿回消息。当这个时间足够短的时候,从宏观上来看就是两个事件同时发生;从微观上来看,这依旧是两件事情。

// RTOS程序
喂饭()
{while (1){喂一口饭();}
}回信息()
{while (1){回一个信息();}
}void main()
{create_task(喂饭);//创建一个任务create_task(回信息);//创建一个任务start_scheduler();//执行任务列表while (1){sleep();}
}

关键在于RTOS让多个任务轮流运行,不再需要我们手工在任务函数去使用状态机拆分程序。

注意: RTOS其实现的原理就是链表的操作,通过优先级的高低,形成遍历链表顺序的先后。达到优先级高,先处理。通过判断链表是否为空,判断是否需要执行函数。同时同一链表,通过分时,在时间片内时间执行一个任务后,将该任务放置链表末尾,进而执行下一个任务。
关于休眠和唤醒,其实就是将要休眠(或者没有达到满足条件的任务)放置到休眠链表中,当条件满足时再唤醒该任务。
后面详细介绍该部分。

RTOS编程要注意的问题

临界资源的访问

这里其实就是和我们平时在Linux上编程一样,要考虑临界资源的访问问题,解决办法依旧还是设置互斥锁。

任务的休眠唤醒

当我们对某一个任务的执行设置了条件的时候,如果我们不将被设置条件的任务进行休眠,那么这个函数就会不停的进行条件判断,如下

void main(){A(){//当A快要执行完,执行此内容(假设,也可能是某个条件)if(xxx){flag=1;}};if(flag){//如果不将B进行休眠,如果A执行100000次,那么这个if判断条件就会执行这么多次。所以这样就会造成浪费资源,没必要的开销。B();}
}

所以设置任务的休眠,将B进行休眠,就让A一直执行,当flag为1时,再唤醒B,这样就能避免这个浪费。

总结

提示:这里对文章进行总结:
非常充实的一天,温故了以前的知识。同时理解到了RTOS和裸机开发的区别,以及RTOS的工作实质。
继续加油,不断努力!

深入理解裸机与RTOS开发模式相关推荐

  1. 如何理解Unity组件化开发模式

    Unity的开发模式核心:节点和组件,组件可以加载到任何节点上,每个组件都有 gameobject 属性,可以通过这个属性获取到该节点,即游戏物体. 也就是说游戏物体由节点和组件构成,每个组件表示物体 ...

  2. 疫情下更合适的开发模式

    问题的提出 任何复杂的软件都是团队工作的产物,所以我们会利用版本控制工具和不同的分支策略来协助团队的日常开发和交流,mainline开发模式和pull request开发模式(以下简称PR)则是最常用 ...

  3. 说说对npm的开发模式和生产模式的理解

    2019独角兽企业重金招聘Python工程师标准>>> nodejs这些年的发展非常快,相信没有哪个前端不知道的了,npm也成为了前端开发中经常用到了的一个命令.那么npm不是只用一 ...

  4. 百问网七天物联网课程学习笔记——单片机开发模式

    单片机有几种不同的开发模式,即:寄存器开发.C语言裸机开发以及使用实时操作系统进行. 1.使用寄存器开发单片机程序,分为使用汇编指令操作寄存器以及使用C语言操作寄存器两种.该方法有着无与伦比的效率提升 ...

  5. 认识Web前端、Web后端、桌面app和移动app新开发模式 - 基于Node.js环境和VS Code工具...

    认识Web.桌面和移动app新开发模式 - 基于Node.js环境和VS Code工具 一.开发环境的搭建(基于win10) 1.安装node.js和npm 到node.js官网下载安装包(包含npm ...

  6. qt框架的开发模式_Flutter 混合开发框架模式探索

    Flutter 混合开发框架模式探索 由于 Google 官方提供的 Flutter 混合式开发方案过于简单,仅支持打开一个 Flutter View 的能力,而不支持路由间传参.统一的生命周期.路由 ...

  7. dao模式和前端控制器结合使用_前端技术及开发模式的演进,带你了解前端技术的前世今生...

    先声明,本篇不会讲带有年代性的前端发展史,不讲故事,想了解的读者可以去查阅一些其他的资料和文章,本篇仅仅从技术发展角度结合案例分析,说明前端技术的发展和开发模式的演进变化.本篇内容重点说明PC端技术, ...

  8. Django框架深入了解_01(Django请求生命周期、开发模式、cbv源码分析、restful规范、跨域、drf的安装及源码初识)

    阅读目录 一.Django请求生命周期: 二.WEB开发模式: 三.cbv源码分析: 四.认识RESTful 补充知识:跨域 五.基于原生django开发restful的接口 六.drf安装.使用.A ...

  9. javaweb学习总结(二十一)——JavaWeb的两种开发模式

    SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式. 一.JSP+JavaBean开发模式 1 ...

  10. react 哲学_细聊Concent amp; Recoil , 探索react数据流的新开发模式

    开源不易,感谢你的支持,❤ star me if you like concent ^_^ 序言 之前发表了一篇文章 redux.mobx.concent特性大比拼, 看后生如何对局前辈,吸引了不少感 ...

最新文章

  1. 实践指南 | 用PyTea检测 PyTorch 中的张量形状错误
  2. Sql Server 日志清理 (数据库压缩方法)
  3. 【推导】【NTT】hdu6061 RXD and functions(NTT)
  4. 思科收购网络安全管理厂商Pari Networks
  5. python的[:-1]和[::-1]用法及结果实例(取反、删除末尾字符串)
  6. Chrome 开发工具 Workspace 使用
  7. mysql中两列拼接_python之Pandas读写操作mysql数据库
  8. 转载:margin外边距合并问题以及解决方式
  9. SLAM中直接法分类及对应的项目
  10. iOS网络加载图片缓存与SDWebImage
  11. 【js】:利用javascript打开网页
  12. 分布式系统可用性与一致性
  13. 《垃圾回收算法手册 自动内存管理的艺术》——引言、标记清扫(笔记)
  14. Unity 生成随机房间、洞穴(2D、3D地图)总结
  15. 基于soot的过程内数据流分析
  16. ROS中对URDF文件检查时出现的问题及解决方式
  17. 【自定义Matlab函数】利用Kronecker积求解矩阵方程AXB=D
  18. 窄带物联网(NB-IoT)科技的关键技术是什么?
  19. 人只需要成功一次就够了
  20. 利用Vivado逻辑分析仪ila观察信号

热门文章

  1. ArcGis Server10.2 授权文件教程
  2. IP地址、网络地址、网关的作用
  3. 四、Sensor组成
  4. Java动态代理为什么要求必须实现顶级接口
  5. matlab eval函数代替,matlab eval函数用法
  6. 用tushare数据自定义期货大宗商品指数(3)
  7. kvm 上部署虚拟机两种方法
  8. cenntos7安装Nginx添加passenger模块
  9. 移动服务器连接响应慢,移动打开网页速度慢的解决方法
  10. emacs 学习成长记