IAP简介(简介部分copy自正点原子)

IAP(In Application Programming)即在应用编程,IAP 是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。 通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:
1)检查是否需要对第二部分代码进行更新
2)如果不需要更新则转到 4)
3)执行更新操作
4)跳转到第二部分代码执行
第一部分代码必须通过其它手段,如 JTAG 或 ISP 烧入;第二部分代码可以使用第一部分代码 IAP 功能烧入,也可以和第一部分代码一起烧入,以后需要程序更新时再通过第一部分 IAP代码更新。
我们将第一个项目代码称之为 Bootloader 程序,第二个项目代码称之为 APP 程序,他们存放在 STM32L433FLASH 的不同地址范围,一般从最低地址区开始存放 Bootloader,紧跟其后的就是 APP 程序(注意,如果 FLASH 容量足够,是可以设计很多 APP 程序的,本章我们只讨论一个 APP 程序的情况)。这样我们就是要实现 2 个程序:Bootloader 和 APP。
STM32L433 的 APP 程序不仅可以放到 FLASH 里面运行,也可以放到 SRAM 里面运行,

程序思路介绍


当产品定型投产后每一次烧录程序都必须去修改BOOT硬件引脚的电平很不方便所以我们需要自己编写一套BootLoader程序,也就是在单片机FLASH中编写两套程序,利用第一套程序通过串口接收的方式将第二套程序写在指定地址的内部FLASH上,然后强制跳转单片机的PC指针实现运行第二套程序的方法,从理论上来说,单片机可以保存无数套程序并通过强制跳转指针去运行,当然前提是你的FLASH足够大。
我们称第一套BootLoader程序为引导程序,第二套真正要运行的为APP程序,引导程序只是在单片机上电时执行一次,真正工作的是APP程序,下面我们来细说具体原理。

STM32L433内部FLASH


我们从最底层的开始看,因为单片机内部的FLASH被分配的地址为0x8000000到0x8040000,结束地址减去起始地址是40000,对应的就是262444字节,除以1024刚好是256K字节,这也对应着STM32L433的flash为256K。
所以,我们引导程序分配56K字节,APP程序分配200字节。


单片机上电之后从0x8000000这个地址开始运行程序,STM32L4 的内部闪存(FLASH)地址起始于 0x08000000,一般情况下,程序文件就从此地址开始写入。此外 STM32L4 是基于 Cortex-M4 内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是 0x08000004,当中断来临,STM32L4 的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。在图 中,STM32L4 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求(发生重中断),此时 STM32L4 强制将 PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。
如果假如了IAP程序,那么流程就会变成:

因为每次有中断发生的时候都会跳转到中断向量表中,中断向量表的起始地址为0x8000004,这个是程序强制执行的,所以我们在APP程序的第一句,要对中断向量表的地址进行移位。
SCB->VTOR = FLASH_BASE | 0x10000;
在APP程序的第一句加上或操作即对APP程序的中断向量表进行的移位,当中断发生时,程序会自动跳转至移位后的中断向量表保证程序不会跑飞。
在引导程序中,接收的程序也是直接被存在单片机的SRAM中,STM32L433的SRAM有64K,所以限制了接收的最大字节数,所以可以采用边接收边写FLASH的办法,最好也要对接收到的数据进行校验。

程序部分

主函数

在主函数中我利用了AT指令的方法去进入BootLoader引导程序,程序上电后5秒没有收到固定的AT指令就会强制跳转指针到APP应用程序,如果收到了AT+BOOTLOADER指令程序即关闭定时器并准备接收APP程序,校验办法是我会在发程序之前先发送APP程序的字节数,当接收到相同字节的程序即认为接收成功,当然这种办法很蠢,最好的是CRC去校验。

