一、前言
产品发布后的升级不能再依赖于烧录器,如果能用产品预留的串口就可以更新芯片的APP程序,对于一些不便于拆卸的产品,这种维护方式非常好用。
bootloader的代码有很多选择,比如正点原子的bootloader 和 RT-thread 官方的bootloader。 作者选用ST官方提供的bootloader,使用Ymodem协议传输。
bootloader的工作原理这里不展开说,总结下来就是把bootloader的程序写到原来的0x08000000,而主程序作为App程序,往0x08008000地址烧录,bootloader里提供三个功能,一个是往0x08008000里烧录App,一个是下载0x08008000里的程序,一个是跳转到0x08008000 App程序。 当然刚上电是先进入bootloader,再跳转App。
二、CubeMX生成 IAR 工程
1.CubeMX生成工程
在CubeMX除了选择MCU还可以选择开发板的,有限官方的开发板自带了IAP例程,也就是bootloader。 Toolchain/IDE 选择EWARM就是生成IAR工程了。注意要配置好烧程序用的串口。
2.官方的IAP例程
只有默写板子有IAP例程,可以去安装包Applications的这个路径下找到IAP文件夹。 这是个文件和对应的头文件就是用到的,另外main 函数里有写进入menu.c 的入口函数,也可以复制到自己的程序中。
三、移植bootloader
1.移植相关代码
将bootloader相关的.c和.h 文件加入工程后就编译一下,如果报有缺少头文件添加一下。 注意下面几个函数: 1.Serial_PutString()函数
这个是代替printf用的(printf文件比较大一般bootloader不使用),这个函数在common.c中,注意修改官方例程中的串口为你的烧录串口,比如你CubeMX 配置的是UART_HandleTypeDef huart1,就把用到串口的地方都改成huart1。
2.Main_Menu ()函数 这个是进入bootloader菜单选择的函数,可以看到串口输入1,2,3,4分别有不同的功能,其中1是烧录app,3是跳转app。
void Main_Menu(void)
{
uint8_t key = 0;
Serial_PutString((uint8_t *)"\r\n======================================================================");
Serial_PutString((uint8_t *)"\r\n= (C) COPYRIGHT 2016 STMicroelectronics =");
Serial_PutString((uint8_t *)"\r\n= =");
Serial_PutString((uint8_t *)"\r\n= STM32F4xx In-Application Programming Application =");
Serial_PutString((uint8_t *)"\r\n= =");
Serial_PutString((uint8_t *)"\r\n= By MCD Application Team =");
Serial_PutString((uint8_t *)"\r\n======================================================================");
Serial_PutString((uint8_t *)"\r\n\r\n");
while (1)
{
FlashProtection = FLASH_If_GetWriteProtectionStatus();
Serial_PutString((uint8_t *)"\r\n=================== Main Menu ============================\r\n\n");
Serial_PutString((uint8_t *)" Download image to the internal Flash ----------------- 1\r\n\n");
Serial_PutString((uint8_t *)" Upload image from the internal Flash ----------------- 2\r\n\n");
Serial_PutString((uint8_t *)" Execute the loaded application ----------------------- 3\r\n\n");
if(FlashProtection != FLASHIF_PROTECTION_NONE)
{
Serial_PutString((uint8_t *)" Disable the write protection ------------------------- 4\r\n\n");
}
else
{
Serial_PutString((uint8_t *)" Enable the write protection -------------------------- 4\r\n\n");
}
Serial_PutString((uint8_t *)"==========================================================\r\n\n");
__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_UART_Receive(&huart1, &key, 1, RX_TIMEOUT);
switch (key)
{
case '1' :
SerialDownload();
break;
case '2' :
SerialUpload();
break;
case '3' :
Serial_PutString((uint8_t *)"Start program execution......\r\n\n");
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
JumpToApplication = (pFunction) JumpAddress;
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
JumpToApplication();
break;
case '4' :
if (FlashProtection != FLASHIF_PROTECTION_NONE)
{
if (FLASH_If_WriteProtectionConfig(OB_WRPSTATE_DISABLE) == HAL_OK)
{
Serial_PutString((uint8_t *)"Write Protection disabled...\r\n");
Serial_PutString((uint8_t *)"System will now restart...\r\n");
HAL_FLASH_OB_Launch();
HAL_FLASH_Unlock();
}
else
{
Serial_PutString((uint8_t *)"Error: Flash write un-protection failed...\r\n");
}
}
else
{
if (FLASH_If_WriteProtectionConfig(OB_WRPSTATE_ENABLE) == HAL_OK)
{
Serial_PutString((uint8_t *)"Write Protection enabled...\r\n");
Serial_PutString((uint8_t *)"System will now restart...\r\n");
HAL_FLASH_OB_Launch();
}
else
{
Serial_PutString((uint8_t *)"Error: Flash write protection failed...\r\n");
}
}
break;
default:
Serial_PutString((uint8_t *)"Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r");
break;
}
}
}
3.Ymodem_Receive() 函数
这个函数是执行Ymodem传输协议功能的,参考https://blog.csdn.net/INT_TANG/article/details/117334848
2.flash_if.h文件修改
修改USER_FLASH_END_ADDRESS(片内Flash结束地址),和APPLICATION_ADDRESS(App起始地址),这里改成0x08008000,0x08000000~0x08007FFF用于存放bootloader(Note: the 1st sector 0x08000000-0x08007FFF is reserved for the IAP code)
#define USER_FLASH_END_ADDRESS 0x080FFFFF
#define USER_FLASH_SIZE (USER_FLASH_END_ADDRESS - APPLICATION_ADDRESS + 1)
#define APPLICATION_ADDRESS (uint32_t)0x08008000
这些修改好可以编译跑一下看看bootloader的输出是否正常。
3.准备App程序
bootloader使用Ymodem下载时用的是bin文件,IAR中要修改ROM起始地址,中断向量起始地址和输出文件。
4.烧录*.bin
然后就可以在SecureCRT里调用Send Ymodem来烧录这个*.bin程序了。
移植参考 https://blog.csdn.net/INT_TANG/article/details/117235294 IAR和Keil工程的操作都是一样的。
四、调试及其他注意事项
1.中断向量表偏移没设对
如果debug的时候发现程序进入hard fault大概率是中断向量表没设对,在程序最开始加上下面的这段把向量表的起始地址强制设为App的起始地址。
SCB->VTOR=0x08008000;
2.进入App后时钟起不来或时钟混乱
官方手册上说:一旦启用了PLL,就无法更改主PLL配置参数,因此建议在启用PLL之前先对其进行配置(选择HSI或HSE振荡器为PLL时钟源,以及除法因子M,P,Q和乘法因子N的配置)。
也就是说PLL在启动之后便不能够重新配置。可以先把时钟改为内部时钟然后再配置PLL。
参考 https://blog.csdn.net/xiaoyuanwuhui/article/details/108772487
3.在bootloader的工程里怎么调试app程序
只能看Flash里反汇编的程序,一步步反推app哪里出现了问题。 IAR里看PC寄存器的值() PC指向当前程序运行的地址,跑到0x08008000以后就是进入App程序了。 然后通过看Disassembly反汇编查看对应的App的C程序在哪一行出现问题了。 App生成bin文件和hex文件也可以生成汇编文件的,用这里的反汇编和App的汇编文件去对比。
|