打印

谈谈如何设计MCU的三层架构

[复制链接]
3802|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
程序的三层架构

程序员们都知道软件架构对软件的重要性,那么对于MCU的程序员来说该如何架构一个适合MCU的程序呢?MCU的程序架构有不少,比如常见的MVC架构、三层架构、以及各种无线协议的架构等等。不管用什么架构,我们的目的都是为了编写一个清晰的、可移植的、可复用、可扩展的程序。这里,主要谈谈我们MCU程序员常用的一种软件架构-三层架构。                                   
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq3F3AF51F618E47699C3A439CE720209B/3a34b5f2c798429985265145d6155f91/clipboard.png
           
                    三层架构图
你可能在不少的地方见过这个图,但是即使你知道你可能还是不知道怎么在你自己的项目中去应用这种架构。如果你想应用这种架构,那么,这篇帖子或许能给你一些帮助。
所谓无规矩不成方圆,要架构一个程序就要知道你架构一个程序时应该按照什么规则去做。这里以我自己为例说一我的架构原则。
(1)三层中改变任何一层都不能影响到其他层。
(2)只能上层调用下层,如果下层要调用上层则通过回调函数解决。
下面举一个驱动一个无源蜂鸣器的例子来谈谈怎么按照架构原则架构一个MCU程序,我们先看看驱动电路。
         
                        无源蜂鸣器驱动电路
C:/Users/Administrator/AppData/Local/YNote/data/qq3F3AF51F618E47699C3A439CE720209B/a7040c0558df4ba79ea4a898b32f649b/clipboard.png


这个蜂鸣器电路要做的事情有两个
(1)能够产生DIDI提示音,并且频率以及提示的次数可变
(2)能产生连续的固定频率的报警音


显然,要让无源蜂鸣器产生提示音以及报警音需要向驱动电路提供一定频率的信号以驱动这个蜂鸣器。一般而言,要产生一定频率的信号可以使用PWM,也可以使用一个定时器来模拟PWM,这里我采用后一种方法。


下面我们来看看怎么来按照3层架构的思想来架构这个功能。


(一)硬件层
首先硬件层要完成的事情有两个
(1) 操作硬件寄存器
(2)向驱动层提供操作硬件的接口
由于是硬件层,所以我们的操作都是围绕硬件寄存器操作。
我们定义一个fm_hal.c文件,里面定义操作硬件寄存器的函数。再定义一个fm_hal.h文件,里面声明驱动层要调用的接口以及一些宏定义。


在fm_hal.h文件中有如下声明,以备驱动层调用
#ifndef _FM_HAL_
#define _FM_HAL_


/*设置定时器计数寄存器的值,改变这个值可以改变定时器的中断频率从而产生不一样的频率输出*/
void fm_hal_timer_fre_set(unsigned char fre);
/*使能定时器,以打开声音*/
void fm_hal_timer_en(void );
/*关闭定时器,以关闭声音*/
void fm_hal_timer_dis(void );
/*蜂鸣器要用到的定时器的初始化,包括使能定时器中断等等*/
void fm_hal_timer_init(void );


#endif


在fm_hal.c中实现fm_hal.h中声明的函数功能
#include "fm_hal.h"
//定时器的硬件中断,取反蜂鸣器驱动电路的IO口,
void fm_hal_timer_interrupt(void )
{
  //取反驱动IO
}
其他函数实现在此省略了。


好了,我们完成了硬件层的架构。再说一次,该层的职责是操作硬件寄存器并向上层提供接口,不允许在硬件层中调用上层函数或者数据,当我们要换掉硬件层时则不需要改变上层,以隔离变化。


(二)驱动层
首先明确驱动层要完成的事
(1)操作硬件层提供的接口完成相关功能
(2)向应用层提供驱动接口
同样的先定义 fm_dev.h以及fm_dev.c文件
在fm_dev.h文件中声明如下函数以供应用层调用


#ifndef _FM_DEV_
#define _FM_DEV_


/*初始化驱动层*/
void fm_dev_init(void );


/*设置蜂鸣器的驱动频率,以及DIDI次数,以便应用层调用*/
void fm_dev_set_fre_count(unsigned char fre, unsigned char count);


/*管理蜂鸣器驱动,该函数由应用层调用,并需要应用层提供一个时钟,比如10ms以便控制
蜂鸣器的鸣叫时间。这里会调用硬件层的fm_hal_timer_en()以打开蜂鸣器,调用fm_hal_timer_dis()关闭蜂鸣器,停止响声*/
void fm_dev_manage(void );


#endif


在fm_dev.c定义fm_dev.h中声明的函数
void fm_dev_init(void )
{
  fm_hal_timer_init();  //调用硬件层初始化函数
}
其他函数实现在此省略。


好了,我们完成了驱动层架构,该层职责是调用硬件层并完成一定的功能。同样的,该层函数可以调用硬件层但是不能调用应用层函数以及数据。如果非要调用应用层数据或者函数的话还是建议使用回调函数隔离应用层。
我们可以看到,驱动层除了要完成硬件操作,实际上,对于应用层来说驱动层更像是一组功能的抽象。比如这个例子,驱动层完成的是要让蜂鸣器响,至于响几声以及以什么频率去响则不是驱动层关心的事。
在举一个例子,在操作GSM时,我们常常需要打电话操作,那么驱动层我们只需要设计好打电话这个操作,至于打通电话后要干什么可以通过回调函数来调用应用层要完成的事情。
有没有发现,这么一来其实提高了驱动层的重用性,如果定义好了也很容易应用到其他项目中。