extern uint8_t u8RxBuf[];
int main(void)
{/*上一次串口接收数值*/uint32_t u32oldcount = 0;/*接收到的app代码长度*/uint32_t applenth = 0;  /*上传代码长度*/  int32_t u32ProgramSize = 0;    gtSysFlag.u8GetDataFlag = false;gtSysFlag.u8GetProgramFlag = false;gtSysFlag.u8JumpAppFlag = false;gtSysFlag.u8ProgramOkFlag = false;HAL_Init();SystemClock_Config();       //初始化系统时钟为80Mdelay_init(80);            //初始化延时函数    80M系统时钟uart_init(115200);          //初始化串口,波特率为115200TIM2_Init(5000 - 1, 8000 - 1);while(1){        /*仅支持大写输入*/if(gtSysFlag.u8GetDataFlag == true){if((u8RxBuf[0] == 'A') && (u8RxBuf[1] == 'T') && (u8RxBuf[2] == '+')){if((u8RxBuf[3] == 'B') && (u8RxBuf[4] == 'O') && (u8RxBuf[5] == 'O') && (u8RxBuf[6] == 'T')){/*关闭超时定时器*/__HAL_RCC_TIM2_CLK_DISABLE();   gtSysFlag.u8JumpAppFlag = false;                           printf("已进入引导程序\r\n");                        }else if((u8RxBuf[3] == 'B') && (u8RxBuf[4] == 'Y') && (u8RxBuf[5] == 'T') && (u8RxBuf[6] == 'E')){u32ProgramSize = ((u8RxBuf[7] - '0')*10000) + ((u8RxBuf[8] - '0')*1000) + ((u8RxBuf[9] - '0')*100) + ((u8RxBuf[10] - '0')*10) + ((u8RxBuf[11] - '0'));printf("代码字节数为:%d\r\n",u32ProgramSize);}           else if((u8RxBuf[3] == 'P') && (u8RxBuf[4] == 'R') && (u8RxBuf[5] == 'O') && (u8RxBuf[6] == 'G')){if((u8RxBuf[7] == 'R') && (u8RxBuf[8] == 'A') && (u8RxBuf[9] == 'M')){if(u32ProgramSize != 0){/*开始接收APP程序*/                          gtSysFlag.u8GetProgramFlag = true;                         printf("准备接收APP程序\r\n");          }   else{printf("请输入要发送的字节数\r\n");}                       }}  else if((u8RxBuf[3] == 'W') && (u8RxBuf[4] == 'R') && (u8RxBuf[5] == 'I') && (u8RxBuf[6] == 'T')){if((u8RxBuf[7] == 'E') && (u8RxBuf[8] == 'F') && (u8RxBuf[9] == 'L')){if(applenth != 0){if(gtSysFlag.u8ProgramOkFlag == true){/*更新Flash的程序*/iap_write_appbin(FLASH_APP1_ADDR, USART_RX_BUF, applenth); //更新FLASH代码printf("固件更新完成!\r\n");     }       else        {printf("请上传代码后进行固件更新!\r\n");}            gtSysFlag.u8ProgramOkFlag = false;                         }else{printf("没有可以更新的固件!\r\n");}}}    else if((u8RxBuf[3] == 'R') && (u8RxBuf[4] == 'U') && (u8RxBuf[5] == 'N')){/*开始执行flash程序*/gtSysFlag.u8JumpAppFlag = true;              }               }           gtSysFlag.u8GetDataFlag = false;}  /*程序接收部分*/if(gtSysFlag.u8GetProgramFlag == true){ if(USART_RX_CNT != 0){if(u32oldcount == USART_RX_CNT) //新周期内,没有收到任何数据,认为本次数据接收完成.{applenth = USART_RX_CNT;u32oldcount = 0;USART_RX_CNT = 0;printf("用户程序接收完成!\r\n");printf("上传代码字节数为:%dBytes\r\n",u32ProgramSize);printf("实际代码长度:%dBytes\r\n", applenth);if(applenth == u32ProgramSize){printf("用户程序校验正确!\r\n");gtSysFlag.u8ProgramOkFlag = true;}else{printf("用户程序校验错误,请重新上传!\r\n");}gtSysFlag.u8GetProgramFlag = false;                          }else {/*将最新计数值赋值给上一次变量*/u32oldcount = USART_RX_CNT;}}delay_ms(50);}/*跳转至APP应用程序*/     if(gtSysFlag.u8JumpAppFlag == true){/*判断APP程序起始地址上是否有正确数据*/if(((*(vu32*)(FLASH_APP1_ADDR + 4)) & 0xFF000000) == 0x08000000){printf("开始执行APP程序");__HAL_RCC_TIM7_CLK_DISABLE();gtSysFlag.u8GetProgramFlag = false;iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码}  else{printf("FLASH无应用程序!");gtSysFlag.u8JumpAppFlag = false;}              }       }
}

