星期三, 三月 04, 2009

《电气传动系统软件设计》之一

帮老板写书的工作一直在进行中, 虽小步走走停停, 但数月不断的累积也终于基本完成了初稿. 也算是对前一段时间APF系统开发的一个总结机会.
往纸上写了, 就要讲求细致准确, 不敢马虎, 巨细之事都要遍查文档以求正解, 自己对一些技术细节也更加清晰了. 之后陆续会把所写的文字陆续整理上传. 今天先贴上"4.2.2.3 C语言编程技巧几则".
.....
4.2.2.3 C语言编程技巧几则

使用条件编译切换程序模块
在程序开发和调试的过程中, 常常需要根据不同的调试目的切换部分程序代码或程序模块, 将局部算法不同的几种工程进行效果比对, 并在次基础上进行分析, 进而得到软件系统设计的依据. 若为不同算法保存不同的源程序文件或不同的工程, 会使切换调试工作变得复杂, 繁杂的文件或工程也不方便管理. 若采用注释和取消注释的方法来实现程序代码的切换, 则可能造成程序的混乱, 极易留下程序错误的隐患.
采用条件编译可以很方便的解决切换程序模块的功能. 条件编译的语法结构如下所示, 其中的#elif和#else段都不是必须.
#if 条件语句1
…… // 条件语句1为真时有效的程序代码
#elif 条件语句2
…… // 条件语句2为真时有效的程序代码
#else
……
#endif
其中, 条件语句可以是任何具有Bool值的C表达式. 当条件语句值为True时, 则相应的程序段就会参与编译, 值为False的条件语句所对应的程序段则不会参加编译, 和注释掉的效果是一样的. 下面的一段代码中, 条件编译的写法可以根据宏定义ADSAMPLES(AD采样平均点数)的数值选择相应的程序编译有效, 这样只需要修改宏定义ADSAMPLES的数值就可以达到修改程序的目的.
#if ADSAMPLES==4
apf.AD[AD_CH_IAL]=iAD_IaAcc>>2; // 4次平均
apf.AD[AD_CH_IBL]=iAD_IbAcc>>2;
#elif ADSAMPLES==2
apf.AD[AD_CH_IAL]=iAD_IaAcc>>1; // 2次平均
apf.AD[AD_CH_IBL]=iAD_IbAcc>>1;
#elif ADSAMPLES==1
apf.AD[AD_CH_IAL]=iAD_IaAcc; // 不做平均
apf.AD[AD_CH_IBL]=iAD_IbAcc;
#endif
类似的我们也可以写出切换使用SPWM和SVPWM的程序, 如下所示.
#if SPWM_SVPWM==1
spwm( ); // 使用spwm方法
#elif SPWM_SVPWM==0
svpwm( ); // 使用svpwm方法
#endif;


使用#ifndef防止重复包含头文件的经典写法
从C语言模块的结构一节中可以看到, 实际的.c原文件中可能需要包含多个头文件(与不同硬件模块相关的头文件、全局宏定义头文件等), 而这些头文件中又可能需要有互相包含的关系, 这时很容易发生重复包含某个头文件的情况, 造成重复宏定义, 最终导致编译报错. 可以使用预处理命令#ifndef来解决这一问题.
TI提供的例程中就采用了如下的防止重复包含头文件的标准写法. 其中使用条件编译语句可以保证宏定义正文只被编译一次, 而不会出现重复宏定义的问题.
#ifndef _文件名_后缀名_
#define _文件名_后缀名_
…… // 宏定义正文
#endif
"_文件名_后缀名_"的写法既是为了形式上与该文件对应, 也是为了确保该宏名的唯一. 如上形式的预处理语句就可以确保文件正文中的内容不会被重复执行, 防止了重复宏定义的问题.

 指针访问数组
数组是若干相同类型变量组成的集合, 数组由连续存储单元组成, 数组名就是数组对应的存储单元的首地址. 指针在C语言中就是地址, 指针变量是存放指针(或地址)类型值的变量. 如果指针变量中存放的值等于数组名, 或者说等于数组的首地址, 那么该指针实际上就成为了指向该数组的指针, 通过该指针可以实现对数组元素的访问. 通过指针可以更高效率的访问数组.
给指针变量赋数组首地址值有2种形式:
int a[10], *pa;
pa = a; 或 pa = &a[0]; //2种正确的写法
pa = &a; //错误, 不能对地址(a)执行取地址(&)操作
用指针访问数组元素有3种形式:
*(pa+n) 或 *(a+n) 或a[n] //a实际上是值为数组a首地址的指针常量

 16位乘法
编程中经常需要进行"16位乘法再右移"的操作. 在C中通过强制类型转换的方法可以保证C访问16位乘法移位后的结果, 而且这种方法不需要调用C语言的32位乘法指令的, 也就是说它具有和ASM实现"16位乘法再右移"同样的效率[9]. 下面给出示例.
对于有符号数:
int m1, m2;
int result;
result = ((long) m1 * (long) m2) >> n; //n是右移位数
对于无符号数
unsigned m1, m2;
unsigned result;
result = ((unsigned long) m1 * (unsigned long) m2) >> n; //n是右移位数
而在如下的形式中, (乘积取低16位后再右移16位的)计算结果result总为0.
result = (m1 *m2) >> n; //n是右移位数


没有评论:

发表评论