查看: 3396|回复: 2

关于IAP的实现

[复制链接]

3

主题

5

帖子

97

积分

中级工程师

Rank: 2

积分
97
发表于 2021-12-1 15:56:13 | 显示全部楼层 |阅读模式
本帖最后由 LZY 于 2021-12-1 16:16 编辑

   采用FM33LCO46芯片,FLASH大小为256K,其中64K空间作为bootloader程序运行,剩余192K作为用户程序,bootloader程序主要参考正点原子例程,现在JLINK分别烧写bootlaoder程序和用户程序到各自指定地址后,bootloader程序运行跳转程序到用户程序地址后,用户程序可以运行。但是在bootloader程序中利用串口接收用户程序的bin文件,然后将数据烧写到指定地址的flash上,这种方式跳转后单片机死机。
下面附上bootloader程序代码
int main(void)
{
    MF_Clock_Init();
    MF_SystemClock_Config();
    MF_Config_Init();
    LedInit();
    Key_GPIO_Config();
    UserInit();
    Uart0_Init(115200);
    printf("运行BOOTLOADER代码\r\n" );
    while(1)
    {
      if(USART_RX_CNT)
      {
        if(oldcount==USART_RX_CNT)//新周期内,没有收到任何数据,认为本次数据接收完成.
        {
          applenth=USART_RX_CNT;
          oldcount=0;
          USART_RX_CNT=0;
          printf("用户程序接收完成!\r\n");
          printf("代码长度:%dBytes\r\n",applenth);
        }
        else oldcount=USART_RX_CNT;                        
      }
      t++;
      DelayMs(10);        
               
     if( Key_Scan(KEY1_GPIO,KEY1_PIN) == KEY_ON )
     {
        printf("KEY1被按下\n");
        printf("串口接收问题%d\r\n",USART_RX_CNT);
        iap_write_appbin(FLASH_APP1_ADDR,(u8*)USART_RX_BUF,USART_RX_CNT);//更新FLASH代码
        //FM_FLASH_Write(ProgAddr,(u32*)TEXT_Buffer,SIZE);
        //LL_FLASH_Program_String(FLASH,ProgAddr,(u32*)TEXT_Buffer,SIZE);
     }        

     if( Key_Scan(KEY4_GPIO,KEY4_PIN) == KEY_ON )
     {
        printf("KEY4被按下\r\n");
                                 
        printf("开始执行FLASH用户代码!!\r\n");
        //if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x00000000)//判断是否为0X08XXXXXX.
        iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
                                                         
       }        

//      LED1_TOG();                        
    }
}
iap_write_appbin函数是将8位的数据转成32位,并写入到FLASH
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
  u32 t;
  u32 i=0;
  u32 temp;
  u32 fwaddr=appxaddr;//当前写入的地址
  u8 *dfu=appbuf;
  for(t=0;t<appsize;t+=4)
  {        
    temp=(u32)dfu[3]<<24;   
    temp|=(u32)dfu[2]<<16;   
    temp|=(u32)dfu[1]<<8;
    temp|=(u32)dfu[0];         
    dfu+=4;//偏移4个字节
    iapbuf[i++]=temp;         
    if(i==512)
    {
      i=0;
      FM_FLASH_Write(fwaddr,iapbuf,512);        
      fwaddr+=2048;//偏移2048   
    }
  }
  if(i)FM_FLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.  
}
iap_write_appbin函数是将串口接收的缓存的数组写入到指定地址的flash中,
已测试利用ap_write_appbin函数将少量的数据到写道flash,然后读出来,数据没有问题。
现在怀疑是不是数据大于2K,跳转扇区的时候数据出错了,有没有大佬帮忙分析一下ap_write_appbin函数
以下是FM_FLASH_Write函数:
void FM_FLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)        
{
  u32 secpos;           //扇区地址  第几个扇区
  u32 secoff;           //扇区内偏移地址(32位字计算)
  u32 secremain;         //扇区内剩余地址(32位字计算)           
  u32 i;   
  u32 offaddr;          //去掉0X00000000后的地址   

  if(WriteAddr<FLASH_BASE||(WriteAddr>=(FLASH_BASE+1024*FM33_FLASH_SIZE)))return;//非法地址
  offaddr=WriteAddr-FLASH_BASE;                //实际偏移地址.
  secpos=offaddr/FM_SECTOR_SIZE;               //扇区地址(即第几个扇区)   
  secoff=(offaddr%FM_SECTOR_SIZE)/4;            //在扇区内的偏移(4个字节为基本单位.)
  secremain=FM_SECTOR_SIZE/4-secoff;            //扇区剩余空间大小   
  if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
  while(1)
  {        
    FM_FLASH_Read(secpos*FM_SECTOR_SIZE+FLASH_BASE,FM_FLASH_BUF,FM_SECTOR_SIZE/4);//读出整个扇区的内容
    for(i=0;i<secremain;i++)//校验数据
    {
      if(FM_FLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除            
    }
    if(i<secremain)//需要擦除
    {
      LL_FLASH_SectorErase(FLASH, secpos*FM_SECTOR_SIZE+FLASH_BASE);  //擦除这个扇区
      for(i=0;i<secremain;i++)//复制
      {
        FM_FLASH_BUF[i+secoff]=pBuffer;         
      }
      FM33_FLASH_Write_NoCheck(FLASH ,secpos*FM_SECTOR_SIZE+FLASH_BASE,FM_FLASH_BUF,FM_SECTOR_SIZE/4);//写入整个扇区                        
    }
    else FM33_FLASH_Write_NoCheck(FLASH,WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                    
   if(NumToWrite==secremain)break;//写入结束了
   else//写入未结束
   {
     secpos++;                       //扇区地址增1
     secoff=0;                       //偏移位置为0         
     pBuffer+=secremain;                //指针偏移
     WriteAddr+=secremain;               //写地址偏移           
     NumToWrite-=secremain;              //字节(16位)数递减
     if(NumToWrite>(FM_SECTOR_SIZE/4))secremain=FM_SECTOR_SIZE/4;//下一个扇区还是写不完
     else secremain=NumToWrite;//下一个扇区可以写完了
   }         
};        
}
FM33_FLASH_Write_NoCheck函数:
void FM33_FLASH_Write_NoCheck(FLASH_Type* FLASHx,u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)   
{                                          
  u32 i;
  for(i=0;i<NumToWrite;i++)
  {
    LL_FLASH_Program_Word(FLASH,WriteAddr,pBuffer);
    WriteAddr+=4;//地址增加4.
  }  
}

LL_FLASH_Program_Word函数就是库函数了

回复

使用道具 举报

3

主题

5

帖子

97

积分

中级工程师

Rank: 2

积分
97
 楼主| 发表于 2021-12-1 16:18:25 | 显示全部楼层
调试了好几天都没有调通,有没有大佬帮忙看一下啊
回复

使用道具 举报

154

主题

846

帖子

4624

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
4624
发表于 2021-12-2 14:19:20 | 显示全部楼层
本帖最后由 顾博文 于 2021-12-2 14:20 编辑
LZY 发表于 2021-12-1 16:18
调试了好几天都没有调通,有没有大佬帮忙看一下啊

1、用jlink烧录,看64K以后的程序写进去的到底是什么。可以从memory窗口看

2、仿真前64K程序, 通过你的程序升级好后。在memory窗口看flash地址 的数据,看写入的与jlink烧写入的是否一致。一般就是程序写入的不一致。从仿真能够看出来。如果不一致那就,仿真升级过程,看哪里写的逻辑不对。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表