模块化编程.docx

上传人:b****5 文档编号:7286300 上传时间:2023-01-22 格式:DOCX 页数:14 大小:39.26KB
下载 相关 举报
模块化编程.docx_第1页
第1页 / 共14页
模块化编程.docx_第2页
第2页 / 共14页
模块化编程.docx_第3页
第3页 / 共14页
模块化编程.docx_第4页
第4页 / 共14页
模块化编程.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

模块化编程.docx

《模块化编程.docx》由会员分享,可在线阅读,更多相关《模块化编程.docx(14页珍藏版)》请在冰豆网上搜索。

模块化编程.docx

模块化编程

两年前,那时的我还是个学生,虽然平时也写一些代码,但没有经过正规项目的训练,对程序的可移植性根本没有太多的考虑,当心情不错时,洋洋洒洒写个几百行,满足一下自己的编程欲,然后在朋友面前显摆一下,得到几句夸奖、赞叹后,把写好的代码扔到一个文件夹或直接删掉,随着时间的推移,这段代码就像石沉大海,再也不会有重见天日的机会。

同样是两年前,一个高手在我面前很是强调了程序的模块化编程,对我说了N多模块化编程的好处,我恁是没听进去一句,只知道他在我面前唾沫飞溅的说了关于模块化编程的很多东西。

两个月前,现在的我已经不是学生,摇身变成了一个专职代码编写器,每天面对的都是一大堆设备的驱动程序,面堆一堆驱动程序并不可怕,最让人郁闷的是每天面对的是相同的设备的不同驱动程序,我就纳闷了,相同的设备为什么需要好几套驱动呢,为什么不能用一套呢,这些还不是最郁闷的,一个设备有多套驱动就有吧,最让人郁闷的是在你写同一设备的新驱动时,以前那些驱动就是派不上什么用场,里面有N多的不知道出处的头文件,还有一堆不知道在哪定义的数据结构,把以前的这些垃圾程序都弄懂,比重新写一份都难,现在才真正体会到了以前那个高手重复强调的模块化编程的重要性。

模块化编程意味着什么?

对于设备驱动来说,难道仅仅是一个设备对应一套驱动吗?

其实这些,在上学时老师早已告诉了我们,那就是我们听的都有点厌烦的”高内聚,低偶合“,这才是模块化编程的本质。

对于设备驱动,最理想的是应该做到跟其他程序没有任何关系,整个驱动只有一两个C文件和一个头文件组成,不需要任何其他程序的支持就可以单独编译,只有做到这样,才能够重复利用代码,即使要修改,也比较容易,不需要为找一个数据结构而搞的心情很是郁闷,我们写代码的也是人,也需要一个好心情,你说呢!

第一次模块化编程2010-8-47:

49:

00

最近在使用一款工业用单片机,作为四通红外遥控器的控制IC,从编写控制程序到初步调试结束,差不多用了四天时间。

值得一提的是,着手编程之前,花了整整一个上午来分析功能要求及程序结构,已保证满足所有要求的同时,有良好的维护性。

事实上,这并不是第一次使用模块化编程。

早在离校之前(一个月前,呵呵),做过一个项目,当时把这个项目分成几个模块,每个模块有自己独立的功能,并保存在单独的文件中。

但结果是并没有看出这样做有什么特别之处,除去一点——一个程序有一大堆文件。

但这次不同,Ipromise!

这款遥控器大致有以下功能:

1、读取遥控指令(电位器,按键等)

2、读取e2prom(保存着以前的指令,断电不会消失)

3、将上述两组指令重新按要求编码,存储

4、发射

只看上述功能,思路简单,没什么特别之处,只需要按步骤编程就可以了。

然而,还有以下要求:

1、开机蜂鸣400ms,频率是1khz;指示灯闪烁,频率5~10hz。

2、系统初始化,主要是读取油门,确定一个上下限,同时在油门最小之前,屏蔽其功能。

