免费定制logo网站,关于农产品电子商务网站的建设,建设的比较好的网站,哪些大型网站有做互联网金融STM32F4 IAP 跳转 APP问题 ST官网IAP例程Chapter1 STM32F4 IAP 跳转 APP问题1. 概念2. 程序2.1 Bootloader 程序 问题现象2.2. APP程序 3. 代码4. 其他问题 Chapter2 STM32-IAP基本原理及应用 | ICP、IAP程序下载流程 | 程序执行流程 | 配置IAP到STM32F4xxxChapter3 STM32基于Y… STM32F4 IAP 跳转 APP问题 ST官网IAP例程Chapter1 STM32F4 IAP 跳转 APP问题1. 概念2. 程序2.1 Bootloader 程序 问题现象2.2. APP程序 3. 代码4. 其他问题 Chapter2 STM32-IAP基本原理及应用 | ICP、IAP程序下载流程 | 程序执行流程 | 配置IAP到STM32F4xxxChapter3 STM32基于Ymodem协议IAP升级笔记1.YMODEM 协议说明3.Ymodem升级工具 ST官网IAP例程
ST官网提供的IAP例程有很多很多比如
库有使用标准外设库SPL的、有使用硬件抽象层库HAL的
通信口有使用USART的有使用I2C的有使用ETH的等。
MCU型号STM8S、STM32F1、F4、L1等几乎全系列都有。
ST官网提供的各种IAP其方法和原理其实都类似就是将程序文件二进制文件写入FLASH。
本文以最简单的基于STM32F10x利用SPL库UART的IAP为例来给大家讲述一下。复杂都是从基础开始后续逐步为大家更新更高级的IAP功能
STM32F10xxx in-application programming using the USART官方地址
https://www.stmicroelectronics.com.cn/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32008.html
Chapter1 STM32F4 IAP 跳转 APP问题
原文链接https://blog.csdn.net/qinbo1234567890/article/details/128318895
1. 概念
IAP 的作用网上其他资料已经有很多介绍了这里放一个链接不进行深入的介绍。本文的关注重点是Bootloader在跳转APP程序中出现的问题。 IAP的实现原理讲解以及中断向量表的偏移
2. 程序
本人主要做应用层的开发所有Bootloader和APP程序使用的是STM32CubeMX工具生成代码后然后进行修改。
2.1 Bootloader 程序
1. CubeMX 配置 步骤1使用的芯片为STM32F407ZGT6 步骤2选择时钟源根据自己的板子进行选择 步骤3时钟配置 步骤4项目配置 2. 代码只介绍跳转函数
完整代码
void IAP_ExecuteApp ( uint32_t ulAddr_App )
{int i 0;pIapFun_TypeDef pJump2App; if ( ( ( * ( __IO uint32_t * ) ulAddr_App ) 0x2FFE0000 ) 0x20000000 ) //1 //检查栈顶地址是否合法.{ HAL_SPI_MspDeInit(hspi1); //2__HAL_RCC_GPIOB_CLK_DISABLE();__HAL_RCC_GPIOG_CLK_DISABLE();/* 设置所有时钟到默认状态使用HSI时钟 */HAL_RCC_DeInit(); //3__set_BASEPRI(0x20); //4__set_PRIMASK(1);__set_FAULTMASK(1);/* 关闭所有中断清除所有中断挂起标志 */for (i 0; i 8; i) //5{NVIC-ICER[i]0xFFFFFFFF;NVIC-ICPR[i]0xFFFFFFFF;}SysTick-CTRL 0; //6SysTick-LOAD 0;SysTick-VAL 0;__set_BASEPRI(0); //7__set_PRIMASK(0);__set_FAULTMASK(0);//8/*1不使用OS时 只用到MSP中断和非中断都使用MSP2使用OS时如UCOSII main函数和中断使用MSP 各个Task线程使用PSP即任务栈*/__set_MSP(*(uint32_t*)ulAddr_App);//当带操作系统从APP区跳转到BOOT区的时候需要将SP设置为MSP否则在BOOT区中使用中断将会引发硬件错误__set_PSP(*(uint32_t*)ulAddr_App);__set_CONTROL(0); /* 在RTOS工程这条语句很重要设置为特权级模式使用MSP指针 */__ISB();//指令同步隔离。最严格它会清洗流水线以保证所有它前面的指令都执行完毕之后才执行它后面的指令。//9pJump2App ( pIapFun_TypeDef ) * ( __IO uint32_t * ) ( ulAddr_App 4 ); //用户代码区第二个字为程序开始地址(复位地址) pJump2App (); //跳转到APP.}
}
1 代码作用检查栈顶地址是否合法.0x20000000是sram的起始地址,也是程序的栈顶地址
if ( ( ( * ( __IO uint32_t * ) ulAddr_App ) 0x2FFE0000 ) 0x20000000 )2 代码作用下面这几个关闭的是在Bootloader中初始化过的外设如果没有初始化过其他外设则不需要
HAL_SPI_MspDeInit(hspi1);
__HAL_RCC_GPIOB_CLK_DISABLE();
__HAL_RCC_GPIOG_CLK_DISABLE();3 代码作用PLL在Bootloader中已经配置启动了在APP程序中如果想再进行配置启动的话会返回错误。
问题现象
将Bootloader和APP程序分别下载到板子上Bootlader程序可以正常运行而APP程序会死在Error_Handler()的while(1)循环中。具体调试发现程序是在执行HAL_RCC_OscConfig()函数的PLL 配置部分检测到当前PLL已经被配置为了系统时钟而返回了HAL_ERROR的返回值导致进入了Error_Handler()。 方案一 网上有人的建议是不使用PLL使用HSI作为系统的时钟源经过测试可以正常运行但是这样会有个问题是使用STM32F4的HSI是16M这显然和通过PLL倍频之后使用的168M不在一个数量级上。 方案二 通过查找资料发现可以在Bootloader启动的时候配置PLL倍频到168M然后在跳转程序之前将RCC的配置反初始化为默认设置使用HSI时钟。
HAL_RCC_DeInit(); 4 代码作用关闭所有的中断此处的特殊寄存器设置值可以看如下链接: 嵌入式–Keil5–调试状态下Registers界面解析nrf52832–Cortex-M4内核
__set_BASEPRI(0x20);
__set_PRIMASK(1);
__set_FAULTMASK(1);5 代码作用清除所有中断挂起标志防止在APP中触发该中断导致运行错误 /* 关闭所有中断清除所有中断挂起标志 */for (i 0; i 8; i) //5{NVIC-ICER[i]0xFFFFFFFF;NVIC-ICPR[i]0xFFFFFFFF;}6 代码作用关闭掉系统滴答定时器该定时器会在APP的程序中重新启动调用HAL_Init();函数会启动 注意:6 的代码一定要放到3 之后因为HAL_RCC_DeInit();函数会再次开启滴答定时器。
SysTick-CTRL 0;
SysTick-LOAD 0;
SysTick-VAL 0;7 代码作用重新启动中断开关 __set_BASEPRI(0);__set_PRIMASK(0);__set_FAULTMASK(0);注意:必须重新启动中断的开关本人调试过程中没有开启__set_FAULTMASK(0) ;的中断导致滴答定时器SysTick无法正常运行执行HAL_Delay();死循环在了延时中耽误了一天的时间查找问题。
8 和9 代码都有详细的注释
注意: __set_PSP((uint32_t)ulAddr_App); 和 __set_CONTROL(0); 函数是使用RTOS实时操作系统的读者必须要添加的
2.2. APP程序
CubeMX 配置基本和Bootloader 基本相同不赘述。本人使用的FreeRTOS的实时操作系统。
1. Keil 配置 步骤1配置程序的起始位置根据自己的需要修改配置的时候发现只配置这块没用需要配置步骤2才行 步骤2Linker 中修改ScatterFile 文件 这是本人的配置每个人的可能都不一样本人是需要用ccmram所以使用自己的ScatterFile 文件修改的内容如下图。 步骤2设置APP的中断向量表的位置 首先找到 startup_stm32f407xx.s 文件中调用 SystemInit 函数的地方找到SystemInit 函数的实现然后 找到 USER_VECT_TAB_ADDRESS 的宏定义的地方取消注释。先修改下图 1的位置然后修改 2的位置2的位置根据自己的APP偏移位置来修改。
3. 代码
在main 函数的一开始添加如下代码。 /* USER CODE BEGIN 1 */HAL_DeInit();HAL_RCC_DeInit();/* USER CODE END 1 */HAL_DeInit(); HAL库反初始化 HAL_RCC_DeInit(); RCC配置反初始化
注意: 如果直接查找HAL_RCC_DeInit() 的实现发现是个空函数实际这是HAL库的一个机制实际编译使用的另外一个文件中的如下图1和2
4. 其他问题
4.1 可能导致APP死机的原因 Bootloader启动的外设比APP的多导致死机需要在跳转到APP时清理不需要的外设配置 Bootloader开启了某个终端在APP中没有配置相应的处理函数导致死机该问题可通过Bootloader程序的4 5 7来解决
Chapter2 STM32-IAP基本原理及应用 | ICP、IAP程序下载流程 | 程序执行流程 | 配置IAP到STM32F4xxx
原文链接
Chapter3 STM32基于Ymodem协议IAP升级笔记
原文链接https://blog.csdn.net/weixin_43940932/article/details/128918183
1.YMODEM 协议说明
YMODEM它分为YMODEM-1K与YMODEM-g,平时说的YMODEM传输指是YMODEM-1K传输。 YMODEM-1K可以一次传输1024字节的信息块同时支持传输多个文件。数据的发送会使用CRC校验保证数据传输的正确性。它每传输一个信息块数据时就会等待接收端回应ACK信号接收到回应后才会继续传输下一个信息块保证数据已经全部接收。 2.升级代码分析 代码是用ST官方代码修改的此处只讲升级API部分,调用的就是Ymodem_Receive函数只需要ymodem.c common.c两个文件上传API实际中用的少,就忽略了。 #define APP_FLASH_START_ADDR (0x08002800U) //APP程序跳转运行地址
#define IMAGE_UPDATA_FLASH_SIZE (MCU_FLASH_APP_MAIN_AREA_SIZE) //
unsigned char recv_buff[PACKET_1K_SIZE] {0};//开辟数据包缓存区
unsigned char FileName[] {0}; //文件名缓存区
/*
**app跳转引导函数
*/
void iap_load_app(const unsigned int app_addr)
{//app_addr为新程序的起始地址检查栈顶地址是否合法即栈顶地址是否为0x2000xxxx内置SRAMif (0x20000000 ((*(volatile unsigned int*)app_addr) 0x2FFE0000)) {//__set_PRIMASK(1);//有中断需要先关闭所有中断const unsigned int jump_addr *(volatile unsigned int*)(app_addr 4); __set_MSP(*(volatile unsigned int*)app_addr);((void (*)(void))jump_addr)();// 设置PC指针为新程序复位中断函数的地址}
}/*
** Ymodem OTA升级执行函数
*/
void ymodem_ota_task(void)
{//在执行的过程中随时都可以按下 A按键主动结束升级动作。printf( IAP already,you can pressed A key to end ota....\r\n);//返回值携带升级信息const int file_sz Ymodem_Receive(recv_buff[0],APP_FLASH_START_ADDR);//API改造了一下增加APP跳转地址形参便于全局维护。if (0 file_sz){printf(\r\n--------- updata sucess --------\r\n);printf(file name: %s\r\n, FileName);printf(file size: %d bytes\r\n,file_sz);printf(--------------------------------\r\n);iap_load_app(APP_FLASH_START_ADDR);}else{printf(ota failed ...\r\n\r\n);if (-1 file_sz)//升级文件大于分配的FLASH大小{printf(\r\n alloc flash size is small ...\r\n);}else if (-2 file_sz)//升级文件校验出错{printf(\r\n verif failed ...\r\n);}else if (-3 file_sz)//用户主动结束升级(即按下了A按键){printf(\r\n aborted by user ...\r\n);}else//升级超时或者其它{printf(\r\n receive failed ....\r\n);} //NVIC_SystemReset();}
}/*
**
*/
int Ymodem_Receive(unsigned char *buf, unsigned int app_addr)
{unsigned char packet_data[PACKET_1K_SIZE PACKET_OVERHEAD] , file_size[FILE_SIZE_LENGTH] ,*file_ptr, //*buf_ptr;int i, packet_length, session_done, file_done, packets_received, errors, session_begin, image_size 0;volatile unsigned int flash_dst_addr, bin_data;flash_dst_addr app_addr;//双循环for (session_done 0, errors 0, session_begin 0;;)//等价 while(1){for (packets_received 0, file_done 0, buf_ptr buf;;)//等价 while(1){/* 0x00: 正常返回 | -1:时间溢出或数据包错误 | 0x01:用户终止*///分类处理接收包数据switch (Receive_Packet(packet_data, packet_length, NAK_TIMEOUT)){case 0://收到完整协议包errors 0;//清除累计错误switch (packet_length)//判断包长{case -1:/* 连续的两个CA信号终止传输 */Send_Byte(ACK);return 0;case 0:/*正常返回*/Send_Byte(ACK);file_done 1;break;default:/* 数据区长度 */if ((packet_data[PACKET_SEQNO_INDEX] 0xff) ! (packets_received 0xff))//检验包序号{Send_Byte(NAK);//包序号不一致}else//包序号一致{if (packets_received 0)//起始帧{if (packet_data[PACKET_HEADER] ! 0){//注:file_ptr packet_data PACKET_HEADER 等价 file_ptr packet_data[PACKET_HEADER],即从第PACKET_HEADER字节开始取数据指针for (i 0, file_ptr packet_data PACKET_HEADER; (*file_ptr ! 0) (i FILE_NAME_LENGTH);){FileName[i] *file_ptr;//获取文件名}FileName[i] \0;//补\0字符串结束符操作。for (i 0, file_ptr; (*file_ptr ! ) (i FILE_SIZE_LENGTH);){file_size[i] *file_ptr;//获取文件大小}file_size[i] \0;//补\0字符串结束符操作。Str2Int(file_size, image_size);//将字符串转为hex数据if (IMAGE_UPDATA_FLASH_SIZE image_size )//判断将要升级的文件是否超过预留的FALSH升级空间大小{/* End session */Send_Byte(CA);//超过FALSH升级空间大小发送传输中止应答符CA结束升级Send_Byte(CA);return -1; //返回错误码}/* Erase the needed pages where the user application will be loaded *//* Define the number of page to be erased */const unsigned int nbr_of_pg mcu_flash_page_alloc(image_size);//计算当前文件大小要擦除的页数/* Erase the FLASH pages */const unsigned int erase_pgn mcu_flash_page_num_calc(flash_dst_addr);//计算APP起始地址所在的页序号因为后面擦除FALSH的API是按页序号来操作的//#define SIMLATE_DEBUG#ifdef SIMLATE_DEBUG //仿真接收数据信息printf(image size:%uBytes |erase page num :%d | erase page nbr:%d\r\n,image_size,erase_pgn,nbr_of_pg);#elsemcu_flash_page_erase_cc_num(erase_pgn,nbr_of_pg);//按页序号来擦除FALSH的API#endifSend_Byte(ACK);Send_Byte(CRC16);//发送正常应答符 CRC校验}else //结束帧{Send_Byte(ACK);file_done 1; //session_done 1;//传输完毕break;}}else //数据帧{//注同样:packet_data PACKET_HEADER 等价 packet_data[PACKET_HEADER]memcpy(buf_ptr,packet_data PACKET_HEADER,packet_length);//取有效程序数据(去掉前三个帧头)bin_data (volatile unsigned int )buf; //注packet_length/4表示下面写FLASH函数是按照4字节(字长度)//若是按2字节写入则用packet_length/2//1字节则按packet_lengthfor (unsigned int j 0; (j packet_length/4);j){#ifdef SIMLATE_DEBUG //先仿真交互信息printf(image dating addr :0x%08X | image data:%08X\r\n,flash_dst_addr,*(unsigned int *)bin_data);#else/* Program the data received into STM32F10x Flash */mcu_flash_word_write(flash_dst_addr,*(volatile unsigned int *)bin_data);//每次按4字节写入(1包则按packet_length/4次写完)if (*(volatile unsigned int *)flash_dst_addr ! *(volatile unsigned int *)bin_data)//每次写入后再读取出来进行对比。{/* End session */Send_Byte(CA);Send_Byte(CA);return -2; //写入出错校验失败}#endifflash_dst_addr 4;//每次地址按写入的字节长度偏移当前API是4字节。bin_data 4;//同样数据源(来自包数据)一起同步偏移。} Send_Byte(ACK);//写完后一包发送ACK.}packets_received;//升级包数目计数session_begin 1;//接收一包标记// printf(received packets :%d\r\n,packets_received);}}break;case 1:/*用户终止*/Send_Byte(CA);Send_Byte(CA);return -3;default:/*时间溢出或数据包错误*/if (session_begin 0) //{errors;}if (errors MAX_ERRORS)//允许出错最大次数{Send_Byte(CA);Send_Byte(CA);return 0;}Send_Byte(CRC16);break;}//end Receive_Packet...if (file_done ! 0){break;}}//end 接收while(1);if (session_done ! 0){break;}}return (int)image_size;
}/*
**获取输入\接收单字节数据
*/
unsigned int SerialKeyPressed(unsigned char *key)
{//串口查询方式接收数据if (RESET!USART_GetFlagStatus(USART1,USART_FLAG_RXNE)){*key (uint8_t)USART_ReceiveData(USART1);return 1;}return 0;
}/*
** 接收字节函数
*/
static int Receive_Byte(unsigned char *c, unsigned int timeout)
{volatile unsigned int count timeout;while (count-- 0){//收到数据立马退出if (SerialKeyPressed(c) 1){return 0;}}return -1;
}/*
** 接收包数据函数
*/
int Receive_Packet(unsigned char *data, int *length, unsigned int timeout)
{unsigned short int i, packet_size;unsigned char c;unsigned short int crc;*length 0;/* 接收一个字符 */if (Receive_Byte(c, timeout) ! 0) //没有收到数据退出{return -1;}switch (c){/* SOH表示数据区有128字节 */case SOH:packet_size PACKET_SIZE;break;/* STX表示数据区有1k字节 */case STX:packet_size PACKET_1K_SIZE;break;/* 传输结束 end of transmission */case EOT:return 0;/* 连续的两个CA信号终止传输 */case CA:/* 收到两个连续的CA信号 */if ((Receive_Byte(c, timeout) 0) (c CA)){*length -1;return 0;}else/* 只收到一个CA信号 */{return -1;}/* 用户终止传输 */case ABORT1:case ABORT2:return 1;default:return -1;}*data c;for (i 1; i (packet_size PACKET_OVERHEAD); i){//data i 等价 data[i]if (Receive_Byte(data i, timeout) ! 0)//没有收到数据退出{return -1;}}/* 第PACKET_SEQNO_COMP_INDEX数字2字节是PACKET_SEQNO_INDEX数字1字节的反码 */if (data[PACKET_SEQNO_INDEX] ! ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) 0xff))//正反帧序号校验不通过{return -1;}/* 计算CRC */crc data[packet_size PACKET_HEADER] 8;crc data[packet_size PACKET_HEADER 1];if (Cal_CRC16(data[PACKET_HEADER], packet_size) ! crc)//数据包校验不通过{return -1;}/* 取数据区长度 */*length packet_size;return 0;
}//FLASH擦写函数API参考
/*
** 根据提供的文件大小计算所需要的FLASH页数。
*/
unsigned int mcu_flash_page_alloc(volatile unsigned int img_size)
{unsigned int pg_num 0x0;unsigned int img_sz img_size;if ((img_sz % MCU_FLASH_PAGE_SIZE) ! 0){pg_num (img_sz / MCU_FLASH_PAGE_SIZE) 1;}else{pg_num img_sz / MCU_FLASH_PAGE_SIZE;}return pg_num;
}/*
**页所在的FLASH地址计算当前页序号
*/
unsigned int mcu_flash_page_num_calc(const unsigned int flash_addr)
{/* calculate the number of page to be programmed/erased */return (flash_addr - MCU_FLASH_START_ADDR) / MCU_FLASH_PAGE_SIZE;
}/*
**(按序号)连擦除FLASH函数
*/
void mcu_flash_page_erase_cc_num(const unsigned int erase_pgn,unsigned int erase_pg_nbr)
{unsigned int erase_pgaddr_ofsmcu_flash_page_addr_calc(erase_pgn);/*计算当前页所在FLASH地址*//* Unlocks the FLASH Program Erase Controller */FLASH_Unlock();/* clear all pending flags */FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_WRPRTERR | FLASH_FLAG_PGERR);/* erase the flash pages */for(unsigned int i0;ierase_pg_nbr;i){while(FLASH_COMPLETE ! FLASH_ErasePage(erase_pgaddr_ofs));//擦除当前FLASH地址所在页FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_WRPRTERR | FLASH_FLAG_PGERR);erase_pgaddr_ofsMCU_FLASH_PAGE_SIZE;}/* Locks the FLASH Program Erase Controller */FLASH_Lock();
}
3.Ymodem升级工具
1.网上大多推荐的是SecureCRT终端工具我用的是Tera Term这款终端工具里面有支持Xmodem,Ymodem,Zmodem等多协议文件发送功能
2.Ymodem升级文件使用: 打开终端
2.1 首先设置串口:点击设置- “串口”选择相应串口号以及波特率。 2.2 加载并升级文件:点击文件- “传输” - “YMODEM” - “发送” 选择app升级bin文件即可。 注意:貌似调试发现其发送的包大小是1KB的。 3.后来在gitee上找到了作者biglu制作的专门用于Ymodem升级的上位机Ymodem_tool其支持发送包大小是128B/1024B的界面简洁好用。 4.两款工具升级界面