【小黑嵌入式系统第六课】嵌入式系统软件设计基础——C语言简述、程序涉及规范、多任务程序设计、状态机建模(FSM)、模块化设计(2)

初学者常见问题:

在这里插入图片描述
4)应用程序接口(API)

接口是对软/硬件对象的抽象,对外提供的服务和访问功能,

即:对象封装后对外呈现成什么样?

原则:(1)使用方便(2)功能丰富(3)但不要过多过滥

【例】为液晶显示模块规划应用程序接口:

在这里插入图片描述
5)软件层次

例:菜单程序
在这里插入图片描述
 
(1)杜绝跨层调用
(2)功能模块可以任意拼接、组合
(3)要有硬件隔离层(HAL)
(4)更换任意层代码,上下层建筑不变


(三)版本管理、可配置

在这里插入图片描述
 
(1)尽可能减少软件副本
(2)类似的产品/设计尽可能公用同一代码
(3)利用宏定义配置功能
(4)永远在最新的代码上开发,保留最新版


三 前后台多任务程序设计

(一)前后台程序的基本概念

在这里插入图片描述
任务(Task) :指完成某一单一功能的程序
后台程序:对时间要求不严格的任务,通常在主循环内执行
前台程序:要求快速响应或者时间严格的任务,通常中断内
队列/缓冲:用于暂存后台来不及处理的前台事件


(二)前后台程序的编写基本原则
(1)任何一个任务都不能阻塞CPU。

每个任务都应主动结束,让出CPU。不能有等待、死循环、长延时等环节(对初学者来说是难点!

在这里插入图片描述

(2)关注函数重入问题

可重入(Reentrancy):函数在自己调用自己的时候,不必担心数据被破坏。

从软件工程角度对函数可重入的作用可以解释为:具有可重入性的函数能够被多个任务同时调用。在后台程序中,任务都是顺序执行的,不存在多个任务同时调用一个函数的情况。但可能出现前台中断服务程序与后台任务同时调用某个函数。

因此对于前后台公用函数,必须是可重入的

【例】:

在这里插入图片描述


(3)临界代码保护(Critical Code Protection)

临界代码:运行时不可分割的代码,这部分代码不允许被中断打断

1) 依靠软件产生时间严格时序的程序段。

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\*名称: Pulse 10us()
\*功能: 产生10us脉冲
\*入门参数: 无
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
void Pulse\_10us()
{
	\_DINT();            // -------以下是临界代码区,不允许被中断
	IO=1;               // 置高
	\_delay\_cycles(8); 	// 高电平持续10us (O赋值身耗2us)
	IO=0;               //置低
	_EINTO0;            // -------以上是临界代码区-----------
}

2)共享资源互斥性造成的临界代码

程序中任何可被占用的实体都称为“资源”。资源可以是硬件设备,如定时器、串口、打印机、键盘、LCD显示器等,也可以是变量、数组、队列、等数据。

被一个以上任务所占用的资源叫做“共享资源”。共享资源若不能同时被多任务占用,则具有“互斥性”(Mutual Exclusive)。

【例】:前后台同时调用液晶显示函数造成花屏

在这里插入图片描述

(4)临界代码的保护方法
  • 关中断、开中断
    隐患: 如果在A函数的临界代码区调用了另一个函数B,B函数内也有临界代码区,从B函数返回时,中断被打开了,这将造成A函数后续代码失去临界保护。所以,使用该方法时,不能在临界代码区调用任何具其他有临界代码的函数!
  • 利用硬件栈保存中断使能状态
    优点:不影响函数调用
    缺点:很多单片机的C语言不支持操作硬件栈
  • 利用变量保存中断使能状态
    缺点:每一段临界代码要消耗2-4字节内存
  • 利用模拟栈保存中断使能状态
    缺点:速度慢

典型错误1:在临界代码区调用其他临界代码

A()
{	
	int\_Disable();	
	B();	// 这期间中断是开的,所执行的代码不被临界保护
	int\_Enable();
}


B()
{
	int\_Disable();	
	// ...
	int\_Enable();
}

