收藏 分享(赏)

EMS-模块化编程的分层设计经验.doc

上传人:yjrm16270 文档编号:6914379 上传时间:2019-04-27 格式:DOC 页数:37 大小:522.50KB
下载 相关 举报
EMS-模块化编程的分层设计经验.doc_第1页
第1页 / 共37页
EMS-模块化编程的分层设计经验.doc_第2页
第2页 / 共37页
EMS-模块化编程的分层设计经验.doc_第3页
第3页 / 共37页
EMS-模块化编程的分层设计经验.doc_第4页
第4页 / 共37页
EMS-模块化编程的分层设计经验.doc_第5页
第5页 / 共37页
点击查看更多>>
资源描述

1、模块化编程的分层设计经验操作要点:1、每一层直接对下一层操作,尽量避免交叉调用或越级调用2、某些器件会把硬件驱动层合并成一个文件时,则归于较高的层3、相同功能的外部函数尽量一致,尽量保证通用性4、对于初次编程的模块,要严格保证中间各层的正确性好处:1、对于后期维护扩展,只需修改应用层和物理层,根据需要扩展功能层2、一个新项目只需把要用到的文件加入工程,简单修改调试就出来了3、随着模块的不断积累,新的项目将越来越容易完成,后期的维护扩展也变得非常简单了4、对于 C 语言编程,只需简单修改物理层就可完成不同单片机间的移植一般分为以下几层:-应用层面向用户软| 件|-协议层现成的协议栈、软件包、标准

2、库,大多是移植,不自己写,如 FAT、TCPIP、OS、GAME 等相| 关| -功能层实现器件无关性,实现器件的各种功能扩展和器件通用性处理,如 LCD 的线、圆、矩形等功能,如EEPROM 的块写,自己的 print硬| 件|-器件层实现硬件无关性,保证 IO 无关性,只提供器件的基本功能,如字节读写、点驱| 动 -物理层IO 相关,直接操作硬件,实现硬件连接的多种方案对应文件举例 1:-应用层面向用户的主程序软| 件|-协议层如 FAT、TCPIP、OS 等现成的协议栈、算法、游戏等相| 关| -功能层如文件 lcd.c;led.c;eeprom.c;time.c;ir.c;keybor

3、d.c;harddisk.c;引出 LCD 的线、圆、矩形、填充等功能硬| 件|-器件层文件 lcd61202.c;lcd1520.c;lcd6963.c;lcd133x.c;lcd44780.c;lcd162x.c;lcd856x.c 或者lcd1602.c;lcd12864.c;lcd320240.c 等,引出基本的初始化、定位、写点、写字节函数驱| 动 -物理层文件 lcd61202_io.c;lcd61202_bus.c;引出器件的基本读写函数对应文件应用举例 2:-应用层面向用户的主程序软| 件|-协议层如 FAT、TCPIP、OS 等现成的协议栈、算法、游戏等相| 关| -功能层如

4、文件 lcd.c;led.c;eeprom.c;time.c;ir.c;keybord.c;harddisk.c;如 EEPROM 的块写统一化硬| 件|-器件层文件 ee24xx.c;ee93xx.c;ee_sdcard.c;ee29xx.c;ee28f.c;ee39xx.c;等驱| 动 -物理层文件 bus_i2c.c;bus_spi.c 等一个大的单片机程序往往包含很多模块,我是这样组织的1。每一个 C 源文件都要建立一个与之名字一样的 H 文件,里面仅仅包括该 C 文件的函数的声明,其他的什么也不会有,比如变量的定义啊等等不应该有。2。建立一个所有的文件都要共同使用的头文件,里面当然就

