实现"通过串口升级嵌入式目标板软件"功能的一些心得体会

本文介绍了在ARM7 AT91FR40162平台上通过串口实现软件升级的过程,包括串口升级的原理、不同实现方案的分析和对比,以及在实践中总结的经验和心得。文中提到了三种方案,分别是直接在运行代码中嵌入升级模块、减少升级代码缓冲区和使用专门的启动代码作为Bootloader,强调了内存使用、风险管理和代码设计的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

嵌入式平台 ARM7 AT91FR40162 串口升级
  最近由于项目需要,在AT91FR40162平台上实现了通过串口传输目标板二进制可执行文件并更新固化到储存执行代码的片内ROM中。在这之前,我进入公司以来,这个平台上,通过仿真器把程序下载到目标板是更新目标板固化程序的唯一途径。随着对嵌入式系统的逐步了解,我认识到存在不通过仿真器升级程序的方法:我们日常使用的嵌入式设备,如有线电视机顶盒,可以通过电视线升级软件,如路由器,可以通过网线升级软件,诸如此类,理论上有数据传输的通路就可以实现目标板软件升级。上网翻了下AT91FR40162的资料,不知道现在还有多少人用这块arm7...还好找到几年前的一些文档资料,Q&A,还有ATMEL免费提供的上位机软件ATMEL Host loader,但传说中配合使用的AT91 Flash Uploader却没找到。结合某已离职大牛遗留下没人再用的代码,还有其他平台的经验,终于在这个平台实现了串口升级。当中的一些学习心得和经验体会写下来作个记录并分享。
  首先简要总结串口升级的原理。在我们的目标板里,这块ARM上电复位并执行memory remap之后,0地址映射到片内RAM,0x400000映射到FLASH ROM。由于片内RAM为256k,FLASH ROM为2M,目标板的执行代码储存在FLASH ROM,运行时大部分在FLASH ROM执行,只有少部分对速度要求高的映射到片内RAM。实现软件串口升级的基本思路,就是编写实现接收串口数据并把数据转存至FLASH ROM功能的代码,将其用仿真器下载至目标板,并保持以后每次软件串口升级之后FLASH ROM都保持有这样功能的代码。
  接着就具体不同的需求来分析不同的实现方案。我之前在另一个DSP平台实现过串口升级,方法是目标板的日常运行代码里有串口升级的模块,当收到串口升级命令之后,按照预定的流程把二进制可执行文件数据从串口接收下来,储存在RAM里,在确认无误后,再刷写固化入FLASH ROM中。这个方案的要点是,负责串口升级的模块代码,特别是负责把数据刷写入FLASH ROM以及之后操作的相关执行代码和访问到的常量,必须加载到RAM里,以免升级FLASH ROM后代码位置改变而不能执行;此外RAM要提供一个大的缓冲区,用作储存二进制可执行文件数据。我分析过这次ARM7平台的代码量在100k多一点,也就是二进制可执行文件大小100多k,需要加载到RAM的代码和变量数据之和小于100k,这样256k的RAM刚好能满足要求。就这样,参照离职大牛的代码,了解这块ARM的FLASH操作之后,移植DSP平台的串口升级模块代码,便实现了软件串口升级的功能。我认为这方案特点是:一,对内存的使用量比较多,比较适合RAM相对较充足的情况;二,运行的时候随时升级,但存在风险:一旦在写FLASH的过程中掉电,或者刷入了不含串口升级功能的代码,之后就只能接仿真器刷了;三,编码简单,移植方便。然而,虽然目前我这个项目的代码量还不多,但如果以后要增加功能,势必会增加代码大小和内存使用量,那时候使用这方案就不合适了,因而我必须思考其他实现方案。
  既然内存不充裕,那我就减少升级代码缓冲区的大小,例如设置一个固定的FLASH ROM页面大小,刚好是FLASH进行清除操作的最小单元;这样填满了这个缓冲区,就进行写FLASH操作,写好继续接收。现在我常用的串口波特率最高是115200bps,就是说每秒最多传14400字节,假如刷写一个页面的FLASH ROM需要x个毫秒,从串口驱动的接收缓冲区拷数据到升级代码缓冲区需要y毫秒,那么串口驱动的接收缓冲区必须大于14.4(x+y)个字节,才能避免丢失串口数。我没有查资料和做准确测试,这x+y估计在100以下。所以,相对于方案一,这个方案节省了大量内存,从原来的100多k减少到一个页面4k,而且还不需要限制代码量的大小,只要FLASH ROM装得下;不过,风险大大增加:方案一在串口传输的过程中如果掉电,只是升级失败,旧程序还在,而这方案如果在那时候掉电,新程序还没完全刷好,那又得接仿真器重刷了。一般FLASH ROM的重刷只需不到一秒,但100k的数据传输需要好几秒,为了避免这一风险的增加,我想到在FLASH ROM上做备份。例如现在FLASH ROM有2M字节这么多,假如我现在的代码翻十几倍,还可以多放1个相同的镜像。因此,我在接收时不刷写旧代码所在的页面,找空闲的页面先把新的代码刷进去,在接收完成并确认无误后,再把新的代码从备份页面拷贝到旧代码的页面。这样掉电的风险将和方案一相差不大。
  参照方案二的改进版,要完全消除软件升级时掉电的风险,可以在FLASH ROM中腾出专门一个页面,放置串口升级相关代码;升级时只清空更新其他页面,而这个页面的代码永不擦除,驻留在FLASH ROM。而且,这部分代码每次启动后都要能执行。这也就是ATMEL的Flash Uploader的实现方法。具体的思路如下:当ARM启动之后,跳入串口升级相关代码,等待升级指令,如果接收到指令,便进入串口升级流程,否则等待一小段时间之后,跳入目标板执行的日常功能代码。例如,我在0x400000开始的FLASH ROM写入启动和串口升级代码,在0x500000开始的FLASH ROM写入日常功能代码,ARM的入口代码位于0x400000;当我需要串口升级时,启动目标板并不断通过串口发送预先定义好协议的同步指令