(难以表述)简单说,避免遥控器刚打开时,油门不为零,造成飞机(此遥控器是控制飞机的,玩具~)突然起飞,失控。

3、按键每按一次蜂鸣器响40ms,1khz;若按键次数达到某一数值,蜂鸣器响400ms,2khz。

4、红外发射时载波是38khz,码长22位(不含头码)。

看完以上功能要求,再说下控制用ic的可用资源:

1、系统时钟,最大8M。

时钟周期为晶振周期2分频,指令周期等于时钟周期。

2、片内RAM32bytes

3、ROM1k

4、定时器一个

5、四路复用12位AD转换器

顺便扯一句,谁知道这款单片机都少钱一片?

到目前为止,遥控器功能要求及IC资源都有个大概的了解。

接下来,就该考虑如何实现了。

说实话,最令人恼火的就是IC只有一个定时器,然而系统中用到时间控制的地方却有好多,比如蜂鸣器需要1k或2khz载波,同时要连续蜂鸣400ms;红外发射必须有38khz载波,22位码,每个码又包括数据位和间隔位,每个位长度不定。

除此之外,遥控器还包括低压保护,电量不足时,指示灯闪烁。

........................

不是我故意把简单的事情说的复杂,而是很多时候,我想,应该把一些陌生的七七八八的部分理出一个头绪。

制定一个步骤,先做什么、后做什么,各部分如何协调。

只有这样,才能适应每天变化的生活。

........................

接着说遥控器。

我是这样做的:

首先,确定系统的功能模块。

读电位器、读按键、e2prom读写、蜂鸣器、红外发射等

其次,确定系统框架。

对整个系统来说,应该先干什么,后干什么。

再次,对模块封装,定义接口及消息。

消息是指模块执行后返回的标志位,供主程序使用。

最后,解耦合。

如多个模块复用同一个标志位,或者定时器有限的中断时间内需处理多件事情,应如何分配。

昨天初步测试了遥控器,发现蜂鸣器有问题。

本来需使用有源蜂鸣器,但拿到的却是一个无源的。

从使用的角度看,前者,只要导通,就能蜂鸣;后者,需有载波驱动。

而程序中,蜂鸣器的某个声音是按有源方式控制的。

这样以来,我需要使用定时器产生特定频率的载波,修改这段程序。

比较幸运的是,使用模块化编程。

如前所述,我已将蜂鸣部分进行了封装,现在只需找到不符合要求的代码块,在满足其接口的前提下,重写就可以了,简单十几行代码。

其它的所有程序,不需要任何改变。

这就是模块化编程的好处。

其实,还有另一个突出的特点,复用。

简单说,当我再编写另一款遥控器的时候,可以直接把红外发射部分的代码复制过去,或者只需改下接口。

写到最后,需感谢一个人。

很久很久以前(一年前吧~),我喜欢把上千行代码(汇编)写在一个文件里面,若需改动,要翻来翻去很是麻烦,更不知模块化编程为何物。

某一天,半路遇到一陌生人,他说,“模块化编程,要不要跟我学?

”“你是哪个,躲一边去!

”我这样回答。

呵呵,开玩笑~~~

一个大的DSP程序往往包含很多模块,养成良好的编程习惯,学会模块化编程,下次用到类似功能程序就能直接调用。

1、每一个C源程序都要建立一个与之名字一样的H文件(头文件),里面仅仅包括该C文件的函数的声明,其他的什么也不会有,比如变量的定义啊等等不应该有。

      --格式参见另一篇:

条件编译的#ifndef,#define,#endif的用法

2、建立一个所有文件都要共同使用的头文件,里面当然就是DSP的管脚使用的定义,还有里面放那些需要的系统的头文件,比如#include等等,把这个文件命名为global.h。

3、每个C源文件都应包含自己的头文件以及那个共同的使用的头文件(global.h),里面还放自己本文件内部使用的全局变量或者以extern定义的全局变量