5、是单片机的管脚使用的定义,还有里面放那些需要的KEIL 系统的头文件,比如 #include,#include等等,把这个文件命名为 common.h,或者干脆就叫 main.h3,每个 C 源文件应该包含自己的头文件以及那个共同的使用的头文件,里面还放自己本文件内部使用的全局变量或者以 extern 定义的全局变量4。主文件 main.c 里面包含所有的头文件包括那个共同使用的文件,main.c 里面的函数可以再做一个头文件,也可以直接放在文件的开头部分声明就可以了,里面一般还有中断服务程序也放在 main.c 里面5。对于那些贯穿整个工程的变量,可以放在那个共同的使用的头文件里面,也可以用

6、 extern 关键字在某个 C 源文件里面定义,哪个文件要使用就重复定义一下6.建立工程的时候,只要把 C 源文件加到工程中,把 H 文件直接放到相应的目录下面就可以了,不需要加到工程里。单片机系统模块化编程的一些想法51 核类型单片机是目前应用较为广泛的一款 MCU,编写单片机程序常常成为嵌入式软件开发软件入门级的训练。一般而言,51 程序代码量少,考虑模块化程序相对较少。一种常规做法就是主程序采用 while 循环,再者通过中断中设置一些标志位;笔者在 51 单片机程序开发过程,发现公司的单片机程序更新很快,基本每个人都要修改一点,一段时间后原有代码想法都很难找到。还有一种是移植操作系统

7、后,然后进行代码规范化,比如移植 UCOS-ii 等嵌入式操作系统,但是往往代码量增加很快,对存储容量本来就少的 51 单片机有较大的压力。51 模块化程序设计的最重要问题,笔者认为就是找到一种合理的程序结构,而且它能胜任实际的 51 单片机程序开发。考虑到文中前面提到的问题,笔者主要针对第一种主程序 while 循环结构进行修改。首先增加任务结构体定义,其中函数指针 pFun 指向实际的任务函数,nDelay 表示延时时间,以 ms 为单位,以下涉及时间都一样的。而nRunme 表示运行的次数,nPeriod 表示运行的时间周期。struct _TASK_DEFINE void (code

8、*pTask)(void); / 指向实际任务函数UINT16 nDelay; / 延时时间UBYTE8 nRunme; / 运行次数UINT16 nPeriod; / 运行周期 S_TASK , *pS_TASK;系统中设定全局的任务列表变量 S_TASK SCH_TASK_GTASK_TOTAL_NUM; 其中 TASK_TOTAL_NUM为系统设定的任务最大数量。在进入 while 主循环前,需要添加相应的任务,采用函数 SCH_ADD_TASK(),后面再详细阐述。系统的主循环采用遍历的方法,实现实际任务的运行。while(1)SCH_DISPATCH_TASK(); /* 遍历实现任

9、务切换 */*51 系统进入空闲模式,定时中断能唤醒 */SCH_Goto_Sleep(); SCH_DISPATCH_TASK 遍历函数的主要实现代码如下,UBYTE nIndex;for(nIndex=0; nIndex nRunme 0)(*(SCH_TASK_G+nIndex)-pTask)(); / 运行实际的任务(SCH_TASK_G+nIndex)- nRunme-; / 运行次数减 1if(SCH_TASK_G+nIndex)- nPeriod = 0) /* 执行一次型任务后删除之 */ SCH_DEL_TASK(nIndex); 定时器实现 1ms 定时中断,在中断中进行任

10、务刷新( SCH_UPDATE_TASK 函数),这也是实现系统结构关键一步。定时器可以采用定时 Timer0,有的 52 型单片机也可以采用定时器 Timer2,总之实现 1ms 时间的定时中断。SCH_UPDATE_TASK()的主要实现代码如下UBYTE8 nIndex;for(nIndex=0; nIndex nDelay = 0)(SCH_TASK_G+nIndex)- nRunme+; / 运行次数加 1/* 获得实际的时间周期 */if(SCH_TASK_G+nIndex)- nPeriod 0)(SCH_TASK_G+nIndex)- nDelay = (SCH_TASK_G+

11、nIndex)- nPeriod; else (SCH_TASK_G+nIndex)- nDelay-; 在进行主程序 while 循环前,必须要添加相应的任务函数 SCH_ADD_TASK,其主要的实现代码如下:UBYTE8 SCH_ADD_TASK( void (code *pFun)(void),const UINT16 tDelay,const UINT16 tPeriod)UBYTE8 nIndex;nIndex=0;while( (SCH_TASK_G+nIndex)- pTask != NULL ) SCH_TASK_GnIndex-nDelay = tDelay;SCH_TAS

