1.1 嵌入式环境下的C语言使用技巧
1.1.1 重要的位(bit)操作
位(bit)是程序设计中可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。
位操作的作用:
(1)可以减少除法和取模的运算。灵活的位操作可以有效地提高程序运行的效率,如下:
/* 方法一 */ /* 方法二 */
unsigned int i,j; unsigned int i,j;
i = 156 / 16; i = 156 >> 4;
j = 562 % 32; j = 562 - (562 >> 5 << 5);
(2)实现位间的与(&)、或(|)、非(!)操作。这跟嵌入式系统的程序设计特点有很大关系。因为对底层硬件(处理器、内存、外围器件)的操作实际上都是通过读写相关寄存器进行,而对寄存器的读写,实际上就是按照硬件的器件手册(Date Sheet)的描述对该寄存器的某个或某些位进行与、或、非操作后置位。例如,通过将AM186ER型80186处理器的中断屏蔽控制寄存器的低6位设置为0(开中断2),最通用的做法是:
#define INT_I2_MASK 0X0040
wTemp = inword(INT_MASK);
outword(INT_MASK,wTemp & INT_I2_MASK);
而将该位置为1的做法是:
#define INT_I2_MASK 0X0040
wTemp = inword(INT_MASK);
outword(INT_MASK,wTemp | ~INT_I2_MASK);
判断该位是否为1的做法是:
#define INT_I2_MASK 0X0040
wTemp = inword(INT_MASK);
if(wTemp & ~INT_I2_MASK)
{
... /* 该位为1 */
}
1.1.2 正确使用数据指针
在嵌入式系统的程序设计中,常常要求在特定的存储单元读写内容或者直接访问处理器的某个经过地址编码的寄存器(Register),通常汇编有对应的MOV指令,而除C/C++以外的其他程序设计语言基本没有直接访问绝对地址的能力。在嵌入式系统的实际调试中,多借助C语言指针所具有的对绝对地址单元内容内容的读写能力。以指针直接操作内存多发生在如下几种情况:
(1)某I/O芯片被定位子在CPU的存储空间而非I/O空间,而且寄存器对应于某特定地址;
(2)两个CPU之间以双埠RAM通信,CPU需要在双埠口RAM的特定单元(称为mail box)书写内容以在对方CPU产生中断;
(3)在利用字符迭加做自字符及简单图形显示时,需要读取在ROM或Flash的特定单元所刻录的汉字和英文字母。
例如:
unsigned char *p = (unsigned char *)0xF000FF00;
*p = 11;
1.1.3 函数等价于指令的集合
理解以下3个问题:
(1) 语言中函数名直接对应函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针;
(2)调用函数实际上等同于“调转指令+参数传递处理+回归位置入栈”,本质上最核心的操作是将函数生成的目标代码的首地址赋给CPU的PC寄存器;
(3)因为函数调用的本质是跳转到某一个地址单元的code 去执行,所以可以“调用”一个根本就不存在的函数实体;
186 CPU启动后跳转至绝对地址0xFFFF0(对应C语言指针0xF000FFF0 ,0xF000为段地址,0xFFF0为段内偏移)执行,请看如下代码:
typedef void (*lpFunction) ( ); /* 定义一个无参、无返回值类型的函数指针类型 */
lpFunction lpReset = (lpFunction) 0xF000FF00; /* 定义一个函数指针,指向CPU启动后所执行的第一条指令的位置 */
lpReset( ); /* 调用函数 */
在以上的程序中,根本没有看到任何一个函数实体,但是却执行了函数调用lpReset(),它实际上起到了“软启动”的作用,跳转到CPU启动后第一条要执行的指令的位置。所以函数的本质,实际上就是一个指令集合;你可以调用一个没有函数体的函数,本质上只是换一个地址开始执行指令。
1.1.4 操作有限的存储空间
嵌入式系统的存储空间往往十分有限,不经意的存储空间泄漏可能会很快导致整个系统崩溃。
(1)一定要保证malloc和free语句成对出现,谁申请的空间就有谁来释放
char *function(void)
{
char<