2014年(31)
分类: C/C++
2014-03-22 11:32:48
普通变量的定义和访问同标准C语言,在HCS08 C语言中我们主要要解决映像寄存器变量和某些特殊变量的定位问题,即把这些变量存放在RAM中指定的位置。
1映像寄存器定位
映像寄存器单片机中跟硬件有关的寄存器,它们都有各自固定RAM地址,其定位有3种方法
1)宏定义
例如:#define PortA ( * ( volatile unsigned char * ) 0x0000 )
这样 PortA 成为一个地址在0x0000的unsigned char类型变量。这个定义看起来很复杂,其实它也可以分解成几个很简单的部分来看。 ( volatile unsigned char * )是C语言中的强制类型转换,它的作用是把0x0000这个纯粹的十六进制数转换成为一个(地址)指针,其中volatile并不是必要的,它只是告诉编译器,这个值与外界环境有关,对它优化接下来在外面又加了一个*号,就表示0x0000内存单元中的内容了。经过这个宏定义之后,PortA就被可以做为一个普通的变量来操作,所有出现PortA的地方编译的时候都被替换成( * ( volatile unsigned char * ) 0x0000 ),外面一层括号是为了保证里面的操作不会因为运算符优先级或者其它不可预测的原因被改变而无法得到预期的结果。
这种定义方法适合所有的C编译器,可移植性好,但PortA并不是一个真正的变量,只是一个宏名,当你调试一个程序的时候,无法在调试窗口观察它的值。另外连接器也失去了灵活性,它得防止其它变量跟此变量冲突。
2)使用@关键字
例如: volatile unsigned char PortA @0x0000;
@是编译器扩展的一个特殊修饰符,其它编译器很可能并不认识。这种定义具有很好的可读性,失去了可移植性。
3)使用段定义
这种方法分为2个步骤
首先把变量定义在段中,其次在连接参数文件(*.prm)中把段定位在一个合适的位置
例如:第1步:在源程序文件中
#pragma DATA_SEG PORTB_SEG
volatile unsigned char PortA;
#pragma DATA_SEG DEFAULT
这样变量 PortA 定义在段 PORTB_SEG 中
第2步:在 prm 文件中
SECTIONS
PORTB_SEG = READ_WRITE 0x0000 SIZE 1;
这样段 PORTB_SEG 定位在地址0x0000上。
这种方法可移植性也很差,如果你要把它移植在别的编译器上,你不得不修改源程序。
2变量定义修饰符
变量定义有三个修饰符值得注意,虽然它们与标准C是相同的,但是在嵌入式C语言中又有不同的含义。
1) static
在子函数中static用声明的变量是局部变量,但是退出这个子函数后其值不消失。下一次调用这个函数时仍可以访问到原来的值。注意,在子函数中声明的static变量只对声明他的函数可见,别的函数是不可以使用的。如果static变量是在模块中声明的,那么只有本模块的函数可以使用它,别的模块中的函数是不能访问的。
void MyFunction (void)
{
static char myVar = 0; //用 static声明的局部变量,只有在初始的时候被执行,赋值为0
myVar = myVar + 1;
}
void main (void)
{
MyFunction(); //调用之前myVar = 0,调用之后myVar = 1
MyFunction(); //调用之前myVar = 1,调用之后myVar = 2
}
static有什么用途?
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用
2) volatile
如果一个变量的值可能会被程序操作之外的其它操作所改变,那么你必需用volatile 声明。在嵌入式系统中其它操作是:中断服务程序的操作、硬件动作的操作。
用volatile声明的变量是不会被编译器优化掉的,如:
volatile unsigned char PortA @0x0000;
PORTA做为一个输入端口,其值是由外部设备决定的,由于外部设备的变化是随机的,因此第一次读取的值和第二次读取的值很可能不同,所以我们把它声明为volatile变量。
a = PORTA;
a = PORTA;
由于PORTA是用volatile声明的变量,编译器不会把它优化成一句,而如果不是volatile声明的编译器就会将第二句优化掉,从而程序将会忽略输入端口的变化。
通常把嵌入式设备的所有外围器件寄存器都声明为volatile 的。
3) const
修饰符 const 可以用在任何变量之前, 告诉编译器把此变量存储在ROM中。ROM_VAR段是定位 const 变量的默认段
语法格式:#pragma CONST_SEG <段名>
例如:
#pragma DATA_SEG DEFAULT
#pragma CONST_SEG DEFAULT
static int a;//变量 a 存放在默认的 RAM 段 DEFAULT_RAM 中,DEFAULT_RAM是段名
static const int c0 = 10;//变量 c0 存放在默认的 ROM 段 ROM_VAR 中,ROM_VAR是段名
此时编译器选项-Cc必需是打开的。如果编译器选项-Cc必需是关闭的,则变量a和c0都定位在DEFAULT_RAM中。
例如:
#pragma DATA_SEG MyVarSeg
#pragma CONST_SEG MyConstSeg
static int a; //变量 a 存放在段MyVarSeg中,MyVarSeg是段名
static const int c0 = 10; //变量 c0 存放在段 MyConstSeg 中,MyConstSeg是段名
此时编译器选项-Cc必需是打开的。如果编译器选项-Cc必需是关闭的,则变量a和c0都定位在MyVarSeg中。
3 全局变量和局部变量
全局变量为整个程序而定义,在整个程序运行期间。它们占用固定的RAM资源,因此除非在必需的情况,否则不要轻易使用。局部变量为某个函数而定义,只在此函数运行的时候,占用栈空间,局部变量实质上是函数运行所需要的一段RAM空间。因此函数不运行时,它们不占用RAM资源。全局变量通常是为了给中断服务函数传递参数定义的,建议定义时把它们定义在一个相对集中的RAM空间。
例如:
#pragma DATA_SEG SCI_DATA
char senderBuffer[50];
char receiverBuffer[100];
...
#pragma DATA_SEG DEFAULT
在参数文件中必需指定 SCI_DATA 段。
4 位定义和访问
HCS08 C 语言采用直接位访问的方法来访问位,位的定义采用联合和结构数据类型来实现。
例如:
volatile union {
struct {
unsigned char MWPR:1;
unsigned char STRE:1;
unsigned char VTCK:1;
unsigned char FDISVFP:1;
unsigned char FENLV:1;
unsigned char HVT:1;
unsigned char GADR:1;
unsigned char FSTE:1;
} FEETST_BITS;
unsigned char FEETST_BYTE;
} FEETST_struct @0x00F6;
#define FEETST FEETST_struct.FEETST_BYTE
#define MWPR FEETST_struct.FEETST_BITS.MWPR
#define STRE FEETST_struct.FEETST_BITS.STRE
#define VTCK FEETST_struct.FEETST_BITS.VTCK
#define FDISVFP FEETST_struct.FEETST_BITS.FDISVFP
#define FENLV FEETST_struct.FEETST_BITS.FENLV
#define HVT FEETST_struct.FEETST_BITS.HVT
#define GADR FEETST_struct.FEETST_BITS.GADR
#define FSTE FEETST_struct.FEETST_BITS.FSTE
这里的“:1”表示仅需要一个位,HCS08会把它们包装在一起形成一个字节。这样我们就可以以字节方式或位方式访问整个寄存器和位。
例如:
FEETST =0X80;//字节方式
MWPR = 1;//位方式
VTCK=1; // BSET 2,FEETST1
FDISVFP=0; // BCLR 4,FEETST1
有关映像寄存器和位定义可以参照具体芯片的头文件。