12、K_GnIndex-nRunme = 0;SCH_TASK_GnIndex-nPeriod = tPeriod;return nIndex;主体代码基本实现,51 系统开发主要的工作就是增加一个任务函数,在实际的任务实现相应的功能,这样构成的单片机系统就比较好控制,维护代码也很简单。当然还有就是任务函数的执行时间必须控制 1ms 以内,即是每 1ms 时间标度中要执行的任务时间不超过 1ms。如果执行时间较长的任务,总有一些办法对其划分为较小的若干个小任务。笔者在实际开发中也设想过一个问题,假定 A 任务每 2ms 执行一次,执行需要的时间为 0.5ms;而 B 任务每 4ms 执行一次,执行

13、所需的时间为 0.5ms。如果两个任务运行在同一个时标(1ms )中,就可能导致运行单个时标运行任务超过 1ms 的限制;由于 4ms 的间隔也是 2ms 延时的整数倍关系,执行完全有可能。一种常见的解决方法是加大定时中断时间的时标,把 1ms 修改成 2ms,但是同时系统的时间敏感度减少了。笔者想到一种方法,设置 两个任务结构体中延时 nDelay 不同,A 任务延时初值 0,而 B 任务延时初值为 1;这样实际 A 任务执行时间标度为 2*n, 而 B 为4*m+1(n, m 为正整数),而 2*n != 4*m+1(奇偶数),从而使 A 与 B 任务永远不可能同时在一个时标(1ms )中

14、执行。以上是在 51 单片机开发模块化设计的一些想法,在几个实际项目开发也得到较好的应用,当然可能还会有一些没有考虑的问题,欢迎各位提出更好的建议!模块化设计原则:高内聚第一步:创建头文件(源文件与头文件同名如 delay.c 与 delay.h)第二步:防止重复包含处理在.h 文件里加入#ifndefXXXX#defineXXXX.#endif例如:#ifndef_DELAY_H_#define_DELAY_H_.#endif第三步: 代码封装(内部调用【.h 封装外部调用的部分】 )封装成函数或者宏定义以便提高可读性和可修改文件,尽量少用或者不用全局变量第四步:使用源文件.c 文件添加到文

15、件中模块化编程实例:delay.h 文件#ifndef _DELAY_H_#define _DELAY_H_#define uchar unsingned char #define uint unsigned intvoid delay50us(uint t);void delay50ms(uint t);#endifdelay.c 文件#include#include“模块化编程实例.h“void delayus(uint t)/延时函数 uint j;for(;t0;t-)for(j=6245;j0;j-);void delayms(uint t)/延时函数 uint j;for(;t0;

16、t-)for(j=6245;j0;j-);数码管.h 文件#ifndef _DELAY_H_#define _DELAY_H_#define“模块化编程实例 .h“ #define uint unsigned intvoid dispaytable(uchar *p);void dispayt(uchar num0,uchar num1,uchar num2,uchar num3,uchar num4,uchar num5,uchar num6,uchar num7,);#endif数码管.c 文件#include“数码管.h“#include“模块化编程实例.h“unsigned char

