H750_QSPI_Flash运行程序

H750_QSPI_Flash运行程序

STM32H750如何在外部QPI接口的FLASH上运行程序?

2020.10.24 屋脊雀工作室

本次实验硬件是Albatross H750小板

STM32 H750 QPI xip

相关代码在百度云,目录W107-H750VB-Albatross\5 Mdk_Demo\H750_QSPI_外部FLASH执行代码\

链接: https://pan.baidu.com/s/1H4sD9XpXz83YTTXoSIyFXQ 提取码:52f3

 

概述

STM32H750VB官方号称内部FLASH只有128K(实际有2M)。

128K空间太小,随随便便一个模块就能占满了。H750是M7架构,有内部Cache。所以,能够在QSPI上直接取指令执行程序,速度并不慢。

那么如何在QSPI上运行程序呢?有以下几步:

  1. 将程序下载到外部QSPI Flash。

    有两种手段:

    1是通过MDK等工具直接将某些指定代码下载到外部Flash。

    2是拆分功能,将在外部运行的代码单独编译为一个BIN,做一个BOOT放到内部FLASH,用BOOT将这个单独的BIN下载到QSPI FLASH上。量产产品通常是这样做。

  2. 程序下载后,将QSPI Flash映射到程序地址空间(要芯片支持才可以)。映射前,QSPI上的数据只能通过SPI接口读写;映射后,可直接读(不能直接写)。有点绕口。

    比如,要读一个字节数据,用SPI操作,则需要通过SPI发送地址、发送直接、读取数据等操作。

    如将QSPI映射到0x90000000,那么程序中可以直接读,比如定义一个指针P,赋值0x90000000给P,然后 *p就能读出数据。执行代码同理,只不过代码是内核自动取指令。

我们本次只是验证STM32H750VB在QSPI上执行代码的功能,因此使用MDK直接将部分代码下载到QSPI FLASH上。

预备

  1. 用普通的SPI接口控制FLASH的知识要先了解。
  2. 在做本次验证之前,先要验证硬件是否正常,也就是用QSPI读写例程验证。

关键知识

  1. 我们常说QSPI falsh,QSPI通信,我认为是有误解的。
  2. SPI、DSPI、QSPI,其实都是SPI,那么,也就都是串行总线。
  3. 真正4线总线的,应该叫 QPI,QPI是并行总线。

请看W25Q64的规格书,

Flash支持SPI和QPI两种模式,其中我们常说的SPI/DSPI/QSPI都是SPI。

芯片上电后,默认都是SPI模式,注意,是上电,也就是掉电再上电

先置位状态寄存器2的QE位,然后发送0x38命令,就可以从SPI模式切换到QPI模式。

在QPI模式发送0XFF指令,就可以返回SPI模式。

这几种模式到底有什么区别?

  1. 不同的模式有不同的指令,请查规格书。
  2. 指令的时序不一样。

比如0x06指令,在SPI模式和QPI模式都支持,时序如下,左边是SPI的时序,右边是QPI的时序。不同点就是,发送0x06这个数字时,SPI只用了DI一根信号,需要8个时钟周期,QPI用了4根信号,只需要2个时钟周期。

看一个DSPI的命令,3BH。这时一个快速读指令。命令和地址,只用DI,也就是IO0通信。返回数据却用IO0和IO2。

那么在QSPI的读指令时序会是怎么样嗯?和DSPI的区别就是,返回数据用4根线。

更多指令请自行查看文档。

总结:

不同模式有不同的指令。

不同的指令时序不一样:指令用几个IO发送,地址用几根IO发送,地址多少位,数据用几根IO发送。

其中SPI/DSPI/QSPI 由使用的指令决定。

但是QPI就需要配置FLASH进入QPI模式。所以呢,QPI有些指令的值和SPI是一样的,但是时序不一样。

调试步骤

本次目标是在FLASH上执行代码。调试步骤如下。

  1. 用SPI接口读ID,能读到说明硬件基本没问题。
  2. 用QPI接口读写FLASH,数据正常说明QPI模式可以。
  3. 在程序中定义一个数组,指定放在FLASH上,通过MDK下载。程序映射后访问数组正常。
  4. 将一段代码放在FLASH上,执行正常。

跟多细节见代码,下面只说关键处。

读ID

  1. 调用MX_QUADSPI_Init函数初始化STM32的SPI接口。
  2. 调用函数BSP_QSPI_Init初始化FLASH。在初始化中设置QE位。
  3. 读ID

现在说的都是SPI下的指令,有种,分别是SPI的0x90,DSPI的0x92,QSPI的0x94。

那要怎么读呢?如下,是一个完整的SPI通信过程。设置一个指令结构体,用HAL_QSPI_Command发送指令,在用HAL_QSPI_Receive接收数据。

不同的接口不同的指令区别就在命令结构体。

InstructionMode: 用几根IO发送命令

Instruction:命令

AddressMode:用几根IO发送地址

AddressSize:地址有多少位

Address:地址

DataMode:数据用几根IO

DummyCycles:等待周期

。。。

上面几个设置,都可以从指令的时序图看出来。

 

90指令读ID,就是命令1根IO,地址1根IO,数据1根IO。

92指令读ID,就是命令1根IO,地址2根IO,数据2根IO。

92指令读ID,就是命令1根IO,地址4根IO,数据4根IO。

读写测试

见代码

流程是擦除,写,读,比较内容,其中读使用QPI模式。

地址映射

  1. 添加一个test_qspi.c
  2. 在文件中添加一个数组。
  3. 修改分散加载文件,将test_qspi.c放到外部FLASH,地址是0x90000000。

上面只说了流程,至于很多原理,请大家自行学习。

比如外什么外部地址是0X90000000,分散加载文件是什么,,,

4 添加下载算法到MDK。

先将STM32H750_W25Q64_WJQ.FLM文件拷贝到keil安装目录,比如D:\Keil_v5\ARM\Flash\

修改工程调试配置,将外部FLASH算法添加到工程。

修改前面读写测试的流程,不擦除不写,仅仅读。

编译将工程下载后,应该能读到我们定义的数据的内容。

注意要重新断电上电,此时我们的程序还不够健壮,在切换SPI和QPI模式还不够完善

5 添加地址映射

所谓的地址映射,就是,我们只提供一个地址,CPU帮我们完成读写流程,所以呢,命令设置和前面读操作是一样的。然后调用HAL_QSPI_MemoryMapped函数,将命令配置读到芯片中。

当然,我们需要新让FLASH芯片进入QPI模式。

然后定义一个指针指向0x90000000,就可以访问到FLASH上的数据了。

或者,我们直接访问定义的test_qspi_tab数组也是可以的。

执行程序

很简单,我们在test_qspi.c中添加一个小程序,看下是否能正常运行。

定义函数, 很简单,对传入的num进行+1操作。

测试

测试有一个现象要注意, 现在0x90000000地址不再是我们定义的数组了,因为我们多加了一个函数,这个函数在数组的前面。

测试肯定成功。

再确认

如何确定数组和程序就真的放在外部FLASH了呢?

  1. 看map文件,查数组和函数的地址。
  2. 把FLASH管脚撬开,看还能不能读到。

其他

本次测试仅仅说明过了如何在外部执行程序。

对性能并没有测试,比如,需要认真配置MPU中的CACHE设置