4、main.c里包含所有的头文件(global.h和其他C的头文件),main.c里面的函数可以再做一个头文件,也可以直接放在文件的开头部分声明就可以了,里面一般还有中断服务程序也放在main.c里面

5、对于贯穿工程的全局变量,可以放在global.h里面,也可以用extern关键字在某个C源文件里面定义,哪个文件要使用就重复定义一下,对于全局变量用大写字母以示区别(习惯,非规定)

详细的工程文件例程在我的另一篇---2407编程模板中有下载

一个大的单片机程序往往包含很多模块,我是这样组织的

1。

每一个C源文件都要建立一个与之名字一样的H文件,里面仅仅包括该C文件的函数的声明,其他的什么也

不会有,比如变量的定义啊等等不应该有。

2。

建立一个所有的文件都要共同使用的头文件,里面当然就是单片机的管脚使用的定义,还有里面放那些需

要的KEIL系统的头文件,比如#include,#include等等,把这个文件命名为common.h,

或者干脆就叫main.h

3,每个C源文件应该包含自己的头文件以及那个共同的使用的头文件,里面还放自己本文件内部使用的全局

变量或者以extern定义的全局变量

4。

主文件main.c里面包含所有的头文件包括那个共同使用的文件,main.c里面的函数可以再做一个头文件,

也可以直接放在文件的开头部分声明就可以了,里面一般还有中断服务程序也放在main.c里面

5。

对于那些贯穿整个工程的变量,可以放在那个共同的使用的头文件里面,也可以用extern关键字在某

个C源文件里面定义,哪个文件要使用就重复定义一下

6.建立工程的时候,只要把C源文件加到工程中,把H文件直接放到相应的目录下面就可以了,不需要加到工程里面。

 

AVRc语言优秀编程风格

作为一个初学者如何具有良好的程序设计风格呢?

我想引用一个关于初学者请教编程大师的故事让读者自己去领悟。

有一位编程大师,他写非结构化的程序,一位初学者刻意模仿他,也写非结构化的程序。

当他让大师看他的进步时,大师批评了他的非结构化程序:

“对一位编程大师合适的东西未必对一个初学者同样合适,在超越结构化之前,你必须理解编程之道。

”我个人认为作为一个初学者应该踏踏实实的打好程序设计的基础,不要急功近利,舍本逐末。

我走过不少弯路,希望大家能和我一样能牢记编程大师的忠告:

“对编程大师合适的东西未必对一个初学者同样合适”。

本文所描述的优秀编程风格适合于大部分语言,文章中可能提到你不是很了解的概念,没有关系,你放心的读下去,当你使用AVR一个月之后,你什么都明白了。

AVRc语言优秀编程风格

文件结构

模块化的程序应该是有一个很好的程序结构的。

AVRC语言程序有两种用户文件,.c程序文件,.h头文件,程序中编写过程中需要在.c文件中包含.h头文件。

初学者往往出现重复包含或者头文件包含错误的问题,我当时也时常为这种错误而发愁。

下面我以我写的电机驱动例程来给大家说明一下,优秀的编程文件结构。

这个工程中有8个文件,一个说明文件,如下图:

下载程序例子电机控制案例。

我写的成型的程序的文件个数基本上都是偶数,因为每一个结构化的函数定义.c文件都会对应一个.h文件。

main.c对应config.h。

我们来看看各文件的包含关系。

下面我们看看这些文件的包含关系与内容:

[推荐的文件包含顺序与关系]

∙所有.c文件都包含了config.h文件。

如:

#include"config.h"

∙在config.h中有如下代码:

∙#include"delay.h"

∙#include"device_init.h"

#include"motor.h"

∙这样做就不容易出现错误的包含关系,为了预防万一,我们还引入了宏定义与预编译。

如下:

∙#ifndef_UNIT_H__

∙#define_UNIT_H__1

∙//100us

∙externvoidDelay100us(uint8n);

∙//1s

∙externvoidDelay1s(uint16n);//n<=6,whenn==7,itis1.

∙//1ms