17、code smg_du=0xfe,0xfd,0xfb,0xf7,0xef,0xbf,0x7f;unsigned char code smg_we=0x00,0x00,0x3e,0x41,0x41,0x41,0x3e,0x00;void display_table(uchar *p)uchar i;foe(i=0;i#include“模块化编程实例.h“sbit rst=P36;unsigned char table=2,3,4,5,6,7,8,9;void main()rst=0;while(1) display_tale(table);C 语言高效编程编写高效简洁的 C 语言代码,是许多软件

18、工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。第 1 招:以空间换时间计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第 1 招以空间换时间。例如:字符串的赋值。方法 A,通常的办法:#define LEN 32char string1 LEN;memset (string1,0,LEN);strcpy (string1,“This is a example!”);方法 B:const char string2LEN =“This is a example!”;char * cp;cp = st

19、ring2 ; (使用的时候可以直接用指针来操作。)从上面的例子可以看出,A 和 B 的效率是不能比的。在同样的存储空间下,B 直接使用指针就可以操作了,而A 需要调用两个字符函数才能完成。 B 的缺点在于灵活性没有 A 好。在需要频繁更改一个字符串内容的时候,A 具有更好的灵活性;如果采用方法 B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。该招数的变招使用宏函数而不是函数。举例如下:方法 C:#define bwMCDR2_ADDRESS 4#define bsMCDR2_ADDRESS 17int

20、BIT_MASK(int _bf) return (1U 3;J = 456 - (456 4 #include“co-os.h”void tskfunc1(int argc,void *argv);void tskfunc2(int argc,void *argv);void subfunc(void);volatile int cnt,test;int main(void)int i;init_coos(400);creat_tsk(tskfunc1,12,NULL,400);creat_tsk(tskfunc2,0,NULL,400);i=0;while(1)WAITFOR(cnt= =

21、8);while(i+argc);test=0x55;/*使用函数调用在子程序中测试 WAITFOR*/subfunc();while(i+15);cnt=0;void subfunc(void)int i;WAITFOR(cnt/*选择 X86_VC6,X86_BC5,AVR_GCC 或M68H_SDS.*/#define X86_VC6#define MAX_TSK 10typedef struct void (*entry)(int argc,void *argv);jmp_buf env;int argc;void *argv;TVB;extern TCB tcbMAX_TSK; -e

22、xtern int task_num,tskid;void init_coos(int mainstk);int creat_tsk(void(*entry)(int argc,void *argv),int argc,void *argv,int stksize);#define WAITFOR(condition)dosetjmp(tcbtskid.env);if(!(condition)tskid+;if(tskid=task_num)tskid=0;longijmp(tcbtskid.env,1);while(0)-(2)co-os.c 文件清单 #include “co-os.h“#

23、if defined(X86_VC6)|defined(X86_BC5)#define SAVE_SP(p) _asm mov p,esp#define RESTORE_SP(p) _asm mov esp,p#elif defined(AVR_GCC)#include#define SAVE_SP(p) p=(int*)SP#define RESTORE_SP(p) SP=(int)p#elif defined(M68K_SDS)#define SAVE_SP(p) asm(“MOVE.L A7,“#p“)#define RESTORE_SP(p) asm(“MOVE.L “#p“,A7“)

24、#endifTCB tcbMAX_TSK;Int task_num=1;Int tskid;Static int stktop,oldsp;Void init_coos(int mainstk)SAVE_SP(stktop);stktop=stktop+sizeof(void(*)(void)/sizeof(int)-(mainstk+sizeof(int)-1)/sizeof(int);int creat_tsk(void(*entry)(int argc,void *argv),int argc,void *argv,int stksize)if(task_num=MAX_TSK)teru

25、rn-1;SAVE_SP(oldsp);RESTORE_SP(stktop);If(!setjmp(tcbtask_num.env)RESTORE_SP(oldsp);tcbtask_num.entry=entry;tcbtask_num.argc=argc;tcbtask_num.argv=argv;task_num+;stktop-=(stksize+sizeof(int)-1)/sizeof(int);else tcbtskid.entry(tcbtskid.argc,tcbtskid.argv);return 0;3 代码说明任务代码通过执行 setjmp 设置本任务下次查询时的返回点

26、,然后在等待条件放弃掉 CPU 跳转到下一任务的返回点处执行。如此周而复始,让各任务都获得轮转运行的机会,也要求各任务都需要主动通过等待条件的方式放弃掉 CPU。系统中除了中断服务程序之外,所有任务都是平等的,都应该遵循同样的规则和其它任务一起协作运行。基本系统中没有设计杀死任务的调用,这要求各任务都应当设计成某种形式的无限循环。任务中等待的条件可以是任务复杂的表达式或都函数调用,也可以是中断服务程序设置的全局变量(注意加 volatile定义)。一般在任务执行时会让下次等待的条件不再满足,避免某个任务一直霸占 CPU 将系统饿死。在嵌入式软件中还经常会遇到任务定时启动和超时等待在 I/O 操

27、作上,在我们的系统中可以维护一个时间计数器,只需在适当的地方记录时刻,然后在任务查询条件中判断当前计数器和记录时刻之间的差值就可以了。内核实现的代码则相当简法。由于主要的保护和恢复任务现场的工作都由 C 语言标准库 setjmp 实现了,我们就只需要操纵一下堆栈指针防止不同的任务使用了重叠的局部环境,这个工作在初始化和创建任务的时候通过预定义的两个宏来实现。在 init_coos 函数中,记录了主任务(main 函数)保留 mainstk 字节堆栈后的新栈顶位置(stktop),然后在每次 creat_task 时都根据要求为每个任务保留 stksize 字节的堆栈并重新计算下一个 stkto

28、p。在 creat_task 函数中利用了 setjmp 函数的返回值(直接返回时为 0,longjmp 跳转返回时非 0),使得一方面 creat_task 能正常回到调用者,又让下次轮转到新任务时能够找到创建时的入口。co-os.h 中定义的 WAITFOR 使用了一个 dowhile(0)实现多语句宏的 C 语言小技巧,这样能保证在任何情况下WAITFOR 都可以如单条语句一样在源程序中使用,需要担心多了或者少了大括弧破坏 if/else 匹配之类的问题,并且,所有的编译器都会优化掉这个假循环。为了尽量使程序简单并说明问题,以上代码中没有考虑中断相关的数据保护问题。实际运行的系统中,如果

29、首先写堆栈指针不能一步完成(如 AVR 这样的 8 位机),那么,在写操作正在进行的时候绝对不能允许中断;另外,在任务中查询的条件如果和中断有关,那么也必须考虑数据的完整性。4 性能分析所有的协作式多任务系统中任务切换时间都和用户代码(是否长期占用 CPU)、就绪时刻有关,比标准系统略差的是,我们的简单系统中不支持任务优先级,并且完成一轮查询调度的时间还和任务数目有关。这也是为了达到简单和可移植性目标而不得已作出的牺牲。在各任务间切换查询条件通过 C 语言标准库函数 longjmp 实现,一般来讲 longjmp 函数要从内存中恢复大部分 CPU 寄存器,执行它也需要若干条指令的时间。为了面向

30、嵌入式系统应用,任务控制块(TCB)采用静态数组来实现,这样要求预先确定系统的最大任务数( co-os.h 中的 MAX_TSK)。如果需要,也可以通过环波链表来动态管理任务控制块(TCB),这时可以简单实现任务的动态创建和删除,并且通过指针来访问 TCB 也要比通过下标( tskid)略快一点。在某些情况下,如果在中断返回后需要执行某关键任务,可以考虑通过设置“高级中断”的方法来实现。具体地讲,就是在中断返回前改变返回地址到某函数入口(“高级中断服务程序”),同时保留原返回地址到堆栈中,这样在“高级中断服务程序”完成后执行 return 就又回到了正常的多任务查询流程。使用 “高级中断”时要

31、注意现场保护的衔接,并且这种技巧显然和 CPU 和体系结构有关。5 结论setjmp 是标准 C 语言中用于远程跳转的库函数,利用它可方便实现一个简单易移植的协作式多任务系统。该系统功能完备、编程简单、易于学习,适合一些中小规模的嵌入式软件使用;并且,以此为基础,还可以用一些与平台相关的编程技巧提高其实时性和灵活性。嵌入式程序设计中 C 代码的优化1 引言嵌入式计算机系统是指完成特定功能的计算机系统,它具有软件代码小,自动化程度高,响应速度快等特点。特别适合于要求实时和多任务的应用体系。由于嵌入式系统受时间、空间和运行环境的严格限制,使得嵌入式系统软件的开发变得非常困难,为了开发出高性能的嵌入

32、式系统,开发语言的选择十分关键。目前,在嵌入式系统开发过程中可使用的语言很多,其中 C 和 C+应用得最广泛。C+ 在面向对象、结构化等方面对 C 进行了改进,但在程序代码容量、执行速度等方面性能不如 C 语言。C 语言既有低级语言的直接控制硬件的能力,又有高级语言的灵活性,在嵌入式系统中得到了最广泛的应用。在嵌入式的系统开发中,出于对低价产品的需求,硬件的设计者需要提供刚好足够的存储器和完成工作的处理能力。所以在嵌入式软件设计的最后一个阶段变成了对代码的优化阶段。经过本人在嵌入式系统设计过程中的实践,下面介绍几种简单且行之有效的 C/C+代码的优化方法。考虑到读者已经学过了 C/C+的原理,

33、有一些已经是经验丰富的编程员,本文的叙述将尽可能深入浅出。2 变量的处理开始编程时,一般首先遇到的是初始化的问题。建议使用英文单词的缩写代表变量,比如代表高度指针的变量可取名 highpoint 或简写为 hiptr。用缩写减少字母可加快程序的运行速度并减少占用的内存。另外变量类型选取的范围越小运行速度越快占用的内存越少。比如 int 型变量比 long 型变量运行速度越快,但变量类型选取的范围越小算术运算时越容易产生溢出错误。在声明局部变量的时候可以使用寄存器变量 register 关键字。这就使得编译器把变量放入寄存器中,而不是在堆栈中,合理使用这种方法可以提高执行速度。函数调用越频繁,提

34、高代码的效率越明显。当使用变量作为函数返回值时,当变量值很大时,将花费很多的复制时间。参数返回没有变量返回的复制时间的问题,在时间上要快。但参数返回破坏了参数值,在安全性能上和函数的重复使用性能上差。建议在编程时,小的常量返回可用变量,小的变量返回用指针,大的常量返回可用参数或静态方法。另外,使用全局变量比函数传递参数更加有效率。这样做去除了函数调用参数入栈和函数完成后参数出栈所需的时间。然而,决定使用全局变量会影响程序的模块化和重入,故要慎重使用。3 数组的处理(1)数组在使用前需要对其先初始化。一种提高速度的方法是数组的范围在初始化时空着不添,这样程序运行的速度会更快。以函数引用数组为例:

35、void p(int a63)for(int j=0;j6);两种编程方法在逻辑上完全相同,因为但通过移位进行 2 的整数次幂的乘除法要比直接进行乘除法运算速度快,所以后一种编程所需的运算时间短。(3) int d=a/cb/c; 与之对应的int d=(ab)*(1/c)在不改变表达式含义的情况下,因为乘法运算比除法运算的度快,所以后一个表达式运算的速度快。(4)如果精度许可,运算数据的位数选取应尽量短。当运算的数据超过 CPU 一次所能处理的最大数据时,CPU 在系统管理下一般会对位数超过的大数自动分割处理,但这会使运算速度减慢。比如在 16 位机中,如果两相加数的位数都小于或等于 15

36、位,两数的运算在 CPU 中都可一次完成。当两相加数的位数都为 17 位时,两数的运算在 CPU 中需两次才能完成。在精度许可的前提下,如果想减少运算时间,应尽量使两个数的运算在 CPU 中一次完成,以提高运算速度。从存储 方面来说,存储数据的位数大将使存储控制器的负担过重,使它的处理速度变慢。需要注意的是,位数过大的数在存储器中造成的运算速度变慢的情况常常比在 CPU 中更严重。5 函数调用函数调用属于嵌套,分层的优秀编程方法,但它使程序处理的时间长,同时它占用的内存较多,同时使运算速度变慢,即它这一方法是有代价的。先说函数调用,int d=appl(5,4,3); int appl(int

37、 a,int b,int c)int d=a*b*c;return d; 在逻辑上这就等同于int a=5, b=4, c=3;int d=a*b*c;第二个种方法运行的速度快,但灵活性差,不便于管理。将函数调用改为大的单一程序虽然使程序运行的速度更快,但编写,调试和维护是很不容易的。再举例来说,调用一次函数 int add1(int a, int b )return a= a*a + b*b等同于重复调用两次函数 int add2(int a)return a=a*a;然后再相加两次调用的结果。但调用两次 add2 函数需分别在内存中建立和释放两次资源,所用的时间要比调用一次函数增加近一倍。

38、所以编程时,需要考虑如何在方便的调试维护和程序运行效率之间寻找平衡点。5、1 If 函数在条件语句的判别中,先执行前面的判别,如果条件不满足再执行后面的判断。建议你将出现可能性最大的条件放在前面,举例来说,如果出现的 x 数值为 1100 中的任何一个数,并且概率相同。if(x5) int x=6; else if(x20void InitTimer0();BOOL SetTimerCallback(TIMERPROC lpTimerFunc); /必须在 SetTimer0 之前调用BOOL SetTimer0(BYTE nID, WORD wInterval); /通过 nID(nID0)

39、来区分 /BOOL KillTimer0(BYTE nID);/以下为内部使用typedef struct tagTIMERINFOBYTE nID; /定时器 IDWORD wInterval; /此定时器的设定间隔时间WORD wElapse; /剩余的时间TIMERINFO;static BOOL AddTail(const TIMERINFO* pTimerInfo);static BOOL Remove(BYTE nID);static BYTE FindID(BYTE nID);#endif其中用到的的 const.h 定义如下:#ifndef _CONST_H_#define _

40、CONST_H_#include #define TRUE 1#define FALSE 0typedef unsigned char BYTE;typedef unsigned int WORD;typedef unsigned long DWORD;typedef float FLOAT; typedef char CHAR;typedef unsigned char UCHAR;typedef int INT;typedef unsigned int UINT;typedef unsigned long ULONG;typedef UINT WPARAM;typedef ULONG LP

41、ARAM;typedef ULONG LRESULT;typedef void VOID;typedef const CONST;typedef void *PVOID;typedef bit BOOL; #define MAKEWORD(lo, hi) (WORD)(BYTE)(lo) | (WORD)(BYTE)(hi) 16) idata TIMERINFO TimerInfoArrayMAX_TIMER_EVENT_NUM = 0;TIMERPROC g_pfnTimerFunc = NULL;BYTE g_nTimerInfoNum = 0; /当前队列的元素个数void InitT

42、imer0()TMOD |= T0_M0_; /定时器 0,工作方式 1TH0 = HIBYTE(TIMER0_INIT_VALUE);TL0 = LOBYTE(TIMER0_INIT_VALUE);TR0 = 0; /停止定时器 0ET0 = 0; /关定时器 0 中断 EA = 1;BOOL SetTimerCallback(TIMERPROC lpTimerFunc)if(lpTimerFunc = NULL)return FALSE;g_pfnTimerFunc = lpTimerFunc; return TRUE; BOOL SetTimer0(BYTE nID, WORD wInt

43、erval)TIMERINFO ti;if(g_pfnTimerFunc = NULL | nID = 0 | wInterval = 0)return FALSE;if(wInterval % TIMER0_BASE_INTERVAL != 0) /定时间隔必须是 TIMER0_BASE_INTERVAL 的整数倍return FALSE;if(FindID(nID) != MAX_TIMER_EVENT_NUM) /若已经有相同的 ID 存在return FALSE;ti.nID = nID;ti.wInterval = wInterval;ti.wElapse = wInterval;i

44、f(!AddTail(TR0 = 1; /启动定时器 0ET0 = 1; /开定时器 0 中断 return TRUE;/*BOOL KillTimer0(BYTE nID)if(!Remove(nID) | nID = 0)return FALSE;if(g_nTimerInfoNum = 0) /若最后一个定时事件已经停止,则关定时器中断ET0 = 0;return TRUE; */void Timer0ISR() interrupt TF0_VECTORBYTE i = 0;TF0 = 0;TH0 = HIBYTE(TIMER0_INIT_VALUE); TL0 = LOBYTE(TIM

45、ER0_INIT_VALUE);for(i = 0; i 1 “共同占用一行。 if ( ) do while( ); else 8.6 switch 语句 每个 case 和其判据条件独占一行。 每个 case 程序块需用 break 结束。特殊情况下需要从一个 case 块顺序执行到下一个 case 块的时候除外,但需要在交界处明确注释如此操作的原因,以防止出错。 case 程序块之间空一行,且只空一行。 每个 case 程序块的执行语句保持 4 个空格的缩进。 一般情况下都应该包含 default 分支。 Switch ( ) case x: break; case x: break;

46、default: break; 9 单片机 C51 编程规范程序结构 9.1 基本要求 有 main()函数的.c 文件应将 main()放在最前面,并明确用 void 声明参数和返回值。 对由多个.c 文件组成的模块程序或完整监控程序,建立公共引用头文件,将需要引用的库头文件、标准寄存器定义头文件、自定义的头文件、全局变量等均包含在内,供每个文件引用。通常,标准函数库头文件采用尖角号标志文件名,自定义头文件采用双撇号标志文件名。 每个.c 文件有一个对应的.h 文件,.c 文件的注释之后首先定义一个唯一的文件标志宏,并在对应的.h 文件中解析该标志。 在.c 文件中: #define FIL

47、E_FLAG 在.h 文件中: #ifdef FILE_FLAG #define XXX #else #define XXX extern #endif 对于确定只被某个.c 文件调用的定义可以单独列在一个头文件中、单独调用。 9.2 可重入函数 可重入函数中若使用了全局变量,应通过关中断、信号量等操作手段对其加以保护。 9.3 函数的形参 由函数调用者负责检查形参的合法性。 尽量避免将形参作为工作变量使用。 9.4 循环 尽量减少循环嵌套层数 在多重循环中,应将最忙的循环放在最内层 循环体内工作量最小 尽量避免循环体内含有判断语句正确区分 LJMP、AJMP、SJMP 、JMP 指令MCS-

48、51 的控制转移类指令,共 17 条,分为无条件转移指令、条件转移指令、子程序调用和返回指令、空操作指令等四类。无条件转移指令(共 4 条)LJMP addr16 ; PC addr16AJMP addr11 ; PC PC+2 , PC10-0 addr11SJMP rel ; PC PC+2 , PC PC+relJMP A+DPTR ; PC A+DPTR 第一条指令称为长转移指令(Long Jump);第二条指令叫作绝对转移指令(Absolute Jump);第三条指令称作短转移指令(Short Jump);第四条指令是变址寻址转移指令(散转指令)。显然,每条指令均以改变程序计数器 PC(Program Counter)中的内容为宗旨。(1)长转移指令(64KB 范围内转移指令)长转移指令的功能是:把指令码中的目标地址 addr16 装入程序计数器 PC,使机器执行下一条指令时无条件转

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

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报