IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
以上是IAP的官方解释,通俗一点来说,iap就是一个专门用来升级应用程序的程序。
首先,为什么存在IAP?
在产品不能进行直接烧写时,例如已经包装好了外壳,或已经发到了客户手里等不能接触,只能通过iap来更新应用程序的功能。
其次,IAP是怎么工作的,它和APP有什么关联,共用的外设怎么分配?
我们知道单片机的执行位置是PC指针所指的地方,如下图所示,它指向IAP时,则运行IAP的代码,此时所有的外设自然都服务于IAP,指向APP时同理。
上图的每个方框代表flash上的一块区域,可以是一个或多个扇区,IAP工程生成的bin文件最后就烧写在我们指定给它的flash区域中,APP的bin文件同理。
当执行IAP中的各个函数时,PC指针就会在这个区域中跳来跳去(其实各个函数的地址在map文件中也能看到),
当执行IAP中的跳转到APP的指令时,PC就指向了APP的区域,继续在那一块区域跳来跳去。
下面我用cubemx + stm32f4 来举个栗子,详细说明iap具体怎么实现跳转
IAP实例
使用cubemx ,配置好串口1和时钟,生成iap和app两个工程,app工程需要更改执行起始地址,本例中app改为了0x8010000,具体如何更改也很简单,可以参考我的另一篇文章,
先做好串口重定向,跳转函数,跳转地址宏,代码如下:
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
#define APP_FLASH_ADDR 0x8010000
typedef unsigned int u32;
typedef void (*iapfun)(void); //定义一个函数类型的参数.
iapfun jump2app;
void iap_load_app(u32 appxaddr)
{
printf("iap jump to app,addr:%08xrn",appxaddr);
jump2app=(iapfun)*(u32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
__set_MSP(*(u32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app();
}
/* USER CODE END 0 */
main函数中改动代码如下
/* USER CODE BEGIN 2 */
printf("iap demorn");
iap_load_app(APP_FLASH_ADDR);
printf("iap demo endrn");//正常情况下,永远不会执行这行以及下面的代码。
/* USER CODE END 2 */
tips1:关于jump指令
定义函数指针类型的参数,方便阅读,如果想紧凑一点可以参考下方代码一样可以跳转,不论哪种写法,流程是一样的,
1,*(u32*) >> 取0x8010000处的第二字的内容,也就是tips2中的08012ccd
2, ((void(*)()) >> 将08012ccd强转为无返回无入参的函数指针,
3,执行函数指针,也就是执行地址为08012ccd的函数
void iap_load_app(u32 appxaddr)
{
printf("iap jump to app,addr:%08xrn",appxaddr);
//jump2app=(iapfun)*(u32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
__set_MSP(*(u32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
//jump2app();
((void(*)())(*(u32*)(appxaddr+4)))();
}
tip2:为什么不是跳转到0x8010000,set_msp又是什么鬼?
我们先来看看app烧写成功之后,0x8010000处是什么样子的,如下图
可以看到第一个字是20000570其实是栈顶地址,那其他的又是什么,我们再来看两张图
一张是APP的启动文件中的中断向量表,另一张是APP编译后的map文件所打印出来的各函数的地址,时间关系没有框全,但我们将几张图的序号对应起来看,不难发现,其实用户代码开始几个字,是按顺序对应的栈顶地址,Reset_Handler地址等。
当我们从IAP跳转到第二个字也就是Reset_Handler之后,会执行SystemInit来初始化时钟,后面的__iar_program_start是iar的一条内部指令,对C的运行,以及全局变量等做初始化操作,一系列准备工作就绪之后再进入main.c函数,执行APP代码,至此,跳转成功完成。
结果展示
先附上APP的代码
/* USER CODE BEGIN 2 */
printf("diy8 demorn");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(1000);
printf("uart testrn");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
再看来自串口的打印信息
由图可知,跳转成功,IAP中的iap demo end也确实没有执行打印,end。
IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
以上是IAP的官方解释,通俗一点来说,iap就是一个专门用来升级应用程序的程序。
首先,为什么存在IAP?
在产品不能进行直接烧写时,例如已经包装好了外壳,或已经发到了客户手里等不能接触,只能通过iap来更新应用程序的功能。
其次,IAP是怎么工作的,它和APP有什么关联,共用的外设怎么分配?
我们知道单片机的执行位置是PC指针所指的地方,如下图所示,它指向IAP时,则运行IAP的代码,此时所有的外设自然都服务于IAP,指向APP时同理。
上图的每个方框代表flash上的一块区域,可以是一个或多个扇区,IAP工程生成的bin文件最后就烧写在我们指定给它的flash区域中,APP的bin文件同理。
当执行IAP中的各个函数时,PC指针就会在这个区域中跳来跳去(其实各个函数的地址在map文件中也能看到),
当执行IAP中的跳转到APP的指令时,PC就指向了APP的区域,继续在那一块区域跳来跳去。
下面我用cubemx + stm32f4 来举个栗子,详细说明iap具体怎么实现跳转
IAP实例
使用cubemx ,配置好串口1和时钟,生成iap和app两个工程,app工程需要更改执行起始地址,本例中app改为了0x8010000,具体如何更改也很简单,可以参考我的另一篇文章,
先做好串口重定向,跳转函数,跳转地址宏,代码如下:
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
#define APP_FLASH_ADDR 0x8010000
typedef unsigned int u32;
typedef void (*iapfun)(void); //定义一个函数类型的参数.
iapfun jump2app;
void iap_load_app(u32 appxaddr)
{
printf("iap jump to app,addr:%08xrn",appxaddr);
jump2app=(iapfun)*(u32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
__set_MSP(*(u32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app();
}
/* USER CODE END 0 */
main函数中改动代码如下
/* USER CODE BEGIN 2 */
printf("iap demorn");
iap_load_app(APP_FLASH_ADDR);
printf("iap demo endrn");//正常情况下,永远不会执行这行以及下面的代码。
/* USER CODE END 2 */
tips1:关于jump指令
定义函数指针类型的参数,方便阅读,如果想紧凑一点可以参考下方代码一样可以跳转,不论哪种写法,流程是一样的,
1,*(u32*) >> 取0x8010000处的第二字的内容,也就是tips2中的08012ccd
2, ((void(*)()) >> 将08012ccd强转为无返回无入参的函数指针,
3,执行函数指针,也就是执行地址为08012ccd的函数
void iap_load_app(u32 appxaddr)
{
printf("iap jump to app,addr:%08xrn",appxaddr);
//jump2app=(iapfun)*(u32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
__set_MSP(*(u32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
//jump2app();
((void(*)())(*(u32*)(appxaddr+4)))();
}
tip2:为什么不是跳转到0x8010000,set_msp又是什么鬼?
我们先来看看app烧写成功之后,0x8010000处是什么样子的,如下图
可以看到第一个字是20000570其实是栈顶地址,那其他的又是什么,我们再来看两张图
一张是APP的启动文件中的中断向量表,另一张是APP编译后的map文件所打印出来的各函数的地址,时间关系没有框全,但我们将几张图的序号对应起来看,不难发现,其实用户代码开始几个字,是按顺序对应的栈顶地址,Reset_Handler地址等。
当我们从IAP跳转到第二个字也就是Reset_Handler之后,会执行SystemInit来初始化时钟,后面的__iar_program_start是iar的一条内部指令,对C的运行,以及全局变量等做初始化操作,一系列准备工作就绪之后再进入main.c函数,执行APP代码,至此,跳转成功完成。
结果展示
先附上APP的代码
/* USER CODE BEGIN 2 */
printf("diy8 demorn");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(1000);
printf("uart testrn");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
再看来自串口的打印信息
由图可知,跳转成功,IAP中的iap demo end也确实没有执行打印,end。
举报