典型错误2:在主函数和中断中都调用LCD操作函数

  • LCD操作耗时长,不应放在中断中。
  • LCD操作是临界代码,LCD是互斥性共享资源,LCD操作函数都是不可重入的,如果主函数里没有做临界代码保护,结果就是没多久屏幕就花了或呈死机效果。
讨论与总结
  1. 后台中任务顺序执行。每个后台任务中的内存(局部变量)在任务结束后可以全部释放,让给下一个任务使用。即使在RAM很少的处理器上也能同时执行众多任务。
  2. 后台中任务顺序执行。天然避免了后台任务资源互斥问题,但仍需考虑前后台之间的资源互斥问题。
  3. 前后台程序的结构灵活,实现形式与实现手段多样,是使用最广的程序结构,但缺乏架构标准,维护、升级、排错都很困难。
  4. 必须要程序员自己来判断和处理临界代码的隐患。
  5. 程序多任务的执行依靠每个任务的非阻塞性来保证,是编程最大的难点,下一节将介绍的FSM将是解决这一问题的利器!

四 FSM:状态机建模

在这里插入图片描述

1、什么是状态机?

有限状态自动机(Finite-State Machine, FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移动作等行为的数学模型。

2、为什么要引入状态机?

流程图的缺陷:

1、不能用于描述“任何时候”发生的事件
2、无法描述“由外部事件决定流程”的程序
3、不适合描述带有阻塞的并发过程
4、难以描述大量的独立事件。

需要:能够描述并发结构的软件,能从行为的角度来描述软件,且能够根据模型生成代码,也能够对软件进行完整测试的新手段—状态机模型。

3、流程图无法描述的软件行为举例

(1)不能用于描述“任何时候”发生的事件
在这里插入图片描述
(2)不能用于描述由外部事件决定流程的事件

【例】:电子表有两个按键,但按键顺序完全由外部决定
在这里插入图片描述
(3)不适合描述带有阻塞的并发过程

【例】:一个单片机同时控制2台电动机完成运转时序

在这里插入图片描述
(4)不适合描述大量的独立事件

【例】:投币式自动咖啡机程序

流程图:投币->选咖啡->出咖啡->结束
然 而…

  • 如果没有放咖啡杯就选择咖啡,会发生什么情况?
  • 如果咖啡杯未满之前,用户取走了咖啡杯,会发生什么情况?
  • 如果咖啡灌了一半时,用户取走咖啡杯,再放回,能否把剩下一半继续加满?
  • 如果咖啡灌了一半时,用户取走咖啡杯,不再放回,剩下一半是否会加到下一用户杯中?
  • 如果咖啡杯未满之前,用户取走了咖啡杯,然后按退币扭,该如何处理?
  • 如果还没有完成一次点单之前,用户再次投币,会发生什么情况?
  • 如果一杯咖啡尚未加满时,用户又点了另一种咖啡,会发生什么情况?
  • 如果用户一次投入两杯咖啡的钱,该怎样处理?
  • 如果咖啡存储容器已空,用户点单时能否正确提示?
  • 如果咖啡杯未满之前,咖啡机空了,能否给用户退款?
4、引入“状态机” 的优势

上面这些例子中,软件下一步要执行的功能不仅与外界入信息有关,还与系统的“当前状态”有关。状态机就是一种基于“状态”与“事件”的软件描述手段!其优势在于:

(1) 能够处理并发。由于系统在每一时刻只能有唯一的状态,在每一个状态下,可能发生的事件也是有限的。因此系统中即使存在有大量的独立事件,软件描述也会简单得多。

(2) 能够消除阻寒。真正需要CPU处理的只有系统状态发生改变的那一刻,在系统等待事件到来的期间,是不需要CPU处理的。如果能够用状态与事件的形式来描述软件,能够将CPU从等待事件发生的过程中解放出来,从而生成无阻塞的代码。

(3) 能够降低系统的复杂度,提高可测性

5、状态机的表示方法:状态转移图

在这里插入图片描述

圆圈:状态 (有限个)
有向箭头:状态转移
Event事件:事件发生时将触发状态转移
Action动作:状态转移的同时执行动作

6、由状态转移图生成代码

方法1、在状态中判断事件(事件查询)

在这里插入图片描述

switch(State)		//根据当前状态决定程序分支
{
	case S0:			//在s0状态
		if(Event0)
		{			//如果查询到Event0事件
					Action0();
		}
		else if(Event1)
		{			//如果查询到Event1事件
					Action1();
					State=S1;
		}
		else if(Event2)
		{			//如果查询到Event2事件
					Action3();
					State=S2;
		}
		break;
	case S1:			//在s1状态
		if(Event2)
		{			//如果查询到Event2事件
					Action2();
					State=S2;
		}
		break;
......

方法2、在事件中判断状态(事件触发)

在这里插入图片描述

7、状态机建模举例

例1 电子表程序

在这里插入图片描述

====A键中断====
switch(Status)		/\*根据当前状态处理A键所引发的状态跳跳转\*/
{
	case DISP_TIMME: Status = DISP_DATE;break://时间显示时按A键,显示日期
	case DISP_DATE: Status = DISP_SEC; break;
	case DISP_SEC: Status= DISP_TIME; break;
	case SET_HOUR: if(++Hour>23) Hour=0; break; //小时设置时按A键调整小时case SET\_MINUTE: if(++Min>59) Min=0; break;
	case SET_MONTH: if(++Month>12) Month=1; break;
	case SET_DATE: if(++Date>31) Date=0; break;
}

====B键中断=====
switch (Status)			/\*/根据当前状态处理B键所引发的状态跳转/\*/
{
case DISP_TIME;		Status= SET_HOUR; break;	//时间显示时按B键,设置小时
case DISP_DATH;		Status= SET_HOUR: break;	//日期显示时按B键,设置小时
case DISP_SEC;		Second 0; break;			//秒钟显示时按B键,秒归零
case SET_HOUR;		Status= SET_MINUTE; break;	//小时设置时按B键,分钟设置
case SET_MINUTE;	Status= SET_MONTH; break;	//分钟设置时按B键月设置
case SET_MONTH;		Status= SET_DATE; break;	//月设置时按B键,日设置
case SET_DATE;		Status= DISP_TIME; break;	//日设置时按B键,显示时间
}

例2 长短按键识别

在这里插入图片描述
例3 事件序列匹配

在这里插入图片描述


五 模块化程序设计

1、模块化程序的原则
  1. 非阻寒性 不能长时间占用CPU,更不死等待某个事件的发生,尽快地让出CPU,供后续任务执行。
  2. 硬件隔离 屏蔽硬件特征,起到硬件隔离层的作用。上层软件将不涉及直接硬件操作。
  3. 模块独立性 各个程序库之间没有代码关联性(没有互相调用和隶属关系),在使用时,各个模块文件可以任意拼接、组合、拆分。
  4. 时间独立性 使用缓冲区、事件队列等手段,对时间关联性强的事件进行斩存和缓冲,使之可以被异步处理。
  5. 可移植性 代码都按照标准C的规范,很容易的移植到其他处理器上,或者其它硬件平台上。
  6. 100%注释 注释关键代码的功能或设计意图,以及每个函数的功能.入口出口参数、说明及注意事项,使用范例等。
  7. 开放性 方便日后修改、添加新的功能函数。
2、键盘模块化程序

在这里插入图片描述

3、带长短键识别的键盘模块化程序

在这里插入图片描述

4、串口收发模块化程序

在这里插入图片描述

5、数码管显示模块化程序

在这里插入图片描述

6、时钟和日历程序

在这里插入图片描述


六 事件触发多任务程序设计

1、什么是事件触发程序?

在这里插入图片描述
任务全部在中断内完成,主程序休眠。
可以看做前后台程序中,只有前台任务。

特点:1)实时性较好,事件响应较快;2)低功耗

