用过F2812的朋友一定会对cmd文件很熟悉,因为这个文件中为每个程序和数据分配了相应的地址。我们常用的cmd文件包括连个:
(1)
DSP281x_Headers_nonBIOS.cmd
(2)
F2812_EzDSP_RAM_lnk.cmd
DSP281x_Headers_nonBIOS.cmd
上面第一个文件用于对DSP外设分配地址,而第二个文件是为系统的程序和数据分配地址。当然,如果DSP的外设地址我们用C语言已经自己定义,那第一个文件我们就可以不用了,笔者就是自己定义的,所以没有用到第一个文件。对于为什么要自己定义外设寄存器以及中断地址,有这几个原因:
(1)
自己定义外设寄存器地址可以很清楚的了解DSP的工作原理,虽然这样很耗费时间,但是会了解到DSP的中断等等是怎么工作的。
(2)
因为DSP外设寄存器地址的分配时采用寄存器形式分配到的。举个例子,以sci串口通信为例,其他的外设以及中断都一样。
比如我们设置波特率,
肯定是设置某个寄存器的相应位来实现。而DSP是这样实现的:
ScibRegs.SCIHBAUD = (BRRVal >> 8);
ScibRegs.SCILBAUD = (BRRVal);
上面的程序, ScibRegs是串口的寄存器结构体,也就说说DSP的每个寄存器都是以结构体为单位来分配地址的,而每个寄存器的具体地址通过偏移来分配,因为在DSP中,某个外设的寄存器地址是基本连续的。而这个结构体的定义是这样的:
extern
volatile struct
SCI_REGS SciaRegs;
extern
volatile struct
SCI_REGS ScibRegs;
大家可以看到,这里定义了两个结构体,一个是SciaRegs
,一个是ScibRegs,因为DSP内部有两个串行通信,A和B,这两个都是结构体,而类型就是前面的SCI_REGS的,这个结构体类型是这样定义的:
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;
// Recieve status register
Uint16
SCIRXEMU; // Recieve emulation buffer
register
union
SCIRXBUF_REG
SCIRXBUF; // Recieve data
buffer
Uint16
rsvd1;
// reserved
Uint16
SCITXBUF; // Transmit data
buffer
union
SCIFFTX_REG
SCIFFTX;
// FIFO transmit register
union
SCIFFRX_REG
SCIFFRX; //
FIFO recieve register
union
SCIFFCT_REG
SCIFFCT;
// FIFO control register
Uint16
rsvd2;
// reserved
Uint16
rsvd3;
// reserved
union
SCIPRI_REG
SCIPRI;
// FIFO Priority
control
};
也是就就是说,在分配地址的时候,只要给定义好的结构体一个起始地址,后面的寄存器地址就可以根据后面的字长进行分配。rsvd1,rsvd2,rsvd3表示不分配给任何寄存器,只是为了获得后面地址的偏移而已。这个结构体中还有union union SCIFFTX_REG等,而这个共同体是这样定义:
union SCIFFTX_REG {
Uint16
all;
struct
SCIFFTX_BITS bit;
}
对这个共同体进行操作,要么是all,要么是struct SCIFFTX_BITS
bit,而后面的这个结构体是这样定义的:
struct
SCIFFTX_BITS
{
// bit
description
Uint16
TXFFILIL:5;
// 4:0
Interrupt level
Uint16
TXFFIENA:1;
//
5
Interrupt enable
Uint16
TXINTCLR:1;
//
6
Clear INT flag
Uint16
TXFFINT:1;
//
7
INT flag
Uint16
TXFFST:5;
// 12:8 FIFO status
Uint16
TXFIFOXRESET:1;
//
13
FIFO reset
Uint16
SCIFFENA:1;
//
14
Enhancement enable
Uint16
SCIRST:1;
//
15
SCI reset rx/tx channels
};
大家一看就知道这是每个寄存器的位,而后面的数字,例如TXFFILIL:5后面的5,表示这个寄存器位是连续使用5个位。其实说到这里,大家应该明白DSP是怎么使用外设寄存器的了吧,例如下面:
ScibRegs.SCIRXST.bit.RXRDY =
1;
就是把ScibRegs
结构体中,SCIRXST
寄存器的RXRDY位置1
,而
ScibRegs.SCIRXST.all =
1;就是把SCIRXST所有位置1.
DSP的外设和系统寄存器都是这样分配的。这样有一个好处,就是这样看到每个寄存器每个位,省去了查阅datasheet的麻烦,也会时操作更明显,当然,任何事物都有两面性,这样调用至少一个结构体,会浪费很多堆栈,使系统运行迟钝。说到这里还没说地址到底置怎么分配,好我们看文件DSP281x_GlobalVariableDefs.c。还是串口通信的例子。
//----------------------------------------
#ifdef __cplusplus
#pragma
DATA_SECTION("SciaRegsFile")
#else
#pragma
DATA_SECTION(SciaRegs,"SciaRegsFile");
#endif
volatile
struc t SCI_REGS
SciaRegs;
//----------------------------------------
#ifdef __cplusplus
#pragma
DATA_SECTION("ScibRegsFile")
#else
#pragma
DATA_SECTION(ScibRegs,"ScibRegsFile");
#endif
volatile
struct SCI_REGS
ScibRegs;
看到上面的程序,我又必要说一个#pragma
.DSP中,pragma对象告诉编译器的预处理器如何对待函数,而且必须在声明、定义、引用程序或数据之前指定
pragma
。#pragma后面一般接两种形式:
(1)
#pragma
DATA_SECTION
(2)
#pragma
CODE_SECTION
顾名思义,这两种一个是针对数据,一个是针对程序。举例说明:
#pragma
CODE_SECTION(funcA,“codeA ”);
char
funcA(int
i);
//对函数funcA
的声明
上面中,funcA函数一定是外部声明或者定义的函数,CODE_SECTION为funcA
在一个名为codeA
的块中指定空间。如果你有一个代码对象并想将其链接到不同于.txt
的空间时,该语法就非常有用。而codeA在.cmd文件中指定了物理地址。
#pragma
DATA_SECTION(bufferB,“my_sect”);
char bufferB
[512];
//
对bufferB的定义放在pragma
的后面
DATA_SECTION为bufferB
在一个名为 my_sect
的块中指定空间。如果你有一个数据对象并想将其链接到不同于.bss
的空间时,该语法就非常有用.
数据块bufferB被定位于my_sect段中,my_sect段在
.cmd
文件中规定了物理地址。
看完上面的一段解释,估计您会对#pragma有了很好的了解了吧
我们使用的结构体是数据而不是函数,所以我们使用DATA_SECTION。其实就是把SciaRegs和ScibRegs分别在块SciaRegsFile和ScibRegsFile中指定空间,而块SciaRegsFile和ScibRegsFile在.cmd文件中指定了物理地址。这样一来,是不是结构体SciaRegs和ScibRegs的起始地址就知道了?答案是肯定的。我们在看分析.cmd文件。.cmd文件的源代码如下:
MEMORY
{
PAGE
0:
PAGE
1:
DEV_EMU
: origin = 0x000880, length =
0x000180
PIE_VECT
: origin = 0x000D00, length =
0x000100
FLASH_REGS : origin = 0x000A80, length =
0x000060
CSM
: origin = 0x000AE0, length =
0x000010
XINTF
: origin = 0x000B20, length =
0x000020
CPU_TIMER0 : origin = 0x000C00, length =
0x000008
PIE_CTRL
: origin = 0x000CE0, length =
0x000020
ECANA
: origin = 0x006000, length =
0x000040
ECANA_LAM : origin = 0x006040,
length =
0x000040
ECANA_MOTS : origin = 0x006080, length =
0x000040
ECANA_MOTO : origin = 0x0060C0, length =
0x000040
ECANA_MBOX : origin = 0x006100, length =
0x000100
SYSTEM
: origin = 0x007010, length =
0x000020
SPIA
: origin = 0x007040, length =
0x000010
SCIA
: origin = 0x007050, length =
0x000010
XINTRUPT :
origin = 0x007070, length =
0x000010
GPIOMUX
: origin = 0x0070C0, length =
0x000020
GPIODAT
: origin = 0x0070E0, length =
0x000020
ADC
: origin = 0x007100, length =
0x000020
EVA
: origin = 0x007400, length =
0x000040
EVB
: origin = 0x007500, length =
0x000040
SCIB
: origin = 0x007750, length =
0x000010
MCBSPA
: origin = 0x007800, length =
0x000040
CSM_PWL
: origin = 0x3F7FF8, length =
0x000008
}
SECTIONS
{
PieVectTableFile : >
PIE_VECT, PAGE = 1
DevEmuRegsFile : >
DEV_EMU,
PAGE = 1
FlashRegsFile
: > FLASH_REGS,
PAGE = 1
CsmRegsFile
: >
CSM,
PAGE
= 1
XintfRegsFile
: >
XINTF,
PAGE
= 1
CpuTimer0RegsFile : > CPU_TIMER0,
PAGE =
1
PieCtrlRegsFile
: >
PIE_CTRL,
PAGE
=
1
SysCtrlRegsFile
: >
SYSTEM,
PAGE = 1
SpiaRegsFile
: >
SPIA,
PAGE =
1
SciaRegsFile
: >
SCIA,
PAGE
= 1
XIntruptRegsFile
: >
XINTRUPT,
PAGE = 1
GpioMuxRegsFile : >
GPIOMUX,
PAGE = 1
GpioDataRegsFile : >
GPIODAT
PAGE =
1
AdcRegsFile
: >
ADC,
PAGE =
1
EvaRegsFile
: >
EVA,
PAGE
= 1
EvbRegsFile
: >
EVB,
PAGE =
1
ScibRegsFile
: >
SCIB,
PAGE
= 1
McbspaRegsFile
: >
MCBSPA,
PAGE = 1
ECanaRegsFile
: >
ECANA,
PAGE =
1
ECanaLAMRegsFile : >
ECANA_LAM
PAGE =
1
ECanaMboxesFile : >
ECANA_MBOX
PAGE = 1
ECanaMOTSRegsFile : >
ECANA_MOTS
PAGE = 1
ECanaMOTORegsFile : >
ECANA_MOTO PAGE = 1
CsmPwlFile
: >
CSM_PWL,
PAGE = 1
}
上面的程序可以看到,
SciaRegsFile
: >
SCIA,
PAGE
=
1
和
ScibRegsFile
: >
SCIB,
PAGE
= 1
Section的作用就是把输入段定义到那个地址空间,以及哪一页。伪指令
MEMORY用来标示每个存储器的名字、起始范围和长度。Page0
是第0
页,通常规定为程序存储器;Page1
是第1
页,规定为数据存储器。伪指令SECTIONS
描述输入段怎样被组合到输出段并规定在存储器内如何放置输出段。这样,SciaRegsFile的地址就是第一页的SCIA的地址,ScibRegsFile的地址就是第二页的SCIB的地址,在伪指令MEMORY中,SCIA和SCIB是这样定义的:
SCIA
: origin = 0x007050, length =
0x000010
SCIB
: origin = 0x007750, length =
0x000010
加载中,请稍候......