高效可移植程序的一些习惯

时间:2022-12-06 22:05:29
高效可移植程序的一些习惯

这些都是本人的一些个人习惯,也许读者并不认同。

首先介绍两个大师提出的习惯,而我深感认同的。

对于一些状态的集合,尽可能使用enum而不是#define 来定义。

如:

enum {OFF = 0, ON = 1};

远比
#define OFF  0

#define ON  1

好。

两种写法编译的效果是一样的,但是写程序及看程序的人员来说感觉就不一样了。用enum定义时,可以觉得OFF与ON是一个集合,有相关性(且可减少程序中#define的定义)。但用#define定义时,完全可认为OFF与ON不相干。特别是在程序中#define定义过多时,查找并理解它的意思都是一个痛苦。(如:uc/os,看见它的一大堆#define都头大,要完全明白那一堆定义都得花不少时间)

第二,程序中少用

#if

#ifdef

等条件编译。不符合条件的程序段是不被编译的,都不检查语法错误,当在某些情况下打开了条件开关时,也许一堆错误就出现了。而且程序并不利于阅读,当读程序时突然得去查找某东东是否#define真是痛苦。



好了,下面是我的习惯

1、I/O口输出电平定义一下,以后万一I/O电平驱动取反,一改就好。

//I/O Port Stutes

enum {OFF = 0, ON = 1};
//Led Stutes
enum {LED_ON = 0, LED_OFF = 1};
eg. LED = LED _ON;    //低电平驱动LED亮。

2、I/O口与任何的其它IC接口,I/O口都要定义,不能直接使用I/O口。(方便以后用于其它的应用中)

如93C46:

#define CS93C46        P1_0      //片选信号

#define SK93C46        P1_1   //时钟信号

#define DI93C46             P1_2      //数据输入,相对93C46而言 (即93C46的数据输入)

#define DO93C46       P1_3      //数据输出

如字符型LCD:

#define          LCDPORT     P0          //lcd数据口

#define          RS                P1_7      //数据指令选择。低--ins  高--data

#define          RW               P1_6      //读写选择。       低--w    高--r

#define          EN                P1_5      //使能。         高到低--使能。



3、键盘口要定义输入口及屏蔽字

如:
#define KEYMASK  0x0f

#define KEYPORT   P2


4、串口用中断驱动,要带Buf。



5、取按键的程序及串口的程序编2个,一个为非阻塞式,一个为阻塞式(不按键或不收到数据不返回)。



6、晶振频率要定义
#define FOSC   11059200ul      //频率



7、波特律定义

#define BAUD   4800         //波特率4800

#define SMODX  0            //是否倍频        0-NO or 1-YES

#define T1H_VAL (256 - (UINT8)(FOSC * (1<<SMODX) * 1.0 / 384 / BAUD))

#define T1L_VAL  T1H_VAL


8、延时程序赋值时写成FOSC函数,编译器会预先算,不占用MCU资源
以后移植方便

9、液晶程序一定要定义长宽

如点阵型:
#define ROW  64
#define CLUM 128   /*128*64*/

字符型:

#define ROW  4
#define CLUM 20   /*128*64*/

#define ROW1_ADDR  (0x80 + 0)                      //第一行地址

#define ROW2_ADDR  (ROW1_ADDR + 0x40)         //第二行地址

#define ROW3_ADDR  (ROW1_ADDR + CLUM)   //第三行地址

#define ROW4_ADDR  (ROW2_ADDR + CLUM)   //第四行地址
然后所有的和液晶地址有关的计算全部引用这2个量。
换用任何屏直接一改,高层不变

10、驱动层与应用接口层分在不同的文件,移植方便。

如:键盘、LCD、串口等。



网友:gdtyy跟贴


你的习惯很好,但有些观点似乎过时了。 

    使用宏的好处是:1、只影响编译时间,不影响运行时间;2、条件编译的控制粒度可以达到单条语句,能够精确有效地裁减代码。
    你所说的,“看见uc/os的一大堆#define都头大,要完全明白那一堆定义都得花不少时间”和“条件编译都不检查语法错误,当读程序时突然得去查找某东东是否#define真是痛苦”,自从有了自动化的配置工具后,已经烟消云散,成了历史。
    配置工具采用图形化界面,用户看图钩选配置项和设置配置值。工具自动帮助检查冲突和生成配置头文件。比如:
    用户要定义uc/os任务数“#define OS_MAX_TASKS 60”,图形工具就会在OS_MAX_TASKS选项下弹出一个文本框供用户输入数值。当用户输入165时,工具会立即鸣响提示此选项最大值为64,最小为1,缺省为3。如果用户鼠标双击此选项,会马上看到在线帮助,了解是谁在什么时间定义了这个选项,那个人的电话和EMAIL,这个值的含义,若干范例,在源码中的位置(超链接),数据库中存储的碰到同样问题的人的关于这个值的留言及他们的联系方式。一旦用户输入合法值,工具最后会自动生成头文件,用户看到的不再是一堆define定义,而是漂亮的彩色图形。
    用户在裁减代码时往往会非常头痛,因为,各个裁减项之间存在网状关系。比如:裁减掉UDP模块,而保留TFTP部分,就会造成冲突。模块化的思想是抽取任何一个模块都不影响整个系统正常工作,但事实上,模块之间存在网状关系,那种只有一个入口和一个出口的模块在现实中很少见,如果强行简单化相互关系,就会非常别扭,不自然。在承认模块关系复杂的前提下,配置工具可以有效地辅助裁减系统而不出错。例如:有A、B、C、D、E五个模块,它们之间的关系是:有A就不能有B(A、B模块不能共存);C依赖于A、D,缺一不可;D独立存在;E依赖于A或B。在配置这样的系统前,用户先用脚本语言CDL描写出模块之间的依赖互斥关系,然后配置工具使用推理机穷举遍历所有关系的排列组合,寻找冲突项,因为计算机速度很快,用户刚一钩选某个模块,工具就能根据脚本立即判断配置是否存在冲突。这样就可以避免低级错误。用户的主要职责是编写配置脚本,检查的工作由推理机完成。一旦发生冲突,配置工具可以列出所有不冲突的选项配置集合供用户选择,用户不必费脑子来回尝试不冲突的选项组合。
    如果每次都从头配置选项,重复的工作量就比较大。幸运地是,配置工具提供模版,使用户可以在一个比较高的起点开始工作。比如:事先准备好若干模版,用户选择一个,然后通过增加和删除选项进行配置,不必从头开始,所有未配选项均有缺省值,不强求用户显式配置每个选项。
    有了配置工具机制的保障,面对上千个配置项,我们也不会犯低级错误。总之,有了配置工具,裁减/配置系统就像点菜一样地惬意!



网友:汽车电子跟贴:

可移植性好的程序少不了条件编译的影子...... 

   楼主:我有个疑问,两个大师提出的习惯. 竟然少用条件编译???
   提出这样的习惯,还叫大师,笑话!提出这个问题的人,他的编程经历有限,应用面也有限,极少在不同的系统间移植程序

   如果想程序的可移植性好,条件编译必不可少,而且要大量的用......

   有两个良好的习惯
1:常量 -- 尽量不要用数字,而用符号代替
   我很喜欢AVR的编译系统,每个寄存器的每一位都用符号定义的,如
/* ADCSR / ADCSRA */
#define ADEN    7
#define ADSC    6
#define ADFR    5
#define ADIF    4
#define ADIE    3
#define ADPS2    2
#define ADPS1    1
#define ADPS0    0
写允许AD转换的程序是用  ADCSR  = (1<<ADEN);  而不是用 ADCSR  = 0x80
当然,完全可以把预定义加个左移,就直接用   ADCSR  = ADEN;

2、对于注释,尽可能要“语句本身就是注释”
  比如,在LPC2000系列ARM中,要对一个驱动LED的引脚作配置,先用预定义
#define        LED_PIN     25       // 第25脚驱动LED
#define        LED_ON      (IOSET0 = (1<<LED_PIN))
#define        LED_OFF     (IOCLR0 = (1<<LED_PIN))
#define        SET_LED_PIN_OUTPUT     (IODIR0  = (1<<LED_PIN))
程序中直接用 SET_LED_PIN_OUTPUT 和 LED_ON 和 LED_OFF

而不是直接用 IOSET0 = 0x00020000; 或 IOSET0 = (1<<25); 之类的语句