观海 发表于 2021-8-1 16:00

华大HC32F系列MCU的IAP流程及示例

准备两个工程,1个BOOT,1个APP

1、FLASH刷写,boot将片内FLASH后面一半保存的新程序刷到前面一半,APP则是将通过通讯接口获取升级包,将升级包刷写到后一半的FLASH

//IAP写入
uint8_t IAP_Write(void)
{
        uint16_t j;
        uint32_t left_len,addr=0;
        static uint16_t check_sum = 0;
        static uint16_t flash_read_checknum;
       
        uint8_t retry_time = 0;

        left_len = System_Para.Filesize-4;
        while(left_len)
        {
                if(left_len > 256)
                {
                        Flash_Read_Data(addr,256,Prog_data);
                        addr += 256;
                        left_len = System_Para.Filesize-4-addr;
                        for(j=0;j<256;j++)
                        {
                                check_sum += Prog_data;
                        }
                }
                else
                {
                        Flash_Read_Data(addr,left_len,Prog_data);
                        for(j=0;j<left_len;j++)
                        {
                                check_sum += Prog_data;
                        }
                        left_len = 0;
                }
        }
       
        flash_read_checknum = Flash_Read_Byte(System_Para.Filesize-2);
        flash_read_checknum = (flash_read_checknum<<8) + Flash_Read_Byte(System_Para.Filesize-1);
       
        if(flash_read_checknum == check_sum)
        {
                //校验和正确
                Flash_Read_Data(0,256,Prog_data);
                if(((*(__IO uint32_t*)Prog_data) & 0x2FFE0000 ) != 0x20000000)
                {
                        //程序错误,直接返回主程序
                        return 10;
                }
        }
        else
        {
                //校验和错误,直接返回主程序
                return 10;
        }
       
        total_data = System_Para.Filesize;
       
        //擦除程序区,失败就返回0
        if(FlashErase((uint32_t)PGM_START_ADDR))   //清除程序空间
        {
                //FLASH烧写擦除过程中失败,不能直接返回主程序,因为主程序已经没有代码了
                return 0;
        }

        addr = 0;
        left_len = total_data;
       
        //开始写入程序
        while(left_len)
        {
                if(left_len > 256)
                {
                        Flash_Read_Data(addr,256,Prog_data);
                        for(j=0;j<256;j++)
                        {
                                Flash_WriteByte(addr+PGM_START_ADDR+j,Prog_data);
                                while(*((volatile uint8_t*)addr+PGM_START_ADDR+j) != Prog_data)
                                {
                                        retry_time ++;
                                        delay1ms(10);
                                        if(retry_time>10)
                                                return 0;
                                }
                                delay100us(2);
                        }
                        addr += 256;
                        left_len = total_data-addr;
                }
                else        //剩余不足256字节
                {
                        Flash_Read_Data(addr,left_len,Prog_data);
                        for(j=0;j<left_len;j++)
                        {
                                Flash_WriteByte(addr+PGM_START_ADDR+j,Prog_data);
                                while(*((volatile uint8_t*)addr+PGM_START_ADDR+j) != Prog_data)
                                {
                                        retry_time ++;
                                        delay1ms(10);
                                        if(retry_time>10)
                                                return 0;
                                }
                                delay100us(2);
                        }
                        left_len = 0;                       
                }
        }
        return 10;
}


观海 发表于 2021-8-1 16:01

2、上面的流程就是一般情况下的刷写流程,都是通用的,重点是刷写完后跳转到新的APP的地址:

typedefvoid (*iapfun)(void);                                //定义一个函数类型的参数.
uint32_t JumpAddress;
iapfun jump2app;

void MSR_MSP(unsignedint addr);        //设置堆栈地址

//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(unsignedint addr)
{
    MSR MSP, r0                         //set Main Stack value
    BX r14
}

//程序跳转
void Check_And_Jump(void)
{
   if(((*(__IO uint32_t*)PGM_START_ADDR)&0x2FFE0000)==0x20000000)        //检查栈顶地址是否合法
        {
                jump2app=(iapfun)*(__IO uint32_t*)(PGM_START_ADDR+4);//APP程序复位地址
                MSR_MSP(*(__IO uint32_t*)PGM_START_ADDR);        //初始化APP堆栈指针
                jump2app();                                                                        //跳转到APP程序
        }
}


观海 发表于 2021-8-1 16:01

主程序处,需要更改启动的.S文件(startup_hc32f072.s),配置中断向量表偏移

1、开头定义一个新的终端向量偏移地址new_vect_table,指向APP程序地址,如下文指向了8K地址(0x00002000 )

Stack_Size      EQU   0x00000200
       
new_vect_tableEQU   0x00002000         ;中断向量偏移长度8K

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
2、将该地址装载到0xE000ED08即中断向量表保存的地址下

            ; reset Vector table address.
                LDR   R0, =0xE000ED08
                                LDR   R2, =new_vect_table
                STR   R2,                         ;向量表重定义
                LDR   R0, =SystemInit
                BLX   R0
                LDR   R0, =__main
                BX      R0
                ENDP
