模块化编程Word文件下载.docx
《模块化编程Word文件下载.docx》由会员分享,可在线阅读,更多相关《模块化编程Word文件下载.docx(14页珍藏版)》请在冰豆网上搜索。
到目前为止,遥控器功能要求及IC资源都有个大概的了解。
接下来,就该考虑如何实现了。
说实话,最令人恼火的就是IC只有一个定时器,然而系统中用到时间控制的地方却有好多,比如蜂鸣器需要1k或2khz载波,同时要连续蜂鸣400ms;
红外发射必须有38khz载波,22位码,每个码又包括数据位和间隔位,每个位长度不定。
除此之外,遥控器还包括低压保护,电量不足时,指示灯闪烁。
........................
不是我故意把简单的事情说的复杂,而是很多时候,我想,应该把一些陌生的七七八八的部分理出一个头绪。
制定一个步骤,先做什么、后做什么,各部分如何协调。
只有这样,才能适应每天变化的生活。
接着说遥控器。
我是这样做的:
首先,确定系统的功能模块。
读电位器、读按键、e2prom读写、蜂鸣器、红外发射等
其次,确定系统框架。
对整个系统来说,应该先干什么,后干什么。
再次,对模块封装,定义接口及消息。
消息是指模块执行后返回的标志位,供主程序使用。
最后,解耦合。
如多个模块复用同一个标志位,或者定时器有限的中断时间内需处理多件事情,应如何分配。
昨天初步测试了遥控器,发现蜂鸣器有问题。
本来需使用有源蜂鸣器,但拿到的却是一个无源的。
从使用的角度看,前者,只要导通,就能蜂鸣;
后者,需有载波驱动。
而程序中,蜂鸣器的某个声音是按有源方式控制的。
这样以来,我需要使用定时器产生特定频率的载波,修改这段程序。
比较幸运的是,使用模块化编程。
如前所述,我已将蜂鸣部分进行了封装,现在只需找到不符合要求的代码块,在满足其接口的前提下,重写就可以了,简单十几行代码。
其它的所有程序,不需要任何改变。
这就是模块化编程的好处。
其实,还有另一个突出的特点,复用。
简单说,当我再编写另一款遥控器的时候,可以直接把红外发射部分的代码复制过去,或者只需改下接口。
写到最后,需感谢一个人。
很久很久以前(一年前吧~),我喜欢把上千行代码(汇编)写在一个文件里面,若需改动,要翻来翻去很是麻烦,更不知模块化编程为何物。
某一天,半路遇到一陌生人,他说,“模块化编程,要不要跟我学?
”“你是哪个,躲一边去!
”我这样回答。
呵呵,开玩笑~~~
一个大的DSP程序往往包含很多模块,养成良好的编程习惯,学会模块化编程,下次用到类似功能程序就能直接调用。
。
1、每一个C源程序都要建立一个与之名字一样的H文件(头文件),里面仅仅包括该C文件的函数的声明,其他的什么也不会有,比如变量的定义啊等等不应该有。
--格式参见另一篇:
条件编译的#ifndef,#define,#endif的用法
2、建立一个所有文件都要共同使用的头文件,里面当然就是DSP的管脚使用的定义,还有里面放那些需要的系统的头文件,比如#include<
absacc.h>
等等,把这个文件命名为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<
reg52.h>
#include<
等等,把这个文件命名为common.h,
或者干脆就叫main.h
3,每个C源文件应该包含自己的头文件以及那个共同的使用的头文件,里面还放自己本文件内部使用的全局
变量或者以extern定义的全局变量
4。
主文件main.c里面包含所有的头文件包括那个共同使用的文件,main.c里面的函数可以再做一个头文件,
也可以直接放在文件的开头部分声明就可以了,里面一般还有中断服务程序也放在main.c里面
5。
对于那些贯穿整个工程的变量,可以放在那个共同的使用的头文件里面,也可以用extern关键字在某
个C源文件里面定义,哪个文件要使用就重复定义一下
6.建立工程的时候,只要把C源文件加到工程中,把H文件直接放到相应的目录下面就可以了,不需要加到工程里面。
AVRc语言优秀编程风格
作为一个初学者如何具有良好的程序设计风格呢?
我想引用一个关于初学者请教编程大师的故事让读者自己去领悟。
有一位编程大师,他写非结构化的程序,一位初学者刻意模仿他,也写非结构化的程序。
当他让大师看他的进步时,大师批评了他的非结构化程序:
“对一位编程大师合适的东西未必对一个初学者同样合适,在超越结构化之前,你必须理解编程之道。
”我个人认为作为一个初学者应该踏踏实实的打好程序设计的基础,不要急功近利,舍本逐末。
我走过不少弯路,希望大家能和我一样能牢记编程大师的忠告:
“对编程大师合适的东西未必对一个初学者同样合适”。
本文所描述的优秀编程风格适合于大部分语言,文章中可能提到你不是很了解的概念,没有关系,你放心的读下去,当你使用AVR一个月之后,你什么都明白了。
文件结构
模块化的程序应该是有一个很好的程序结构的。
AVRC语言程序有两种用户文件,.c程序文件,.h头文件,程序中编写过程中需要在.c文件中包含.h头文件。
初学者往往出现重复包含或者头文件包含错误的问题,我当时也时常为这种错误而发愁。
下面我以我写的电机驱动例程来给大家说明一下,优秀的编程文件结构。
这个工程中有8个文件,一个说明文件,如下图:
下载程序例子电机控制案例。
我写的成型的程序的文件个数基本上都是偶数,因为每一个结构化的函数定义.c文件都会对应一个.h文件。
main.c对应config.h。
我们来看看各文件的包含关系。
下面我们看看这些文件的包含关系与内容:
[推荐的文件包含顺序与关系]
∙所有.c文件都包含了config.h文件。
如:
#include"
config.h"
∙在config.h中有如下代码:
∙#include"
delay.h"
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<
iom128v.h>
∙#ifCPU_TYPE==M64
iom64v.h>
∙#ifCPU_TYPE==M32
iom32v.h>
∙#ifCPU_TYPE==M16
iom16v.h>
∙#ifCPU_TYPE==M8
iom8v.h>
∙#include<
filename>
与#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<
<
WGM20)|(1<
WGM21)|(1<
COM21)|0x06;
//starttimer快速pwm模式,匹配清零,溢出置位256分频
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);
35.back_ward(0);
//默认速度运转反
36.Delay1s(5);
37.speed_add(20);
//加速
38.Delay1s(5);
39.speed_subtract(20);
//减速
40.Delay1s(5);
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<
b)?
(a):
(b))
12.#defineMAX(a,b)((a>
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<
TOIE2)
28.//禁止TC2再定时比较和溢出
#defineTC2_DISTIMSK&
=~(1<
OCIE2)|(1<
关于注释
为了增加程序的可读性,方便合作者读动程序,或者程序作者在一段时间之后还能看懂程序,我们需要在程序中写注释。
1.在比较特殊的函数使用或者命令调用的地方加单行注释。
使用方法为:
2.Tbuf_putchar(c,RTbuf);
//将数据加入到发送缓冲区并开中断
externvoidDelay1s(uint16n);
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.**日 期:
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:
:
vector<
size>
vs;
typedef还可以掩饰符合类型,如指针和数组。
例如,你不用象下面这样重复定义有81个字符元素的数组:
charline[81];
chartext[81];
定义一个typedef,每当要用到相