This article takes HC32F072 as an example.
The IAP method and process of BGI's MCU are basically the same as STM32. You can refer to this article:
https://blog.csdn.net/zhangfls/article/details/111685866
Prepare two projects, 1 BOOT and 1 APP
1. FLASH flashing, boot flashes the new program saved in the back half of the on-chip FLASH to the front half, APP will obtain the upgrade package through the communication interface, and flash the upgrade package to the second half of the 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[j];
}
}
else
{
Flash_Read_Data(addr,left_len,Prog_data);
for(j=0;j<left_len;j++)
{
check_sum += Prog_data[j];
}
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[j]);
while(*((volatile uint8_t*)addr+PGM_START_ADDR+j) != Prog_data[j])
{
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[j]);
while(*((volatile uint8_t*)addr+PGM_START_ADDR+j) != Prog_data[j])
{
retry_time ++;
delay1ms(10);
if(retry_time>10)
return 0;
}
delay100us(2);
}
left_len = 0;
}
}
return 10;
}
2. The above process is the general flashing process, which is universal. The key point is to jump to the address of the new APP after flashing:
typedef void (*iapfun)(void); //定义一个函数类型的参数.
uint32_t JumpAddress;
iapfun jump2app;
void MSR_MSP(unsigned int addr); //设置堆栈地址
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(unsigned int 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程序
}
}
In the main program, you need to change the startup .S file (startup_hc32f072.s), configure the interrupt vector table offset
1, define a new terminal vector offset address new_vect_table at the beginning, point to the APP program address, and point to the 8K address (0x00002000) )
Stack_Size EQU 0x00000200
new_vect_table EQU 0x00002000 ;中断向量偏移长度8K
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
2. Load the address to 0xE000ED08, which is the address saved in the interrupt vector table
; reset Vector table address.
LDR R0, =0xE000ED08
LDR R2, =new_vect_table
STR R2, [R0] ;向量表重定义
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
3. In this way, boot can jump to APP normally (pay attention to the start address configuration of BOOT and APP project)
Special attention:
(1) The FALSH erasing and writing functions of BGI MCU chip should be defined before 32K. You can configure the declaration of the FLASH erasing and writing functions to define them before 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) The interrupt must be turned off during the FLASH erasing process, otherwise it may fail
/**
*****************************************************************************
** \brief FLASH 字节写
**
** 用于向FLASH写入1字节数据.
**
** \param [in] u32Addr Flash地址
** \param [in] 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 [in] 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);
}