做自媒体可以参考的外国网站,东莞常平天气,成都网站建设价格表,做优化网站建设一、IAP简介
IAP#xff08;In Application Programming#xff09;即在应用编程#xff0c; IAP 是用户自己的程序在运行过程中对 User Flash 的部分区域进行烧写#xff0c;目的是为了在产品发布后可以方便地通过预留的通信口对产 品中的固件程序进行更新升级。
通常实…一、IAP简介
IAPIn Application Programming即在应用编程 IAP 是用户自己的程序在运行过程中对 User Flash 的部分区域进行烧写目的是为了在产品发布后可以方便地通过预留的通信口对产 品中的固件程序进行更新升级。
通常实现 IAP 功能时即用户程序运行中作自身的更新操作需要在设计固件程序时编写两部分代码第一部分程序不执行正常的功能操作而只是通过某种通信方式(如 USB、 USART)接收程序或数据执行对第二部分代码的更新第二部分代码才是真正的功能代码。
两部分代码都同时烧录在 User Flash 中当芯片上电后首先是第一部分代码开始运行它作如下操作
检查是否需要对第二部分代码进行更新如果不需要更新则转到第二部分代码执行执行更新操作跳转到第二部分代码执行
第一部分代码必须通过其它手段如 JTAG 或 ISP 烧入二部分代码可以使用第一部分代码 IAP 功能烧入也可以和第一部分代码一起烧入以后需要程序更新时再通过第一部分 IAP代码更新。
将第一部分项目代码称之为 Bootloader 程序第二部分代码称之为 APP 程序他们存放在STM32F4 FLASH 的不同地址范围一般从最低地址区开始存放 Bootloader紧跟其后的就是APP 程序注意如果 FLASH 容量足够是可以设计很多 APP 程序的同时还可以分两个bank区。这样我们就是要实现 2 个程序 Bootloader 和 APP。
STM32F4 的 APP 程序不仅可以放到 FLASH 里面运行也可以放到 SRAM 里面运行。
二、正常的程序运行流程 内部闪存FLASH地址起始于 0x08000000一般情况下程序文件就从此地址开始写入。
程序启动后将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动而这张“中断向量表”的起始地址是 0x08000004当中断来临 内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处并根据中断源取出对应的中断向量执行中断服务程序。
复位后先从 0X08000004 地址取出复位中断向量的地址并跳转到复位中断服务程序如图标号①所示在复位中断服务程序执行完之后会跳转到我们的 main 函数如图标号②所示而我们的 main 函数一般都是一个死循环在 main 函数执行过程中如果收到中断请求发生重中断 PC 指针指回中断向量表处如图标号③所示然后根据中断源进入相应的中断服务程序如图标号④所示在执行完中断服务程序以后程序再次返回 main 函数执行如图标号⑤所示。
三、IAP 程序的运行流程 程序还是从 0X08000004 地址取出复位中断向量的地址并跳转到复位中断服务程序在运行完复位中断服务程序之后跳转到 IAP 的 main 函数
在执行完 IAP 以后即将新的 APP 代码写入 FLASH灰底部分。新程序的复位中断向量起始地址为 0X08000004NM跳转至新写入程序的复位向量表取出新程序的复位中断向量的地址并跳转执行新程序的复位中断服务程序随后跳转至新程序的 main 函数如图标号②和③所示同样 main 函数为一个死循环但在不同位置上共有两个中断向量表。
通俗讲其实就是在正常运行的程序APP程序之前加上一个程序bootloader程序 bootloader程序运行完之后再运行APP程序两个是独立的个体都需要有自己的中断。所以bootloader程序运行完之后就需将中断向量表位置移到APP程序的位置不然APP程序发生中断响应回去执行bootloader程序的中断程序。
IAP 程序必须满足两个要求
新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始必须将新程序的中断向量表相应的移动移动的偏移量为 x
四、运用实例
bootloader程序开机的时候先显示提示信息然后等待串口输入接收 APP 程序无校验一次性接收在串口接收到 APP 程序之后即可执行 IAP。
按下 KEY1 按键将串口接收到的 APP 程序存放到 STM32F103 的 FLASH之后再按 KEY2 既可以跳转执行这个 FLASH APP 程序。
Bootloader 程序
main.c
#include stm32f10x.h
#include bsp_led.h
#include bsp_key.h
#include bsp_TiMbase.h
#include bsp_usart.h/* 用于获取接收数据的计数 */
uint32_t USART_RX_CNT; /* 串口接收的数据保存在ram的0X20001000地址中 */
uint8_t USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));/* 接收数据函数处理 */
void Receive_App_Data(void)
{static uint16_t oldcount 0; static uint16_t applenth 0;static uint16_t com_delay 0;if (com_delay 1000){com_delay;}else{if (USART_RX_CNT) {if (oldcount USART_RX_CNT){applenth USART_RX_CNT;printf(Receiving APP data succeeded.\r\n);printf(Code len: %dBytes\r\n,applenth);oldcount 0;USART_RX_CNT 0;}else{oldcount USART_RX_CNT;}}}/* 扫描Key1, 将接收的app数据写进内部flash中 */if(Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) KEY_ON){if(applenth){printf(Start updating firmware...\r\n); /* 判断APP程序的复位地址是否在范围内 */if(((*(volatile uint32_t *)(0x20001000 4)) 0xFF000000) 0x08000000){ Iap_Write_Appbin(FLASH_APP1_ADDR, USART_RX_BUF, applenth); printf(Firmware update complete!\r\n); }else { printf(Non-flash applications!\r\n);}}else {printf(There is no firmware to update!\r\n);} }
}int main(void)
{USART_Config();LED_GPIO_Config();Key_GPIO_Config();BASIC_TIM_Init();printf(IAP Test !\n);while(1){if( Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) KEY_ON){printf(Start APP code!\r\n);if(((*(volatile uint32_t *)(FLASH_APP1_ADDR 4)) 0xFF000000)0x08000000){ printf(success\n);/* 跳转到App程序中最好放在主循环里不要放在定时器里 */Iap_Load_App(FLASH_APP1_ADDR);}else {printf(Non-flash applications cannot be executed!\r\n);}} }
}主要运行跳转到app的函数。
iap.c
#include iap.h uint16_t iapbuf[1024]; //2K字节缓存
iapfun jump_to_app;/* 以每2k字节进行写入 */
void Iap_Write_Appbin(uint32_t appxaddr, uint8_t *appbuf, uint32_t appsize)
{uint16_t t;uint16_t i0;uint16_t temp;uint32_t fwaddr appxaddr;//当前写入的地址uint8_t *dfu appbuf;for(t 0; t appsize; t 2){ temp (u16)dfu[1] 8;temp (u16)dfu[0]; dfu 2;//偏移2个字节iapbuf[i] temp; if(i 1024){i 0;Flash_Write(fwaddr, iapbuf, 1024); fwaddr 2048;//偏移2048 162*8.所以要乘以2.}}if (i)Flash_Write(fwaddr, iapbuf, i);//将最后的一些内容字节写进去.
}void Iap_Load_App(uint32_t appxaddr)
{/* 检查栈顶地址是否合法 */if(((*(volatile uint32_t *)appxaddr) 0x2FFE0000) 0x20000000) { /* 关中断 */__disable_irq(); /* 关闭外设时钟 */RCC-APB1ENR 0;RCC-APB2ENR 0;SysTick-CTRL 0; /* 设置主堆栈指针 */__set_MSP(*(volatile uint32_t *)appxaddr);/* APP程序的复位地址 */jump_to_app (iapfun) * (volatile uint32_t *)(appxaddr 4);jump_to_app();}
} /*********************************************END OF FILE**********************/主要是将app程序写入flash和跳转到app操作。
注意一定要关中断和关闭外设由于本程序用了TIM,所以关了中断没用也可以不关但最好是关
串口中断-
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE)!RESET){ ucTemp USART_ReceiveData(DEBUG_USARTx);USART_SendData(DEBUG_USARTx,ucTemp); if(USART_RX_CNT USART_REC_LEN){USART_RX_BUF[USART_RX_CNT] ucTemp;USART_RX_CNT; }}
}
主要是接收app程序。
定时器中断-bsp_TiMbase.c
void BASIC_TIM_IRQHandler (void)
{if (TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) ! RESET ) { LED_Test();Receive_App_Data();TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update); }
}
运行LED闪烁程序和接收app数据并写入到flash里。
bootloader程序大小的确定
双击工程得到.map文件看到该程序占用10.99kb大小。 使用11kb进行计算得出2c00但我使用了5000为了方便扩展当然你可以设置该大小。 APP 程序
#include stm32f10x.h
#include bsp_led.h
#include bsp_TiMbase.hvolatile uint32_t time 0; // ms 计时变量 int main(void)
{SCB-VTOR 0x08005000;__enable_irq();LED_GPIO_Config();BASIC_TIM_Init();while(1){if ( time 1000 ) /* 1000 * 1 ms 1s 时间到 */{time 0; LED1_TOGGLE; } }
}
主要使1s LED闪烁一次
/* 设置中断向量表的偏移量 */
SCB-VTOR 0x08005000;
/* 使能中断 */
__enable_irq();
这两个必须要加上否则无法跳转。 用XCOM进行app文件的发送 按下KEY1: 按下KEY2: 五、源代码地址
https://gitee.com/xu-fuyong/stm32f103-iap