基于 STM32F407 的 SPI Flash下载算法

目录

  • 一、概述
  • 二、自制 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 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
}

这里的 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_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 文件):

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

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

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

相关文章

Apollo配置中心实战

本地快速部署请参见Quick Start 编译打包Apollo cd ~/Documents/workspace/apollo sh scripts/build.sh运行Apollo sh src/main/scripts/startup.sh # $SERVICE_NAME".jar"配置服务 元数据服务 cd ~/Documents/workspace/apollo/apollo-configservice nohup java …

【重学 MySQL】四十八、DCL 中的 commit 和 rollback

【重学 MySQL】四十八、DCL 中的 commit 和 rollback commit的定义与作用rollback的定义与作用使用场景相关示例注意事项DDL 和 DML 的说明 在MySQL中&#xff0c;DCL&#xff08;Data Control Language&#xff0c;数据控制语言&#xff09;用于管理数据库用户和控制数据的访问…

windows C++-创建基于代理的应用程序(上)

本文介绍了如何创建基本的基于代理的应用程序。 在这个案例中&#xff0c;你可以创建一个代理&#xff0c;从文本文件中异步读取数据。 应用程序使用 Adler-32 校验和算法来计算该文件内容的校验和。 创建控制台应用 本部分介绍了如何创建 C 控制台应用程序&#xff0c;用于引…

螺蛳壳里做道场:老破机搭建的私人数据中心---Centos下docker学习02(yum源切换及docker安装配置)

2 前期工作 2.1 切换yum源并更新 删除/etc/yum.repos.d/原有repo文件&#xff0c;将Centos-7.repo库文件拷贝到该目录下。 然后清楚原有缓存yum clean all 生成新的缓存yum makecache 更新yum update –y 然后再确认/etc/yum.repos.d/不会有其他库文件&#xff0c;只留下…

RabbitMq生产者可靠性

基本概念&#xff1a; RabbitMQ生产者可靠性主要涉及确保消息能够可靠地发送到RabbitMQ服务器&#xff0c;并且在出现故障时能够进行适当的处理。 消息确认机制&#xff08;Publisher Confirms&#xff09;&#xff1a; 启用 Publisher Confirms&#xff1a;生产者在发送消息之…

elasticsearch设置账号和密码

1、es安装&#xff0c;挂载路径根据实际情况修改 docker run -d --restart always \ --name es \ -e "ES_JAVA_OPTS-Xms512m -Xmx512m" \ -e "discovery.typesingle-node" \ -e "TZAsia/Shanghai" \ -v /mnt/data/efk/es/data:/usr/share/elast…

Android高级控件

文章目录 1.下拉列表Spinner1.数组适配器ArrayAdapter2.简单适配器SimpleAdapter3.基本适配器BaseAdapter 2.列表视图ListView3.网格视图GridView4.翻页视图ViewPager5.翻页标签栏pagerTabStrip6.ViewPager实现引导页7.碎片Fragment1.Fragment静态注册2.Fragment生命周期3.Frag…

【数据库】MongoDB的索引功能及其在Java中的实现

MongoDB 的索引功能极大地提高了查询性能。通过创建索引&#xff0c;MongoDB 可以快速定位到数据&#xff0c;而无需扫描整个集合。本文将介绍 MongoDB 的索引功能及其在 Java 中的实现方法。 1. 什么是索引&#xff1f; 索引是数据库中用于快速查找和排序数据的一种数据结构。…

Hack Uboot

在硬件评估过程中&#xff0c;经常会遇到采用U-Boot的设备。本文旨在阐述U-Boot是什么&#xff0c;从攻击角度来看它为何如此吸引人&#xff0c;以及这种流行的引导程序所关联的攻击面。 U-Boot 特性 U-Boot&#xff0c;即通用引导加载程序&#xff08;Universal Boot Loader…

SpringBoot实现:古典舞在线交流平台全攻略