(三)应用层
应用层就好说了,由于驱动层已经定义好了抽象功能,我们只需要根据我们的需求来完成提示音以及报警功能就可以。


同样的,我们也需要定义两个文件,fm_app.c以及fm_app.h
在fm_app.c
#include "fm_app.h"


//蜂鸣器模块需要的软定时时钟,以调用驱动层的fm_dev_manage()函数
void fm_app_10ms(void )
{
   fm_dev_manage();
}


fm_main()
{
  fm_dev_init();
  fm_dev_set_fre_count(100,3) //以一定频率响3声didi提示音
  if(alarm)
  {
    fm_dev_set_fre_count(200,100)  //当alarm = 1后,连续调用fm_dev_set_fre_count产生另一个频率并连续的报警音
  }
}


好了,至此为止,我们按三层架构实现了关于无源蜂鸣器驱动的程序架构。


最后,虽然架构MCU程序需要按照一些原则设计,但是原则永远都不可能通用。不同的MCU的编程环境其实有可能很不一样,有的资源很少,有的不允许你使用函数指针,有这种限制有那种限制。对于MCU的程序员来说只有合适的,没有最好的,就像我们找对象一样。嗯,写的有点多了,希望对你能起到一些作用。















相关帖子

沙发
红蛋大叔|  楼主 | 2017-11-4 15:56 | 只看该作者

使用特权

评论回复
板凳
dirtwillfly| | 2017-11-4 19:53 | 只看该作者

使用特权

评论回复
地板
zhangxiyi5277| | 2017-11-5 18:23 | 只看该作者
很不错

使用特权

评论回复
5
linqing171| | 2017-11-5 20:14 | 只看该作者
现在流行在蜂鸣器电路模块里面写一点程序。
以前三层架构的时候存储过程就都在数据库里了,参考最新的MVC6。

使用特权

评论回复
6
caijie001| | 2017-11-5 23:29 | 只看该作者
哇,赞,学习了
其实我一直调用函数,也只是知道函数已经给我们封装好了,也知道函数的底层其实是控制寄存器的。哈哈,现在有深一点的理解硬件层,驱动层,应用层

使用特权

评论回复
7
zjut_zone| | 2017-11-6 17:40 | 只看该作者
MARK

使用特权

评论回复
8
小鱼儿1045| | 2017-11-6 17:40 | 只看该作者
不错,我感觉我一直都是这么做的,底层驱动、然后应用层调用。这样底层驱动可以复制到很多项目要去,方便快捷

使用特权

评论回复
9
飞舸house| | 2017-11-6 21:28 | 只看该作者

使用特权

评论回复
10
疯子8972| | 2017-11-7 10:04 | 只看该作者
近几年ARM体系MCU的发展飞快,整体架构、运算、存储能力与 “51” 时代的MCU及“后51”时代的“AVR MSP430”相比
如同成年人与婴儿的体能对比

于是计算机软件开发体系也就越来越多被用到MCU中来
硬件驱动—->BSP—->任务调度器—->中间件(通讯协议、文件系统、图形系统、调试工具。。。)—->应用层

10元都不到的M3都能实施这样软件工程,这在几年前简直难以想象
发展这么快,不知道是电子工程师的 幸 还是 不幸啊。。。。

使用特权

评论回复
11
杨爱林林| | 2017-11-10 08:37 | 只看该作者
要想可读性、移植性、可维护性好,肯定得分层来写,现在主流的一些公司都是这么做。
比如很多应用层都是通过模型来做,再与底层进行接口对接。

使用特权

评论回复
12
lisasa| | 2017-11-11 19:31 | 只看该作者
好贴,学习中

使用特权

评论回复
13
杨爱林林| | 2017-11-13 13:29 | 只看该作者
更喜欢叫做底层、中间层、应用层,任意平台移植的话,更改底层就可以了

使用特权

评论回复
14
风过琴弦| | 2017-11-13 19:48 | 只看该作者
这个适合大一点的软件吧,大于千多行数量级,架构清晰就很有意义了

使用特权

评论回复
15
liang118038l| | 2018-2-10 11:25 | 只看该作者
疯子8972 发表于 2017-11-7 10:04
近几年ARM体系MCU的发展飞快,整体架构、运算、存储能力与 “51” 时代的MCU及“后51”时代的“AVR MSP430 ...

其实是好事,毕竟穷要过日子富也是过日子,但富一点多点资源总比穷着勒裤头好吧!

使用特权

评论回复
16
zqx1000| | 2018-4-10 18:27 | 只看该作者
kankan

使用特权

评论回复
17
qinxingtech| | 2018-5-15 09:12 | 只看该作者
楼主是厉害的人  又喜欢分享的人 好人呀

使用特权

评论回复
18
宇容创行| | 2018-5-15 10:16 | 只看该作者
大型项目,现在还在**用mcu自己写全部代码的场景已经不多了。上个操作系统,这些就都有了。

使用特权

评论回复
19
w451198326| | 2018-5-15 11:03 | 只看该作者
宇容创行 发表于 2018-5-15 10:16
大型项目,现在还在**用mcu自己写全部代码的场景已经不多了。上个操作系统,这些就都有了。 ...

去看看小企业,还是挺多的

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

25

主题

69

帖子

3

粉丝