单片机FLASH下载算法的制作
环境
硬件使用正点原子STM32F407探索者V2开发板
编程环境使用MDK
下载工具使用JLINK
FLASH芯片使用W25Q128
什么是下载算法
单片机FLASH的下载算法是一个FLM文件,FLM通过编译链接得到,其内部包含一系列对FLASH的操作,包括初始化、擦除、写、读、校验等等操作。
单片机固件下载流程
想要制作下载算法,先要了解下载算法的工作原理。我们下载一个程序的流程大概是这样的:下载工具(比如jlink)先读取FLM文件,然后JLINK提取FLM文件的信息,将其传输到单片机的内部SRAM,下载算法在开始SRAM中运行,由于下载算法包含了一系列对FLASH的操作,那么下载工具通过下发初始化、擦除、写入、校验等指令给单片机,单片机去执行这些指令操作,实现对单片机FLASH的下载。
下载算法FLM文件的制作步骤
首先需要准备一份FLASH的驱动代码,能实现初始化、擦除,读,写等功能。
- 从MDK安装目录下拷贝一份下载算法工程,路径:MDK\ARM\PACK\ARM\CMSIS\5.4.0\Device_Template_Flash,使用的MDK版本不一样路径可能不一样。此时我们得到了一份空的下载算法工程。
- 取消工程的只读属性。
- 给工程添加分组,将Flash驱动代码和用到的库函数添加到对应的分组,和普通工程一样,根据模块添加即可。

- 添加头文件路径,把头文件的路径都包含进来。
- 给C/C++选项卡添加宏STM32F40_41xxx,USE_STDPERIPH_DRIVER。
- Device选项卡选择单片机型号。Target选项卡勾选微库。
- 在FlashDev.c文件中根据实际Flash属性修改FlashDevice结构体变量。
- 将target选项卡中的输出文件名字改成FlashDevice结构体Device Name成员中的设备名字,这一步不是必须的,只是为了输出的FLM文件名称和设备名称一致。
- 在FlashPrg.c文件中根据模板添加Flash操作的相关代码。
- 编译得到一个FLM文件。
其实上边看似繁琐,实则只有修改FlashDevice结构体变量和修改FlashPrg.c文件是我们新接触的,其它步骤在平时单片机编程中已经再熟悉不过了。接下来我们重点分析这两点。
修改FlashDevice结构体变量的值
struct FlashDevice const FlashDevice = {FLASH_DRV_VERS, // Driver Version, do not modify!"W25Q128_16M_FLM", // Device Name EXTSPI, // Device TypeFLASH_BASE_ADDR, // Device Start Address0x01000000, // Device Size in Bytes (256kB) 2M4096, // Programming Page Size 0, // Reserved, must be 00xFF, // Initial Content of Erased Memory3000, // Program Page Timeout 3000 mSec3000, // Erase Sector Timeout 3000 mSec// Specify Size and Address of Sectors0x001000, 0x000000, // Sector Size 8kB (8 Sectors)SECTOR_END
};
其实每一个成员的作用注释已经解释的很清楚了,注意这里把页编程大小改成了4096个字节,不是W25Q128指定的256个字节,这不是必须要改的,修改成4096只是为了提高下载效率。我实测把4096改成8,下载速度非常明显的变慢。
第四个实参使用了一个宏FLASH_BASE_ADDR来初始化的,我这里对FLASH_BASE_ADDR定义的是0x00000000,就以SPI FLASH为例,FLASH的存储空间是从0开始的,为什么我没有固定写0而是写了一个宏定义呢?这在我们验证下载算法的时候介绍。
器件大小、扇区大小、扇区擦除超时时间、页编程超时时间些都根据实际FLASH芯片参数填写即可。
添加FLASH接口代码到FlashPrg.c文件
在FlashPrg.c文件中有Init、UnInit、EraseChip、EraseSector、ProgramPage、Verify这些函数,每个函数的功能一目了然。函数实现如下:
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {SystemInit();//系统初始化W25QXX_Init();//初始化W25QXX芯片return (0); // Finished without Errors
}
int UnInit (unsigned long fnc) {return (0); // Finished without Errors
}
int EraseChip (void) {for(int i=0;i<4096;i++)//我用的是W25Q128{W25QXX_Erase_Sector(i);//注意这里参数是扇区的编号}return (0); // Finished without Errors
}
int EraseSector (unsigned long adr) {uint32_t sector = 0;//扇区编号adr -= FLASH_BASE_ADDR; sector = adr /4096;//扇区的大小是4096 计算出了扇区的编号W25QXX_Erase_Sector(sector);return (0); // Finished without Errors
}
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {adr -= FLASH_BASE_ADDR; W25QXX_Write_NoCheck(buf,adr,sz);return (0); // Finished without Errors
}
uint8_t read_buf[4096];
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf) {unsigned long remain = sz; //剩余的字节数unsigned long current_add = 0;//当前的地址unsigned int index = 0;//用于buf的索引current_add = adr - FLASH_BASE_ADDR;while(remain >= 4096){W25QXX_Read(read_buf,current_add,4096);for(int i=0;i<4096;i++){if(read_buf[i] != buf[index+i])return adr+index+i;}current_add += 4096;remain -= 4096;index += 4096;}W25QXX_Read(read_buf,current_add,remain);for(int i=0;i<remain;i++){if(read_buf[i] != buf[index+i])return adr+index+i;}return (adr+sz); // 校验成功
}
编译我们就能得到一个.FLM文件。
验证测试下载算法
这里我们只验证下载算法的功能,测试是否能正常下载,至于下载进去以后如何运行代码,这里不讨论,留在下一章介绍,因为涉及到链接脚本、拷贝、跳转、等等操作。
把该文件放在\MDK\ARM\Flash路径下,随便打开一个工程,添加下载算法
编译下载,发现报错,如下图:

