LZY 发表于 2021-12-1 15:56:13

关于IAP的实现

本帖最后由 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<<24;   
    temp|=(u32)dfu<<16;   
    temp|=(u32)dfu<<8;
    temp|=(u32)dfu;         
    dfu+=4;//偏移4个字节
    iapbuf=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!=0XFFFF)break;//需要擦除            
    }
    if(i<secremain)//需要擦除
    {
      LL_FLASH_SectorErase(FLASH, secpos*FM_SECTOR_SIZE+FLASH_BASE);//擦除这个扇区
      for(i=0;i<secremain;i++)//复制
      {
      FM_FLASH_BUF=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函数就是库函数了

LZY 发表于 2021-12-1 16:18:25

调试了好几天都没有调通,有没有大佬帮忙看一下啊

顾博文 发表于 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烧写入的是否一致。一般就是程序写入的不一致。从仿真能够看出来。如果不一致那就,仿真升级过程,看哪里写的逻辑不对。
页: [1]
查看完整版本: 关于IAP的实现