1)、定义一个寄存器文件结构体,SCI外设的寄存器在结构体中按实际的地址由低向高依次列出。
/********************************************************************
* 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
};
2)、上面的定义本身并没有建立任何的变量,只是定义了一个结构体,而并没有实例化。下面即定义了具体的变量。注意在这里使用了volatile关键字,它在这里的作用很重要,这使得编译器不会做一些错误的优化。
/********************************************************************
* 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;
3)、利用DATA_SECTION Pragma,将寄存器文件结构体变量分配到特殊的数据段中。如果不使用这条指令,那么定义的寄存器文件结构体变量默认是被分配在.ebss或者.bss段的,但通过使用DATA_SECTION Pragma指令,编译器会将其放在了一个特殊的数据段中。具体实现如下:
/********************************************************************
* 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;
通过上面的代码可以看到,定义的SciaRegs被分配到了SciaRegsFile段中,ScibRegs被分配到了ScibRegsFile段中。
4)、上面只是将定义的寄存器结构体变量分配到了一个特殊的数据段中,通过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
...
}
5)、添加位域定义。
获取寄存器中特定的位经常是很有用的,位域的定义就提供了这种方便性;但是与此同时位域也缺乏硬件平台之间的可移植性。在位域的定义中,最低位,也就是0位,是寄存器中的第一个位域;位域不能超过寄存器的位数,最多为16位。
/********************************************************************
* SCI header file
********************************************************************/
//----------------------------------------------------------
// SCICCR communication control register bit definitions:
//
struct SCICCR_BITS { // bit deion
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 deion
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
};
在上面的定义中,使用了操作符“:”,用来说明位域的长度,即当前位域占几位。
6)、使用联合体。除了能够方便的访问位域外,有时候也希望能够对寄存器整体访问,使用联合体能够实现这种操作。
/********************************************************************
* SCI header file
********************************************************************/
union SCICCR_REG {
Uint16 all;
struct SCICCR_BITS bit;
};
union SCICTL1_REG {
Uint16 all;
struct SCICTL1_BITS bit;
};
7)、将添加位域后的寄存器结构体重新实现。
/********************************************************************
* 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
};
进行“读-修改-写”操作时需要注意的寄存器,及其解决方案
1、在“读-修改-写”操作时,硬件可能修改的寄存器。
(1)在需要清除PIEIFRx某个位的值的时候,需要借助CPU的中断来清除。这时将修改中断向量表,将对应的中断重新分配到一个假的ISR中,然后让CPU进入这个假的ISR,自动清除相应的位,然后再恢复中断向量表。
(2)当对GPxDAT进行操作时,由于GPxDAT反映的是引脚上的值,在对其连续“读-修改-写”操作时,由于读和写操作的时间不同,会得到不希望的结果。解决措施是:不通过GPxDAT改变引脚的值,而使用其他寄存器GPxSET/GPxCLEAR/GPxTOGGLE,由于这些寄存器只对具体的位操作,因而不会影响到其他的位。
2、具有写1清除位的寄存器。
例如TCR寄存器中的TIF位,当向其中写1的时候回将其清零。在读取它的值之前如果先要停止寄存器,就要对TSS位操作,这时就会发生一次“读-修改-写”操作。如果此时TIF为1,经过这个操作后就会被清零,所以后面的质量永远也检测到TIF为1。比如下面的例子:
// Stop the CPU-Timer
CpuTimer0Regs.TCR.bit.TSS = 1; 3F80C7 MOVW DP,#0x0030
3F80C9 OR @4,#0x0010
// Check to see if TIF is set 3F80CB TBIT @4,#15
if (CpuTimer0Regs.TCR.bit.TIF == 1) 3F80CC SBF L1,NTC
{ 3F80CD NOP
// TIF set, insert action here 3F80CE L1:
// NOP is only a place holder ....
asm(" NOP");
}
解决的方法是使用一个虚拟的寄存器,在停止定时器时,对TIF位写0,这样就不会改变TIF的值了。示例代码如下:
union TCR_REG shadowTCR;
// Use a shadow register to stop the timer
// and preserve TIF (write 1-to-clear bit)
shadowTCR.all = CpuTimer0Regs.TCR.all; 3F80C7 MOVW DP,#0x0030
shadowTCR.bit.TSS = 1; 3F80C9 MOV AL,@4
shadowTCR.bit.TIF = 0; 3F80CA ORB AL,#0x10
CpuTimer0Regs.TCR.all = shadowTCR.all; 3F80CB MOVL XAR5,#0x000C00
3F80CD AND AL,@AL,#0x7FFF
// Check the TIF flag 3F80CF MOV *+XAR5[4],AL
if(CpuTimer0Regs.TCR.bit.TIF == 1) 3F80D0 TBIT *+XAR5[4],#15
{ 3F80D1 SBF L1,NTC
// TIF set, insert action here 3F80D2 NOP
// NOP is only a place holder 3F80D3 L1:
asm(" NOP");
}
3、需要特定值的寄存器。
在向WDCHK中的检查位写数的时候必须始终为1,0,1;否则就会被认为是不合法的,将复位器件。但是在读取的时候这几位始终为0,0,0;如果将这个值写回,那么就会造成器件的复位。解决方法是:在头文件中,不对WDCHK定义位域操作,这样就避免了对WDCHK的“读-修改-写”操作,在对其操作时只有一个固定的写操作。示例代码如下:
SysCtrlRegs.WDCR = 0x0068;
对F28335的程序来讲,它充分利用了位域和寄存器文件结构体,通过这种结构将众多的外设组织起来了,甚至中断向量表也是通过这种结构来实现的。
此文章转自:http://blog.21ic.com/user1/8225/archives/2011/85639.html