嵌入式系统程序架构的进化
admin
2024-04-14 00:27:56
0

实用的嵌入式系统我们一般采用分层的架构,可以分成3层(硬件层、驱动层和应用层)或4层(硬件层、驱动层、操作系统层和应用层),这在例解嵌入式系统分层结构已有说明,但是应用程序本身是非常复杂的,尤其是可能涉及到各种人机交互、机机交互的问题,使得系统更加复杂,所以对系统程序的架构做进一步的分析是很有必要的。

下面的分析我们都假定系统在大的层次上分为3层,设备驱动已准备好,下面的代码为伪代码,看懂程序结构即可。

1.无交互,少量任务

例:流水灯

/*exe-1*/
#include"config.h"
int main(void){unsigned char i=0;//leds_init();//while(1){for(i=0;i<8;i++){leds_on(i);delay(1000);leds_off();}}return 0;
}

2.有少量交互,少量任务

例:按键控制数码管,数码管显示按下的按键名称1、2、3

1)采用查询方式

/*exe-2*/
#include "config.h"
int main(){unsigned char key=0;//初始化segment_init();key_init();//主循环while(1){key=getKey();switch(key){case 1:segment_off();segment_on(0,1);break;case 2:segment_off();segment_on(0,2);break;case 3:segment_off();segment_on(0,3);break;default::segment_off();segment_on(0,_OFF_);break;}delay(100);
}

2)采用中断方式

按键触发外部中断,在中断服务程序中处理按键,并显示键值

/*exe-3*/
#include "config.h"
void eint_isr(void){key=getKey();switch(key){case 1:segment_off();segment_on(0,1);break;case 2:segment_off();segment_on(0,2);break;case 3:segment_off();segment_on(0,3);break;default::segment_off();segment_on(0,_OFF_);break;} 
}
int main(){unsigned char key=0;//初始化segment_init();key_init();VIC_init();//主循环while(1);
}

用上了中断后你就会爱上它,舍不得不用了。

3.有多种交互,多种任务

例:处理按键、显示、控制、测量电机转速

按键三个,分别为加速,减速,停止,属于输入。显示属于输出,电机属于输出,电机测试属于输入。

完成上述工作需要实现的功能很多,不用操作系统的情况下,我们一般采用中断驱动的前后台结构

1)轮询结构--超级循环结构

主函数的主循环中处理各种交互及业务,适合于简单任务

/*exe-4*/
#include "config.h"
void  handle_key(){...
}
void   handle_disp(){...
}
void   handle_motor(){...
}int main(){unsigned char key=0;//初始化segment_init();key_init();motor_init();//主循环while(1){handle_key();handle_disp();handle_motor();} 
}

2)中断驱动的前后台结构

a中断驱动结构

代码和上述exe-4相似,将按键、显示等都放在中断程序中,主循环空转

中断驱动结构的主要问题是将业务逻辑也放在了中断服务程序中,但业务逻辑比较复杂时,中断响应可能不及时,并且结构复杂。

b轮询与中断结合的结构

  • 中断作为后台,主要处理各种设备控制及交互等;
  • 业务逻辑放在主函数的主循环中,作为前台;
  • 中断服务程序中一般不进行业务处理(避免处理时间太长);
  • 前后台之间、任务之间通过变量标志进行同步。

 在各种任务中,有些任务是必然存在的:数据的的显示、按键的处理等,一般来说业务逻辑负责处理和产生数据。这时候我们可以采用所谓的MVC架构,M即模型(model),可以看作就是数据,V即视图(view),如何显示数据;而C即控制(control),也就是处理、产生数据;业务逻辑(C)处理数据、模型保存数据、视图显示数据,对于有交互的系统来说这是一种非常有效的架构,通常我们定时器中断周期性的处理显示问题、按键扫描问题。下面的结构仔细体会:

