如果是汇编语言编程,实现指针寻址的方法肯定就是用 FSR 寄存器,PICC 也不例外。
为了生成高效的代码,PICC 在编译 C 原程序时将指向 RAM 的指针操作最终用 FSR 来实现
间接寻址。这样就势必产生一个问题:FSR 能够直接连续寻址的范围是 256 字节(bank0/1
或 bank2/3),要覆盖最大 512 字节的内部数据存储空间,又该如何让定义指针?PICC 还是
将这一问题留给编程员自己解决:在定义指针时必须明确指定该指针所适用的寻址区域,例
如:
unsigned char *ptr0; //①定义覆盖 bank0/1 的指针
bank2 unsigned char *ptr1; //②定义覆盖 bank2/3 的指针
bank3 unsigned char *ptr2; //③定义覆盖 bank2/3 的指针
上面定义了三个指针变量,其中①指针没有任何 bank 限定,缺省就是指向 bank0 和 bank1;
②和③一个指明了 bank2,另一个指明了 bank3,但实际上两者是一样的,因为一个指针可
以同时覆盖两个 bank 的存储区域。另外,上面三个指针变量自身都存放在 bank0 中。我们
将在稍后介绍如何在其它 bank 中存放指针变量。
既然定义的指针有明确的 bank 适用区域,在对指针变量赋值时就必须实现类型匹配,
下面的指针赋值将产生一个致命错误:
unsigned char *ptr0;
bank2 unsigned char buff[8];
程序语句:
//定义指向 bank0/1 的指针
//定义 bank2 中的一个缓冲区
ptr0 = buff; //错误!试图将 bank2 内的变量地址赋给指向 bank0/1 的指针
若出现此类错误的指针操作,PICC 在最后连接时会告知类似于下面的信息:
Fixup overflow in expression (...)
同样的道理,若函数调用时用了指针作为传递参数,也必须注意 bank 作用域的匹配,
而这点往往容易被忽视。假定有下面的函数实现发送一个字符串的功能:
void SendMessage(unsigned char *);
那么被发送的字符串必须位于 bank0 或 bank1 中。如果你还要发送位于 bank2 或 bank3 内的
字符串,必须再另外单独写一个函数:
void SendMessage_2(bank2 unsigned char *);
这两个函数从内部代码的实现来看可以一模一样,但传递的参数类型不同。
按笔者的应用经验体会,如果你看到了“Fixup overflow”的错误指示,几乎可以肯定
是指针类型不匹配的赋值所至。请重点检查程序中有关指针的操作。
&O1540; 指向 ROM 常数的指针
如果一组变量是已经被定义在 ROM 区的常数,那么指向它的指针可以这样定义:
const unsigned char company[]=”Microchip”;
const unsigned char *romPtr;
程序中可以对上面的指针变量赋值和实现取数操作:
romPtr = company; //指针赋初值
data = *romPtr++; //取指针指向的一个数,然后指针加 1
//定义 ROM 中的常数
//定义指向 ROM 的指针
反过来,下面的操作将是一个错误,因为该指针指向的是常数型变量,不能赋值。
*romPtr = data; //往指针指向的地址写一个数
&O1540; 指向函数的指针
单片机编程时函数指针的应用相对较少,但作为标准 C 语法的一部分,PICC 同样支持
函数指针调用。如果你对编译原理有一定的了解,就应该明白在 PIC 单片机这一特定的架
构上实现函数指针调用的效率是不高的:PICC 将在 RAM 中建立一个调用返回表,真正的
调用和返回过程是靠直接修改 PC 指针来实现的。因此,除非特殊算法的需要,建议大家尽
量不要使用函数指针。
&O1540; 指针的类型修饰
前面介绍的指针定义都是最基本的形式。和普通变量一样,指针定义也可以在前面加上
特殊类型的修饰关键词,例如“persistent”、“volatile”等。考虑指针本身还要限定其作用域,
因此 PICC 中的指针定义初看起来显得有点复杂,但只要了解各部分的具体含义,理解一个
指针的实际用图就变得很直接。
㈠ bank 修饰词的位置含义
前面介绍的一些指针有的作用于 bank0/1,有的作用于 bank2/3,但它们本身的存放位置
全部在 bank0。显然,在一个程序设计中指针变量将有可能被定位在任何可用的地址空间,
这时,bank 修饰词出现的位置就是一个关键,看下面的例子:
//定义指向 bank0/1 的指针,指针变量为于 bank0 中
unsigned char *ptr0;
//定义指向 bank2/3 的指针,指针变量为于 bank0 中
bank2 unsigned char *ptr0;
//定义指向 bank2/3 的指针,指针变量为于 bank1 中
bank2 unsigned char * bank1 ptr0;
从中可以看出规律:前面的 bank 修饰词指明了此指针的作用域;后面的 bank 修饰词定义了
此指针变量自身的存放位置。只要掌握了这一法则,你就可以定义任何作用域的指针且可以
将指针变量放于任何 bank 中。
㈡ volatile、persistent 和 const 修饰词的位置含义
如果能理解上面介绍的 bank 修饰词的位置含义,实际上 volatile、persistent 和 const 这
些关键词出现在前后不同位置上的含义规律是和 bank 一词相一致的。例如:
//定义指向 bank0/1 易变型字符变量的指针,指针变量位于 bank0 中且自身为非易变型
volatile unsigned char *ptr0;