一.概述
1.要实现串口IAP升级,首先要编写一个bootloader程序,然后再写支持IAP的app程序;
2.keil下bootloader的程序rom和ram设置
3.app程序要用bin文件
注:本文以STM32H743举例,其他stm32单片机IAP升级原理类似。
二.实现
1.bootloder程序实现
(1)基本知识
stm32的flash地址起始于0x0800 0000,结束地址是0x0800 0000加上芯片实际的flash大小,不同的芯片flash大小不同。
RAM起始地址是0x2000 0000,结束地址是0x2000 0000加上芯片的RAM大小。不同的芯片RAM也不同。
(2)STM32H743 bootloader设置
说明: 由于 STM32H7 的扇区大小固定为 128KB 一个,最小擦除单位也是以扇区为单位的,因此,至少分配一个扇区给 Bootloader,否则后续擦写 APP 的时候,可能把 Bootloader 也给擦了!所以,我们使用 128K(0X20000)字节来存放 Bootloader。
(3)代码实现
int main(void)
{
u8 t;
u16 wait_s = 0;
u8 key;
u32 applenth=0; //接收到的app代码长度
u8 clearflag=0;
u32 Buffer[4];
Cache_Enable(); //打开L1-Cache
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(160,5,2,4); //设置时钟,400Mhz
delay_init(400); //延时初始化
uart_init(115200); //串口初始化
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
SDRAM_Init(); //初始化SDRAM
while(1)
{
if(USART_RX_STA&0x8000)
{
USART_RX_STA=0; //清空
applenth=USART_RX_CNT; // applenth
USART_RX_CNT=0;
printf("用户程序接收完成!\r\n");
printf("代码长度:%dBytes\r\n",applenth);
}
else
{
if(wait_s >= 500)
{
wait_s = 0;
printf("等待用法发送程序... \r\n");
}
}
t++;
wait_s ++;
delay_ms(10);
if(t==30)
{
LED0_Toggle;
t=0;
if(clearflag)
{
clearflag--;
}
}
key=KEY_Scan(0);
if(key==WKUP_PRES) //WK_UP按键按下
{
if(applenth)
{
printf("开始更新固件...\r\n");
if(((*(vu32*)(0x24001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码 #define FLASH_APP1_ADDR 0x08020000
printf("固件更新完成!\r\n");
}else
{
printf("非FLASH应用程序!\r\n");
}
}else
{
printf("没有可以更新的固件!\r\n");
}
clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示
}
if(key==KEY1_PRES) //KEY1按下
{
if(applenth)
{
printf("固件清除完成!\r\n");
applenth=0;
}else
{
printf("没有可以清除的固件!\r\n");
}
clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示
}
if(key==KEY2_PRES) //KEY2按下
{
printf("开始执行FLASH用户代码!!\r\n");
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}else
{
printf("非FLASH应用程序,无法执行!\r\n");
}
clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示
}
}
}
(4)代码功能逻辑:
需要先按下 KEY_UP 按键,将串口接收到的 APP 程序存放到 STM32 的内部 FLASH;
再按 KEY2 既可以执行这个 FLASH APP 程序。
通过 KEY1 按键,可以手动清除串口接收到的APP 程序。
DS0 用于指示程序运行状态。
2.app程序及逻辑
(1)IAP流程逻辑
(2)keil设置
(3)代码实现
int main(void)
{
SCB->VTOR = FLASH_BASE|0x20000;//设置偏移量 FLASH_BASE : (uint32_t)0x08000000
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
u8 tbuf[40];
u8 t=0;
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(160,5,2,4); //设置时钟,400Mhz
delay_init(400); //延时初始化
uart_init(115200); //串口初始化
usmart_dev.init(200); //初始化USMART
printf("RTC APP running\r\n");
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
SDRAM_Init(); //初始化SDRAM
RTC_Init(); //初始化RTC
RTC_Set_WakeUp(RTC_WAKEUPCLOCK_CK_SPRE_16BITS,0); //配置WAKE UP中断,1秒钟中断一次
while(1)
{
t++;
if((t%100)==0) //每100ms更新一次显示数据
{
HAL_RTC_GetTime(&RTC_Handler,&RTC_TimeStruct,RTC_FORMAT_BIN);
sprintf((char*)tbuf,"Time:%02d:%02d:%02d",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds);
printf("RTC time=%s\r\n",tbuf);
HAL_RTC_GetDate(&RTC_Handler,&RTC_DateStruct,RTC_FORMAT_BIN);
sprintf((char*)tbuf,"Date:20%02d-%02d-%02d",RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date);
printf("RTC date=%s\r\n",tbuf);
sprintf((char*)tbuf,"Week:%d",RTC_DateStruct.WeekDay);
printf("RTC week=%s\r\n",tbuf);
}
if((t%20)==0)LED0_Toggle; //每200ms,翻转一次LED0
delay_ms(10);
}
}
说明: 重点是程序开始要有语句:****SCB->VTOR = FLASH_BASE|0x20000;****后面代码和普通app程序无区别
3.app生成bin文件
通过在 User 选项卡,设置编译后调用 fromelf.exe,根据.axf 文件生成.bin 文件,用于
IAP 更新。
具体见之前文章:
4.串口升级app
(1)首先用keil把bootloader程序用烧写器烧写到单片机中
(2)通过串口IAP升级app程序