基于 STM32F407 的 SPI Flash下载算法

news2024/11/27 8:43:47

目录

  • 一、概述
  • 二、自制 FLM 文件
    • 1、修改使用的芯片
    • 2、修改输出算法的名称
    • 3、其它设置
    • 4、修改配置文件 FlashDev.c
    • 5、文件 FlashPrg.c 的实现
  • 三、验证算法


一、概述

本文将介绍如何使用 MDK 创建 STM32F407SPI Flash 下载算法。

其中,SPI Flash 芯片使用的是 W25Q128,其相关操作源码可以参考 STM32 通过 SPI 驱动 W25Q128,本文所使用的驱动 SPI Flash 的 API 和里面是一样的。

单片机的 Flash 下载算法是一个 FLM 文件,FLM 通过编译链接得到,其内部包含一系列对 FLASH 的操作,包括初始化、擦除、写、读、校验等等操作。

想要制作下载算法,先要了解下载算法的工作原理。我们下载一个程序的流程大概是这样的:

  1. 下载工具(比如 jlink)读取 FLM 文件
  2. 然后 JLINK 提取 FLM 文件的信息,将其传输到单片机的内部 SRAM
  3. 下载算法开始在 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 段的独立性,即与地址无关。

如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPIRead-only position independence)。ROPI 段通常是位置无关代码(PICposition-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 Type
   0x00000000,                 // Device Start Address
   0x00040000,                 // Device Size in Bytes (256kB)
   1024,                       // Programming Page Size
   0,                          // Reserved, must be 0
   0xFF,                       // Initial Content of Erased Memory
   100,                        // Program Page Timeout 100 mSec
   3000,                       // Erase Sector Timeout 3000 mSec

// Specify Size and Address of Sectors
   0x002000, 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
}

这里的 SystemInitsystem_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_region
  ER_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_region
  ER_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 文件):

说明算法编写成功 (^人^)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能专业就业方向与前景

随着产业结构升级的持续推进&#xff0c;未来行业领域对于人工智能专业人才的需求量会逐渐增加&#xff0c;一部分高校也开始陆续在本科阶段开设人工智能专业&#xff0c;以缓解人工智能领域人才缺口较大的问题。下面是小编整理的人工智能专业就业方向与前景&#xff0c;欢迎阅…

Leecode热题100-41.缺失的第一个正数

给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff1a;范围 [1,2] 中的数字都在数组…

C++面向对象:继承!

前言 继承是面向对象三大特性之一&#xff0c;所有的面向对象的语言都具备这三个性质&#xff0c;我们之前已经介绍过了封装的相关概念&#xff0c;今天我们来学习一下第二大特性&#xff1a;继承。 一.继承的概念 什么是继承&#xff1f; 定义&#xff1a;继承&#xff08;…

AI相关的整理

AI相关的整理 初体验记不住如何部署如何微调 整理AI学习&#xff0c;AI小白&#xff0c;业余爱好。持续更新&#xff0c;谨慎参考&#xff01; 初体验 试了一下本地直接下载安装ollama和open-webui&#xff0c;然后运行指定的模型&#xff0c;跟着文档做&#xff0c;很简单。但…

(Linux驱动学习 - 7).阻塞IO和非阻塞IO

一.阻塞IO和非阻塞IO定义 1.阻塞IO 当应用程序对设备驱动进行操作的时候&#xff0c;如果不能获取到设备资源&#xff0c;那么阻塞式IO就会将应用程序对应的线程挂起&#xff0c;直到设备资源可以获取为止。 在应用程序中&#xff0c;用户调用 open 函数默认是以阻塞式打开设备…

54.二叉树的最大深度

