(1)复习C语言中的位域、共同体、结构体;
(2)使用位定义、共同体、结构体来对DSP的寄存器进行操作;(理解.h文件的作用)
(3)对存储器进行管理;(理解.cmd文件)
一、位域、共同体与结构体
关于数组、结构体、共同体用一个比较好的解释是这么说的:
数组是同类型数据的集合;
结构体是不同类型数据的集合;
共同体是不同类型共用存储单元。
1.位域
(1)为了节省内存空间,有些数据的存储只需要内存中的一位或几位二进制位,位域提供了这一功能。
位域的定义事实上就是一个结构体
struct weiyu
{int a:3;
int b:5;
int c:8;}
表明定义了一个位域结构weiyu,它有三个位域a、b、c,位域a占3个二进制位,b占5个二进制位,c占8个
二进制位。
(这里还有一个问题就是在定义位域前面有一个int,这样看来,这三个位域岂不是占了6个字节?事实上
是,编译器会将其中的某几个位域捆绑起来按int做对齐,详细情况请参加见
http://hi.baidu.com/xiao1dian/item/7cfa8e0e9d0e51cc905718ed
http://blog.sina.com.cn/s/blog_66ec4d660100j3wi.html)
(2)位域的几点说明:
定义顺序从低位到高位,即从右到左;
一个位域只能存在一个字节中,不能横跨两个字节,否则需中间插入空位域来补齐一字节;
位域长度不大于一个字节长度;
位域可以没有名,这时用来做补齐用;
如struct weiyu
{int a:4;
int :2;(这两位不用)
int c:8;};
(3)定义位域实例及使用
struct weiyu weiyu1;//声明了一个weiyu型变量weiyu1
weiyu1.a=2;
weiyu1.b=1;
weiyu.c=0;(如果放进去的数字大于你所规定的位域长度,会出现不正确的结果,注意,规定一个位域有
x位,那么只能放进去2^(x-1)位的数字,因为还有一位要做符号位用)
2.共同体
当需要把不同类型的变量存放到同一段内存单元,或对同一段内存单元的数据按不同类型处理,则需要
使用“共用体”数据结构。把一个整型变量、一个字符型变量、一个实型变量放在同一个地址开始的内
存单元中。
共用体的定义形式:
union 共用体名
{
成员列表;
} 变量列表;
例如 union data
{char a;
int b;
float c;};
union data data1;
data1.a='2'
data1.b=20;
data1.c=1.2;
data1的a、b、c共享一段起始内存,以最后一次赋值为准,并且其长度以最长的成员长度为
准。如上例中,共同体中最后一次赋值的变量值为1.2,前两次赋值已无意义。
请参见http://blog.21ic.com/user1/7959/archives/2011/81841.html
二、使用位定义、共同体来对DSP寄存器进行操作
1.位定义操作
这里我们以ADC的控制寄存器1ADCTRL1为例来进行位操作定义:
ADC寄存器的构造参见datasheet或者《手把手》293页,我们可以做位定义如下:
struct ADCTRL1_BITS{
Uint 16 rsvd:4;//最低4位为保留位;
Uint 16 SEQ_CASC:1;//级联序列发生器工作方式;
Uint 16 SEQ1_OVRD:1;//序列发生器忽略位;
Uint 16 CONT_RUN:1;//连续运行;
Uint 16 CPS:1;//内核时钟定标位;
Uint 16 ACQ_PS:4;//采集窗口大小;
Uint 16 SUSMOD:2;//仿真暂停方式;
Uint 16 RESET:1;//ADC软件复位;
Uint 16 rsvd2:1;//保留位,注意不能与前面的rsvd重名;};
这样定义完之后,我们将它存放在.h头文件里头,这就是为什么我们在日常写程序中,只要写一句
AdcRegs.ADCTRL1.bit.RESET=1,就可以软件复位整个ADC了,事实上,机器是并不知道我们所写的哪一个寄
存器哪一位代表什么的,之所以它可以那么“智能”的找到这些寄存器的位置,就是因为我们在这里对它进
行了位分配以及之后要提及的cmd定义,让这些寄存器名称和他们实实在在的物理地址对应了起来,所以这
个过程可以看做是“起名字,分位子”的过程。
2.共同体操作
为了方便对寄存器既可以按位进行操作,也可以整体进行操作,我们可以使用共同体,就是定义一个
共同体,里面的成员既包括按位操作的属性,又包括整体操作的属性,具体做法如下:
union ADCTRL1_REG{
Unit 16 all;//all属性,表示可以对寄存器整体操作;
struct ADCTRL1_BITS bit;//bit属性,表示可以对寄存器按位操作;};
上例中,我声明了一个共同体ADCTRL1,我继续声明一个共同体变量ADCTRL1_REG1即
union ADCTRL1_REG ADCTRL1_REG1;
则我既可以通过按位访问访问它ADCTRL1_REG1.bit.SEQCASC=1;
又可以通过整体对它进行赋值ADCTRL1_REG1.all=0x……;
所以,我们在DSP的头文件中一般可以看到以下的定义形式:(我们以CPU定时器的头文件对TCR寄存器的定义
来看一下实际中头文件是如何定义出来的:
struct TCR_BITS { // bits description
Uint16 rsvd1:4; // 3:0 reserved
Uint16 TSS:1; // 4 Timer Start/Stop
Uint16 TRB:1; // 5 Timer reload
Uint16 rsvd2:4; // 9:6 reserved
Uint16 SOFT:1; // 10 Emulation modes
Uint16 FREE:1; // 11
Uint16 rsvd3:2; // 12:13 reserved
Uint16 TIE:1; // 14 Output enable
Uint16 TIF:1; // 15 Interrupt flag
};
union TCR_REG {
Uint16 all;
struct TCR_BITS bit;
};
可以看到,位定义之后跟着就在后面定义了一个结构体,是为了方便对寄存器进行按位和整体操作。
3.结构体操作
某个功能模块,比如ADC的寄存器往往不止一个,有很多,我们可以创建一个结构体来对它进行管理,比如,
我要创建一个结构体ADC_REGS,来对ADC所有的寄存器来进行管理,我就可以这样做:
struct ADC_REGS {
union ADCTRL1_REG ADCTRL1; // ADC Control 1
union ADCTRL2_REG ADCTRL2; // ADC Control 2
union ADCMAXCONV_REG ADCMAXCONV; // Max conversions
union ADCCHSELSEQ1_REG ADCCHSELSEQ1; // Channel select sequencing control 1
union ADCCHSELSEQ2_REG ADCCHSELSEQ2; // Channel select sequencing control 2
union ADCCHSELSEQ3_REG ADCCHSELSEQ3; // Channel select sequencing control 3
union ADCCHSELSEQ4_REG ADCCHSELSEQ4; // Channel select sequencing control 4
union ADCASEQSR_REG ADCASEQSR; // Autosequence status register
Uint16 ADCRESULT0; // Conversion Result Buffer 0
Uint16 ADCRESULT1; // Conversion Result Buffer 1
Uint16 ADCRESULT2; // Conversion Result Buffer 2
Uint16 ADCRESULT3; // Conversion Result Buffer 3
Uint16 ADCRESULT4; // Conversion Result Buffer 4
Uint16 ADCRESULT5; // Conversion Result Buffer 5
Uint16 ADCRESULT6; // Conversion Result Buffer 6
Uint16 ADCRESULT7; // Conversion Result Buffer 7
Uint16 ADCRESULT8; // Conversion Result Buffer 8
Uint16 ADCRESULT9; // Conversion Result Buffer 9
Uint16 ADCRESULT10; // Conversion Result Buffer 10
Uint16 ADCRESULT11; // Conversion Result Buffer 11
Uint16 ADCRESULT12; // Conversion Result Buffer 12
Uint16 ADCRESULT13; // Conversion Result Buffer 13
Uint16 ADCRESULT14; // Conversion Result Buffer 14
Uint16 ADCRESULT15; // Conversion Result Buffer 15
union ADCTRL3_REG ADCTRL3; // ADC Control 3
union ADCST_REG ADCST; // ADC Status Register
Uint16 rsvd1;
Uint16 rsvd2;
union ADCREFSEL_REG ADCREFSEL; // Reference Select Register
union ADCOFFTRIM_REG ADCOFFTRIM; // Offset Trim Register
};
可以看出,将所有的uint16成员和union成员全部放在了一起,构成了ADC_REGS的结构体,注意到这里
面,union成员是既可以进行按位操作,又可以进行整体操作的,而uint16成员只能进行整体操作。
综上所述,我们可以看出构建.h文件是一个自下而上的构建过程:
1.先进行位定义,对某一个寄存器每一个位进行名称定义,然后这些位构成了某一个特定的寄
存器;
2.构成的特定的寄存器,又与寄存器整体定义(uint 16 all)一起构成了共同体,这样可以方便
对寄存器进行整体操作和按位操作;
3.一个功能模块内所有的寄存器(无论它被定义成共同体还是未定义过的),都通过结构体一
起构成了一个特定模块。