学习版主的一个精华帖,学习c2000的启动过程和搬运代码过程。
[原创文章] 【TI C2000的使用经验】CMD文件解读&从flash里搬运RAM函数
https://bbs.eeworld.com.cn/thread-459167-1-5.html
下面我也说说我自己理解的启动过程和代码搬运过程吧,求勿喷!!!
用到TI的c2000,cmd文件是避不开的一个槛,TI这样干个人感觉有点奇葩,或者说让程序员去参与存储的分配有点奇葩,也或许是我没能理解其真正的意图所在吧。既然是绕不开的行业器件,又是绕不开的技术话题,只能硬着头皮慢慢啃吧。 片子上给用户存储程序和数据的主要是saram和flash,代码既可以在saram里运行,也可以在flash里运行,但是saram高速但容量小,flash刚好相反。c2000主要应用在电机控制和数字电源领域,实时性要求很高,那么怎么办?首先c2000系列产品自带的ram都偏小,通过器件选型不能根本解决问题。外扩ram,是个不错的选择,如果工程足够大,实时性要求足够高,这个是唯一选择。但是针对一般应用,可以考虑代码存储在flash,然后将有实时性要求的代码拷贝到ram进行执行,而实时性要求低的,还可以留在flash里面运行。
怎么搬呢?搬运工程是在片子复位之后的初始化过程,所以我们先从片子上电复位之后的过程开始说起。
芯片上电复位之后,从BOOT ROM开始执行(别问我为什么从这里开始),BOOT ROM是一个片内ROM,不同芯片大小不一样,比如2812是4Kx16,而28335是8Kx16。
对的,就是它,这里看得还不够清楚,用两个指头滑动放大一些看看里面是什么东西。
这下看到了吧,里面有一些我英文不好,但是我看懂了两个:Reset vector(CPU vector table),还有Boot loader functions。查牛津字典第一个是复位矢量,第二个是Boot loader functions。看看Reset vector怎么起作用的。
上面第一个小红框里面说了VMAP=1的时候复位之后从这里开始,下来的那个框的意思是复位的时候VMAP=1,所以复位之后从boot rom开始就可以解释了。接着看。
下面下横红线的那句话,意思是这个复位矢量被编程成指向InitBoot函数。InitBoot的执行是几个GPIO(这个文档说的是2833x,不同的器件在这个GPIO开始有所不同)检测不同的引导模式,有下面这几种
讲得好像有点啰嗦了,上个流程图
到这里出现分支,call boot loader,如果是NO就是从IO的状态检测到入口地址,不需要调用boot loader。那么boot loader是什么?
英文不好,你们自己看吧。在外面这个帖子里,是要选择flash启动的,所以不需要boot loader。
上面是一个flash启动的流程,
这里说明了ADC_cal的作用,偷偷把一些adc的寄存器给校定了。
(以上更详细的介绍可以参照TI的c2000各个型号的boot rom文件,我这一个参照的2833x的)
接下来退出InitBoot,然后跳到了地址0x33 FFF6,这是哪里?赶紧看memory map去
就是这里,跳到了flash的接近末尾2个字节的地方。这里面是什么鬼,这个地方要去cmd里面看看了
在用于flash启动的cmd文件里面(F28335.CMD)看到这个地方,在page0中圈出来一块叫做BEGIN,然后有一个程序包叫codestart指定存放在这个BEGIN里面。好了,现在要去找codestart了。打开DSP2833x_CodeStartBranch.asm(好虚啊,不会汇编)
***********************************************************************
WD_DISABLE .set 1 ;set to 1 to disable WD, else set to 0
.ref _c_int00
.global code_start
***********************************************************************
* Function: codestart section
*
* Description: Branch to code starting point
***********************************************************************
.sect "codestart"
code_start:
.if WD_DISABLE == 1
LB wd_disable ;Branch to watchdog disable code
.else
LB _c_int00 ;Branch to start of boot.asm in RTS library
.endif
;end codestart section
***********************************************************************
* Function: wd_disable
*
* Description: Disables the watchdog timer
***********************************************************************
.if WD_DISABLE == 1
.text
wd_disable:
SETC OBJMODE ;Set OBJMODE for 28x object code
EALLOW ;Enable EALLOW protected register access
MOVZ DP, #7029h>>6 ;Set data page for WDCR register
MOV @7029h, #0068h ;Set WDDIS bit in WDCR to disable WD
EDIS ;Disable EALLOW protected register access
LB _c_int00 ;Branch to start of boot.asm in RTS library
.endif
;end wd_disable
.end
;//===========================================================================
;// End of file.
;//===========================================================================
代码一段送给大家,不谢,哈哈。
.sect "codestart"
code_start:
.if WD_DISABLE == 1
LB wd_disable ;Branch to watchdog disable code
.else
LB _c_int00 ;Branch to start of boot.asm in RTS library
.endif
;end codestart section
意思应该是把框起来这段程序放到codestart段里面,刚才cmd文件里面的那个程序包codestart啊,还记得吗?好的来看里面的程序。
if else逻辑,判断是否要禁止看门狗,最终都会跳转到c_int00 。这个是什么东西 呢?
据说这是位于C编译器支持库rts.lib中的初始化子程序的入口符号。只有运行这个子程序之后,其他C代码才可以被执行。又有说此函数是用来建立C语言环境,为进入main()函数进行系统初始化,主要工作是简历堆栈,初始化全局变量。我就当做是TI的库里面的东西了,不管。那么接下来就是执行main函数了。
觉得好失败,讲了大半天没讲到重点。flash copy to saram。
5min later,上了个厕所回来继续。
到了main函数,你会怎么写呢?先关闭看门狗,然后初始化时钟,然后呢?然后初始化flash,TI是这样干的,但是
TI说了:
FLASH初始化的函数不能在FLASH里面运行,必须拷到别的内存空间里运行才能对FLASH进行初始化。(好的,先小试牛刀,你懂这一步copy,你就知道该怎么copy了。)
首先你要写一个Flash_Initialize()函数用来初始化flash的(这个TI有现成代码的),当然的这个程序一开始还是存储在flash里面。写好的程序需要在flash里面给他找个地方呆着。
#pragma CODE_SECTION(Flash_Initialize, "ramfuncs");//指定函数存放的代码段
上面这句话需要写在Flash_Initialize的定义文件内。
然后在头文件里面定义几个用到的变量(TI例程里面是在DSP2833x_Platform.h)。
extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadEnd;
extern Uint16 RamfuncsRunStart;
还需要修改对应的cmd文件,对ramfuncs进行一些说明。
//**************************************************
ramfuncs : LOAD = FLASH,
RUN = RAML01K,
LOAD_START(_RamfuncsLoadStart),
LOAD_END(_RamfuncsLoadEnd),
RUN_START(_RamfuncsRunStart),
PAGE = 0
//**************************************************end
说明在page 0 下面,有个代码段ramfuncs,这段代码保存在FLASH,需要再RAML01K里面运行,FLASH开始的地址在_RamfuncsLoadStart里面,结束的地址在_RamfuncsLoadEnd里面,RAML01K内开始运行的地址在_RamfuncsRunStart里面。
好的,到这里已经万事具备了,只要在main里面大喊一声开始copy就行了:
//Step2:Flash Initialize.
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);//开始复制
Flash_Initialize();//开始执行初始化Flash。
其中MemCopy函数代码如下,比较简单:
*******************************************************************************/
void MemCopy(Uint16 *SourceAddr, Uint16* SourceEndAddr, Uint16* DestAddr)
{
//
while(SourceAddr < SourceEndAddr)
{
//
*DestAddr++ = *SourceAddr++;
}
}
//===========================================================================
// End of file.
//===========================================================================
好的,到这里基本就讲完了怎么copy了。
比如我用PWM控制电机,实时性要求高,所以会copy到saram里面去运行。
函数名:SVPWM_GenerateNextPWM
在同一个文件声明代码存放的段名字
#pragma CODE_SECTION(SVPWM_GenerateNextPWM, "TimeCriticalFuncs");
在头文件里面定义变量
extern Uint16 TimeCriticalFuncsLoadStart;
extern Uint16 TimeCriticalFuncsLoadEnd;
extern Uint16 TimeCriticalFuncsRunStart;
在CMD文件里面声明段:
TimeCriticalFuncs : LOAD = FLASH,
RUN = RAML0L3,
LOAD_START(_TimeCriticalFuncsLoadStart),
LOAD_END(_TimeCriticalFuncsLoadEnd),
RUN_START(_TimeCriticalFuncsRunStart),
PAGE = 0
然后在主程序里面执行MemCopy函数:
MemCopy(&TimeCriticalFuncsLoadStart, &TimeCriticalFuncsLoadEnd, &TimeCriticalFuncsRunStart);
OK啦,终于写完了。第一次发这么长的贴。