报错原因是下载算法没有找到08000000H这个地址,我这里使用的是默认的链接脚本:
LR_IROM1 0x08000000 0x00100000 { ; load region size_regionER_IROM1 0x08000000 0x00100000 { ; load address = execution address*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20000000 0x00020000 { ; RW data.ANY (+RW +ZI)}
}
下载算法文件里定义的FLASH起始地址是0x00000000,大小是0x01000000,那么下载算法的空间就是0x00000000~0x00FFFFFF。这里加载地址是0x08000000,就是要往0x08000000开始的地址写入数据。这样一来,很显然下载算法判断出了非法地址就报错了。
既然这个加载地址报错,那就把加载地址改成0x00000000,如下:
LR_IROM1 0x00000000 0x00100000 { ; load region size_regionER_IROM1 0x0800000 0x00100000 { ; load address = execution address*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20000000 0x00020000 { ; RW data.ANY (+RW +ZI)}
}
这总合法了吧,但遗憾的是编译报错,报错的原因是程序里调用了__main函数,__main函数中不能被链接到非启动区域,也就是加载地址和链接地址不一样。这里不讨论报错的原因和应对措施。其实有很多种解决方法。我暂时将链接地址也改成0x00000000如下图:
LR_IROM1 0x00000000 0x00100000 { ; load region size_regionER_IROM1 0x0000000 0x00100000 { ; load address = execution address*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20000000 0x00020000 { ; RW data.ANY (+RW +ZI)}
}
编译下载,下载成功。

下载成功了,我用一个SPI FLASH demo程序把FLASH0地址开始的数据读出来,打印出来:

在把我们烧写进去的固件对应的bin文件打开,对比一下。

可以看到是完全一样的。
证明制作的下载算法是没问题的。FLASH中存储的代码如何执行下一篇文章介绍。
相关文章:
单片机FLASH下载算法的制作
环境 硬件使用正点原子STM32F407探索者V2开发板 编程环境使用MDK 下载工具使用JLINK FLASH芯片使用W25Q128 什么是下载算法 单片机FLASH的下载算法是一个FLM文件,FLM通过编译链接得到,其内部包含一系列对FLASH的操作,包括初始化、擦除、写…...
[nlp] 损失缩放(Loss Scaling)loss sacle
在深度学习中,由于浮点数的精度限制,当模型参数非常大时,会出现数值溢出的问题,这可能会导致模型训练不稳定。为了解决这个问题,损失缩放(Loss Scaling)技术被引入,它通过缩放损失值来解决这个问题。 在深度学习中,损失缩放技术通常是通过将梯度进行缩放来实现的。具…...
Django框架之视图层
【一】三板斧 【1】HttpResponse 返回字符串类型 【2】render 返回html页面,并且在返回给浏览器之前还可以给html页面传值 【3】redirect 重定向页面 在视图文件中写视图函数的时候不能没有返回值了,默认返回的是None,页面上就会报错 d…...
商城免费搭建之java商城 java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c
1. 涉及平台 平台管理、商家端(PC端、手机端)、买家平台(H5/公众号、小程序、APP端(IOS/Android)、微服务平台(业务服务) 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…...
AI机器学习实战 | 使用 Python 和 scikit-learn 库进行情感分析
专栏集锦,大佬们可以收藏以备不时之需 Spring Cloud实战专栏:https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏:https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏:https:/…...
CANoe-Logging模块如何抓取总线数据
在CANoe测量期间(CANoe运行时),总线数据经由Measurement Setup界面的各分析模块的输入口流入Trace、Graphics、Data等窗口中,或统计、或显示、或分析。总线数据除了能流入分析窗口中做解析外,还可以保存到log文件中,留作其他人分析或复现的文件。 在Measurement Setup界…...
Unity中Shader的矩阵加减法
文章目录 前言一、什么是矩阵矩阵就是一组数的阵列 二、矩阵的加法三、矩阵的负值四、矩阵的减法五、矩阵的表示 前言 Unity中Shader用到的矩阵加减法,以及矩阵的一些基础常识 一、什么是矩阵 矩阵就是一组数的阵列 1 2 3 4 5 6 二、矩阵的加法 两个矩阵相加就是…...
IIC总线概述和通信时序代码详细图文解析
IIC总线 1 IIC总线概述 I2C总线两线制包括:串行数据SDA(Serial Data)、串行时钟SCL(Serial Clock)。总线必须由主机(通常为微控制器)控制,主机产生串行时钟(SCL&#x…...
EtherCAT 伺服控制功能块实现
EtherCAT 是运动控制领域主要的通信协议,开源EtherCAT 主站协议栈 IgH 和SOEM 两个项目,IgH 相对更普及一些,但是它是基于Linux 内核的方式,比SOEM更复杂一些。使用IgH 协议栈编写一个应用程序,控制EtherCAT 伺服电机驱…...
如何基于OpenCV和Sklearn算法库开展机器学习算法研究
大家在做机器学习或深度学习研究过程中,不可避免都会涉及到对各种算法的研究使用,目前比较有名的机器学习算法库主要有OpenCV和Scikit-learn(简称Sklearn),二者都支持各种机器学习算法,主要有监督学习、无监…...
在 Node.js 中发出 HTTP 请求的 5 种方法
在 Node.js 中发出 HTTP 请求的 5 种方法 学习如何在 Node.js 中发出 HTTP 请求可能会让人感到不知所措,因为有数十个可用的库,每个解决方案都声称比上一个更高效。一些库提供跨平台支持,而另一些库则关注捆绑包大小或开发人员体验。 在这篇…...
pipeline agent分布式构建
开启 agent rootjenkins:~/learning-jenkins-cicd/07-jenkins-agents# docker-compose -f docker-compose-inbound-agent.yml up -d Jenkins配置添加 pipeline { agent { label docker-jnlp-agent }parameters {booleanParam(name:pushImage, defaultValue: true, descript…...
MySQL(17):触发器
概述 MySQL从 5.0.2 版本开始支持触发器。MySQL的触发器和存储过程一样,都是嵌入到MySQL服务器的一段程序。 触发器是由 事件来触发 某个操作,这些事件包括 INSERT 、 UPDATE 、 DELETE 事件。 所谓事件就是指用户的动作或者触发某项行为。 如果定义了触…...
挖掘PostgreSQL事务的“中间态”----更加严谨的数据一致性?
1.问题 今天在上班途中,中心的妹纸突然找我,非常温柔的找我帮忙看个数据库的报错。当然以我的性格,妹子找我的事情对我来说优先级肯定是最高的,所以立马放下手中的“小事”,转身向妹子走去。具体是一个什么样的问题呢…...
多种方法实现conda环境迁移
Conda 为包管理器和虚拟环境管理器。在配置完项目环境,进行了编写和测试代码,需要大量数据测试运行时,需要将其移至另一台主机上。Conda 提供了多种保存和移动环境的方法。 方法1: scp拷贝法,直接将envs的环境文件夹…...
C++ string类(一)
1.C语言中的字符串 C语言中,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符 OOP(Object Oriented Programming)的思想,而且…...
系统时间和JVM的Date时间不一致问题解决
通过Java得到的时间与操作系统时间不一致,如何修改Java虚拟机时间? 造成这种问题的原因可能是:你的操作系统时区跟你JVM的时区不一致。 你的操作系统应该是中国的时区吧,而JVM的时区不一定是中国时区,你在应用服务器…...
23111701[含文档+PPT+源码等]计算机毕业设计javaweb点餐系统全套餐饮就餐订餐餐厅
文章目录 **项目功能简介:****点餐系统分为前台和后台****前台功能介绍:****后台功能介绍:** **论文截图:****实现:****代码片段:** 编程技术交流、源码分享、模板分享、网课教程 🐧裙:77687156…...
RabbitMQ 部署及配置详解(集群部署)
单机部署请移步: RabbitMQ 部署及配置详解 (单机) RabbitMQ 集群是一个或 多个节点,每个节点共享用户、虚拟主机、 队列、交换、绑定、运行时参数和其他分布式状态。 一、RabbitMQ 集群可以通过多种方式形成: 通过在配置文件中列出群集节点以…...
基于蝠鲼觅食算法优化概率神经网络PNN的分类预测 - 附代码
基于蝠鲼觅食算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于蝠鲼觅食算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于蝠鲼觅食优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对PNN神…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
