1、几条重要语句
1、RELOCATE_TO
该语句在prm文件中使用,如果一个代码段在运行时被移动到另一个不同的地方,则使用该语句定义。由于MC9S12XE系列单片机的FLASH模块在运行时不同同时进行读写,所以需要将对FLASH进行操作的代码移动到RAM内运行,而且如果在进行FLASH操作时,若有中断发生,那么也需要将中断程序移动到RAM中。所以对于存放FLASH操作和中断代码的内存需要利用RELOCATE_TO重定位到RAM中,示例如下:RAM_CODE = READ_ONLY 0xF000 TO 0xFEFF RELOCATE_TO 0x3800;
2、#pragma
#pragma是比较复杂的预处理指令,本文只讲涉及到的#pragma CODE_SEG xxx,预处理指令#pragma CODE_SEG xxx指示链接器将该句以下的代码放入xxx,直到遇到#pragma CODE_SEG DEFAULT.
3、#pragma与RELOCATE_TO的关系
在我们开发bootloader时,需要对FLASH进行操作,将存放APP代码的FLASH区域擦除并写入新的APP代码。由于bootloader代码也在FLASH内,故需要将bootloader中对FLASH操作的代码移动到RAM内执行。在程序中我们需要把移动到RAM内的代码整合到一起,放到一个代码段,利用#pragma CODE_SEG可以完成这个任务,比如本文中采用#pragma CODE_SEG CODE_RAM
来定义一个代码段CODE_RAM,表示运行时要移动到RAM中的代码,然后我们定义一个存放CODE_RAM的内存区RAM_CODE,由于代码下载时下载到FLASH内,所以我们需要重定位,利用RELOCATE_TO完成,RAM_CODE = READ_ONLY 0xF000 TO 0xFEFF RELOCATE_TO 0x3800;
,表示代码存储地址为0xF000-0xFEFF,而实际运行时需要移动到0x3800也就是RAM中运行。最后我们在PLACEMENT中将CODE_RAM放入RAM_CODE,也就是CODE_RAM INTO RAM_CODE;
。
4、#define __SEG_START_REF(a)…
RELOCATE_TO虽然定义了CODE_RAM的存放区域和要移动的RAM的起始地址,但是单片机并不会自动将FLASH中的代码移动到RAM内,需要程序自己移动,故我们需要CODE_RAM代码的具体起始地址和大小,可利用以下代码完成.
#define __SEG_START_REF(a) __SEG_START_ ## a
#define __SEG_END_REF(a) __SEG_END_ ## a
#define __SEG_SIZE_REF(a) __SEG_SIZE_ ## a
#define __SEG_START_DEF(a) extern byte __SEG_START_REF (a) []
#define __SEG_END_DEF(a) extern byte __SEG_END_REF (a) []
#define __SEG_SIZE_DEF(a) extern byte __SEG_SIZE_REF (a) []
__SEG_START_DEF (CODE_RAM);
__SEG_END_DEF (CODE_RAM);
__SEG_SIZE_DEF (CODE_RAM);
上述代码定义了代码段的起始地址、大小和结束地址,在移动代码到RAM时需要用到。
2、bootloader开发流程
1、将运行时需要移动RAM内的代码用"#prama CODE_SEG xxx"单独分类,包括FLASH的擦除、写入、中断向量表和中断代码,之所以要将中断向量表和中断代码移入RAM,是为了防止FLASH擦写时对FLASH读取造成错误;
示例代码如下(.c部分,但是.h中的函数声明也需放在#pragma CODE_SEG CODE_RAM内):
1)Flash擦除与烧写部分
#pragma CODE_SEG CODE_RAM
byte FLASH_EraseSector(dword g_addr)
{
if((g_addr>=0x780000)&&(g_addr<=0x7FFFFF)&&((g_addr&0x00000007)==0))//判断全局地址的正确性,XEQ512 P_FLASH全局地址范围为0x78_0000-0x7F_FFFF
{ //P_FLASH Sector地址要求为对齐地址,低3位需为0
while(!FSTAT_CCIF); //等之前的FLASH指令完成
if(FSTAT_ACCERR|FSTAT_FPVIOL)
{
FSTAT = 0x30; //清ACCERR/FPVIOL
}
FCCOBIX = 0x00;
FCCOBHI = FLASH_CMD_ERASE_SECTOR; //写入擦除P_FLASH SECTOR指令
FCCOBLO = (byte)(g_addr>>16); //写入全局地址的高7位
FCCOBIX = 0x01;
FCCOB = (word)(g_addr & 0x0000FFFF); //写入全局地址的低16位
FSTAT = 0x80; //清零FLASH_CCIF,启动FLASH 指令
while(!FSTAT_CCIF); //等待指令完成
if(FSTAT_ACCERR | FSTAT_FPVIOL) //判断指令执行结果
return 0;
else
return 1;
}
else
{
return 0;
}
}
byte FLASH_EraseBlock(dword g_addr)
{
if((g_addr>=0x780000)&&(g_addr<=0x7FFFFF)) //判断全局地址的正确性,XEQ512 P_FLASH全局地址范围为0x78_0000-0x7F_FFFF
{
while(!FSTAT_CCIF); //等之前的FLASH指令完成
if(FSTAT_ACCERR|FSTAT_FPVIOL)
{
FSTAT = 0x30; //清ACCERR/FPVIOL
}
FCCOBIX = 0x00;
FCCOBHI = FLASH_CMD_ERASE_BLOCK; //写入擦除P_FLASH Block指令
FCCOBLO = (byte)(g_addr>>16); //写入全局地址的高7位
FCCOBIX = 0x01;
FCCOB = (word)(g_addr & 0x0000FFFF); //写入全局地址的低16位
FSTAT = 0x80; //清零FLASH_CCIF,启动FLASH 指令
while(!FSTAT_CCIF); //等待指令完成
if(FSTAT_ACCERR | FSTAT_FPVIOL) //判断指令执行结果
return 0;
else
return 1;
}
else
{
return 0;
}
}
byte FLASH_ProgramRecordPhrase(dword g_addr, byte *phrase)
{
if((g_addr>=0x780000)&&(g_addr<=0x7FFFFF)) //判断全局地址的正确性,XEQ512 P_FLASH全局地址范围为0x78_0000-0x7F_FFFF
{
byte i;
while(!FSTAT_CCIF); //等之前的FLASH指令完成
if(FSTAT_ACCERR|FSTAT_FPVIOL)
{
FSTAT = 0x30; //清零 ACCERR/FPVIOL
}
FCCOBIX = 0x00;
FCCOBHI = FLASH_CMD_PROGRAM_RECORD; //写入烧录P_FLASH Record的指令
FCCOBLO = (byte)(g_addr>>16); //写入全局地址的高7位
FCCOBIX = 0x01;
FCCOB = (word)(g_addr & 0x0000ffff); //写入全局地址的低16位
for(i=2;i<6;i++) //写8个字节数据到FCCOB寄存器
{
FCCOBIX = i;
FCCOBHI = *phrase;
FCCOBLO = *(phrase+1);
phrase = phrase+2;
}
FSTAT = 0x80; //清零FLASH_CCIF,启动FLASH 指令
while(!FSTAT_CCIF); //等待指令完成
if(FSTAT_ACCERR | FSTAT_FPVIOL) //判断指令执行结果
return 0;
else
return 1;
}
else
{
return 0;
}
}
#pragma CODE_SEG DEFAULT
2)中断函数部分
#pragma CODE_SEG CODE_RAM
interrupt void PIT0_Isr(void){
g_Pit0_Cnt++;
if(g_Pit0_Cnt>=3600000)
{
g_Pit0_Cnt=0;
}
PITTF_PTF0=1; //清零超时标志位
}
dword CAN2MCU(dword id)
{
dword result=0;
dword temp_id1=0;
dword temp_id2=0;
temp_id1=(id>>1)&0x3FFFF;
temp_id2=(id>>3)&0x1FFC0000;
result=temp_id1|temp_id2;
return result;
}
interrupt void CAN0_Rx_Isr(void)
{
g_Rx_Can_Msg.id.id_byte[0] = CAN0RXIDR0;
g_Rx_Can_Msg.id.id_byte[1] = CAN0RXIDR1;
g_Rx_Can_Msg.id.id_byte[2] = CAN0RXIDR2;
g_Rx_Can_Msg.id.id_byte[3] = CAN0RXIDR3;
g_Rx_Can_Msg.id.id_dword=CAN2MCU(g_Rx_Can_Msg.id.id_dword);
g_Rx_Can_Msg.data[0] = CAN0RXDSR0;
g_Rx_Can_Msg.data[1] = CAN0RXDSR1;
g_Rx_Can_Msg.data[2] = CAN0RXDSR2;
g_Rx_Can_Msg.data[3] = CAN0RXDSR3;
g_Rx_Can_Msg.data[4] = CAN0RXDSR4;
g_Rx_Can_Msg.data[5] = CAN0RXDSR5;
g_Rx_Can_Msg.data[6] = CAN0RXDSR6;
g_Rx_Can_Msg.data[7] = CAN0RXDSR7;
g_Rx_Can_Msg.len=CAN0RXDLR&0x0f;
g_Rx_Can_Msg.rx_flag=1;
Quene_AddMsg(g_Rx_Can_Msg);
CAN0RFLG_RXF=1;
}
#pragma CODE_SEG DEFAULT
2、修改prm文件,根据需移入RAM代码段大小分配FLASH大小和RAM起始地址;
3、将flash内代码移动到对应的RAM地址内;
#define __SEG_START_REF(a) __SEG_START_ ## a
#define __SEG_END_REF(a) __SEG_END_ ## a
#define __SEG_SIZE_REF(a) __SEG_SIZE_ ## a
#define __SEG_START_DEF(a) extern byte __SEG_START_REF (a) []
#define __SEG_END_DEF(a) extern byte __SEG_END_REF (a) []
#define __SEG_SIZE_DEF(a) extern byte __SEG_SIZE_REF (a) []
__SEG_START_DEF (CODE_RAM);
__SEG_END_DEF (CODE_RAM);
__SEG_SIZE_DEF (CODE_RAM);
void CopyCodeToRAM(void)
{
byte *p_src;
byte *p_dst;
word seg_size;
word i=0;
p_src = (byte *)__SEG_START_REF(CODE_RAM);
p_dst = (byte *)0x3800;
seg_size =(word)__SEG_SIZE_REF(CODE_RAM);
for(i=0;i<seg_size;i++)
{
if((word)p_dst > 0x3F00)
_asm(nop);
*p_dst++ = *p_src++;
}
}
4、对中断向量表进行修改,主要是将中断向量表定义在RAM区;
#define VECTORTABLEBASEADDR 0x3F00
#define PIT0ISRVECTORNUM 0x7A
#define CAN0RXISRVECTORNUM 0xB2
void Isr_Init()
{
IVBR=VECTORTABLEBASEADDR>>8;
*(word*)(VECTORTABLEBASEADDR+PIT0ISRVECTORNUM)=(word)PIT0_Isr;
*(word*)(VECTORTABLEBASEADDR+CAN0RXISRVECTORNUM)=(word)CAN0_Rx_Isr;
}
5、进行所需驱动的设计;
6、擦除存放APP代码的FLASH区域;
7、根据接收的上位机发送的S19文件中的record将代码按地址烧写到Flash内;
8、程序烧写完毕后,跳转到APP的启动地址,开始运行APP。