第一章 绪论 1.1研究背景 在当今的社会&#xff0c;可以说是信息技术的发展时代&#xff0c;在社会的方方面面无不涉及到各种信息的处理。信息是人们对客观世界的具体描述&#xff0c;是人们进行交流与联系的重要途径。人类社会就处在一个对信息进行有效合理的加工中。它将促进…

java中创建不可变集合

一.应用场景 二.创建不可变集合的书写格式&#xff08;List&#xff0c;Set&#xff0c;Map) List集合 package com.njau.d9_immutable;import java.util.Iterator; import java.util.List;/*** 创建不可变集合:List.of()方法* "张三","李四","王五…

问题解决实录 | bash 中 tmux 颜色显示不全

点我进入博客 如下图&#xff0c;tmux 中颜色显示不全: echo $TERM输出的是 screen 但在 bash 里面输出的是 xterm-256 color 在 bash 里面输入&#xff1a; touch ~/.tmux.conf vim ~/.tmux.conf set -g default-terminal "xterm-256color"使之生效 source …

【设计模式-中介者模式】

定义 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为设计模式&#xff0c;通过引入一个中介者对象&#xff0c;来降低多个对象之间的直接交互&#xff0c;从而减少它们之间的耦合度。中介者充当不同对象之间的协调者&#xff0c;使得对象之间的通信变得简单且…

微知-梅林系统设置无线桥接模式后如何登录梅林路由器?

背景&#xff1a; 本文将介绍2种使用路由器桥接后无法登录桥接路由器的解决办法&#xff0c;主要涉及到华为梅林系统&#xff08;刷机或者原生&#xff09;的无线桥接模式。 当图中梅林路由器设置为无线桥接模式后&#xff0c;连接到梅林路由器的调试电脑E&#xff0c;因为会从…

基于ucontext库实现协程类

文章目录 前言协程基础知识协程上下文对称协程与⾮对称协程有栈协程与⽆栈协程 ucontext库接口熟悉一个简单的函数切换自动调用 协程类的实现接口全局变量线程局部变量malloc封装协程切换构造函数协程执行的方法 测试协程切换手动切换复用 前言 协程&#xff08;Coroutine&…

【从0开始搭建微服务并进行部署】SpringBoot+dubbo+zookeeper

文章目录 说明环境搭建创建项目父模块设置子模块 dubbo-api子模块 dubbo-provider子模块 dubbo-consumer测试项目 docker部署项目完整项目地址 说明 jdk1.8SpringBoot2.x低版本dubbo&#xff1a;请查看之前教程【微服务】SpringBootDubboZooKeeper 实战 关于本教程将采用jdk1…

HTML流光爱心

文章目录 序号目录1HTML满屏跳动的爱心&#xff08;可写字&#xff09;2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心&#xff08;简易版&#xff09;7HTML粒子爱心8HTML蓝色动态爱心9HTML跳动的爱心&#xff08;双心版&#xff09;1…

百度飞桨(paddlepaddle)安装

百度飞桨&#xff08;paddlepaddle&#xff09;安装 Anaconda升级 打开 Anaconda Prompt &#xff08;或者 Mac 下的终端&#xff09;&#xff0c;键入&#xff1a; conda upgrade --all pip 安装 python -m pip install paddlepaddle -i https://mirror.baidu.com/pypi/s…

如何用AI绘画工具生成中国风插画?Midjourney保持风格一致出图

​ 如何运用AI绘画工具如Midjourney&#xff0c;生成符合我们特定要求的艺术作品是一门精进的技巧&#xff0c;尤其当你想生成具有鲜明特色的国风插画时&#xff0c;纯文本提示词的局限性常常使我们难以达到预期效果。然而&#xff0c;借助Midjourney的高级参数功能——特别是s…

【课程学习】随机过程之泊松过程

随机过程之泊松过程 泊松分布泊松过程 泊松分布 二项分布是离散性的分布&#xff0c;泊松分布是把二项分布取n趋于无穷得到的连续分布。也就是在一段时间内不停的观察某件事情发生的次数。 如&#xff1a;一个小时内观察一段路上经过行人的数目&#xff0c;如果每个半个小时观…