引言
当项目小组做一个相对较复杂的工程时,意味着你不再独自单干。而是和小组成员分工合作,这就要求小组成员各自负责一部分工程。比如你可能只是负责通讯或者显示这一块。
这个时候,你就应该将自己的这一块程序写成一个模块,单独调试,留出接口供其它模块调用。
最后,小组成员都将自己负责的模块写完并调试无误后,由项目组长进行组合调试。
像这些场合就要求程序必须模块化。模块化的好处是很多的,不仅仅是便于分工,它还有助于程序的调试,有利于程序结构的划分,还能增加程序的可读性和可移植性。
初学者往往搞不懂如何模块化编程,其实它是简单易学,而且又是组织良好程序结构行之有效的方法之一。
本文将先大概讲一下模块化的方法和注意事项,最后将以初学者使用最广的keil c编译器为例,给出模块化编程的详细步骤。
模块化程序设计应该理解以下概述:
模块即是一个.c 文件和一个.h 文件的结合,头文件(.h)中是对于该模块接口的声明;
这一条概括了模块化的实现方法和实质:将一个功能模块的代码单独编写成一个.c文件,然后把该模块的接口函数放在.h文件中.举例:假如你用到液晶显示,那么你可能会写一个液晶驱动模块,以实现字符、汉字和图像的现实,命名为: led_device.c,该模块的.c文件大体可以写成:
注:此处只写出这两个函数,第一个延时函数的作用范围是模块内,第二个,它是其它模块需要的。为了简化,此处并没有写出函数体. .h文件中给出模块的接口.在上面的例子中, 向lcd写入字符函数:wr_lcd (uchar dat_comm,uchar content)就是一个接口函数,因为其它模块会调用它,那么.h文件中就必须将这个函数声明为外部函数(使用extrun关键字修饰),另一个延时函数:void delay (uint us)只是在本模块中使用(本地函数,用static关键字修饰),因此它是不需要放到.h文件中的。 .h文件格式如下: 这里注意三点:
在keil 编译器中,extern这个关键字即使不声明,编译器也不会报错,且程序运行良好,但不保证使用其它编译器也如此。强烈建议加上,养成良好的编程规范。
.c文件中的函数只有其它模块使用时才会出现在.h文件中,像本地延时函数static void delay (uint us)即使出现在.h文件中也是在做无用功,因为其它模块根本不去调用它,实际上也调用不了它(static关键字的限制作用)。
注意本句最后一定要加分号”;”,相信有不少同学遇到过这个奇怪的编译器报错: error c132: 'xxxx': not in formal parameter list,这个错误其实是.h的函数声明的最后少了分号的缘故。
模块的应用:假如需要在lcd菜单模块lcd_menu.c中使用液晶驱动模块lcd_device.c中的函数void wr_lcd (uchar dat_comm,uchar content),只需在lcd菜单模块的lcd_menu.c文件中加入液晶驱动模块的头文件lcd_device.h即可。
某模块提供给其它模块调用的外部函数及数据需在.h 中文件中冠以extern 关键字声明;
这句话在上面的例子中已经有体现,即某模块提供给其它模块调用的外部函数和全局变量需在.h 中文件中冠以extern 关键字声明。 下面重点说一下全局变量的使用。使用模块化编程的一个难点(相对于新手)就是全局变量的设定,初学者往往很难想通模块与模块公用的变量是如何实现的,常规的做法就是本句提到的,在.h文件中外部数据冠以extern关键字声明。 比如上例的变量value就是一个全局变量,若是某个模块也使用这个变量,则和使用外部函数一样,只需在使用的模块.c文件中包含#include“lcd_device.h”即可。 另一种处理模块间全局变量的方法来自于嵌入式操作系统ucos-ii,这个操作系统处理全局变量的方法比较特殊,也比较难以理解,但学会之后妙用无穷,这个方法只需用在头文件中定义一次。方法为: 在定义所有全局变量(ucos-ii将所有全局变量定义在一个.h文件内)的.h头文件中: .h 文件中每个全局变量都加上了xxx_ext的前缀。xxx 代表模块的名字。 该模块的.c文件中有以下定义: 当编译器处理.c文件时,它强制xxx_ext(在相应.h文件中可以找到)为空,(因为xxx_globals已经定义)。 所以编译器给每个全局变量分配内存空间,而当编译器处理其他.c 文件时,xxx_global没有定义,xxx_ext 被定义为extern,这样用户就可以调用外部全局变量。为了说明这个概念,可以参见uc/os_ii.h,其中包括以下定义: 同时,ucos_ii.h 中有以下定义: 当编译器处理ucos_ii.c 时,它使得头文件变成如下所示,因为os_ext 被设置为空。 这样编译器就会将这些全局变量分配在内存中。当编译器处理其他.c 文件时,头文件变成了如下的样子,因为os_global没有定义,所以os_ext 被定义为extern。 在这种情况下,不产生内存分配,而任何 .c文件都可以使用这些变量。这样的就只需在 .h文件中定义一次就可以了。
模块内的函数和全局变量需在.c 文件开头冠以static 关键字声明;
这句话主要讲述了关键字static的作用。static是一个相当重要的关键字,他能对函数和变量做一些约束,而且可以传递一些信息。 比如上例在lcd驱动模块.c文件中定义的延时函数static void delay (uint us),这个函数冠以static修饰,一方面是限定了函数的作用范围只是在本模块中起作用,另一方面也给人传达这样的信息:该函数不会被其他模块调用。 下面详细说一下这个关键字的作用,在c 语言中,关键字static 有三个明显的作用:
在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
前两个都比较容易理解,最后一个作用就是刚刚举例中提到的延时函数(static void delay (uint us)),本地化函数是有相当好的作用的。
永远不要在.h 文件中定义变量!
比较一下代码: 代码一: 以上程序的结果是在模块1、2、3 中都定义了整型变量a,a 在不同的模块中对应不同的地址元,这个世界上从来不需要这样的程序。正确的做法是: 代码二: 这样如果模块1、2、3 操作a 的话,对应的是同一片内存单元。 注:一个嵌入式系统通常包括两类(注意是两类,不是两个)模块:
硬件驱动模块,一种特定硬件对应一个模块;
软件功能模块,其模块的划分应满足低偶合、高内聚的要求。
JabraEngage50专业有线耳机评测 专为客服人员设计
运算放大器学习的12个常见问题
诺思高频率、宽频带BAW滤波器开启5G新时代
莫斯科法院裁定破产债务人的加密货币资金不能用于支付债务
华润微IGBT产品逐渐成为汽车行业的新宠
模块化原理和方法 模块化的方法和注意事项
自动化的基本概念 自动化设备有哪些
德国莱茵TUV认证用于三星Galaxy M31的新电池
华为扔出三颗炸弹,不留任何喘息机会!
村田多层陶瓷电容器获日经优秀产品与服务大奖
Atmel ATA5577C 125kHz无接触RFID IDIC读写器解决方案
小米6什么时候上市?小米6白色版最新消息:小米6白色版终于上市,2999元仅限6GB+128GB版
MOSFET器件手册关键参数解读
MAX4208, MAX4209 超低失调/漂移、高精度仪表
低功耗、多通道生物电势测量模拟前端芯片AFE94x概述
惠州雷曼成功入选广东省智能制造生态合作伙伴
解析白光LED调配及LED散热陶瓷的方法
我们可以利用土壤养分速测仪来提高土壤的品质
ADC模拟电路皇冠上的明珠(1)
iOS11什么时候出?iOS11测试版如何升级/降级?iOS10.3.3发布值不值得升?32位系统手机的最后一次更新