单片机软件构架
单片机程序相比于普通的程序有以下几个方面的不同。
- 中断程序
- 驱动程序
- 多任务
C语言发展至今已相当成熟,所以本文以C语言为基础,尽量避免汇编。
基本结构
大部分人学习单片机是从51开始的,并且好多人除了51也不会其他的单片机。
下面是一个单片机程序的基本结构。
驱动程序
由于单片机资源有限,所以在编写单片机驱动的时候不可能像Linux设备驱动那样考虑十分周全。面对具体的场景(Scenario),有时我们需要不同的决策。
可移植性
大部分的普通程序员都有一定的完美主义者倾向,这方面单片机程序员稍微好一点。
可移植性是一个大问题,单片机程序的可移植性更加是个问题。
当编写可移植程序的时候,你需要决定你的程序有多少的可移植性。有人看到这可能会觉得有些奇怪,程序要么可移植,要么不可移植,何来多少。但当你考虑到以下这些问题时候,你就可以明白我为什么要这么说。
- 你的驱动程序是只用在同一类单片机内可移植,还是要在不同的单片机之间可以移植?
- 你的驱动程序是只支持某一个特定的编译器还是想要支持所有的主流编译器?
- 你的驱动程序是否需要支持多任务操作系统?
- 你的驱动程序如何处理样硬件中断?
当你考虑到这些问题的时候,问题已经变得很复杂了。而这些,都是你在写之前需要决策的。当然,很少有人注意到这些问题,大部分人觉得不用汇编就不错了,C语言写得脏就脏一点,能跑起来就行。
如何使用
既然我们是在谈写好一个驱动程序,当然是希望一次编写,多次使用了。但当你的驱动程序用在不同的地方的时候,你是希望无需修改,通过正确的配置就可以直接使用,还是希望简单一点,把配置硬编码到代码中,每次使用都根据具体的应用修改?
不用修改直接使用当然是最好的,但有时我们会发现这几乎是不可能的。
多实例
你本来精心编写了一个驱动程序,并且运行得很好。但是突然出现了新的需求:在系统中添加一个相同的设备。然而这时你悲剧地发现,你的配置(比如引脚配置)是硬编码在程序中的。
这时,要么重写所有的驱动,要么把原来的代码复制粘贴一份,再将里面所有的全局函数和变量重命名。不管是哪种方法,都会让人感到沮丧。
多余的功能
由于单片机的资源往往是有限的,你准备如何处理不使用的功能。
说了这么多,还是动手实践吧。
所有程序都经过测试(实物或Proteus)。
比如我们现在要写一个LCD1602的程序,首先,我们来进行上面的决策。既然是说明,那我就挑选最难的。
- 支持多个编译器
- 支持多种体系结构
- 支持多实例
- 无需修改库程序,配置后直接使用
- 支持屏蔽多余的功能
- 支持多线程
你可以在LCD1602@GitHub下载完整的驱动程序代码;软件框架可以在SCM Software Architecture@GitHub下载。
首先新建两个文件:LCD1602.c和LCD1602.h(点击查看完整内容)。
在LCD1602.h
中,首先找到下面这一句:
由于我们要让这个驱动库在所有的硬件框架下可用,所以我们没有包含特定于任何芯片的头文件,比如reg51.h
。我们包含了一个software-config.h
,库的使用者有责任创建这个头文件,我们把这当作库的作者和使用者的一个约定就好。
但是,让用户去实现这个software-config.h
,就相当于把复杂性强加给了用户,所以我们在上面提供了一个框架代码。这个框架为常见的单片机提供模板,以使编写software-config.h
更加容易。
一般来说和硬件有关的配置是将某一个GPIO置位或复位,对于不同的单片机,语法一般是不一样的。对于驱动的编写者,我们可以为每一个单片机都编写一遍代码,也可以将这个问题抛给用户,用户只需对其特定的单片机编写代码。从用户方便的角度来讲,驱动的作者完成所有的工作当然是最好的,但这几乎是不现实的,因为驱动作者不可能熟悉每中单片机,更何况每种单片机在不同的编译器下语法也是不一样的。所以,只给常见单片机和和常见编译器编写算是一个折中。
LCD1602液晶显示器一般使用HD44780驱动芯片,此芯片支持8位和4位数据总线。所以我们的驱动程序也支持这两种模式。使用何种模式在software-config.h
中配置。对于这种二选一的配置,一般有两种方法。
-
两个define二选一 要求在
software-config.h
中在#define HD44780_USE_8BIT
和#define HD44780_USE_8BIT
这两个宏定义中二选一。 -
有一个默认值 当什么都没有定义的时候,默认使用8位模式。如果定义了
HD44780_USE_4BIT
,则使用4位的总线模式。
这两种方法各有优缺点,方法2相对来说比较简单,
blog comments powered by Disqus