3、这样boot就可以正常跳转到APP了(注意BOOT和APP工程的起始地址配置)


观海 发表于 2021-8-1 16:02

特别注意:
(1)华大的MCU片内FALSH擦除和写入的函数要定义在32K之前,可以这样配置FLASH擦除和写入的函数的声明,将其定义到32K之前

en_result_t Flash_SectorErase(uint32_t u32SectorAddr) __attribute__((section(".ARM.__at_0x2200")));

en_result_t Flash_WriteByte(uint32_t u32Addr, uint8_t u8Data) __attribute__((section(".ARM.__at_0x2400")));
(2)FLASH擦写过程中必须要关中断,不然有可能失败

/**
*****************************************************************************
** \brief FLASH 字节写
**
** 用于向FLASH写入1字节数据.
**
** \param u32Addr          Flash地址
** \param u8Data         1字节数据
**
** \retval Ok                  写入成功.
** \retval ErrorInvalidParameter FLASH地址无效
** \retval ErrorTimeout          操作超时
*****************************************************************************/
en_result_t Flash_WriteByte(uint32_t u32Addr, uint8_t u8Data)
{
    en_result_t             enResult = Ok;   
    volatile uint32_t       u32TimeOut = FLASH_TIMEOUT_PGM;

    if (FLASH_END_ADDR < u32Addr)
    {
      enResult = ErrorInvalidParameter;
      return (enResult);
    }
               
                __disable_irq();

    //busy?
    u32TimeOut = FLASH_TIMEOUT_PGM;
    while (TRUE == M0P_FLASH->CR_f.BUSY)
    {
                        if(0 == u32TimeOut--)
                        {
                                __enable_irq();
                                return ErrorTimeout;
                        }
    }

    //set OP
    u32TimeOut = FLASH_TIMEOUT_PGM;
    while(Program != M0P_FLASH->CR_f.OP)
    {
      if(u32TimeOut--)
      {
            FLASH_BYPASS();
            M0P_FLASH->CR_f.OP = Program;
      }
      else
      {
                                        __enable_irq();
            return ErrorTimeout;
      }
    }

    //Flash 解锁
    Flash_UnlockAll();

    //write data
    *((volatile uint8_t*)u32Addr) = u8Data;

    //busy?
    u32TimeOut = FLASH_TIMEOUT_PGM;
    while (TRUE == M0P_FLASH->CR_f.BUSY)
    {
      if(0 == u32TimeOut--)
      {
                                        __enable_irq();
            return ErrorTimeout;
      }
    }

    //Flash 加锁
    Flash_LockAll();
               
                __enable_irq();

    return (enResult);
}
/**
*****************************************************************************
** \brief FLASH 扇区擦除
**
** FLASH 扇区擦除.
**
** \param u32SectorAddr    所擦除扇区内的地址
**
** \retval Ok                  擦除成功.
** \retval ErrorInvalidParameter FLASH地址无效
** \retval ErrorTimeout          操作超时
*****************************************************************************/
en_result_t Flash_SectorErase(uint32_t u32SectorAddr)
{
    en_result_t             enResult = Ok;   
    volatile uint32_t       u32TimeOut = FLASH_TIMEOUT_ERASE;

    if (FLASH_END_ADDR < u32SectorAddr)
    {
      enResult = ErrorInvalidParameter;
      return (enResult);
    }
               
                __disable_irq();

    //busy?
    u32TimeOut = FLASH_TIMEOUT_ERASE;
    while (TRUE == M0P_FLASH->CR_f.BUSY)
    {
      if(0 == u32TimeOut--)
      {
                                        __enable_irq();
            return ErrorTimeout;
      }
    }

    //Flash 解锁
    Flash_UnlockAll();

    //set OP
    u32TimeOut = FLASH_TIMEOUT_ERASE;
    while(SectorErase != M0P_FLASH->CR_f.OP)
    {
      if(u32TimeOut--)
      {
            FLASH_BYPASS();
            M0P_FLASH->CR_f.OP = SectorErase;
      }
      else
      {
                                        __enable_irq();
            return ErrorTimeout;
      }
    }

    //write data
    *((volatile uint8_t*)u32SectorAddr) = 0;

    //busy?
    u32TimeOut = FLASH_TIMEOUT_ERASE;
    while (TRUE == M0P_FLASH->CR_f.BUSY)
    {
      if(0 == u32TimeOut--)
      {
                                        __enable_irq();
            return ErrorTimeout;
      }
    }
               
    //Flash 加锁
    Flash_LockAll();
               
                __enable_irq();

    return (enResult);
}


caigang13 发表于 2021-8-2 21:16

谢谢,有源代码文件吗?

两只袜子 发表于 2021-8-11 20:49

参考参考
页: [1]
查看完整版本: 华大HC32F系列MCU的IAP流程及示例