串口接收程序

#include "usart.h"
#include "delay.h"
#include "sys.h"
#include "timer.h"
tSysFlag gtSysFlag;
extern TIM_HandleTypeDef TIM7_Handler;      //定时器句柄
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{int handle;
};FILE __stdout;
/*** @brief    定义_sys_exit()以避免使用半主机模式** @param   void** @return  void*/
void _sys_exit(int x)
{x = x;
}
/*** @brief    重定义fputc函数** @param    ch      输出字符量* @param  f       文件指针** @return  void*/
int fputc(int ch, FILE *f)
{while((USART1->ISR & 0X40) == 0); //循环发送,直到发送完毕USART1->TDR = (u8) ch;return ch;
}
#endif#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]  __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.
uint8_t u8RxBuf[20];
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0,   接收到的有效字节数目
u16 USART_RX_STA = 0;   //接收状态标记
u32 USART_RX_CNT=0;        //接收的字节数
uint16_t u16LoopData = 0;UART_HandleTypeDef UART1_Handler; //UART句柄void uart_init(u32 bound)
{//UART 初始化设置UART1_Handler.Instance = USART1;                    //USART1UART1_Handler.Init.BaudRate = bound;               //波特率UART1_Handler.Init.WordLength = UART_WORDLENGTH_8B; //字长为8位数据格式UART1_Handler.Init.StopBits = UART_STOPBITS_1;    //一个停止位UART1_Handler.Init.Parity = UART_PARITY_NONE;       //无奇偶校验位UART1_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件流控UART1_Handler.Init.Mode = UART_MODE_TX_RX;         //收发模式HAL_UART_Init(&UART1_Handler);                      //HAL_UART_Init()会使能UART1__HAL_UART_ENABLE_IT(&UART1_Handler, UART_IT_RXNE); //开启接收中断HAL_NVIC_EnableIRQ(USART1_IRQn);                   //使能USART1中断通道HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);              //抢占优先级3,子优先级3
}/*** @brief   HAL库串口底层初始化,时钟使能,引脚配置,中断配置** @param   huart   串口句柄** @return  void*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/**USART1 GPIO ConfigurationPB6     ------> USART1_TXPB7     ------> USART1_RX*/GPIO_InitTypeDef GPIO_InitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};        GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_USART1;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}/*** @brief   串口1中断服务程序** @remark    下面代码我们直接把中断控制逻辑写在中断服务函数内部*          说明:采用HAL库处理逻辑,效率不高。** @param   void** @return  void*/
void USART1_IRQHandler(void)
{   uint8_t Res;static uint8_t u8Res = 0;if(gtSysFlag.u8GetProgramFlag == true){if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  {HAL_UART_Receive(&UART1_Handler,&Res,1,1000); if(USART_RX_CNT<USART_REC_LEN){  USART_RX_BUF[USART_RX_CNT]=Res;USART_RX_CNT++;                         }          }}     else if(gtSysFlag.u8GetProgramFlag == false){if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  {HAL_UART_Receive(&UART1_Handler,&Res,1,1000); u8RxBuf[u16LoopData]=Res;  u16LoopData++;if(u16LoopData >= 20){u16LoopData = 0;}if(Res == 0x0D){u8Res = Res;}        if((Res == 0x0A) && (u8Res == 0x0D)){u8Res = 0;u16LoopData = 0;gtSysFlag.u8GetDataFlag = true;}                }        }    HAL_UART_IRQHandler(&UART1_Handler);__HAL_UART_ENABLE_IT(&UART1_Handler, UART_IT_RXNE);
}#endif

