这是以前就想写的一个小专题关于IAP,以及IAP在STM32编程的应用,专题分三小节,主要介绍常见的单片机烧录方式,IAP的实际应用,以及Ymodem协议在IAP编程中应用,在笔记吃灰很久了,终于拿出来晒晒太阳了@-@。
第一章:浅析ICP与ISP、及IAP三种单片机烧录方式
第二章:STM32应用IAP进行程序更新详解及实例
第三章:Ymodem协议解析-基于STM32的IAP实现
0、序
- 硬件平台:STM32F103RCT6
- 软件平台:GCC + STM32CubeMX + STM32CubeProgram
- Bin文件传输工具:Tera Term
1、新建IAP工程
1.1、工程配置
由于本次实际是通过串口进行bin文件的传输,所以在新建工程的时候最少使能一组串口用于传输烧录文件使用,新建工程的话在这里就不赘述了,毕竟本文的center是IAP的应用。
1.2、移植官方demo中的相关文件
官网demo传送门:https://www.st.com/content/st_com/zh/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32cube-expansion-packages/x-cube-iap-usart.html
其中我们所需要的文件如下:
- common.c,ymodem.c,menu.c,flash_if.c
- common.h,ymodem.h,menu.h,flash_if.h
2、分区规划
2.1、根据实际的使用需要对Flash进行分区
在STM32F103RCT6的datasheet的第4章节中主要解析了该芯片的内部存储的映射地址,Flash是从0x08000000 ~ 0x0807FFFF = 512K(下图中为该MCU内存映射表,所以其映射的地址的范围是0x08000000 ~ 0x0807FFFF),即该型号MCU的Flash容量大小。
根据本次demo开发时所作的一个内存的分区,主要分为三个部分如下表所示。其中IAP domain:大小16K,用于存储IAP升级的执行程序; APP domain: 为用户程序所在;Share domian:大小16K,用户程序与IAP共享区,用于相关标志量等的存储。
2.2、修改启动文件
IAP程序的启动文件无需修改,因为该程序烧录的位置是默认的位置,即从映射地址0x08000000起大小为16K的内存块。由于用户APP烧录的位置非默认,需根据自定义的内存划分进行修改。ld文件定义程序入口地址,定义Flash、RAM中代码和数据的存放位置十分关键。
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
FLASH (rx) : ORIGIN = 0x8004000, LENGTH = 256K
}
3、用户程序与IAP之间的切换
3.1、IAP切换用户程序
3.1.1、跳转函数
Share domain内存块是我额外划分为用于存储一些两个应用程序所共享的一些数据的内存区域。比如:用于存放启动程序的标志Flag之类的数据。开机程序启动后进行程序运行的选择,根据写在该区域地质中的信息进行IAP和APP之间的选择切换,实现从用户程序跳转IAP进行程序更新。
void BootmodeSwitch(void)
{
/* close interruption */
__set_FAULTMASK(1);
/* set the vector table */
if(AppBootflagRead()) //read the boot mask in the share zoom
{
/* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
JumpToApplication = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
JumpToApplication();
}
}
/* enable interruption */
__set_FAULTMASK(0);
return;
}
上述代码还是挺经典的,为何取APPLICATION_ADDRESS地址指向的前四个字节的值与0x2FFE0000呢,那为什么是0x20005000呢?
首先来看看中断向量表,我们的重点在第一二个DWORD,第一个Initial SP value是栈顶地址的值,第二个是Reset复位,这也是我们接下来实现程序跳转的关键所在。
下面就来分析一下上面这段代码。
((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000
(*(__IO uint32_t*)APPLICATION_ADDRESS) 即取0x8004000開始到0x8004003的4个字节的值,由于我们的应用程序APP中设置把中断向量表放置在0x08004000 开始的位置;而中断向量表里第一个放的就是栈顶地址的值,通过判断栈顶地址值是否正确(是否在0x20000000 ~ 0x 20002000之间)来推断是否应用程序已经烧录,也确保程序没有跑飞,若程序没烧录的话该地址的值一定为0xFFFFFFFF(初始化后的值flash一般为0xFF)。这里的0x2FFE0000需要根据不同芯片的SRAM的大小进行修改,假如使用的是STM32F103C8T6, RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K,具有20K的SRAM,所以RAM区地址范围为0x20000000~0x20005000,则应用起始地址与的对象为0x2FFFA000。
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
APPLICATION_ADDRESS+ 4 即为0x08004000起始的第二个DWORD ,参考上面的中断向量表,第二个4字节为复位中断向量的入口地址。
//typedef void (*pFunction)(void);
JumpToApplication = (pFunction) JumpAddress;
JumpToApplication 其实质是一个函数指针,指向了复位函数所在的地址。
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
JumpToApplication();
最后是设置主函数栈指针和运行复位函数。
3.1.2、用户程序重定向中断向量
void BootReset(void)
{
/* close interruption */
__set_FAULTMASK(1);
/* set the vector table */
SCB->VTOR = FLASH_BASE | 0x4000;
/* enable interruption */
__set_FAULTMASK(0);
}
3.2、用户程序切换IAP
通过读写标志位app_boot_mask_addr来进行程序切换,app_boot_mask_addr的实质是存储在Share domain区域的值,对该标志位写值后,对系统进行软件复位。复位后,程序一进入main函数便读取app_boot_mask_addr的值,若符合设置的进行IAP的条件便进入IAP程序。
void IAPModeEnter(void)
{
uint32_t app_boot_mask_addr, gdata;
app_boot_mask_addr = APP_BOOT_FLAG;
gdata = 0;
if(iap_enable_flag)
{
iap_enable_flag = FALSE;
FlashWritedata(app_boot_mask_addr, gdata);
/* reset the system */
HAL_NVIC_SystemReset();
}
}
4、Demo演示
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/116942.html