∙externvoidDelay1ms(uint16n);

∙#endif

∙第一次包含本文件的时候正确编译,并且#define_UNIT_H__1,第二次包含本文件#ifndef_UNIT_H__就不再成立,跳过文件。

∙预编译还有更多的用途,比如可以根据不同的值编译不同的语句,如下:

∙//#pragmaREGPARMS

∙#ifCPU_TYPE==M128

∙#include

∙#endif

∙#ifCPU_TYPE==M64

∙#include

∙#endif

∙#ifCPU_TYPE==M32

∙#include

∙#endif

∙#ifCPU_TYPE==M16

∙#include

∙#endif

∙#ifCPU_TYPE==M8

∙#include

∙#endif

∙#include与#include"filename"的区别:

前者是包含系统目录include下的文件,后者是包含程序目录下的文件。

变量名与函数名

变量以及函数命名应该按照尽量短,按需长,具有实际意义。

可以通过下划线或者大小写结合的方法组合动词和名词组成变量函数名。

下面对比好的命名方法与不好的命名方法:

1.好的:

   Delay100us();

不好的:

   Yanshi();

2.好的:

   init_devices();

不好的:

   Chengxuchushihua();

3.好的:

   inttemp;

不好的:

   intdd;

外部调用

1.首先在模块化程序的.h文件中定义extern

2.//端口初始化

3.externvoidport_init(void);

4.

5.//T2初始化

6.voidtimer2_init(void);

7.

8.//各种参数初始化

externvoidinit_devices(void);

9.模块化程序的.c文件中定义函数,不要在模块化的程序中调用程序,及不要出现向timer2_init();这样函数的使用,因为你以后不知道你到底什么地方调用了函数,导致程序调试难度增加。

可以在定义函数的过程中调用其他函数作为函数体。

10./**************************采用timer2产生波形***********************/

11.//PWM频率=系统时钟频率/(分频系数*2*计数器上限值))

12.voidtimer2_init(void)

13.{

14.TCCR2=0x00;//stop

15.TCNT2=0x01;//setcount

16.OCR2=0x66;//setcompare

17.TCCR2=(1<

18.//占空比=高比低为:

(OCR2-0X01)/(0XFF-OCR2)OX01++++++(OCR2)__________OXFF(+表示输出高,_表示输出低)

19.//即OCR2越大,输出越大

}

20.在少数几个文件中调用函数,在main.c中调用大部分函数,在interupts.c中根据不同的中断调用服务函数。

21.voidmain(void)

22.{

23.

24./******************************************************************************/

25.//初始工作

26./******************************************************************************/

27.init_devices();

28.

29.while

(1)

30.{

31.for_ward(0);//默认速度运转正

32.Delay1s(5);//延时5s

33.motor_stop();//停止

34.Delay1s(5);//延时5s

35.back_ward(0);//默认速度运转反

36.Delay1s(5);//延时5s

37.speed_add(20);//加速

38.Delay1s(5);//延时5s

39.speed_subtract(20);//减速

40.Delay1s(5);//延时5s

41.}

42.

}

宏定义

宏定义主要用于两个地方:

1.一是用得非常多的命令或语句,利用宏将其简化。

2.#ifndefTRUE

3.#defineTRUE1

4.#endif

5.#ifndefFALSE

6.#defineFALSE0

7.#endif

8.#ifndefNULL

9.#defineNULL0

10.#endif

11.#defineMIN(a,b)((a

(a):

(b))

12.#defineMAX(a,b)((a>b)?

(a):

(b))

13.#defineABS(x)((x>)?

(x):

(-x))

14.typedefunsignedcharuint8;/*定义可移植的无符号8位整数关键字*/

15.typedefsignedcharint8;/*定义可移植的有符号8位整数关键字*/

16.typedefunsignedintuint16;/*定义可移植的无符号16位整数关键字*/

17.typedefsignedintint16;/*定义可移植的有符号16位整数关键字*/