2、事件触发程序的程序架构

在这里插入图片描述

中断:获取原始的事件信息。
事件引擎:判断何种事件发生,并调用相应处理程序。
(一个中断有多个中断源时)
事件处理程序:只负责某个事件的处理,要求非阻塞。

3、事件触发程序设计范例

有3个按键 (S1、S2、S3),按下为低电平。外部输入电压从ADC输入。设计一款超低功耗的电压表,要求用事件触发结构实现下列功能:

  1. 实现电压测量功能,每秒刷新显示2次电压测量值。
    每次测量值由ADC连续采样4次求平均得到。
  2. 按下S1键时,斩停采样与显示刷新(保持功能)
  3. 按下S2键时,恢复采样与刷新。
  4. 按下S3键时,将采样数据从串口发出。

在这里插入图片描述
在这里插入图片描述
第3步:编写5个事件处理程序,此时并不关心事件如何发生如何被检测到,以及按什么顺序发生

第4步:测试。按照预先设定的顺序,依次调用5个事件处理程序(并不需要硬件上真的发生事件),模拟事件发生,即可进行代码测试。

第5步:编写事件引擎,在中断内检测和判判断事件,并分别调用相应的事件处理程序。


七 时间触发系统

时间触发系统可以理解为一种特殊的事件触发系统,但它另有自己的特点。

