很多初学者往往对TI DSP的大量的寄存器定义和组织形式感到迷茫,我从TI文档中翻译了这篇文章,希望能对初学者有所帮助。
以往寄存器定义一般用宏定义。例如:
/********************************************************************
* Traditional header file
********************************************************************/
#define Uint16 unsigned int
#define Uint32 unsigned long
// Memory Map
// Addr Register
#define SCICCRA (volatile Uint16 *)0x7050 // 0x7050 SCI-A Communications Control
#define SCICTL1A (volatile Uint16 *)0x7051 // 0x7051 SCI-A Control Register 1
#define SCIHBAUDA (volatile Uint16 *)0x7052 // 0x7052 SCI-A Baud Register, High Bits
#define SCILBAUDA (volatile Uint16 *)0x7053 // 0x7053 SCI-A Baud Register, Low Bits
#define SCICTL2A (volatile Uint16 *)0x7054 // 0x7054 SCI-A Control Register 2
#define SCIRXSTA (volatile Uint16 *)0x7055 // 0x7055 SCI-A Receive Status
#define SCIRXEMUA (volatile Uint16 *)0x7056 // 0x7056 SCI-A Receive Emulation Data Buffer
#define SCIRXBUFA (volatile Uint16 *)0x7057 // 0x7057 SCI-A Receive Data Buffer
#define SCITXBUFA (volatile Uint16 *)0x7059 // 0x7059 SCI-A Transmit Data Buffer
#define SCIFFTXA (volatile Uint16 *)0x705A // 0x705A SCI-A FIFO Transmit
#define SCIFFRXA (volatile Uint16 *)0x705B // 0x705B SCI-A FIFO Receive
#define SCIFFCTA (volatile Uint16 *)0x705C // 0x705C SCI-A FIFO Control
#define SCIPRIA (volatile Uint16 *)0x705F // 0x705F SCI-A Priority Control
#define SCICCRB (volatile Uint16 *)0x7750 // 0x7750 SCI-B Communications Control
#define SCICTL1B (volatile Uint16 *)0x7751 // 0x7751 SCI-B Control Register 1
#define SCIHBAUDB (volatile Uint16 *)0x7752 // 0x7752 SCI-B Baud Register, High Bits
#define SCILBAUDB (volatile Uint16 *)0x7753 // 0x7753 SCI-B Baud Register, Low Bits
#define SCICTL2B (volatile Uint16 *)0x7754 // 0x7754 SCI-B Control Register 2
#define SCIRXSTB (volatile Uint16 *)0x7755 // 0x7755 SCI-B Receive Status
#define SCIRXEMUB (volatile Uint16 *)0x7756 // 0x7756 SCI-B Receive Emulation Data Buffer
#define SCIRXBUFB (volatile Uint16 *)0x7757 // 0x7757 SCI-B Receive Data Buffer
#define SCITXBUFB (volatile Uint16 *)0x7759 // 0x7759 SCI-B Transmit Data Buffer
#define SCIFFTXB (volatile Uint16 *)0x775A // 0x775A SCI-B FIFO Transmit
#define SCIFFRXB (volatile Uint16 *)0x775B // 0x775B SCI-B FIFO Receive
#define SCIFFCTB (volatile Uint16 *)0x775C // 0x775C SCI-B FIFO Control
#define SCIPRIB (volatile Uint16 *)0x775F // 0x775F SCI-B Priority Control
使用位域寄存器文件结构,可以更加灵活和高效的访问DSP的寄存器。
寄存器文件结构:用C/C++中结构体成员的方式将DSP各种外设的寄存器的集合在一起,称之为寄存器文件结构。
位域定义:位域用于定义寄存器中每个功能块的名字和长度。
(1))寄存器文件的定义
以F2812的SCI外设寄存器为例介绍寄存器文件的定义。下面的程序段用C/C++结构体成员的方式将SCI各个寄存器组合成为一个结构体类型SCI_REGS。
/********************************************************************
* SCI header file
* Defines a register file structure for the SCI peripheral
********************************************************************/
#define Uint16 unsigned int
#define Uint32 unsigned long
struct SCI_REGS {
Uint16 SCICCR_REG SCICCR; // Communications control register
Uint16 SCICTL1_REG SCICTL1; // Control register 1
Uint16 SCIHBAUD; // Baud rate (high) register
Uint16 SCILBAUD; // Baud rate (low) register
Uint16 SCICTL2_REG SCICTL2; // Control register 2
Uint16 SCIRXST_REG SCIRXST; // Receive status register
Uint16 SCIRXEMU; // Receive emulation buffer register
Uint16 SCIRXBUF_REG SCIRXBUF; // Receive data buffer
Uint16 rsvd1; // reserved
Uint16 SCITXBUF; // Transmit data buffer
Uint16 SCIFFTX_REG SCIFFTX; // FIFO transmit register
Uint16 SCIFFRX_REG SCIFFRX; // FIFO receive register
Uint16 SCIFFCT_REG SCIFFCT; // FIFO control register
Uint16 rsvd2; // reserved
Uint16 rsvd3; // reserved
Uint16 SCIPRI_REG SCIPRI; // FIFO Priority control
};
单纯的结构体定义本身并没有生成任何变量,下面的代码段用结构体类型SCI_REGS定义了DSP的两个SCI模块的寄存器文件SciaRegs和ScibRegs。
/********************************************************************
* Source file using register-file structures
* Create a variable for each of the SCI register files
********************************************************************/
volatile struct SCI_REGS SciaRegs;
volatile struct SCI_REGS ScibRegs;
上面代码中的关键字volatile非常重要,一个被声明为volatile的变量可以随时被程序本身之外的事件改变。例如,外设寄存器可以被DSP硬件本身或中断改变,如果寄存器没有被声明为volatile,编译器就会假设寄存器只会在它在代码中出现的地方被改变,从而编译器可能会把在其他地方访问寄存器看作不必要而优化掉。一个被声明了volatile的变量的访问是不会被优化掉的。
(2)寄存器文件结构的空间分配
编译器为代码和数据分配和重定位的存储块,这些块称为section。将section指定到存储空间是在链接命令文件(linker command file)中完成的。
默认情况下,编译器会将SciaRegs和ScibRegs这样的全局和静态变量指定到.ebss或者.bss section。但是,在基于硬件抽象层(abstraction layer, hardware abstraction layer是操作系统中逻辑层中的硬件层,通过硬件抽象层,可以使操作系统忽略硬件细节,以一种抽象方式来访问硬件)的情况下,寄存器文件变量被定位到同一个外设寄存器区域。应用编译器的预处理指令DATA_SECTION,每一个寄存器变量都被指定到.bss/.ebss之外的一个特定的数据段。
C中,预处理指令DATA_SECTION的语法是:
#pragma DATA_SECTION (symbol,"section name")
C++中,预处理指令DATA_SECTION的语法是:
#pragma DATA_SECTION ("section name")
DATA_SECTION预处理指令将symbol定位到名为section name的存储空间中。下面的代码中,应用预处理指令DATA_SECTION将寄存器文件结构变量SciaRegs和ScibRegs分别定位到名为SciaRegsFile和ScibRegsFile的section中,SciaRegsFile和ScibRegsFile是被DSP的SCI外设寄存器占据的空间。
/********************************************************************
* Assign variables to data sections using the #pragma compiler statement
* C and C++ use different forms of the #pragma statement
* When compiling a C++ program, the compiler will define __cplusplus automatically
********************************************************************/
//----------------------------------------
#ifdef __cplusplus
#pragma DATA_SECTION("SciaRegsFile")
#else
#pragma DATA_SECTION(SciaRegs,"SciaRegsFile");
#endif
volatile struct SCI_REGS SciaRegs;
//----------------------------------------
#ifdef __cplusplus
#pragma DATA_SECTION("ScibRegsFile")
#else
#pragma DATA_SECTION(ScibRegs,"ScibRegsFile");
#endif
volatile struct SCI_REGS ScibRegs;
重复使用这种方式可将DSP每个外设寄存器文件配置到特定的寄存器空间中去。外设寄存器存储空间地址是在链接命令文件(linker command)中指定的,例如F2812的SCI-A寄存器空间地址是从0x7050开始的,因此寄存器文件结构体变量SciaRegs被分配到从0x7050开始的空间中,空间分配是在命令文件(.cmd)中定义的,如下面代码所示:
/********************************************************************
* Memory linker .cmd file
* Assign the SCI register-file structures to the corresponding memory
********************************************************************/
MEMORY
{
...
PAGE 1:
SCIA : origin = 0x007050, length = 0x000010 /* SCI-A registers */
SCIB : origin = 0x007750, length = 0x000010 /* SCI-B registers */
...
}
SECTIONS
{
...
SciaRegsFile : > SCIA, PAGE = 1
ScibRegsFile : > SCIB, PAGE = 1
...
}
将寄存器结构体变量分配到DSP寄存器空间地址中,就可以在C/C++代码通过引用结构体成员的方式来访问寄存器。如
/********************************************************************
* User's source file
********************************************************************/
...
SciaRegs.SCICCR = SCICCRA_MASK;
ScibRegs.SCICCR = SCICCRB_MASK;
...
(3)位域定义
在实际中经常会对寄存器的某些特点位进行读写,位域定义可以灵活的完成这些操作。这里应用了C/C++中位段的概念。
在C28x器件中,遵循如下位域规则:
<1>在存储空间中,位域成员存储顺序是从右到左。即最低有效位(零位),对应寄存器的第一个位域。
<2>C28x编译器限制位域的长度为最大16位。
<3>如果在一个结构体中定义的所有位域的总位数超过16位,那么位域会在下一个字中连续存放。
以SCICCR和SCICTL1寄存器为例说明位域的定义,两个寄存器位定义如下图所示:
应用位域定义可以将上面的寄存器用下面代码表示:
/********************************************************************
SCI header file
********************************************************************/
//----------------------------------------------------------
// SCICCR communication control register bit definitions:
//
struct SCICCR_BITS { // bit description
Uint16 SCICHAR:3; // 2:0 Character length control
Uint16 ADDRIDLE_MODE:1; // 3 ADDR/IDLE Mode control
Uint16 LOOPBKENA:1; // 4 Loop Back enable
Uint16 PARITYENA:1; // 5 Parity enable
Uint16 PARITY:1; // 6 Even or Odd Parity
Uint16 STOPBITS:1; // 7 Number of Stop Bits
Uint16 rsvd1:8; // 15:8 reserved
};
//-------------------------------------------
// SCICTL1 control register 1 bit definitions:
//
struct SCICTL1_BITS { // bit description
Uint16 RXENA:1; // 0 SCI receiver enable
Uint16 TXENA:1; // 1 SCI transmitter enable
Uint16 SLEEP:1; // 2 SCI sleep
Uint16 TXWAKE:1; // 3 Transmitter wakeup method
Uint16 rsvd:1; // 4 reserved
Uint16 SWRESET:1; // 5 Software reset
Uint16 RXERRINTENA:1; // 6 Receive interrupt enable
Uint16 rsvd1:9; // 15:7 reserved
};
(4)应用共用体
位域定义可以实现对寄存器位的操作,但是对整个寄存器的操作也是必需的。应用共用体可以方便的实现这个功能。对SCI communications control 寄存器和控制寄存器1的共用体定义如下面代码所示:
/********************************************************************
* SCI header file
********************************************************************/
union SCICCR_REG {
Uint16 all;
struct SCICCR_BITS bit;
};
union SCICTL1_REG {
Uint16 all;
struct SCICTL1_BITS bit;
};
一旦寄存器的位域和共用体定义建立起来,那么寄存器文件结构体可以以共用体为成员建立。如下面代码所示,注意并不是所有寄存器都有位域定义,有的寄存器,例如SCITXBUF,只会被整体访问,没有必要对它的某些位进行访问。
/********************************************************************
* SCI header file
********************************************************************/
//---------------------------------------------------------------------------
// SCI Register File:
//
struct SCI_REGS {
union SCICCR_REG SCICCR; // Communications control register
union SCICTL1_REG SCICTL1; // Control register 1
Uint16 SCIHBAUD; // Baud rate (high) register
Uint16 SCILBAUD; // Baud rate (low) register
union SCICTL2_REG SCICTL2; // Control register 2
union SCIRXST_REG SCIRXST; // Receive status register
Uint16 SCIRXEMU; // Receive emulation buffer register
union SCIRXBUF_REG SCIRXBUF; // Receive data buffer
Uint16 rsvd1; // reserved
Uint16 SCITXBUF; // Transmit data buffer
union SCIFFTX_REG SCIFFTX; // FIFO transmit register
union SCIFFRX_REG SCIFFRX; // FIFO receive register
union SCIFFCT_REG SCIFFCT; // FIFO control register
Uint16 rsvd2; // reserved
Uint16 rsvd3; // reserved
union SCIPRI_REG SCIPRI; // FIFO Priority control
};
经过这样定义后,可以方便灵活在C/C++中对DSP的寄存器进行读写,如下面代码所示:
/********************************************************************
* User's source file
********************************************************************/
// Access registers without a bit field definition (.all, .bit not used)
SciaRegs.SCIHBAUD = 0;
SciaRegs.SCILBAUD = 1;
// Write to bit fields in SCI-A SCICTL1
SciaRegs.SCICTL1.bit.SWRESET = 0;
SciaRegs.SCICTL1.bit.SWRESET = 1;
SciaRegs.SCIFFCT.bit.ABDCLR = 1;
SciaRegs.SCIFFCT.bit.CDC = 1;
// Poll (i.e., read) a bit
while(SciaRegs.SCIFFCT.bit.CDC == 1) { }
// Write to the whole SCI-B SCICTL1/2 registers (use .all)
ScibRegs.SCICTL1.all = 0x0003;
ScibRegs.SCICTL2.all = 0x0000;