MC9S12XE bootloader开发

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。

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值