TI公司的DSP处理器(本文以C2000的F28377D为例,其他处理器同样原理)的程序为了实现精确的定时,使用了一个用汇编编写的延时函数DSP28x_usDelay(),该函数在文件F2837xD_usDelay.asm中,程序的细节请阅读该文件。为了调用该函数,在F2837xD_Examples.h中做了如下声明:
extern void F28x_usDelay(long LoopCount);// DO NOT MODIFY THIS LINE.
#define DELAY_US(A) F28x_usDelay(((((long double) A * 1000.0L) / (long double)CPU_RATE) - 9.0L) / 5.0L)
利用宏定义的方式做了宏定义修正,来确保定时更加精确。
值得特别注意的是:
函数F28x_usDelay(long LoopCount)要想非常精确,必须在SARAM中运行,因此需要在运行时将其调入SARAM中。
下面谈谈如何实现将其调入SARAM中,需要做以下几个工作:
首先,在F2837xD_usDelay.asm中使用
.sect "ramfuncs"
上面代码意思是将该段代码定义到段“ramfuncs”中,段ramfuncs的位置在编译时指定,实际上是在CMD文件中的如下语句来指定:
ramfuncs LOAD = FLASHD, PAGE = 0,
RUN = RAML0, PAGE = 1,
LOAD_START(_RamfuncsLoadStart),
LOAD_END(_RamfuncsLoadEnd),
RUN_START(_RamfuncsRunStart)
第1行是将该段的装载在PAGA0的FLASHD中(PAGA0和FLASHD的分段请见CMD文件)
第2行表示该段的运行地址在PAGE1的RAML0中(PAGA1和RAML0的分段请见CMD文件);
LOAD_ START(_RamfuncsLoadStart)
令编译器创建了一个变量RamfuncsLoadStart
,该变量指向段ramfuncs的装载地址的首地址(其中,LOAD_ START为编译伪指令,详见CCS的帮助文档);
LOAD_ START(_RamfuncsLoadEnd)
令编译器创建了一个变量RamfuncsLoadEnd
,该变量指向段ramfuncs的装载地址的末地址(LOAD_ END为编译伪指令,详见CCS的帮助文档);
LOAD_ START(_RamfuncsRunStart)
令编译器创建了一个变量RamfuncsRunStart
,该变量指向段ramfuncs的运行地址的首地址(LOAD_ START为编译伪指令,详见CCS的帮助文档);
从第1和2行可以看出,段ramfuncs中的函数DSP28x_usDelay()的装载地址和运行地址是不同的,本程序中装载在Flash的块FLASHD中,而在SARAM L0中运行,但这只是需求目标,在实际运行时,DSP并不会自动将Flash中的代码拷贝到SARAM中,因此我们需要手动添加代码来完成。
在C函数中,为了使用变量RamfuncsLoadStart
、RamfuncsLoadEnd
和RamfuncsRunStart
,必须先声明,在工程文件DSP2837xD_GlobalPrototypes.h中已经有做了如下声明:
extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadEnd;
extern Uint16 RamfuncsLoadSize;
extern Uint16 RamfuncsRunStart;
extern Uint16 RamfuncsRunEnd;
extern Uint16 RamfuncsRunSize;
但更常用的做法是直接在main程序前做如下定义:
extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadSize;
extern Uint16 RamfuncsRunStart;
在main函数中调用如下函数,然后就可以使用了。
memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);// 把分配到 ramfuncs 段中的实时性要求高的关键代码从Flash中拷贝到RAM里执行
在上面main.c中,使用memCopy()
函数将段ramfuncs中的函数DSP28x_usDelay()
的代码从装载地址RamfuncsLoadStart—RamfuncsLoadEnd
拷贝到RamfuncsRunStart
开始的SARAM空间中。
在此之后我们便可以在主程序中调用延时函数DELAY_US(A)
了,在程序运行时,只要调用DSP28x_usDelay()函数,都会自动地指向SARAM中相应的函数入口地址,这一点是自动完成的。memCopy()
函数原型在memCopy.c中,在DSP2837xD_GlobalPrototypes.h声明。
最后但值得注意的是:即使这样做了上面的一系列校准工作,定时仍然有可能不完全精确,因为可能有中断可能会打断DSP28x_usDelay()的运行,所以在需求延时函数精准无误的情况下,在调用之前务必要关中断。