基于 STM32F407 的 SPI Flash下载算法
目录
- 一、概述
- 二、自制 FLM 文件
- 1、修改使用的芯片
- 2、修改输出算法的名称
- 3、其它设置
- 4、修改配置文件 FlashDev.c
- 5、文件 FlashPrg.c 的实现
- 三、验证算法
一、概述
本文将介绍如何使用 MDK 创建 STM32F407
的 SPI Flash
下载算法。
其中,
SPI Flash
芯片使用的是W25Q128
,其相关操作源码可以参考 STM32 通过 SPI 驱动 W25Q128,本文所使用的驱动 SPI Flash 的 API 和里面是一样的。
单片机的 Flash 下载算法是一个 FLM 文件,FLM 通过编译链接得到,其内部包含一系列对 FLASH 的操作,包括初始化、擦除、写、读、校验等等操作。
想要制作下载算法,先要了解下载算法的工作原理。我们下载一个程序的流程大概是这样的:
- 下载工具(比如
jlink
)读取 FLM 文件 - 然后
JLINK
提取 FLM 文件的信息,将其传输到单片机的内部 SRAM - 下载算法开始在 SRAM 中运行,由于下载算法包含了一系列对 Flash 的操作,那么下载工具通过下发初始化、擦除、写入、校验等指令给单片机,单片机去执行这些指令操作,实现对单片机 Flash 的下载。
二、自制 FLM 文件
我参照的是 MDK 给的程序模板来完成 Flash 下载程序,然后在模板的基础上加上自己的代码。
模板路径如下:D:\Keil_v5\ARM\Packs\ARM\CMSIS\5.8.0\Device\_Template_Flash
,不同的 MDK 版本可能路径不一样。
然后将项目拷贝到你的工作目录下,并取消该工程项目的只读属性。
打开项目如下:
然后开始我们的工作。
1、修改使用的芯片
首先选择你的芯片类型和型号。
2、修改输出算法的名称
这一步不是必须的,改个名称方便自己查看。
注意这个名称只是项目最终生成输出的 FLM 文件的名称,和下面位置识别出的算法名(后面会介绍这个名称如何修改)无关。
3、其它设置
注意:
这里的设置在模板文件中已经设置好了,这里主要是介绍一些,可以跳过
这两个设置是为了保证生成的算法文件中 RO 和 RW 段的独立性,即与地址无关。
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI,Read-only position independence)。ROPI 段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是 PIC 和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
- 加载以响应运行事件。
- 在不同情况下使用其他例程的不同组合加载到内存中。
- 在执行期间映射到不同的地址。
使用 Read-Write position independence 同理,表示的可读可写数据段。
通过下面的命令就可以将生成的 axf
可执行文件修改为 FLM
。
我们这里的分散加载文件直接使用 MDK 模板工程里提供好的即可,无需任何修改。
4、修改配置文件 FlashDev.c
模板工程里面提供简单的配置说明:
struct FlashDevice const FlashDevice = {FLASH_DRV_VERS, // Driver Version, do not modify!"New Device 256kB Flash", // Device Name ONCHIP, // Device Type0x00000000, // Device Start Address0x00040000, // Device Size in Bytes (256kB)1024, // Programming Page Size0, // Reserved, must be 00xFF, // Initial Content of Erased Memory100, // Program Page Timeout 100 mSec3000, // Erase Sector Timeout 3000 mSec// Specify Size and Address of Sectors0x002000, 0x000000, // Sector Size 8kB (8 Sectors)0x010000, 0x010000, // Sector Size 64kB (2 Sectors) 0x002000, 0x030000, // Sector Size 8kB (8 Sectors)SECTOR_END
};
这里的注释已经说得很明白了,大家根据自己的芯片来进行修改即可,我使用的是 W25Q128
,其存储大小为 16MB,一个扇区 4KB,所以修改如下:
W25Q128
一页是 256KB,但这里写的 4096 是为了提高下载速率和擦除速率,如果你把 4096 改为 8,可以很明显得感受到下载速度变慢了
struct FlashDevice const FlashDevice = {FLASH_DRV_VERS, /* 驱动算法,由 MDK 制定,勿动 */"Yux_STM32F407VE_SPI_W25Q128", /* 算法名称 */ EXTSPI, /* 设备类型,外扩展 SPI-Flash */SPI_FLASH_MEM_ADDR, /* Flash 起始地址 */0x01000000, /* Flash 大小,16MB */4096, /* 编程页大小 */0, /* 保留,必须为 0 */0xFF, /* 擦除后的数值 */3000, /* 页编程等待时间 */3000, /* 扇区擦除等待时间 */0x001000, 0x000000, /* 扇区大小,扇区地址 */SECTOR_END
};
其中,SPI_FLASH_MEM_ADDR
是我在 FlashOS.h
文件中定义的一个宏,表示 Flash 的起始地址:
#define SPI_FLASH_MEM_ADDR 0x00000000
这里的算法名称就体现在这里:
5、文件 FlashPrg.c 的实现
模板文件中提供了这几个函数,也是我们完成 Flash 下载算法最关键的地方:
// Flash 初始化
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {/* Add your Code */return (0); // Finished without Errors
}// Flash 复位
int UnInit (unsigned long fnc) {/* Add your Code */return (0); // Finished without Errors
}// 擦除整个 Flah 芯片
int EraseChip (void) {/* Add your Code */return (0); // Finished without Errors
}// 擦除指定扇区
int EraseSector (unsigned long adr) {/* Add your Code */return (0); // Finished without Errors
}// 页编程
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {/* Add your Code */return (0); // Finished without Errors
}// 校验
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
{/* Add your Code */return (0); // Finished without Errors
}
这里涉及到了对 W25Q128
的相关操作,详细内容参照: STM32 通过 SPI 驱动 W25Q128,这里主要是调用之前实现的函数。
我使用的是标准库,所以还要添加一些相关的文件进来:
实现如下:
- 初始化函数
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {SystemInit(); // 初始化系统和时钟w25qxx_init(); // 初始化 w25q128 /* Add your Code */return (0); // Finished without Errors
}
这里的 SystemInit
是 system_stm32f4xx.c
中的函数,在 STM32 时钟树(基于 STM32F407) 一文中讨论过。
- 复位函数
Uninit
没有用到,所以不用改。
- 擦除整个芯片
int EraseChip (void) {w25qxx_erase_chip();/* Add your Code */return (0); // Finished without Errors
}
- 擦除指定扇区
int EraseSector (unsigned long adr) {uint32_t sector = 0;adr -= SPI_FLASH_MEM_ADDR;sector = adr / 4096;w25qxx_erase_sector(sector);/* Add your Code */return (0); // Finished without Errors
}
- 页编程
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {adr -= SPI_FLASH_MEM_ADDR;w25qxx_write(buf, adr, sz);/* Add your Code */return (0); // Finished without Errors
}
- 校验
unsigned char aux_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 - 0xC0000000;while(remain >= 4096){w25qxx_read(aux_buf, current_add, 4096);for(int i = 0; i < 4096; i++){if(aux_buf[i] != buf[index+i])return adr+index+i;}current_add += 4096;remain -= 4096;index += 4096;}w25qxx_read(aux_buf, current_add, remain);for(int i = 0; i < remain; i++){if(aux_buf[i] != buf[index+i])return adr + index + i;}return (adr + sz); // 校验成功
}
为什么要
adr -= SPI_FLASH_MEM_ADDR;
?
因为实际传递进来的地址是带了首地址的,即0x00000000
(如果你定义的是其它地址,而不执行adr -= SPI_FLASH_MEM_ADDR;
就会出错)。特别注意,我们这里的0xC0000000
是随意设置的,因为 STM32F4 的标准 SPI 外设并不支持内存映射。
这里执行的擦除大小要前面
FlashDev.c
文件中配置的扇区大小一致,这里是执行的 4KB 为扇区进行擦除。
现在编译之后就可以在项目目录下看见一个 FLM 文件。下面就来验证一下我们的下载算法是否正确。
三、验证算法
首先把我们的 FLM 文件放到如下目录中:D:\Keil_v5\ARM\Flash
,可以看到这里有很多 FLM 和 FLX 文件。
这里我随便找了一个项目,按如下方式添加自己的 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)}
}
有关链接脚本的部分可以参考:
浅析 Keil 中的 sct 文件,
分散加载文件 scatter files。
这部分的内容比较复杂,我就直接给出解决方案了:
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)}
}
这下编译成功了。但至于写没写入并不清楚,写没写对也不知道。所以我又写了个 W25Q128 的读取程序:
w25q32_dev.rd(data, 0x00000000, sizeof(data));for (int i = 0; i < sizeof(data); ++i){printf("%2x ", data[i]);if ( (i + 1) % 16 == 0 )printf("\r\n");}
话不多说,看结果(比较的是 bin 文件):
说明算法编写成功 (^人^)。
相关文章:

