要做到嵌入式使用的代码逻辑明晰,且防止重复的造轮子,没有好的使用架构怎样行。
假如没有好的架构,移植将会是一件很苦楚的作业。
假如没有好的架构,复用是最大的难题,无法更大极限的复用原有的代码。
假如没有好的架构,一旦驱动改了,一切的当地都要改,费时吃力且很简单犯错。
假如没有好的架构,使用层中穿插着硬件驱动层的代码,看着会是一片紊乱,逻辑不清,代码保护起来会很困难。
这儿总结下我的嵌入式程序规划思路,共享出来与咱们一起讨论,一起也欢迎提出不同定见。
现在的小朋友都爱玩搭积木的游戏,一个模块一个模块的组装起来,快速组成各种不同的模型。
现在的产品规划也很少从零开始。大都复用现有老练的模块,专心于某个拿手范畴。
我的嵌入式使用架构思路来历与此,即功用模块规划与分层。
把API分为驱动层和使用层API,而不是一切程序都调用驱动层API。(整个使用中都调用驱动层API会导致使用中驱动调用随处可见,无法移植和最大极限的复用)
先把一个使用进行功用模块区分,并对全体结构进行分层,然后规划出功用独立的各个模块(如算法模块,文件库模块,通讯库模块),在模块之上敞开公共接口。
驱动层供给出公共接口供上层调用。各个功用模块能够独立编译(如算法模块纯ANSI C,可在恣意渠道复用),或许调用驱动层接口(文件库模块调用了驱动读写Flash),总而言之,言而总归,封装出各个功用独立的可复用的功用模块。
整体分 硬件驱动层–》功用模块层–》使用接口层–》事务逻辑层–》使用层
整体结构暗示框图:
使用层,为程序的整体的运转结构,安排调用事务逻辑。能够用某种嵌入式操作体系完结几种使命 。如守时使命,卡处理使命,菜单使命,通讯使命。
事务逻辑层,如CPU卡处理,交通部卡处理,银联卡处理,M1卡处理,通讯记载上传,黑名单下载,票价参数下载等。
使用接口层,供给公共的api接口供使用接口供上层调用。这些接口也可由基层的功用模块敞开出来,使用接口层担任汇总。
功用模块层,能够封装不同的功用模块。如算法库,文件库,通讯库,银联库,向上供给使用接口层的接口,向下调用驱动接口。
硬件驱动层,由各个驱动模块组成,向上供给一致的接口。
遵从一些约好,
1.每个模块供给出的接口要一致,后续只能增,不能改本来的接口。
2.模块与模块之间彼此独立,互不影响,不能彼此调用,只能调用它基层的接口。
3.由模块构成层,层与层之间不能跨级调用。如在使用层中不能看到直接调用驱动层的代码。
4.模块中又能够持续分层,如接口层,驱动层,硬件层。
假如驱动变动了,或许换不同渠道,只需更改驱动层,使用层不受影响。
假如功用模块变动了,只需晋级功用功用模块,其他的模块不受影响,使用层也不受影响。
依照这种逻辑规划好之后,首要的作业便是在事务逻辑层。使用层则为程序的整体流程和结构,首要调用事务逻辑层完结不同的功用。
咱们现在的代码结构,基本是按这个思路来的。
硬件驱动层–》功用模块层–》使用接口层–》事务逻辑层–》使用层。
看看以下两种风格的代码,你更喜爱哪个。
另一种风格:
同样是保存参数,非要拆成 AlgCRC16 ,WritePraFlash( (unsigned char *)&NetPra , NETPRA_ADDR , sizeof(_NetPra) )两步吗?
还有AH_Para_Verify这个,在使用层中真是剩余啊,检测失利又从Flash读取。关于参数,一开机就应该检测合法性了。
已然都是要保存参数,就应该做个封装,如上图所示,把体系用到的不同参数做个规划。使用层调用APP_Open_UseFile 或许APP_Read_UseFile,
而不是直接的去读写Flash。
来看看大名鼎鼎的谷歌的android架构,尽管很杂乱,但从框图上看,也像是搭积木,各个功用模块独立,层次分明。最低层建立在linux Kernel基础上,然后是各个组件库libraries,再往上是使用结构和使用。
以NC_FileLib,文件库模块为例,假如要用在其他渠道,如EH0918手持机设备,只需要移植几个硬件层接口即可。
NC_FileSys文件库,跟硬件相关的接口在Hook文件夹,
void HW_FRAM_Init( void )
unsigned int HW_FRAM_Read( unsigned int addr,unsigned
int size,unsigned char *buffer)
unsigned int HW_FRAM_Write( unsigned int addr,
unsigned int size,unsigned char *buffer )
//擦除FLASH一页 (FLASH擦除的最小单元)
unsigned int HW_Flash_PageErase( unsigned int page )
unsigned int HW_Flash_Read( unsigned int addr,
unsigned int size, unsigned char *buffer )
unsigned int HW_Flash_NotEraseWrite( unsigned
int addr, unsigned int size, unsigned char *buffer )
//擦除FLASH一页 (FLASH擦除的最小单元)
unsigned int HW_Flash_PageErase( unsigned int page )
依照以上模块化规划思维,很简单完结一模仿pos机。
以开发一个智能pos使用为例:
一个智能pos涉及到的功用模块有:
读写卡功用,保存与读取消费记载,查找保存黑名单,界面显现,菜单显现,通讯下载参数上传记载等。
以下为移植功用模块到电脑上,自己做的一个模仿Pos东西:
在电脑上完结一模仿pos(仅仅功用上的完结,完结刷卡消费,记载存储,记载上传,黑名单,票价下载等功用。界面为Dos窗口。后续假如用QT把界面也做出来,便是一功用完全的模仿POS机,不过得把荒废多年的C++从头捡起来了。能够持续完善做一个上位机模仿pos,改动编译器在上位机仿真调试并穿插编译后运转在实在POS上)。
用到的功用模块有 文件存储模块,卡处理模块,算法模块,银联库模块。我把这些模块移植到电脑上。
关于卡处理模块的完结,因为电脑上没读卡头,所以用外接读卡器。把读卡器串口接电脑上。电脑上做一读写卡服务,供给TCP接口的读写卡接口。
移植文件库,嵌入式程序中是操作的flash,在电脑上把文件库中用到的接口用读写文件的方式替换。
移植算法库,算法库都是c写的,直接用gcc在windows渠道从头编译即可。