例子:一个医生,必须在一些护理人员的帮助下,通宵照顾十个危重病人,方案可以是

● 安排护理人员在某个病人出现严重问题时唤醒他
——事件触发
● 每小时闹铃,闹铃响时起床去探访每个病人
——时间触发

以每小时的间隔探访病人,医生能在严重并发症出现之前探访每个病人并安排合理的治疗。另外,工作量在整个晚上平摊。于是,所有病人都能平安地度过这个夜晚。

而前一种方式可能会出现护理人员在唤醒医生时,有多个病人都出现了严重并发症,需要依次做手术,后面的病人有可能来不及得到手术治疗。

这个医院的例子在事件驱动的系统中,即可能同时发生几个事件。需要处理同时发生的多个事件不但增加了系统复杂性,而且降低了对事件触发系统在所有情况下的行为做出预计的能力。

相比而言,在时间触发的嵌入式系统中,设计人员能够通过仔细安排可控的顺序,保证一次只处理一个事件。

许多嵌入式系统并不需要睡眠,采用时间触发方法有很多好处,有助于改善可靠性和安全性(如广泛应用于航空航天工业和汽车工业),主要原因在于系统的行为可以预计。

时间触发方法还能降低CPU的负荷,并减少存储器的使用量。

嵌入式系统需要执行的任务分为两种:

  ● **周期性任务**,比如每100ms执行一次
  ● **单次任务**,如在50ms的延迟后执行一次

考虑方案1:

void main(void)
{
    Init\_System();
    while(1)
    {
        X();                     // 执行任务(耗时10ms)
        Delay\_90ms();   // 延迟90ms
    }
}

需要知道任务X的精确运行时间

这个运行时间永不变化—— 不可行

考虑方案2:

基于定时器中断,在一定的时刻调用函数

如果要支持多个任务(这些任务一般具有不同的运行时间并以不同的时间间隔运行),可以每个任务都使用一个定时器。

但:浪费硬件资源(或者不够用)、硬件的维护增加、会同时产生多个定时器中断。——不合理

解决方案:使用调度器

● 调度器可以看作是一个简单的操作系统,允许以周期性或单次方式来调用任务。

● 从底层的角度来看,调度器可以看作是一个由许多不同任务共享的定时器中断服务程序。

用调度器来调度三个任务的执行:

void main(void)
{
    SCH\_Init();     // 设置调度器一次

    /\*---- 增加各任务至任务队列(时间分辨率为1ms)----\*/
    // Function\_A将每隔2ms运行一次
    SCH\_Add\_Task(Function_A, 0, 2);
    // Function\_B将每隔10ms运行一次
    SCH\_Add\_Task(Function_B, 1, 10); 
    // Function\_C将每隔15ms运行一次
    SCH\_Add\_Task(Function_C, 3, 15); 

    SCH\_Start();   // 启动调度
    while(1)
    {
       // 按调度算法执行任务队列中各任务
       SCH\_Dispatch\_Tasks();   
    }
}

