历史上的今天
返回首页

历史上的今天

今天是:2025年07月14日(星期一)

正在发生

2021年07月14日 | MC9S12XE bootloader开发

2021-07-14 来源:eefocus

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  {

      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。

推荐阅读

史海拾趣

CDIL[Continental Device India Pvt. Ltd.]公司的发展小趣事

近年来,CDIL积极响应政府政策,利用生产挂钩激励计划(PLI)和电子元件和半导体制造促进计划(SPECS),计划将其产能从目前的5亿片基础上提高1亿片。同时,公司还计划在未来几年内建立两条新的ATMP生产线,以进一步提升产能和技术水平。此外,CDIL还在碳化硅(SiC)等新技术领域进行了深入研发,以满足电动汽车、电源管理设备等新兴市场的需求。

AIM公司的发展小趣事

AIM公司深知质量是企业的生命线。因此,公司始终将质量管理放在首位,建立了严格的质量控制体系。从原材料采购到产品出厂,每一个环节都经过严格把关,确保产品的质量稳定和可靠。同时,AIM还注重品牌建设,通过提供优质的产品和服务,树立了良好的品牌形象和口碑。

安纳森(AnaSem)公司的发展小趣事

在追求经济效益的同时,安纳森始终不忘企业的社会责任。公司深知电子产品在生产和使用过程中可能对环境造成的影响,因此将环保理念融入产品设计和生产过程中。安纳森的产品原料材质标准均符合全球最新的环保指令和有害物质禁用条约,确保用户在使用产品的同时,也能为地球环境保护做出贡献。这一举措不仅赢得了用户的广泛赞誉,也为整个电子行业的绿色发展树立了典范。

Elpress AB公司的发展小趣事

在稳固了国内市场后,Elpress AB开始积极拓展国际市场。公司设立了多个海外子公司和物流中心,如丹麦的锡尔克堡、德国的Viersen以及中国的北京等地。这些海外机构的建立为Elpress AB的产品提供了更广阔的销售渠道和更便捷的服务支持。同时,Elpress AB还与国际知名电子企业开展合作,共同研发新产品、开拓新市场,进一步提升了公司的国际影响力。

GPD Optoelectronics Corp公司的发展小趣事

随着电子技术的不断进步和市场需求的变化,Elpress AB意识到只有不断创新才能保持竞争力。因此,公司加大了对研发的投入,引进了一批先进的研发设备和人才。经过数年的努力,Elpress AB成功推出了一系列具有创新性和领先性的电气连接产品,如高压电缆接头、母线连接器等。这些产品的推出不仅丰富了公司的产品线,也进一步提升了Elpress AB在电气连接领域的地位。

Conexcon Group公司的发展小趣事

面对日益激烈的市场竞争,Conexcon Group积极寻求与国内外知名企业的战略合作。通过与行业巨头的联合研发和市场推广,公司成功将自身产品推向了更广阔的市场。同时,这些合作也为公司带来了先进的技术和管理经验,进一步提升了公司的核心竞争力。在合作中,Conexcon Group始终坚持开放、共赢的原则,与合作伙伴共同推动电子行业的发展。

问答坊 | AI 解惑

现代汽车智能安全气囊采用FRAM作为数据存储

非易失性铁电存储器(FRAM)和集成半导体产品供应商及开发商Ramtron International宣布,韩国现代Hyundai Autonet公司选用了其生产的FRAM产品用于该公司的汽车智能安全气囊和乘客传感器中。 据介绍,非易失性存储器(FRAM)具有读写寿命长、写入数 ...…

查看全部问答>

原创用VC写的『步进电机升速曲线生成器』

自己用VC写的双时钟24M晶震可以达15KHz的转速 有问题联系我QQ:247256301…

查看全部问答>

全面解析Windows Embedded CE文件系统

随着Windows Embedded CE的发展,对象存储的作用越来越小,而大容量的永久存储设备被越来越多地采用。Windows Embedded CE文件系统是一种灵活的模块化设计,它允许自定义文件系统、筛选器和多种不同的块设备类型。一般来说,Windows Embedded CE基 ...…

查看全部问答>

交换机,个人PC机维护需要哪些专业工具!

如题,谁知道做这两件事要哪些专业的工具??谁有介绍下或者发点资料下载下。谢谢,路过的大侠快来帮忙呀 如题,如果要负责公司交换机PC等维护,需要些什么工具了,本人之前没有做过,现在有可能转行了!所以请教各位大侠!!谢谢!! [ 本帖最后 ...…

查看全部问答>

Altium.Designer.6教程

Altium.Designer.6教程…

查看全部问答>

求助,arm连接称重显示控制器,通信问题

arm板与称重显示控制器通过rs232连接,在arm上打开串口调试程序,波特率什么的设置好,但是一点反应都没有,主要是想要把称重显示控制器上的读数读取过来。请问,是什么原因,还需要在arm的系统wince中安装称重显示控制器的驱动程序才行?大家帮下 ...…

查看全部问答>

config.bib之NK SIZE RAMIMAGE 与 ROMSIZE关系?

NK              80100000        01F00000        RAMIMAGE RAM             82000000        ...…

查看全部问答>

菜鸟请教:我在使用Application Verifier for Mobile 5.0时为什么总报shimeng错误

我想用Application Verifier for Mobile 5.0测试我的应用程序,我按照教程将 Transport 和 startup设置为Microsoft ActiveSync,然后进行“Connect...”结果总是提示“Unable to load shimeng(shim.dll)on device” 注:这个时候PPC是打开了的,与 ...…

查看全部问答>

STM32不同端口的相同编号引脚不能同时做EXTI源?

                                 例如PORTA的引脚2和PORTB的引脚2,好像不能同时作为EXTI的外部中断引脚?我从手册上看,第n个EXTI源只能来自指定端 ...…

查看全部问答>

如何抵制职场的六大心理污染

据调查,许多写字楼都不同程度地存在着危害公共健康的污染,空气品质的问题以及湿度的平衡、光照、通风状况和清洁程度都直接关系人们的健康。办公室的环境也会直接影响员工的情绪,某种意义上比大气、水质、噪声等污染更为严重,它会涣散人们工作的 ...…

查看全部问答>