### 嵌入式软件工程师实习周记模 以下是针对嵌入式软件工程师实习生设计的一份通用周记模,旨在帮助记录每周的工作进展、学习成果以及遇到的问题。 --- #### **第X周 实习周记** **日期范围**: YYYY-MM-DD 至 YYYY-MM-DD **本周目标**: 简要描述本周的主要工作任务和预期达成的目标。例如,“熟悉项目的整体架构并完成模块A的功能测试。” **已完成工作**: 1. 描述具体完成了哪些任务,包括但不限于代码编写、调试、文档阅读等。 - 示例:完成了UART通信协议的实现,并通过串口工具验证了数据传输功能[^1]。 2. 如果涉及多个子任务,请逐条列出并说明每项工作的具体内容及其重要性。 **学习心得**: 分享在工作中学到的新知识或技能,可以是技术层面的内容(如某种算法的应用),也可以是非技术方面的经验(如团队协作技巧)。 - 示例:深入理解了ARM Cortex-M系列微控制器中断机制的设计原理,进一步掌握了如何优化实时系统的响应速度[^2]。 **存在问题及解决方案**: 列举本周遇到的技术难题或其他障碍,并阐述解决方法或者计划采取何种措施克服这些困难。如果没有完全解决问题,则需注明当前进展情况及下一步打算。 - 示例:遇到了内存分配不连续导致的数据丢失问题;经过查阅资料发现可以通过调整结构体成员顺序来改善缓存命中率从而缓解该现象[^3]。 **下周计划**: 制定下一周的具体安排,明确即将开展的任务清单和个人成长方向。 - 示例:继续完善现有驱动程序接口函数,同时尝试移植Linux内核到目标平台之上以便支持更复杂应用需求。 --- ### 示例填充版 #### **第三周 实习周记** **日期范围**: 2023-09-04 至 2023-09-10 **本周目标**: 全面了解项目背景和技术栈,初步参与部分基础编码环节。 **已完成工作**: 1. 学习了STM32CubeMX工具链的基础操作流程,能够独立配置GPIO引脚模式及时钟树参数设置。 2. 编写了简单的LED闪烁控制逻辑作为入门练习案例,在实际硬件上运行成功验证效果正常。 3. 对接收到的一个小型实验课题进行了前期调研分析,明确了后续开发路径图谱框架思路。 **学习心得**: 通过对MCU内部寄存器映射关系的学习认识到低层编程对于提升效率有着不可替代的作用;另外还了解到版本控制系统Git的基本命令集有助于提高多人合作环境下的文件管理能力。 **存在问题及解决方案**: 初次接触裸机环境下无操作系统支撑情况下面临诸多挑战比如缺少标准库支持使得某些常用API无法直接调用必须自行封装适配层才能满足特定场景需求——目前正参考官方手册逐步摸索构建属于自己的辅助类库集合以降低重复劳动强度加快迭代周期进度。 **下周计划**: 着手搭建基于FreeRTOS的操作系统框架模型探索多线程并发执行条件下资源竞争规避策略研究最佳实践方案。 --- ```c // 示例代码片段:简单 LED 控制 void led_init(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 启用 GPIOC 的时钟 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13; // 设置 PC13 引脚 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 输出速度为 50 MHz GPIO_Init(GPIOC, &GPIO_InitStruct); } void toggle_led(void){ static uint8_t state = 0; if(state == 0){ GPIO_SetBits(GPIOC, GPIO_Pin_13); // 打开 LED state = 1; } else{ GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 关闭 LED state = 0; } } ``` ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值