在这里插入图片描述
调度器的分类

  ● 合作式调度器
  ● 抢占式调度器

合作式调度器:
● 任务在特定的时刻被调度运行(以周期性或单次方式)

● 当任务需要运行时,被添加到等待队列

● 当CPU空闲时,运行等待任务中的下一个(如果有的话)

● 前一任务运行直到完成,才轮到下一个等待任务

● 简单,用少量代码即可实现

● 一次只为一个任务分配存储器

● 通常完全由高级语言如C实现

● 小心设计可以实现快速响应外部事件
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

img

img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)

img

最后

资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!

开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-Z7gULOgx-1712325249303)]

[外链图片转存中…(img-sMFnRJkm-1712325249304)]

[外链图片转存中…(img-dBI6Qw6s-1712325249305)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

[外链图片转存中…(img-806BYkr4-1712325249306)]

[外链图片转存中…(img-TFC7NFNM-1712325249307)]

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)

img

最后

资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!

更多资料点击此处获qu!!

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 小黑课堂计算机二级c语言安装包1.3是一款针对C语言学习者的软件。该软件包含了C语言学习的基本工具和环境,包括编译器、编辑器、调试器等。学习者可以通过该软件进行代码的编写、调试和运行,以提高对C语言的理解和掌握程度。 安装小黑课堂计算机二级c语言安装包1.3时,需要首先下载安装包,然后进行安装。安装时需要注意选择正确的安装路径,并按照软件向导的提示进行操作。安装完成后,打开软件即可开始学习C语言小黑课堂计算机二级c语言安装包1.3是一款功能齐全、使用简便的软件。除了提供基本的编译器、编辑器和调试器外,还提供了丰富的示例代码和学习资源,方便学生进行参考和学习。 总之,小黑课堂计算机二级c语言安装包1.3是一款非常实用的软件,对C语言学习者来说具有很高的价值和意义。无论是初学者还是有一定基础的学习者,都可以通过该软件进行系统的学习和实践,从而提高C语言编程的能力和水平。 ### 回答2: 小黑课堂计算机二级c语言安装包1.3是一款专门为计算机二级c语言考试学习者设计的软件。该安装包包含了大量的c语言编译器、编辑器以及相关的编程工具和实用函数库等资源。这些资源可以为用户提供一个完整的c语言学习环境,方便用户进行c语言编程的学习和实践。 在安装小黑课堂计算机二级c语言安装包1.3时,用户需要按照安装向导的指引依次进行安装。安装完成后,用户可以打开软件中的编辑器,进行c语言程序的编写。该安装包还提供了一些编程实例,方便用户进行练习和学习。 小黑课堂计算机二级c语言安装包1.3还具备自动编译和调试功能,可以帮助用户快速发现和修复错误。同时,该安装包还提供了一些实用的辅助功能,比如可以生成用于调试的代码、生成代码统计报告以及支持代码格式化等功能。 总的来说,小黑课堂计算机二级c语言安装包1.3是一款非常完善的c语言编程学习软件,可以有效地帮助用户提高编程技能,为计算机二级c语言考试做好准备。 ### 回答3: 小黑课堂计算机二级C语言安装包1.3是一款可用于学习C语言的软件,被广泛应用于计算机专业的学生和开发人员之间。 1.3版本是较新的版本,相较于之前的版本有一些改进。该安装包最主要的优点是提供了完整的学习计算机编程所需的工具和资料,包括了一些基础的C程序实例和教学视频。通过这些教学资料,学生们可以更好地理解计算机编程的基础理论和应用技巧。 同时,该安装包也提供了一些简单的集成开发环境软件,用户可以在安装后直接使用这些软件来编写和调试程序,从而提高编程效率。此外,1.3版本还改进了一些常见C语言编程错误的修复问题,使得用户更加稳定和优化地编写程序,提高程序质量和执行效率。 总之,小黑课堂计算机二级C语言安装包1.3是一个学习和应用C语言编程的必备工具,可以帮助用户更好地理解计算机编程和实践,提高自己的编程能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值