18.typedefunsignedlonguint32;/*定义可移植的无符号32位整数关键字*/

19.typedefsignedlongint32;/*定义可移植的有符号32位整数关键字*/

20.二是利用宏定义方便的进行硬件接口操作,再程序需要修改时,只需要修改宏定义即可,而不需要满篇去找命令行,进行修改。

21.//PD4,PD5电机方向控制如果更改管脚控制电机方向,更改PORTD|=0x10即可。

22.#definemoto_en1PORTD|=0x10

23.#definemoto_en2PORTD|=0x20

24.#definemoto_uen1PORTD&=~0x10

25.#definemoto_uen2PORTD&=~0x20

26.//启动TC2定时比较和溢出

27.#defineTC2_ENTIMSK|=(<<1OCIE2)|(1<

28.//禁止TC2再定时比较和溢出

#defineTC2_DISTIMSK&=~(1<

关于注释

为了增加程序的可读性,方便合作者读动程序,或者程序作者在一段时间之后还能看懂程序,我们需要在程序中写注释。

1.在比较特殊的函数使用或者命令调用的地方加单行注释。

使用方法为:

2.Tbuf_putchar(c,RTbuf);//将数据加入到发送缓冲区并开中断

externvoidDelay1s(uint16n);//n<=6,whenn==7,itis1.

3.在模块化的函数中使用详细段落注释:

4./***********************

5.**函数名称:

Com_putchar

6.**功能描述:

从串行口输出一个字符c

7.**输 入:

c:

输出字符

8.**输出:

0:

失败1:

成功

9.**全局变量:

10.**调用模块:

11.**说明:

12.**注意:

********************/

13.在文件头上加文件名,文件用途,作者,日期等信息。

14./*********************************************************************************************************

15.**serialdriver

16.**(c)Copyright2005-2006,limaokui

17.**AllRightsReserved

18.**

19.**V1.1.0

20.**

21.**

22.**--------------文件信息--------------------------------------------------------------------------------

23.**文件名:

sio.c

24.**创建人:

李茂奎

25.**最后修改日期:

2005年7月13日

26.**描述:

serialdriver

27.**

28.**--------------历史版本信息----------------------------------------------------------------------------

29.**创建人:

李茂奎

30.**版本:

V1.00

31.**日 期:

2005年7月13日

32.**描 述:

原始版本

33.**

*********************************************************************************************************/

要清楚,注释是为了方便阅读,增强程序的可度性,不要本末倒置,不要给很简单大家都能看明白的程序加注释,不要让注释淹没了你的程序结构。

对于函数,变量等尽量使用文件名自注释的方法,及通过文件名就可以知道意思。

本文结束了,新手教程也结束了,希望我们教程能让你轻松进入AVR的世界。

原文出处:

UsingtypedeftoCurbMiscreantCode

摘要:

Typedef声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。

不管怎样,使用typedef能为代码带来意想不到的好处,通过本文你可以学习用typedef避免缺欠,从而使代码更健壮。

 

typedef声明,简称typedef,为现有类型创建一个新的名字。

比如人们常常使用typedef来编写更美观和可读的代码。

所谓美观,意指typedef能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。

本文下面将竭尽全力来揭示typedef强大功能以及如何避免一些常见的陷阱。

如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?

 

使用typedefs为现有类型创建同义字。

定义易于记忆的类型名

  typedef使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。

类型出现在所声明的变量名字中,位于''typedef''关键字右边。

例如:

typedefintsize;

此声明定义了一个int的同义字,名字为size。

注意typedef并不创建新的类型。

它仅仅为现有类型添加一个同义字。

你可以在任何需要int的上下文中使用size:

voidmeasure(size*psz);

sizearray[4];

sizelen=file.getlength();

std:

:

vectorvs;

typedef还可以掩饰符合类型,如指针和数组。

例如,你不用象下面这样重复定义有81个字符元素的数组:

charline[81];

chartext[81];

定义一个typedef,每当要用到相

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 表格类模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1