相对于传统的宏定义方式,F28335的寄存器定义方式更加复杂。为了说清楚这个问题,以系统控制寄存器为例。
系统控制寄存器
名称 | 地址 | 大小(*16) | 描述 |
---|---|---|---|
PLLSTS | 0x7011 | 1 | PLL状态寄存器 |
保留 | 0x7012~0x7019 | 8 | |
HISPCP | 0x701A | 1 | 高速外设时钟分频寄存器 |
LOSPCP | 0x701B | 1 | 低速外设失踪分频寄存器 |
PCLKCR0 | 0x701C | 1 | 外设时钟控制寄存器0 |
PCLKCR1 | 0x701D | 1 | 外设时钟控制寄存器1 |
LPMCR0 | 0x701E | 1 | 低功模式控制寄存器0 |
保留 | 0x701F | 1 | |
PCLKCR3 | 0x7020 | 1 | 外设时钟控制寄存器 |
PLLCR | 0x7021 | 1 | PLL控制寄存器 |
SCSR | 0x7022 | 1 | 系统控制和状态寄存器 |
WDCNTR | 0x7023 | 1 | 看门狗计数寄存器 |
保留 | 0x7024 | 1 | |
WDKEY | 0x7025 | 1 | 看门狗复位寄存器 |
保留 | 0x7026~0x7028 | 3 | |
WDCR | 0x7029 | 1 | 看门狗控制寄存器 |
保留 | 0x702A~0x702F | 6 |
1.相关结构体
//---------------------------------------------------------------------------
// System Control Register File:
//起始地址应该是0x7010
struct SYS_CTRL_REGS {
Uint16 rsvd7; // 0
union PLLSTS_REG PLLSTS; // 1
Uint16 rsvd1[8]; // 2-9
union HISPCP_REG HISPCP; // 10: High-speed peripheral clock pre-scaler
union LOSPCP_REG LOSPCP; // 11: Low-speed peripheral clock pre-scaler
union PCLKCR0_REG PCLKCR0; // 12: Peripheral clock control register
union PCLKCR1_REG PCLKCR1; // 13: Peripheral clock control register
union LPMCR0_REG LPMCR0; // 14: Low-power mode control register 0
Uint16 rsvd2; // 15: reserved
union PCLKCR3_REG PCLKCR3; // 16: Peripheral clock control register
union PLLCR_REG PLLCR; // 17: PLL control register
// No bit definitions are defined for SCSR because
// a read-modify-write instruction can clear the WDOVERRIDE bit
Uint16 SCSR; // 18: System control and status register
Uint16 WDCNTR; // 19: WD counter register
Uint16 rsvd4; // 20
Uint16 WDKEY; // 21: WD reset key register
Uint16 rsvd5[3]; // 22-24
// No bit definitions are defined for WDCR because
// the proper value must be written to the WDCHK field
// whenever writing to this register.
Uint16 WDCR; // 25: WD timer control register
Uint16 rsvd6[6]; // 26-31
};
此处通过一个结构体,将系统控制寄存器中所有寄存器都包含其中。
2.定义结构体变量
volatile struct SYS_CTRL_REGS SysCtrlRegs;
这个结构体变量就具有了系统控制寄存器的整体结构了。下面的任务就是将这个变量映射到对应的寄存器地址。通过对比可以知道,SysCtrlRegs变量的映射地址应该是0x7010;
在DSP编程中,用#pragma的编程方式将变量映射到对应的地址。其具体的格式为:
//为变量分配地址
#ifdef __cplusplus
#pragma DATA_SECTION("SysCtrlRegsFile")//C++语法
#else
#pragma DATA_SECTION(SysCtrlRegs,"SysCtrlRegsFile");//C语言语法,若是C开发,可以只写这一句。
#endif
//定义结构体变量
volatile struct SYS_CTRL_REGS SysCtrlRegs;
上面代码,即通过#pragma的编程方式,将SysCtrlRegs变量与SysCtrlRegsFile文件所连接的地址连接起来。下面就是解决如何将数据块文件与寄存器地址连接起来。这就需要cmd文件来解决了。
补充
DATA_SECTION函数
//在C语言中
#pragma DATA_SECTION ( symbol , " section name ");
//在C++语言中
#pragma DATA_SECTION (" section name ");
在C语言中,这条代码的含义就是为symbol变量申请数据空间。将symbol变量的数据存储在section name对应的地址。
举例:
#pragma DATA_SECTION(bufferB, "my_sect")
char bufferB[512];//此处不可以是指针,是已经分配空间的数据类型。
CODE_SECTION函数
#pragma CODE_SECTION (symbol , "section name ")
在C语言中,这条代码的含义就是为symbol变量申请程序空间。将symbol变量的数据存储在section name对应的地址。
举例
char bufferA[80];
char bufferB[80];
#pragma CODE_SECTION(funcA, "codeA")
char funcA(int i);//fancA放入codeA映射的位置
char funcB(int i);//fanB放入.txet映射的位置
void main()
{
char c;
c = funcA(1);
c = funcB(2);
}
char funcA (int i)
{
return bufferA[i];
}
char funcB (int j)
{
return bufferB[j];
}
3.cmd文件
MEMORY
{
PAGE 0: /* Program Memory */
...
PAGE 1: /* Data Memory */
...
SYSTEM : origin = 0x007010, length = 0x000020 /* System control registers */
...
}
SECTIONS
{
...
SysCtrlRegsFile : > SYSTEM, PAGE = 1
...
}
如上代码所示,cmd文件中分成MEMORY和SECTIONS两个部分。需要注意的是,在cmd文件中注释方式只能是/**/,不可以为//。(我在CCS6.1上验证发现,//注释方式也是可以的。)
在MEMORY中,最多可以256个PAGE(PAGE0~PAGE255)。习惯上,将PAGE0作为程序空间,将PAGE作为数据空间。程序中的SYSTEM : origin = 0x007010, length = 0x000020,很容易理解,就是将SYSTEM的实际地址的起始地址为0x7010,长度为0x20(32个字长) 。
在SECTIONS中,将SysCtrlRegsFile 和SYSTEM链接起来。因而,上面定义的SysCtrlRegs就与系统控制寄存器建立起了以上关系。
结构体的位定义
取系统控制的第1个寄存器(锁相环状态寄存器)为例。
PLLSTS(锁相环状态寄存器)
位 | 名称 | 描述 |
---|---|---|
15~9 | 保留 | |
8~7 | DIVSEL | 时钟分频选择; 00,01:4分频; 10:2分频;11:1分频 |
6 | MCLKOFF | 时钟检测电路关闭位; 0:默认模式,时钟检测使能; 1:时钟检测电路关闭,系统不会进入limp-mode模式 |
5 | OSCOOF | 振荡器时钟禁止位; 0:晶振时钟信号会被送到PLL电路; 1:晶振时钟信号不会被送到PLL电路。 当此位=1时, 此时不要进入HALT或STANDBY模式,不要写入PLLCR寄存器,否则会倒追不可预知的后果; 看门狗行为与输入时钟有关,若X1或X1与X2,看门狗不工作。XCLKIN:看门狗工作。 OSCOFF用于测试时钟监视逻辑电路。 |
4 | MCLKCLR | 时钟丢失状态清除位 0:写无影响,读取返回0; 1:强制将时钟检测电路进行复位。如果OSCCLK(晶振时钟)依然丢失,时钟检测电路会再次产生一次系统复位信号,并将MCLKSTS置位,此时CPU的工作时钟为limp-mode模式产生的低速时钟 |
3 | MALKSTS | 对该位写无效,写MCLKCLR或者外部复位时候,该位被清除 0:正常模式,时钟信号没有丢失; 1:晶振信号丢失,CPU工作在limp-mode |
2 | PLLOFF | PLL电路关闭位,只有PLLCR=0时候,才能将PLL电路关闭 0:PLL电路正常工作 1:PLL电路关闭 |
1 | 保留 | |
0 | PLLLOCKS | PLL电路状态位 0:表示锁相环依然正在锁相,此时最好等待 1:PLL电路已经锁相完成 |
位区结构体
struct PLLSTS_BITS { // bits description
Uint16 PLLLOCKS:1; // 0 PLL lock status
Uint16 rsvd1:1; // 1 reserved
Uint16 PLLOFF:1; // 2 PLL off bit
Uint16 MCLKSTS:1; // 3 Missing clock status bit
Uint16 MCLKCLR:1; // 4 Missing clock clear bit
Uint16 OSCOFF:1; // 5 Oscillator clock off
Uint16 MCLKOFF:1; // 6 Missing clock detect
Uint16 DIVSEL:2; // 7:8 Divide Select
Uint16 rsvd2:7; // 15:9 reserved
};
上面结构体定义了PLL状态寄存器的每一位的名称。位区结构体定义时需要注意:
- 位定义由低位到高位
- 每个名称后面带有冒号,冒号后接着是位长。
- 并不是所有的寄存器都需要定义位区,从前面SYS_CTRL_REGS结构体中也可看出!
下面需要将位区与整个寄存器链接起来。在DSP的编程中,使用的方法是共同体。
共同体
union PLLSTS_REG
{
Uint16 all;
struct PLLSTS_BITS bit;
};
通过共同体,将结构体PLLSTS_REG中all变量和bit变量共享一块存储空间,而此时,通过操作结构体PLLSTS_REG变量即可方便操作整个寄存器或者寄存器的每一位。这里从上篇博客(F28335第二篇——系统控制初始化)中选取一个例子 。
SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;//保证PLL是4分频
整体操作的也是类似:
SysCtrlRegs.PLLSTS.all = 0