STM32的IAP在线升级相关推荐

  1. STM32的IAP在线升级的源码中的地址解读

    1.ApplicationAddress的内容含义     if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x200 ...

  2. CRC校验原理及STM32 IAP在线升级程序

    CRC校验原理: 什么是CRC校验? CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定.循环冗余检查(CRC)是一种数据传输检错功能,对数据 ...

  3. STM32 IAP 在线升级原理全解析

    点击左上角的"关注",定期更新 STM32 最新资讯,总有你想要的信息! STM32 IAP 在线升级原理全解析 1. 什么是 IAP?    IAP(In-Application ...

  4. STM32F429实现USB通过IAP在线升级

    1.目标 1.实现STM32对U盘文件的读取. 2.实现STM32拓展外部SDRAM. 3.实现STM32拓展外部Flash. 4.实现内存管理. 5.实现Fatfs文件系统,读写U盘和外部Flash ...

  5. LPC2478(22)IAP在线升级

    目录 1.开发环境 2.特性 3.IAR编译器的相关文件 3.1.icf文件 3.2.IcfEditorFile文件内容 3.3.ddf文件 3.4.board文件 4.IAP相关 4.1.软件复位 ...

  6. 基于Ymodem协议的stm32f405rgt6+CubeMx+IAP在线升级

    基于Ymodem协议的stm32f405rgt6+CubeMx+IAP在线升级 目录 一.CubeMX的配置 1.IAP 2.APP 二.移植Ymodem官方代码 1.文件移植 2.MDK文件.路径添 ...

  7. 51单片机IAP在线升级

    51单片机IAP在线升级 爱矽半导体E85F3325单片机IAP在线升级教程,此处可查看更新及demo下载 文章目录 前言 一.ROM资源 二.KEIL有关知识 1.BL51连接器: 2.LX51连接 ...

  8. STM32 IAP 在线升级详解

    (扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP  再烧写APP应用程序要烧写2次增加工人劳动力基础上写了"STM32 IAP+APP ==>双剑合一&q ...

  9. Stm 32 IAP 在线 升级IAP 的 操作

    (扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP  再烧写APP应用程序要烧写2次增加工人劳动力基础上写了"STM32 IAP+APP ==>双剑合一&q ...

  10. Cortex-M3单片机的IAP在线升级上位机和下位机

    最近有个项目要做在线升级功能,我也是第一次做,把学习的过程总结下,希望能够帮助到 其他人吧.本篇博客主要介绍两个部分,下位机和上位机. 首先说下要实现功能: 1.上位机能够把APP的bin文件烧写进下 ...

最新文章

  1. 商用计算机的使用环境,工业一体机电脑和普通的商用电脑区别
  2. 排序字段设计_「原创」第四章、模型设计
  3. Jerry带您了解Restful ABAP Programming模型系列之三:云端ABAP应用调试
  4. Zookeeper 辅助 API
  5. 对弈类游戏的人工智能(3)--博弈树优化
  6. 【安全】JAAS/GSS-API/SASL/Kerberos简介
  7. 什么样的细节会让你觉得一个人的情商高得吓人?
  8. 远程调用——hessian使用入门
  9. typora字体颜色及字体背景颜色快捷方式(亲测实用有效)
  10. mysql 查询优化 ~ 善用profie利器
  11. [Database] 不知道表名和字段查找值=1234的数据.
  12. [转帖]妇联4剧透 看不起电影看剧透.
  13. 广州图书馆跳转中国知网教程
  14. 使用curl查询本机ipv4或者ipv6
  15. bootstrap table合并单元格
  16. nginx反向代理和正向代理
  17. VC/MFC使用OLE操作 EXCEL
  18. 解决win10系统网络连接正常,但是网页打不开的问题
  19. G、S、C、P、T STATE
  20. 音乐及游戏爱好者的福利,小鹏P7上新网易云及阴阳师,赶快看看吧

热门文章

  1. MSSQL · 实现分析 · SQL Server实现审计日志的方案探索
  2. 下载堆糖图片-正则-xpath-BeautifulSoup-selenium-python爬虫
  3. 关于医学影像中的轴位面(横断面)、冠状面、矢状面
  4. mc服务器信息监控,Splunk搭建MC监控控制台
  5. linux怎样编译安装pidgin,Ubuntu 10.04编译安装Pidgin-2.7.3整个过程
  6. 搭建 Python 开发环境
  7. uva424 Integer Inquiry
  8. 近代物理实验 核磁共振的稳态吸收(含数据及参考题)
  9. python gui测试工具_GitHub - Github-Benjamin/LeChu: Python GUI工具 二次开发
  10. day002血字的研究