基于 STM32F407 的 SPI Flash下载算法
目录 一、概述二、自制 FLM 文件1、修改使用的芯片2、修改输出算法的名称3、其它设置4、修改配置文件 FlashDev.c5、文件 FlashPrg.c 的实现 三、验证算法 一、概述 本文将介绍如何使用 MDK 创建 STM32F407 的 SPI Flash 下载算法。 其中,SPI Flash 芯片使用的是 W…...

力扣之1355.活动参与者
题目: Sql 测试用例: Create table If Not Exists Friends (id int, name varchar(30), activity varchar(30)); Create table If Not Exists Activities (id int, name varchar(30)); Truncate table Friends; insert into Friends (id, name, acti…...

数据资产治理:构建敏捷与安全的数据管理体系
在当今数字化的盛况下,作为核心资产的数据已经越发受到企业的重视。但是随着公司的逐步壮大,如何分析这些数据以及如何有效治理数据资产,以确保安全性、合规性以及易用性,是企业面临的重大挑战。数聚股份将从多年从业经验深度探讨…...

Nodejs连接Mysql笔记
框架搭建 安装Node.js 首先,确保你已经在系统上安装了Node.js和npm(Node Packaged Modules)。你可以通过以下命令检查是否已经安装:shell 或者 node -v 或者 npm -v 数据库连接代码 1.导入MySQL2库 npm install mysql2 2.在文件…...

Canvas:AI协作的新维度
在人工智能的浪潮中,OpenAI的最新力作Canvas,不仅是一款新工具,它标志着人工智能协作方式的一次革命性飞跃。Canvas为写作和编程提供了一个全新的交互界面,让用户能够与ChatGPT进行更紧密、更直观的协作。 Canvas的…...

【深度学习】— softmax回归、网络架构、softmax 运算、小批量样本的向量化、交叉熵
【深度学习】— softmax回归、网络架构、softmax 运算、小批量样本的向量化、交叉熵 3.4 Softmax 回归3.4.1 分类问题3.4.2 网络架构 3.4.3 全连接层的参数开销3.4.4 softmax 运算3.4.5 小批量样本的向量化3.4.6 损失函数对数似然softmax 的导数 3.4.7 信息论基础熵信息量重新审…...

C# Wpf 图片按照鼠标中心缩放和平移
C# Wpf 图片按照鼠标中心缩放和平移 1、缩放事件 MouseWheel(object sender, MouseWheelEventArgs e)2、平移相关的事件 MouseMove(object sender, MouseEventArgs e) MouseDown(object sender, MouseButtonEventArgs e) MouseUp(object sender, MouseButtonEventArgs e)3、…...

网络安全产品类型
1. 防火墙(Firewall) 功能:防火墙是网络安全的第一道防线,通过检查进出网络的流量来阻止未经授权的访问。它可以基于预定义的安全规则,过滤数据包和阻止恶意通信。 类型: 硬件防火墙:以专用设备…...

【开源风云】从若依系列脚手架汲取编程之道(五)
📕开源风云系列 🍊本系列将从开源名将若依出发,探究优质开源项目脚手架汲取编程之道。 🍉从不分离版本开写到前后端分离版,再到微服务版本,乃至其中好玩的一系列增强Plus操作。 🍈希望你具备如下…...

金融市场的衍生品交易及其风险管理探讨
金融衍生品市场是现代金融体系的重要组成部分,其交易量和复杂性在过去几十年中迅速增长。衍生品,如期权、期货、掉期等,因其灵活性和杠杆效应,广泛应用于风险管理、投机和资产配置等多个领域。本文将探讨金融衍生品交易的关键特点…...

一、创建型(单例模式)
单例模式 概念 单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。它控制类的实例化过程,防止外部代码创建新的实例。 应用场景 日志记录:确保只有一个日志记录器,以便于管理和避免重复记…...

毕业设计项目-古典舞在线交流平台的设计与实现(源码/论文)
项目简介 基于springboot实现的,主要功能如下: 技术栈 后端框框:springboot/mybatis 前端框架:html/JavaScript/Css/vue/elementui 运行环境:JDK1.8/MySQL5.7/idea(可选)/Maven3(…...

【秋招笔试】10.09华子秋招(已改编)-三语言题解
🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 本次的三题全部上线…...

【算法笔记】双指针算法深度剖析
【算法笔记】双指针算法深度剖析 🔥个人主页:大白的编程日记 🔥专栏:算法笔记 文章目录 【算法笔记】双指针算法深度剖析前言一.移动零1.1题目1.2思路分析1.3代码实现 二.复写零2.1题目2.2思路分析2.3代码实现 三.快乐数3.1题目…...

第二十二天|回溯算法| 理论基础,77. 组合(剪枝),216. 组合总和III,17. 电话号码的字母组合
目录 回溯算法理论基础 1.题目分类 2.理论基础 3.回溯法模板 补充一个JAVA基础知识 什么时候用ArrayList什么时候用LinkedList 77. 组合 未剪枝优化 剪枝优化 216. 组合总和III 17. 电话号码的字母组合 回溯法的一个重点理解:细细理解这句话!…...

关闭IDM自动更新
关闭IDM自动更新 1 打开注册表2 找到IDM注册表路径 1 打开注册表 winR regedit 2 找到IDM注册表路径 计算机\HKEY_CURRENT_USER\Software\DownloadManager 双击LstCheck,把数值数据改为0 完成 感谢阅读...

Go 性能剖析工具 pprof 与 Graphviz 教程
在 Golang 开发中,性能分析是确保应用高效运行的重要环节。本文介绍如何使用 gin-contrib/pprof 在 Gin 应用中集成性能剖析工具,并结合 Graphviz 生成图形化的性能分析结果,如火焰图。这套流程帮助开发者更好地理解和优化 Go 应用的性能。 目…...

【题目解析】蓝桥杯23国赛C++中高级组 - 斗鱼养殖场
【题目解析】蓝桥杯23国赛C中高级组 - 斗鱼养殖场 题目链接跳转:点击跳转 前置知识: 了解过基本的动态规划。熟练掌握二进制的位运算。 题解思路 这是一道典型的状压动态规划问题。设 d p i , j dp_{i, j} dpi,j 表示遍历到第 i i i 行的时候&a…...

JavaScript可视化:探索顶尖的图表库
JavaScript可视化:探索顶尖的图表库 在这个被数据驱动的时代,你有没有想过,数据本身是如何变得有意义的?答案就是数据可视化。通过图表和图形,我们不仅可以看到数据,还可以感受到它,从而做出明…...

谷歌AI大模型Gemini API快速入门及LangChain调用视频教程
1. 谷歌Gemini API KEY获取及AI Studio使用 要使用谷歌Gemini API,首先需要获取API密钥。以下是获取API密钥的步骤: 访问Google AI Studio: 打开浏览器,访问Google AI Studio。使用Google账号登录,若没有账号…...

进入容器:掌控Docker的世界
进入容器:掌控Docker的世界 在这个快速发展的技术时代,你是否曾被Docker的庞大生态所吸引?那么,有没有想过在这个容器化的世界里,如何快速高效地“进入”这些隐藏在虚拟墙后的容器呢?容器就如同魔法箱,装载着应用与服务,而你,通过探索这些容器,能够更好地管理、排除…...

初始Linux(二)基础命令
前言: 之前那一篇我们已经介绍了一部分的基础命令,当然那只不过是九牛一毛,本篇我们继续介绍一些比较重要且需要掌握的基础命令。 mv命令: 其实这个命令有两个功能,一个是移动(剪切)文件&#…...

STM32 OLED
文章目录 前言一、OLED是什么?二、使用步骤1.复制 OLED.C .H文件1.1 遇到问题 2.统一风格3.主函数引用头文件3.1 oled.h 提供了什么函数 4.介绍显示一个字符的函数5. 显示十进制函数的讲解 三、使用注意事项3.1 配置符合自己的引脚3.2 花屏总结 前言 提示ÿ…...

伦敦金实时行情决策辅助!
在伦敦金实时交易的过程中,投资者主要依赖技术分析来辅助自己的投资决策。与基本面分析不同,技术分析侧重于研究金价的走势和市场行为,通过图表和技术指标来预测未来的市场走势。常用的技术分析方法包括: 趋势线和支撑阻力位&…...

Leetcode 746. 使用最小花费爬楼梯 入门dp C++实现
问题:Leetcode 746. 使用最小花费爬楼梯 给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你…...

路由协议常见知识点
路由协议是网络通信的基础,主要负责在网络中传递数据包,并确保它们从源节点传递到目标节点。本文将介绍一些常见的路由协议知识点,包括路由协议的分类、特性、配置与管理以及常见问题。 一、路由协议的分类 距离矢量路由协议: R…...

多模态大语言模型(MLLM)-InstructBlip深度解读
前言 InstructBlip可以理解为Blip2的升级版,重点加强了图文对话的能力。 模型结构和Blip2没差别,主要在数据集收集、数据集配比、指令微调等方面下文章。 创新点 数据集收集: 将26个公开数据集转换为指令微调格式,并将它们归类…...

网页前端开发之Javascript入门篇(7/9):字符串
Javascript字符串 什么是字符串? 答:其概念跟 Python教程 介绍的一样,只是语法上有所变化。 在 Javascript 中,一个字符串变量可以看做是其内置类String的一个实例(Javascript会自动包装)。 因此它拥有一…...

双登股份再战IPO:数据打架,实控人杨善基千万元股权激励儿子
撰稿|行星 来源|贝多财经 近日,双登集团股份有限公司(下称“双登股份”)递交招股书,准备在港交所主板上市,中金公司、建银国际、华泰国际为其联席保荐人。 贝多财经了解到,这并非双登股份首次向资本市场…...

4.Python 函数(函数的定义、函数的传入参数、函数的返回值、None 类型、函数说明文档、变量的作用域)
一、函数快速入门 1、函数概述 函数是组织好的,可重复使用的,用来实现特定功能的代码段 name "Hello World" name_length len(name)print(f"{name} 的长度为 {name_length}") # Hello World 的长度为 11len() 是Python 内置的函…...