В этой статье в качестве примера используется HC32F072.
Метод IAP и процесс MCU BGI в основном такие же, как и STM32. Вы можете обратиться к этой статье:
https://blog.csdn.net/zhangfls/article/details/111685866
Подготовьте два проекта: 1 BOOT и 1 APP
1. Мигает флэш-память, загрузочная мигает новая программа, сохраненная в задней половине встроенной флэш-памяти, на переднюю половину, приложение получит пакет обновления через интерфейс связи и перенесет пакет обновления во вторую половину флэш-памяти.
//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. Вышеупомянутый процесс является общим процессом перепрошивки, который является универсальным. Ключевым моментом является переход к адресу нового приложения после перепрошивки:
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程序
}
}
В основной программе вам необходимо изменить файл запуска .S (startup_hc32f072.s), настроить смещение
1 таблицы векторов прерываний , определить новый адрес смещения вектора терминала new_vect_table в начале, указать адрес программы APP и указать на адрес 8K (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. Загрузите адрес в 0xE000ED08, который является адресом, сохраненным в таблице векторов прерываний.
; 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. Таким образом, загрузка может нормально перейти в APP (обратите внимание на конфигурацию начального адреса проекта BOOT и APP)
Особое внимание:
(1) Функции стирания и записи FALSH микросхемы MCU BGI должны быть определены до 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 [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);
}