/*exe-5*/
#include "config.h"
//处理和显示的数据,相当于model
int keyValue=0;
int motor_cnt=0;//记录电机转速产生的脉冲数
int motorSpeed=0;
unsigned char pattern[]={_OFF_,_OFF_,_OFF_,_OFF_,_OFF_,_OFF_,_OFF_,_OFF_};
//同步变量
int motorCntrl_flag=0;
int speedDisp_flag=1;
int keyPress_flag=0;void sysTick_isr(void){//按键处理,获得键值--getKey()//置处理按键标志if(keyValue!=0){keyPress_flag=1;}//显示速度值----MVC中的viewsegment_disp(pattern,5);//处理电机速度if(time==1S){motorSpeed=motor_cnt*60;motor_cnt=0;speedDisp_flag=1;}
}
//下面都是业务逻辑了
void eint1_isr(void){//外部中断motor_cnt++;//测量发生中断的次数,通过一秒钟发生的中断次数计算电机转速}
void   handle_key(){switch(keyValue){case 1:motorCntrlFlag=1;break;case 2:motorCntrlFlag=2;break;case 3:motorCntrlFlag=3;break;case 4:motorCntrlFlag=4;break;}
}
void  handle_motor(){switch(motorCntrlFlag){case 1://电机加速...;break;case 2://电机减速...;break;case 3://电机停止...;break;case 4://电机启动...;break;}motorCntrlFlag=0;
}
void  handle_disp(){int speed=motorSpeed;for(i=7;i>=0;i--){pattern[i]=speed%10;speed/=10;}
}
int main(){//初始化segment_init();key_init();motor_init();//主循环while(1){//主循环中主要放业务逻辑或需要处理时间很长的任务if(keyPress==1){handle_key();keyPress=0;}if(speedDisp_flag==1){handle_disp();speedDisp_flag=0;}if(motorCntrlFlag!=0){handle_motor();motorCntrlFlag=0;}   } 
}

MVC架构实现例1-4: 

/*exe-1的改进*/
#include"config.h"
//显示的数据,相当于MVC中的model;
unsigned char pattern[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
int main(void){unsigned char i=0;//leds_init();//while(1){for(i=0;i<8;i++){//显示任务,只复杂显示,与显示的内容无关,相当于viewleds_on(pattern[i]);delay(1000);leds_off();}}return 0;
}
/*exe-2的改进*/
#include "config.h"
//数据,相当于model
unsigned char pattern[]={_OFF_,};
int main(){unsigned char key=0;//初始化segment_init();key_init();//主循环while(1){//按键处理key=getKey();switch(key){case 1:pattern[0]=1;break;case 2:pattern[0]=2;break;case 3:pattern[0]=3;break;default::pattern[0]=_OFF_;break;}//显示处理segment_off();//消影segment_on(0,pattern[0]);//在0位显示按键值dealy(100);}
}
/*exe-3的改进*/
#include "config.h"
unsigned char pattern[]={_OFF_,_OFF_,_OFF_,_OFF_,_OFF_,_OFF_,_OFF_,_OFF_,};
void sysTick_isr(void){//按键处理key=getKey();if(key)pattern[0]=key;//显示segment_disp(*pattern,10);
}
int main(){unsigned char key=0;//初始化segment_init();key_init();VIC_init();//主循环while(1);
}

从上述分析可以看出,对于嵌入式系统来说,定时器是非常重要的。但其重要性还不仅如此,我们常常会遇到系统要求在什么样的时间条件下做什么事情、又在什么样的时间条件下做什么事情,这时定时器中断服务程序可能要控制多个同步变量、或进行多种处理、不仅仅计时,为处理效率考虑,我们常常发现定时器不够用了!

从这里我们也可以看出,嵌入式系统中有三个设备非常重要:GPIO、中断、定时器!一定要掌握。

这里还有几个问题需要解决:

随着系统复杂程度的增加,同步变量的数量越来越多,如何管理这些同步变量?----消息事件机制,统一管理

同一个消息,同一个任务,在不同的状态下需要执行不同的操作,业务逻辑复杂如何解决?---有限状态机

某轮询任务复杂、处理时间较长,影响其他任务的及时处理,如何解决?----大任务拆成小任务(部分解决问题,不是长久之计);多任务操作系统

3)多任务操作系统

这是另外一个比较复杂的话题,暂略。

相关内容

热门资讯

4家银行AIC现身存储巨头股东... 近日,资本市场热度颇高的两家存储巨头长鑫科技集团股份有限公司(以下简称“长鑫科技”)、长江存储控股股...
8元无限续杯、0元看电影、老字... 城市的烟火暖意,藏在亲民的消费场景里,也藏在老地标的新生蜕变中。粤汉码头火车旁新开竹林茶馆,8元就能...
2026年水利工程新趋势,这些... 随着全球气候变化和城市化进程的加速,水利工程在保障水资源供给、改善生态环境以及提升人民生活质量中的作...
原创 发... 这几年,身边越来越多人开始换一种活法:不急着买房,不执着“上车”,反而愿意把钱拿去租一套更舒服、更体...
小红书入场Skill分发,B站... 来源:界面新闻 文丨AI价值官 星野 编辑丨美圻 过去半年,Skill 这个词在AI圈的出现...
2026年福州企业门户网站建设... 本篇将回答的核心问题 在数字化转型加速的2026年,企业门户网站建设应遵循哪些核心评估标准,以确保投...
原创 今... 今日金价:2026年5月22日注意了!黄金或现历史类似回调走势 5月22日,金市又热闹起来了,咱们看...
雷军发布YU7 GT、YU7标... 5月21日,小米人车家全生态新品发布会在北京举办,小米集团创始人、董事长兼CEO雷军正式发布小米YU...
留神峪煤矿瓦斯爆炸事故发布会:... 昨晚,山西留神峪煤矿发生瓦斯爆炸,造成重大人员伤亡。今天,当地召开新闻发布会,现场全体默哀。会上介绍...
原创 修... 修复资产负债表,日本花了几十年。 自上世纪90年代初泡沫经济破裂后,日本陷入了长达三十年的通缩螺...
2026年小红书效果化种草白皮... 2026 年小红书正式迈入种草效果化时代,这是品牌追求预算确定性回报与平台升级为消费决策、用户信任场...
连续18年获“全国文化企业30... 南都讯 记者钟欣5月21日,第二十二届中国(深圳)国际文化产业博览交易会开幕。展会期间,光明日报社和...
荣耀确认IPO未终止!开放员工... 5月22日,荣耀因股改满一年未完成IPO,按约定正式开放员工持股退出通道。据《财闻》报道称,当日16...
易方达蓝筹精选有新变动:增聘2... 《每日经济新闻》记者获悉,继景顺长城、中欧等多家基金公司旗下百亿基金经理产品调整后,易方达基金也迎来...
光储龙头,又翻倍了 去年海外光储赛道最受关注的公司,毫无疑问是阳光电源,市值重回巅峰,风光无限。 但今年一季度业绩突然失...
中企出海报告在静安发布,七成受... 来源:滚动播报 (来源:上观新闻) 昨天,在上海静安举办的澳洲会计师公会出海论坛暨澳洲注册会计师颁...
京蒙协作延链强链 科右中旗牛产... 初夏时节,走进内蒙古华阳牛业科技集团有限公司屠宰加工车间,自动化生产线高效运转。作为京蒙协作产业帮扶...
原创 中... 最近发布了一份有关新一线城市魅力的榜单。榜单按照商业资源聚集度、城市枢纽性、城市人活跃度这五个方面来...
突然,全线跳水!超16万人爆仓 来源:宁波晚报 5月23日,被视作反映市场风险偏好指标的加密货币持续跳水。 截至发稿,比特币大跌3....
基民懵了!说好的科技行情,结果... 每经记者:叶峰 每经编辑:赵云 本周股指冲高回落,沪深两市股票型ETF和跨境型ETF合计净流出729...