学校门户网站建设说明网站建设代码优化
学校门户网站建设说明,网站建设代码优化,公司网页免费制作,网站开发 科技STM32 IAP应用开发--bootloader升级程序 Chapter1 STM32 IAP应用开发——通过串口/RS485实现固件升级#xff08;方式2#xff09;前言什么是IAP#xff1f;什么是BootLoader#xff1f; 方案介绍#xff1a;1#xff09;bootloader部分#xff1a;2#xff09;APP部分… STM32 IAP应用开发--bootloader升级程序 Chapter1 STM32 IAP应用开发——通过串口/RS485实现固件升级方式2前言什么是IAP什么是BootLoader 方案介绍1bootloader部分2APP部分 3 程序编写3.1 BootLoader部分3.2 APP的制作 4 修改工程中的内存配置4.1 Bootloader工程内存配置4.2 APP工程内存配置 5 烧录相关配置5.1 BootLoader部分5.2 APP部分 6 运行测试结束语 Chapter2 STM32F1 IAP在线升级功能实现使用串口及心得APP程序 Chapter3 STM32学习笔记之简易Bootloader串口升级设计概念简介复位执行流程实现要点Bootloader程序实现App程序实现 Chapter4 STM32 IAP应用开发——自制BootLoader1 环境搭建2 BootLoader工作原理以及常见分区介绍3 BootLoader的制作4 烧录下载配置5 运行测试结束语 Chapter1 STM32 IAP应用开发——通过串口/RS485实现固件升级方式2
原文链接https://blog.csdn.net/ShenZhen_zixian/article/details/129424077
前言
什么是IAP
IAPIn-Application Programming 指MCU可以在系统中获取新代码并对自己重新编程即可用程序来改变程序。在应用编程IAP是用户的应用代码对片内Flash存储器进行擦除/编程的方法。这种方式的典型应用就是用一小段代码来实现程序的下载实际上单片机的ISP功能就是通过IAP技术来实现的即片子在出厂前就已经有一段小的boot程序在里面片子上电后开始运行这段程序当检测到上位机有下载要求时便和上位机通信然后下载数据到数据存储区从而实现固件升级。
什么是BootLoader
百度百科在嵌入式操作系统中BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图从而将系统的软硬件环境带到一个合适状态以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中通常并没有像BIOS那样的固件程序注有的嵌入式CPU也会内嵌一段短小的启动程序因此整个系统的加载启动任务就完全由BootLoader来完成。
实际上BootLoader不仅仅在操作系统上使用在一些内存小功能应用较为简单的单片机设备上面也可以通过BootLoader来完成固件升级。
我之前也有发过一些关于STM32远程升级的文章实现的方式有很多种感兴趣的同学可以去看一下。 STM32固件升级系列合集https://blog.csdn.net/ShenZhen_zixian/article/details/129074047
那么这一期我来介绍一下如何自己制作一个BootLoader程序并且通过串口或者RS485实现固件升级。
1 环境搭建 关于STM32以及Keil的环境这里就不具体介绍了网上教程也很多不懂的同学自行查阅资料。
2 功能描述 在做bootloader之前一定要先想好升级的途径和方式这样才好规划分区以及制作bootloader。 关于bootloader详细的讲解可以看下我之前发的博客 STM32 IAP应用开发——自制BootLoader
分区介绍 我用的是STM32F407内存是512K的想用内存更小的MCU也是可以的改下各个分区的内存分配就行了。 注F4系列的MCU不像F1那样内存扇区都很大(最少也是16K)而且同一块扇区只能一起擦除所以就没办法分的那么细了。详细的内存分布可以参考下面的两个图。 STM32F4x扇区分布图如下 STM32F1x扇区分布图如下
那么我这里呢就用一个512k的内存分成3个区域来实现一个升级的功能。 分区表如下
方案介绍
1bootloader部分
开始运行后先等待5s在这个时间内如果收到串口2或者RS485的升级命令就进入升级模式如果超时则跳转到用户程序(APP)。 在升级模式可以通过串口2或者RS485传输要升级的固件传输的数据协议我这里图方便就直接用Ymodem了不知道Ymodem协议的可以先自行查阅一下资料。
2APP部分
APP部分修改一下中断向量表地址即可其他的随便你做什么应用。 另外我在分区的时候留了一块settimg区在实际的应该中如果有需要记录一些掉电后还能保存的数据那么这块区域就可以用得上了。
3 程序编写
3.1 BootLoader部分
不管用的是什么MCU要实现固件升级都离不开BootLoaderBootLoader是一个统称它其实只是一段引导程序在MCU启动的时候会先运行这段代码判断是否需要升级如果不需要升级就跳转到APP分区运行用户代码如果需要升级则先通过一些硬件接口接收和搬运要升级的新固件然后再跳转到APP分区运行新固件从而实现固件升级。 BootLoader的制作需要根据实际的需求来做不同的运行方式或者升级方式在做法上都是有区别的包括BootLoader所需要的内存空间也不尽相同。 不过不管是用什么方式Bootloader都应该尽可能做的更小更简洁这样的话内存的开销就更小对于内存较小的MCU来说压力就没那么大了。
注我这里是基于正点原子的工程模板改的增加了自己的功能。
示例代码如下 Bootloader分区定义
#define FLASH_SECTOR_SIZE 1024
#define FLASH_SECTOR_NUM 512 // 512K
#define FLASH_START_ADDR ((uint32_t)0x8000000)
#define FLASH_END_ADDR ((uint32_t)(0x8000000 FLASH_SECTOR_NUM * FLASH_SECTOR_SIZE))//flash sector addr
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) //sector0 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) //sector1 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) //sector2 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) //sector3 addr, 16 Kbytes
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) //sector4 addr, 64 Kbytes
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) //sector5 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) //sector6 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) //sector7 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) //sector8 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) //sector9 addr, 128 Kbytes
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) //sector10 addr,128 Kbytes
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) //sector11 addr,128 Kbytes #define BOOT_SECTOR_ADDR 0x08000000 // BOOT sector start addres
#define BOOT_SECTOR_SIZE 0x4000 // BOOT sector size
#define SETTING_SECTOR_ADDR 0x08004000 // SETTING sector start addres
#define SETTING_SECTOR_SIZE 0x4000 // SETTING sector size
#define APP_SECTOR_ADDR 0x08008000 // APP sector start address
#define APP_SECTOR_SIZE 0x78000 // APP sector size #define BOOT_ERASE_SECTORS_NUM 1 // 16k
#define SETTING_ERASE_SECTORS_NUM 1 // 16k
#define APP_ERASE_SECTORS_NUM 6 // 16k 16k 64k 128k 128k 128kmain函数
#include bootloader.h
#include usart.h
#include rs485.h
#include delay.h
#include ymodem.h#define WAIT_TIMEOUT 5void print_boot_message(void)
{uart_log(---------- Enter BootLoader ----------\r\n);uart_log(\r\n);uart_log( flash pration table \r\n);uart_log(| name | offset | size |\r\n);uart_log(--------------------------------------\r\n);uart_log(| boot | 0x%08X | 0x%08X |\r\n, BOOT_SECTOR_ADDR, BOOT_SECTOR_SIZE);uart_log(| setting | 0x%08X | 0x%08X |\r\n, SETTING_SECTOR_ADDR, SETTING_SECTOR_SIZE);uart_log(| app | 0x%08X | 0x%08X |\r\n, APP_SECTOR_ADDR, APP_SECTOR_SIZE);uart_log(\r\n);
}void print_wait_message(void)
{uart_log(------- Please enter parameter -------\r\n);uart_log([1].Start program\r\n);uart_log([2].Update program\r\n);uart_log(--------------------------------------\r\n);
}int main()
{process_status process;uint16_t timerout 0;delay_init(168);uart_init(115200);ymodem_init();print_boot_message();print_wait_message();while (1) {process get_ymodem_status();switch (process) {case WAIT_START_PROGRAM:uart_log(wait start app...(%ds)\r\n, WAIT_TIMEOUT - timerout);delay_ms(1000);timerout ;if(timerout WAIT_TIMEOUT){set_ymodem_status(START_PROGRAM);}break;case START_PROGRAM:uart_log(start app...\r\n);delay_ms(50);if (!jump_app(APP_SECTOR_ADDR)) {uart_log(start app failed: app no program\r\n);delay_ms(1000);}break;case UPDATE_PROGRAM:ymodem_c();uart_log(update app program...\r\n);delay_ms(1000);break;case UPDATE_SUCCESS:uart_log(update success\r\n);uart_log(system reboot...\r\n);delay_ms(1000);system_reboot();break;default:break;}}
}Ymodem协议处理:
#define YMODEM_SOH 0x01
#define YMODEM_STX 0x02
#define YMODEM_EOT 0x04
#define YMODEM_ACK 0x06
#define YMODEM_NAK 0x15
#define YMODEM_CA 0x18
#define YMODEM_C 0x43#define MAX_QUEUE_SIZE 1200typedef void (*ymodem_callback)(process_status);typedef struct
{process_status process;uint8_t status;uint8_t id;uint32_t addr;uint8_t sectors_size;ymodem_callback cb;
} ymodem_t;//顺序循环队列的结构体定义如下
typedef struct
{uint8_t queue[MAX_QUEUE_SIZE];int rear; //队尾指针int front; //队头指针int count; //计数器
} seq_queue_t; typedef struct
{uint8_t data[1200];uint16_t len;
} download_buf_t;void ymodem_ack(void)
{uint8_t buf[3];buf[0] YMODEM_ACK;buf[1] 0x0D;buf[2] 0x0A;RS485_Send_Data(buf, 3);
}void ymodem_nack(void)
{uint8_t buf[3];buf[0] YMODEM_NAK;buf[1] 0x0D;buf[2] 0x0A;RS485_Send_Data(buf, 3);
}void ymodem_c(void)
{uint8_t buf[3];buf[0] YMODEM_C;buf[1] 0x0D;buf[2] 0x0A;RS485_Send_Data(buf, 3);
}void set_ymodem_status(process_status process)
{ymodem.process process;
}process_status get_ymodem_status(void)
{process_status process ymodem.process;return process;
}void ymodem_start(ymodem_callback cb)
{if (ymodem.status 0) {ymodem.cb cb;}
}void ymodem_recv(download_buf_t *p)
{uint8_t type p-data[0];switch (ymodem.status) {case 0:if (type YMODEM_SOH) {ymodem.process BUSY;ymodem.addr APP_SECTOR_ADDR;uart_log(erase flash: 0x%08X\r\n, APP_SECTOR_ADDR);mcu_flash_erase(ymodem.addr, APP_ERASE_SECTORS_NUM);uart_log(erase flash success\r\n);ymodem_ack();ymodem_c();ymodem.status;}else if (type 1) {uart_log(start program now\r\n);ymodem.process START_PROGRAM;}else if (type 2) {uart_log(enter update mode\r\n);ymodem.process UPDATE_PROGRAM;}break;case 1:if (type YMODEM_SOH || type YMODEM_STX) {if (type YMODEM_SOH) {mcu_flash_write(ymodem.addr, p-data[3], 128);ymodem.addr 128;}else {mcu_flash_write(ymodem.addr, p-data[3], 1024);ymodem.addr 1024;}ymodem_ack();}else if (type YMODEM_EOT) {ymodem_nack();ymodem.status;}else {ymodem.status 0;}break;case 2:if (type YMODEM_EOT) {ymodem_ack();ymodem_c();ymodem.status;}break;case 3:if (type YMODEM_SOH) {ymodem_ack();ymodem.status 0;ymodem.process UPDATE_SUCCESS;}}p-len 0;
}void ymodem_init(void)
{RS485_Init(115200);timer_init();queue_initiate(rx_queue);
}
关于bootloader详细的讲解可以看下我之前发的博客 STM32 IAP应用开发——自制BootLoader 完整代码下载地址https://download.csdn.net/download/ShenZhen_zixian/87553496
3.2 APP的制作
APP部分根据自己实际的功能来做只要记得修改中断向量表地址即可。地址的值等于你APP区的起始地址。
示例代码如下 main函数
#include main.h
#include usart.h
#include delay.h#define APP_VERSION V100
#define NVIC_VTOR_MASK 0x3FFFFF80
#define APP_PART_ADDR 0x08008000void ota_app_vtor_reconfig(void)
{/* Set the Vector Table base location by user application firmware definition */SCB-VTOR APP_PART_ADDR NVIC_VTOR_MASK;
}void led_init(void)
{ GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);GPIO_InitStructure.GPIO_Pin GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_UP;GPIO_Init(GPIOF, GPIO_InitStructure);GPIO_SetBits(GPIOF, GPIO_Pin_9);
}void print_boot_message(void)
{uart_log(\r\n);uart_log(-------------- Enter APP -------------\r\n);uart_log (app version is: %s\r\n, APP_VERSION);uart_log(\r\n);
}int main(void)
{ota_app_vtor_reconfig();delay_init(168);uart_init(115200);print_boot_message();led_init();uart_log (app init success\r\n);while (1){GPIO_SetBits(GPIOF, GPIO_Pin_9);delay_ms(1000);GPIO_ResetBits(GPIOF, GPIO_Pin_9);delay_ms(1000);}
}
完整代码下载地址https://download.csdn.net/download/ShenZhen_zixian/87553496
4 修改工程中的内存配置
因为我们对stm32的内存进行了分区不同的代码要存放在不同的区域因此我们在编译工程之前需要先定义好各自的区域以免出现内存越界。
4.1 Bootloader工程内存配置
Bootloader的起始地址不需要改按flash默认地址即可size需要改成实际分区大小。
4.2 APP工程内存配置
APP的起始地址和size都需要根据实际的分区来改。
5 烧录相关配置
我们的Bootloader做好以后需要烧录到MCU里面可以直接用Keil uVison来下载也可以用J-Flash或者其他这个都没关系但是要注意内存的分配要把固件烧到对应的内存地址上。
5.1 BootLoader部分
1使用Keil uVision下载 如果是用keil下载的话需要注意flash的配置具体如下 2使用其他下载工具 如果是用J-Flash或者STlink的工具烧录的话注意烧录的起始地址是0x08000000就好了。
5.2 APP部分
1使用Keil uVision下载 跟BootLoader一样我们按照前面分配好的空间配置APP的参数即可。 2使用其他下载工具 如果是用J-Flash或者STlink的工具烧录的话注意烧录的起始地址是0x08008000就好了。
6 运行测试
用串口助手查看运行log我这里用的是XShell用其他的也是可以的。
1开始运行代码 等待5s如果不需要升级就跳转到App区如下图 2发送命令1 在等待的5s内通过串口2或者RS485发送一个’1’直接跳转到APP。 注我这里为了方便调试才用的这种方式实际上可以根据自己的需求来做。 3发送命令2进入升级模式 在等待的5s内通过串口2或者RS485发送一个’2’进入升级模式。 注我这里为了方便调试才用的这种方式实际上可以根据自己的需求来做。比如用按键进入或者用其他串口USB之类的也可以在APP部分做这个功能。 串口调试窗口log如下图 4通过Ymodem传输新固件 调试工具我用的是XShell实际上用其他工具也行只要支持Ymodem方式传输文件即可。 5升级固件 固件升级完成后自动重启重新运行Bootloader和APP。 至此整个升级流程就走完了。
结束语
好了关于自制BootLoader并实现串口以及RS485升级固件的介绍就讲到这里本文列举的例子其实只是升级的其中一种方式只是提供一个思路不是唯一的方法实际上最好还是根据自己实际的需求来做。我之前也发给几篇升级相关的文章用的都是不同的方式各有各的优点和缺点感兴趣的同学可以去看一下。
Chapter2 STM32F1 IAP在线升级功能实现使用串口及心得
原文链接https://blog.csdn.net/qq_45625638/article/details/127121199
公司产品要求需要做一个能远程升级程序的功能找了很多例程大多都是需要按键来完成操作的而我需要的是通过串口发送指令来完成于是东拼西凑最后还是用了四天的时间勉强做出来
整个功能需要的程序是两个部分。一个是IAP程序一个是APP程序。对于IAP程序和APP原理方面的内容就不再过多赘述。直接从操作开始吧。
IAP程序 写IAP程序之前首先得配置程序的起始地址和大小。这里根据个人情况而定我这里单片机flash大小是512k所以IAP程序选择分配的大小是64k。 点击魔术棒然后在选择target设置起始地址(start)和大小(size)IAP程序起始地址都是从0x8000000开始的大小就是0x10000也就是64k啦。 在这里我参考了原子哥的源码和一位大神的分享原子的资料一搜一大堆这里就仅贴出大佬的链接 stm32 IAP 程序编写心得 有了前车之鉴做起来也稍显得心应手这里的flash操作的函数和IAP功能函数都是拿来主义了原子IAP例程里拿来就可以用。主要工作还是针对main函数这里直接贴出源码
#include delay.h
#include sys.h
#include usart.h
#include stmflash.h
#include iap.h#define ADDR_CodeWriteFlag 0X08070000 //设置跳转标志位保存地址
#define ADDR_JumpToIAPFlag 0X08070001
int main(void)
{ u16 IAPFlagBuf[2];u16 RxDataCount0; //串口接收到的数据计数u16 RxDataLength0; //串口接收到的数据长度u16 AppCodeLength 0; //接收到的app代码长度u8 RxCmdFlag 0; u8 AppRunFlag 0; //应用程序运行标志u16 JumpToAPPFlag; //跳转至APP程序标志位u16 JumpToIAPFlag; //跳转回IAP标志位NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级2位响应优先级JumpToAPPFlag STMFLASH_ReadHalfWord(ADDR_CodeWriteFlag); //读取APP写入标志位判断是否已有程序JumpToIAPFlag STMFLASH_ReadHalfWord(ADDR_JumpToIAPFlag);uart_init(9600); //串口初始化为9600delay_init(); //延时初始化 IAPFlagBuf[0] 0x11;IAPFlagBuf[1] 0x00;printf(提示输入send发送bin文件!\r\n);while(1){if(JumpToAPPFlag ! 0x11) //判断是否已有APP程序如果已有跳转至APP程序运行{if(JumpToIAPFlag 0x11) //判断是否从APP程序跳转回来,如果是擦除已有APP程序{ JumpToIAPFlag 0x00; }if(USART_RX_CNT) //如果有数据进来{if(RxDataCount USART_RX_CNT) //串口没有再收到新数据{RxDataLength USART_RX_CNT;if(RxCmdFlag 0 RxDataLength 4) //接收到IAP指令{if(USART_RX_BUF[0] s USART_RX_BUF[1] e USART_RX_BUF[2] n USART_RX_BUF[3] d)//判断是否为IAP指令{RxCmdFlag 1; //接收到更新APP代码指令标志位置1RxDataLength 0; //清空指令长度防止影响后面计算APP代码大小printf(准备接收app程序请添加bin文件!\r\n); //准备好接收bin文件等待用户添加}else{CodeUpdateFlag 0;AppCodeLength 0;printf(指令错误!\r\n); //未接收到IAP更新指令其他任何串口发送数据都认为指令错误}}else if(RxCmdFlag 1 RxDataLength 10)//接收APP程序{CodeUpdateFlag 1; //代码更新标志位置位用于应用程序代码接收完成后写FLASHRxCmdFlag 0;AppCodeLength USART_RX_CNT;printf(APP程序接收完成!\r\n);printf(程序大小:%dBytes\r\n,AppCodeLength);}else{RxDataLength 0;printf(文件或指令错误!\r\n); //如果代码大小不足10Bytes认为没有正确添加bin文件}RxDataCount 0;USART_RX_CNT 0;}else {RxDataCount USART_RX_CNT;}}delay_ms(10); //给以串口中断的时间判断是否接收完成if(CodeUpdateFlag) //代码更新标志位置位{CodeUpdateFlag 0;if(AppCodeLength){printf(程序更新中...\r\n);if(((*(vu32*)(0X200010004))0xFF000000)0x08000000) //判断代码合法性{ printf(正在下载程序!\r\n);iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,AppCodeLength); //新代码写入FLASH AppRunFlag 1;}else {printf(程序更新失败请检查bin文件是否正确!\r\n);printf(跳转到原有应用程序!\r\n);iap_load_app(FLASH_APP1_ADDR); //执行FLASH APP代码}}else {printf(没有程序可以更新!\r\n);} }if(AppRunFlag) //App运行标志置位{printf(开始运行程序!\r\n);delay_ms(10); if(((*(vu32*)(FLASH_APP1_ADDR 4)) 0xFF000000) 0x08000000) //判断代码合法性{ AppRunFlag 0;STMFLASH_Write(ADDR_CodeWriteFlag,IAPFlagBuf,2); //写入APP代码标志//RCC_DeInit(); //关闭外设//__disable_irq();iap_load_app(FLASH_APP1_ADDR); //执行FLASH APP代码}else {printf(应用程序错误!\r\n); } }}else{printf(已有一个应用程序!\r\n);printf(开始运行程序!\r\n);delay_ms(10);iap_load_app(FLASH_APP1_ADDR); //执行FLASH APP代码}}
} main函数中选择一块不使用的flash区域来保存跳转标志位。
我选择的是0x8070000开始的区域整个flash大小是512k也就是0x8080000所以最后我又留了64k的大小来存放所以留给APP程序的就是0x08010000到0x08070000的地址也是就384k大小空间记住这个0x08010000到0x08070000后面APP程序要考。
每次进入main函数都要先读这个地址的值若等于0x11则直接跳转到APP因为在跳转APP之前是要对这个地址进行写0x11的所以跳转到APP后这个地址是0x11开机启动会自动进入APP。 到此似乎思路就清晰了原子通过按键来操作接收下载。我这里通过指令来接收下载其他的判断栈顶地址的合法性看两遍代码也能明白了。
APP程序
接下来就是APP程序的操作部分了。 要从IAP跳转到APPAPP的程序也需要修改。首先就是程序的起始地址和大小。 前面的IAP从0x8000000到0x8010000所以我们的APP程序只能从0x8010000开始而0x8070000到0x8080000又要保存跳转标志位那么APP的地址就只能是0x8010000到0x8070000了既然这样那就拉满吧所以大小设置我选择0x60000。 然后就是输出bin文件了要用串口传输不能用hex文件所以在USER界面 选择如图所示操作方框里填的是E:\keil5\ARM\ARMCC\bin\fromelf.exe --bin -o …\output\Project.bin …\output\Project.axf 注意有空格当然这个也得根据自己的文件位置更改。 下一步就是更改偏移地址了这里只需要在主函数里加 SCB-VTORFLASH_BASE|0x10000; 即可我这里偏移0x10000 到此就能编译生成bin文件通过IAP程序使用串口来完成升级了。 但是后续要升级怎么办我们还得从APP跳转到IAP来。所以在APP程序中还需要接收指令来跳转到IAP去。 这里Receive_Data_Point3是接收到的字节长度我设置的跳转指令是APPTOIAP共9位。 然后就是跳转前还需将存放标志位得地址写入0x00前面IAP中会对该地址得值进行判断是否是0x11是的话就又跳回来了。然后有大佬踩坑后得知跳转到IAP直接用NVIC_SystemReset(); 即可。 到此就基本完成了。 第一次写博客也是为了记录和学习。语言不通顺还请多谅解如有错误的地方也请多加指正。同时有疑问的同学也可以评论留言欢迎讨论交流。
可能出现的问题解决方法 在后续的测试中发现串口接收bin文件时还没接收完就进入到了写入flash的动作导致有很大的概率程序升级失败。分析了半天原因可能是接收缓存在10ms内没收到数据就默认接收完毕进入写入跳转了我使用的9600波特率在将下图中的延时增加到100ms后没有再出现问题了。
Chapter3 STM32学习笔记之简易Bootloader串口升级设计
原文链接https://blog.csdn.net/xinghuanmeiying/article/details/79573065
概念简介
在学习制作串口升级 Bootloader 之前,我们先了解一下STM32的 IAP (In Application Programming)即在应用编程,IAP是用户自己的程序在运行过程中对 User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级. 设计这样的功能需要有两个项目代码:
Bootloader 程序: 该部分代码用于实现通过某种通信方式(如 USB,USART)接收程序或数据,执行对第二部分代码的更新,通常存储于 Flash 的起始地址(0x08000000); App 程序: 该部分代码是产品实现业务逻辑正常运行的代码,该部分代码需要在存储的 Flash 中进行相对的地址偏移.
复位执行流程
当芯片上电或者复位后,首先是Bootloader代码开始运行,它作如下操作:
(1) 检查是否需要对第二部分代码进行更新; (2) 如果不需要更新则转到(4); (3) 执行更新操作; (4) 跳转到第二部分代码执行.
加了 Bootloader 程序后,程序运行流程图:
实现要点
Bootloader程序实现
(1) 完成 USART 串口数据接收传输功能,并将接收到的数据绝对定位到 SRAM 的设定地址中,地址的设定根据实际情况而定,应保证设定的地址大于 Bootloader 执行需要的 RAM 的空间; (2) 需要实现对 STM32 Flash 读写操作相关的驱动,并将绝对定位 SRAM 处的固件数据写入到 Flash 中; (3) 完成固件数据接收和写入 Flash 后,需要对 PC 指针进行程序跳转,跳转完成后,即运行固件中的程序.
注: 程序跳转之前需要关闭所有的中断.
App程序实现
(1) 在 main 函数最开头处设置 VTOR 寄存器,实现固件中断向量表的重定向.例如:
SCB-VTOR FLASH_BASE | 0X10000; /*中断向量表偏移量设置*/(2) 设置 App 程序的存储偏移地址,这里以 STM32F103ZE系列的单片机为例,Flash为512k,SRAM为64k.这里设置Flash前64k用于存储 Bootloader 程序,后448k为固件程序空间,SRAM地址不需要设置偏移. 可以用jflash查看生成的HEX文件,查看HEX起始地址是否为设置的偏移地址,如下图所示: 若程序跳转时,发生 HardFault 异常中断,可能是设置的地址偏移量没有生效,应该勾选 MDK 的相关配置. (3) 将编译生成的 .axf 文件 (要勾选生成HEX文件) 通过 MDK 安装时自带的 fromelf.exe 程序转成可以通过串口升级的 bin文件.
语法格式为: [MDK安装目录/fromelf.exe] –bin -o [bin文件生成目录] [axf文件目录] 点击编译即可生成可以通过串口升级的 bin 文件. 总结: App 程序在代码上除了需要设置中断向量表偏移和程序存储地址偏移以外,其余和不加 Bootloader 的程序代码并无差别,难点在于理解 Flash 偏移地址的设置.
Chapter4 STM32 IAP应用开发——自制BootLoader
原文链接https://blog.csdn.net/ShenZhen_zixian/article/details/129064681
实际上BootLoader不仅仅在操作系统上使用在一些内存小功能应用较为简单的单片机设备上面也可以通过BootLoader来完成固件升级。
我之前也有发过一些关于STM32远程升级的文章但用的是第三方BootLoader而且是基于操作系统实现的。BootLoader占用的内存也比较大而且不开源。 所以这一讲我就来介绍一下如何自己制作一个简单的BootLoader程序。
1 环境搭建
关于STM32以及Keil的环境这里就不具体介绍了网上教程也很多不懂的同学自行查阅资料。
2 BootLoader工作原理以及常见分区介绍
不管用的是什么MCU要实现固件升级都离不开BootLoaderBootLoader是一个统称它其实只是一段引导程序在MCU启动的时候会先运行这段代码判断是否需要升级如果不需要升级就跳转到APP分区运行用户代码如果需要升级则先通过一些硬件接口接收和搬运要升级的新固件然后再跳转到APP分区运行新固件从而实现固件升级。
常见分区方式介绍 1.Application 没有加入Bootloader之前我们单片机内部的flash就是一整块的所有的应用代码都放在这。 2.Bootloader Application 在原有的flash区域里面划分出两个区域Bootloader和Application这种分区方式的好处在于既可以实现升级功能App区又可以分到较大的空间缺点是没有存放新固件的区域需要从外部导入进来而且一旦传输的过程被异常打断那么原有的App代码也无法正常运行了也就是传说中的“变砖”。 3.Bootloader Application Download 这种分区方式是比较万能的一种优点是新固件是先存放到Download区的哪怕搬运的过程中出现异常中断的情况也不会“变砖”缺点是需要单独划分一块内存跟APP区差不多的区域用来存放新固件变相的减少了APP区的空间对于内存较小的单片机来说压力就比较大了。 4.Bootloader Application1 Application2 这种方式可以同时存在两套App优点在于升级了新固件以后还保留了原来的旧版固件必要的时候还可以进行版本的回退。
5.Bootloader Setting Application Download 这种方式跟第3种基本一样只是增加了一个区域用来存放升级相关的一些参数以及用户的一些配置。
3 BootLoader的制作
BootLoader的制作需要根据实际的需求来做不同的运行方式或者升级方式在做法上都是有区别的包括BootLoader所需要的内存空间也不尽相同。 不过不管是用什么方式Bootloader都应该尽可能做的更小更简洁这样的话内存的开销就更小对于内存较小的MCU来说压力就没那么大了。
我下面要做的这个bootloader是上面讲的常见分区方式里面的第5种。 分区介绍 我用的是STM32F103内存是128K的想用内存更小的MCU也是可以的改下各个分区的内存分配就行了。
分区表如下 功能描述 运行bootloader的时候先从setting里面读一些参数确定是否需要升级如果需要则把download分区的固件搬运到app分区如果不需要升级则直接跳转到app分区. 至于新固件的下载传输过程我放到App里面去处理了这跟我的项目实际需求有关系App部分这里就先不往下拓展了后面我会专门写一篇博客来介绍。
各个功能模块的具体讲解 1、分区定义 先把各个分区的内存地址以及大小定义好方便后面使用。
#define FLASH_SECTOR_SIZE 1024
#define FLASH_SECTOR_NUM 128 // 128K
#define FLASH_START_ADDR ((uint32_t)0x8000000)
#define FLASH_END_ADDR ((uint32_t)(0x8000000 FLASH_SECTOR_NUM * FLASH_SECTOR_SIZE))#define BOOT_SECTOR_ADDR 0x08000000 // BOOT sector start address
#define BOOT_SECTOR_SIZE 0x3000 // BOOT sector size
#define SETTING_SECTOR_ADDR 0x08003000 // SETTING sector start address
#define SETTING_SECTOR_SIZE 0x1000 // SETTING sector size
#define APP_SECTOR_ADDR 0x08004000 // APP sector start address
#define APP_SECTOR_SIZE 0xE000 // APP sector size
#define DOWNLOAD_SECTOR_ADDR 0x08012000 // Download sector start address
#define DOWNLOAD_SECTOR_SIZE 0xE000 // Download sector size 2、程序跳转 Bootloader作为引导程序最重要的工作之一就是通过内存跳转进入用户程序下面这段代码可以跳转到任何一个内存地址。
uint8_t jump_app(uint32_t app_addr)
{uint32_t jump_addr;jump_callback cb;if (((*(__IO uint32_t*)app_addr) 0x2FFE0000 ) 0x20000000) { jump_addr *(__IO uint32_t*) (app_addr 4); cb (jump_callback)jump_addr; __set_MSP(*(__IO uint32_t*)app_addr); cb();return 1;} return 0;
}
3、处理函数 从setting区里面读取process状态值然后进行对应的处理如果需要升级则把download区的固件搬运到app区然后再运行新APP如果不需要升级则直接跳转到APP。
process get_boot_state();
switch (process)
{case START_PROGRAM:printf(start app...\r\n);delay_ms(50);if (!jump_app(APP_SECTOR_ADDR)) {printf(no program\r\n);delay_ms(1000);}printf(start app failed\r\n);break;case UPDATE_PROGRAM:printf(update app program...\r\n);app_addr APP_SECTOR_ADDR;down_addr DOWNLOAD_SECTOR_ADDR;printf(app addr: 0x%08X \r\n, app_addr);printf(down addr: 0x%08X \r\n, down_addr);printf(erase mcu flash...\r\n);mcu_flash_erase(app_addr, APP_ERASE_SECTORS); printf(mcu flash erase success\r\n);printf(write mcu flash...\r\n);// memset(down_buf, 0, sizeof(down_buf));for (i 0; i APP_ERASE_SECTORS * 8; i){mcu_flash_read(down_addr, down_buf[0], 128);delay_ms(5);mcu_flash_write(app_addr, down_buf[0], 128);delay_ms(5);down_addr 128;app_addr 128;}printf(mcu flash write success\r\n);set_boot_state(UPDATE_SUCCESS);break;case UPDATE_SUCCESS:printf(update success\r\n);boot_state UPDATE_SUCCESS_STATE;write_setting_boot_state(boot_state);set_boot_state(START_PROGRAM);break;default:break;
}完整代码下载地址https://download.csdn.net/download/ShenZhen_zixian/87462312
4 烧录下载配置
我们的Bootloader做好以后需要烧录到MCU里面可以直接用Keil uVison来下载也可以用J-Flash或者其他这个都没关系但是要注意内存的分配要把固件烧到对应的内存地址上。 我这里做出来的bootloader bin只有8K不过为了方便后续在这部分增加新功能我实际分配了12K的空间地址区间是0x08000000-0x08003000。
如果是用keil下载的话需要注意flash的配置具体如下 如果是用J-Flash或者STlink的工具烧录的话注意烧录的起始地址是0x08000000就好了。
5 运行测试
注这里我没讲解App部分代码你们只看Bootloader部分的log就好了不影响的想看APP部分可以看我另外一篇文章或者下载完整的代码实际跑一下也行。APP部分讲解STM32 IAP应用开发——通过USB实现固件升级
运行结果 不需要升级时直接跳转到App区如下图 需要升级时先从download区搬运新固件到app区然后再跳转到App区如下图
结束语
好了关于自制BootLoader的介绍就讲到这里本文只是提供一个思路不是唯一的方法关键还是看你自己实际的需求。 还有App那部分这里没详细讲我单独写了一篇文章链接在下方合到一起看就比较清晰了。或者你也可以下载完整的源码自己去跑一下下面的源码我把BootLoader和APP都上传了。
APP部分讲解STM32 IAP应用开发——通过USB实现固件升级 完整代码下载地址https://download.csdn.net/download/ShenZhen_zixian/87462312
如果你有什么问题或者有更好的方法欢迎在评论区留言。
更多相关文章 STM32固件升级系列合集https://blog.csdn.net/ShenZhen_zixian/article/details/129074047
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/88729.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!