迭代 class Solution {public int maxDepth(TreeNode root) {if(rootnull){return 0;}int de0;Queue<TreeNode> qunew LinkedList<>();TreeNode tn;int le;qu.offer(root);while(!qu.isEmpty()){lequ.size();while(le>0){tnqu.poll();if(tn.left!null){qu.offe…

RTA-OS Port Guide学习(四)-基于S32K324 OS

文章目录 前言PerformanceMeasurement EnvironmentRAM and ROM Usage for OS ObjectsSingle CoreMulti Core Stack UsageLibrary Module SizesSingle CoreMulti Core Execution TimeContext Switching Time 总结 前言 前面一篇文章介绍了硬件的一些特性&#xff0c;本文为最后…

国内目前顶级的哲学教授颜廷利:全球公认十个最厉害的思想家

国内目前顶级的哲学教授颜廷利&#xff1a;全球公认十个最厉害的思想家 颜廷利&#xff0c;字弃安&#xff0c;号求前&#xff0c;山东济南人&#xff0c;当代著名思想家、哲学家、教育家、易经心理学家、中国第一起名大师、国际权威易学大师、中国汉字汉语研究专家、现代最著…

什么是数字化智能工厂的组成

二、数字化智能工厂的主要功能组成 数字化智能工厂主要由以下几个功能部分组成&#xff1a; 自动化生产设备&#xff1a;包括机器人、智能传感器、可编程逻辑控制器&#xff08;PLC&#xff09;等&#xff0c;用于实现生产过程的自动化操作&#xff0c;减少人力依赖&#xff0…

[C#]C# winform部署yolov11-pose姿态估计onnx模型

【算法介绍】 在C# WinForms应用中部署YOLOv11-Pose姿态估计ONNX模型是一项具有挑战性的任务。YOLOv11-Pose结合了YOLO&#xff08;You Only Look Once&#xff09;的高效物体检测算法和Pose Estimation&#xff08;姿态估计&#xff09;专注于识别人体关键点的能力&#xff0…

移动WSL到其他盘

1、首先下载 Move WSL 工具包&#xff0c;并解压。&#xff08;https://github.com/pxlrbt/move-wsl/archive/refs/heads/master.zip&#xff09; 2、管理员身份运行Windows PowerShell。 3、在PowerShell中运行如下命令&#xff0c;停止正在运行的Linux子系统。 wsl --shutd…

柯桥商务英语口语-work-shy 是什么意思?不要理解成“工作害羞”!

ork工作&#xff0c;shy是害羞&#xff0c;那么&#xff0c;work-shy是什么意思&#xff1f; 其实在 "work-shy" 这个短语中&#xff0c;"shy" 的意思并不是害羞&#xff0c;而是表达一种躲避、逃避的意思。 "work-shy" 表示对工作有一种躲避、…

深度学习基础—交并比与非极大值抑制

1.交并比 &#xff08;1&#xff09;定义 交并比是用来衡量目标检测算法的表现的函数。定义如下&#xff1a; 用预测框和真实框的面积的交集除以预测框和真实框的面积的并集&#xff0c;得到的结果本次算法预测的交并比。研究函数可以发现&#xff0c;交并比的范围为[0,1]&…

cnn突破七(四层bpnet网络公式与卷积核bpnet公式相关)

我们要有一个概念&#xff0c;就是卷积核就是我们的w1&#xff0c;w12&#xff0c;w2 那么我们的5*5卷积核怎么表达&#xff0c;当他在14*14的图像中流动时&#xff0c;对应的像素也在变化 这个和我们的上面w1&#xff0c;w12&#xff0c;w2不同&#xff0c;因为这几个都是全…

测绘地理信息赋能新质生产力

在信息化与智能化浪潮的推动下&#xff0c;测绘地理信息作为连接现实世界与数字空间的桥梁&#xff0c;正逐步成为驱动经济社会发展的新质生产力。本文旨在深入探讨测绘地理信息如何通过技术创新与应用拓展&#xff0c;为各行各业赋能&#xff0c;塑造智慧社会的新面貌。 一、…

word无法复制粘贴

word无法复制粘贴 使用word时复制粘贴报错 如下&#xff1a; 报错&#xff1a;运行时错误‘53’&#xff0c;文件未找到&#xff1a;MathPage.WLL 这是mathtype导致的。 解决方法 1&#xff09;在mathtype下载目录下找到"\MathType\MathPage\64"下的"mathpa…

第T3周:CNN实现天气识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 搭建CNN网络模型实现多云、下雨、晴、日出四种天气状态的识别&#xff0c;并用真实天气做预测 具体实现&#xff1a; &#xff08;一&#x…

Win10照片查看器不见了怎么办?

刚换了电脑&#xff0c;发现查看图片默认打开是window画图工具&#xff0c;看图竟然需要一张一张打开&#xff0c;超级不方便。右键图片选择打开方式也不见照片查看器&#xff0c;window自带的看图工具去哪儿了&#xff1f; 不要着急&#xff0c;我们可以把它找回来&#xff0…

windows下DockerDesktop命令行方式指定目录安装

windows下DockerDesktop指定目录安装(重新安装) 因为DcokerDesktop占用内存较大, 并且拉去镜像后占用本地空间较多,所以建议安装时就更改默认安装路径和镜像存储路径 这里,展示了从下载到安装的过程: 首先下载DcokerDesktop;找到Docker Desktop Installer.exe 并重命名为 do…

万界星空科技MES数据集成平台

制造执行系统MES作为连接企业上层ERP系统和现场控制系统的桥梁&#xff0c;承担了实时数据采集、处理、分析和传递的重要任务。MES数据集成平台是一个集成各类数据源&#xff0c;将数据进行整合和统一管理的系统&#xff0c;通过提供标准化接口和协议&#xff0c;实现数据的无缝…