查看: 267|回复: 4
打印 上一主题 下一主题

STM32F7-Discovery驱动外部Quad-SPIFlash初始化分析

[复制链接] qrcode

23

主题

26

帖子

77

积分

注册会员

Rank: 2

积分
77
楼主
跳转到指定楼层
发表于 2015-10-5 09:44 AM | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

**************************************************************************

                                       东方青学STM32F7

                                        ———————转载请注明ICKey————————

**************************************************************************




STM32F7-Discovery驱动外部Quad-SPIFlash

-----东方青

关于Quad-SPI,说实话,以前就听过它的存在,也仅仅就是知道他的存在,一直都没有使用过,但是,知道一点的是,它的速度比标准的SPI通信协议的速度可不是一个等级的,它的带宽是标准SPI4倍,那么速度呢?呵呵!你懂的。那么下面就得先了解一下Quad-SPI

Quad-SPI主要是针对SPIFlash而设计的SPI总线协议,它的存在就是为了彻底摒弃普通FlashEEPROM器件必须经历的写时序,并且最大程度的增加带宽和读写速度。在STM32F7系列MCU的参考手册中,有如下介绍:


模式挺多!但是我想我关注的应该是内存映射模式,其他模式也就是速度快了一点,和普通Flash一样,而内存映射模式就是MCUFlash的内存空间进行映射,就像使用自己的内存空间一样。而普通模式就算是用来存放MCU的程序,那也没有到像使用MCU自己 的内存空间的级别。当然遗憾的一点是,STM32F7虽然可以映射4GB的内存空间,但是遗憾的是,只能寻址256Mbit的内存空间,啊啊啊!而STM32F7-Discovery板卡板载的QuadSPIFlash只有128Mbit

基本的关于QUADSPI的框图如下:


嘿嘿!不用多说,有图有真相。具体参看STM32F7参考手册!

再看看命令序列图:


从图中可以看出,可以分为5个指令序列,但是这不重要,重要的是任一阶段都可以跳过,只要包含指令、地址、交替字节、数据这几个阶段之一就好。这就是摒弃那些好多的复杂的时序啦!有点nandFlash的味道了。

OKQuad-SPI总线的介绍到此结束!是时候让它为我所用的时候了。自己写驱动?嘿嘿!这是不可能的!倒不是驱动复杂,而是没必要!而且到了F7的水平,主要要求的是快速开发和复用!理解原理就好!那么下面就开始移植ST的关于Quad-SPI的驱动到我们自己的程序。

(1)确定硬件

先看原理图:


再看Datasheet,如下图:


嘿嘿!硬件搞定!证据充足!知道自己在玩啥了!知道该操作那些GPIO口和该配置那些外设了!就这么愉快的确定了!哈哈!

(2)添加STM32Cube库与Quad-SPI相关的驱动程序


然后打开控制宏进行编译调用,如下图:


将宏HAL_QSPI_MODULE_ENABLED的注释干掉,才能使工程能够调用STM32Cube库的与Quad-SPI相关的底层驱动程序。

(3)添加Flash相关头文件,并调用

因为板卡使用的Quad-SPIFlash的型号为N25Q128A,通过资料了解,其为128Mbit16MByte大小的Flash。因此要操作外部器件,就必不可少的需要操作外部设备的寄存器,往相应的寄存器写入适当的数据进行对外设的控制,所以就算是Quad-SPIFlash也是需要的,所以,需要添加其寄存器的地址和命令啦!如果没有,那就只能参考SPIFlashDatasheet进行自己编写喽!大不了苦逼一点!但是很幸运,ST已经帮我们干了这件事!所以直接使用即可!

STM32CubeF7库中的如下路径找到n25q128a文件夹,如下图:


在自己的工程Libraries目录下,创建文件夹Components,然后在此文件夹中存放从库中拷贝过来的n25q128a文件夹。如下图:


在工程中的与Quad-SPI相关的设备头文件(我的文件名是QSPI_Device.h)中添加如下包含:


各种方法可以使用!萝卜白菜各有所爱!我就是用这种方法装B

(4)STM32Cube库中,找到与Quad-SPI相关的设备驱动代码,然后Copy到工程当中,我分别存放在QSPI_Device.cQSPI_Device.h文件中。

(5)移植中断处理函数。

STM32CubeF7库的stm32f7xx_hal_qspi.c文件中,存在这样一个函数:void HAL_QSPI_IRQHandler(QSPI_HandleTypeDef *hqspi);它的作用是Quad-SPI接口和控制器的中断处理函数,它已经由ST进行编写,并提供给用户使用,所以直接移植它来使用即可。查看中断向量表,找到与QSPI相关的中断向量地址,以此作为函数名在stm32f7xx_it.c文件中建立QSPI的中断函数,如下图:


完事之后!现在可以Rebuild编译了!基本上可以达到无警告无错误。

(6)代码分析

其实关于Flash的代码都是一样的,无非这几方面:

<1>通信总线相关

A.初始化总线

B.向总线写数据

C.从总线读数据

<2>Flash存储相关

A.初始化

B.擦除Flash(包括扇区擦除、页擦除、全局擦除等)

C.Flash(字节写、扇区写、页写等)

D.Flash(读字节、扇区、页等)

通过以上,基本上就可以确定了,只要Quad-SPI总线通信配置好,能够正常的通信,那么操作Flash就是一件很简单的事了。因为一切都是移植了ST的例程,成功是必然,但是呢,还是很有必要去分析分析代码的实现。

这里主要分析初始化的过程,至于Flash的读写,无非就是操作寄存器写数据和读数据。

如下为初始化函数:


如上图可知,初始化分为5个部分,过程顺序分别如下:

A.Quad-SPI的配置恢复默认,而对于QSPIHandle.Instance = QUADSPI;语句,QUADSPI是一个基地址,并且还是Quad-SPI的基地址。定义如下:


所以恢复默认配置的是QUADSPI的寄存器的配置。

B.系统板级的初始化BSP_QSPI_MspInit(&QSPIHandle, NULL);

嘿嘿!有点意思!且看它的原型:


从上图中,分成了3部走,一堆时钟要打开(可以看到是Quad-SPI的时钟和各个GPIO的时钟)、初始化GPIO端口、启用中断,使能中断。这的确是板级初始化啊。

C.Quad-SPI初始化。

这涉及到两个结构体类型,QSPI_HandleTypeDefQSPI_InitTypeDef。原型如下:


结构体类型QSPI_HandleTypeDef基本上是关乎所有QSPI的配置了,在这里只讨论与初始化相关的结构体成员Init,它管理着所有关于QSPI通信的初始化参数配置,类型为QSPI_InitTypeDef。原型如下:


嘿嘿!成员很齐哦!Quad-SPI基于AHB总线时钟的分频数、缓存FIFO的长度阀值、采样时钟参数、Flash的长度、指定芯片选择高时钟、时钟的模式、FlashID和双闪存模式状态。

所以通过配置全局变量QSPIHandleInit成员,最后通过HAL_QSPI_Init(&QSPIHandle)函数进行初始化Quad-SPI设备。那么且看它是如何配置的:


首先是关于Quad-SPI时钟的配置,分频数为1,那么就得看看系统架构图了,如下图:


从上图可看到Quad-SPI的时钟来源于一个32AHB总线,从前面系统的配置可知,AHB总线的频率被配置为最高频率216MHz,再看时钟树,如图:


可以看到了时钟的来源走线,再看看Quad-SPI自己对时钟的要求:


嘿嘿!一切都清楚了,Quad-SPI的时钟分频系数在寄存器QUADSPI_CR[31...24]位设置,在程序中被设置值为1,即为2分频,即为261MHz/2=108MHz

缓存FIFO的长度阀值的设置:还是QUADSPI_CR寄存器,并且是QUADSPI_CR[12...8]位,且看下图解释:


上图已详细解释,在程序中,阀值设为4

采用时钟参数的设置,还是QUADSPI_CR寄存器,并且是QUADSPI_CR[4]位,如下图:


在程序中设置了半个周期转向移位。

最后就是Flash的长度、指定芯片选择高时钟、时钟的模式、FlashID和双闪存模式状态的配置了,其实无非也就是配置寄存器,当然,在调用库函数时,这些寄存器是看不到的,在这里只是提供一个分析的方法。具体参考程序、参考手册和Quad-SPIFlashDatasheet即可。配置好相应参数之后,直接调用HAL_QSPI_Init(&QSPIHandle)函数初始化,就完事了。

D.重置QSPI内存。

先看看原型,如下图:


从上图可以看到,重置内存分为了四个部分,而且出现了一个新的结构体类型QSPI_CommandTypeDef,且看原型:


从原型和它的注释,基本可以确定此结构体类型是QSPI命令结构定义类型,也就是说所有的命令都将使用此结构体进行玩耍。而且从QSPI_ResetMemory(QSPI_HandleTypeDef *hqspi)函数的原型可知,当配置好QSPI_CommandTypeDef的变量值之后,就是一堆发送命令是语句,并且每次发送都会改变QSPI_CommandTypeDef结构成员Instruction的值。呵呵!这有点意思!那么先看看QSPI_CommandTypeDef结构类型的成员各自代表的含义:


OK!基本上可以确定了。在函数uint8_t QSPI_ResetMemory(QSPI_HandleTypeDef *hqspi)的最前面先配置结构体类型QSPI_CommandTypeDef的局部变量s_command的成员取值,然后就是发送命令了。

关于命令成员的配置,也是QSPI_ResetMemory(QSPI_HandleTypeDef *hqspi)函数的第一部分,配置如下:


我已经详细注释,不在多说!但是可以很明确一点是,其他配置不变,当需要发送不同指令时,更改结构体成员Instruction的取值即可。所以QSPI_ResetMemory(QSPI_HandleTypeDef *hqspi)第二步要干的事是复位Quad-SPIFlas器件,第三步要干的事是复位整个Quad-SPIFlash的存储空间,使其恢复默认值,其实我觉得可以理解成整片擦除Flash,默认值通常为0XFF吧。

关于QSPI_ResetMemory(QSPI_HandleTypeDef *hqspi)函数的第四步,是配置STM32F7Quad-SPI控制器的工作方式,而且从调用可以看出是配置为自动轮询方式工作,并且要等待Flash准备好工作。先看看函数的原型:


嘿嘿!有点意思,又多出了一个结构体类型QSPI_AutoPollingTypeDef的局部变量s_config,而且根据注释可知,此结构体类型为自动轮询工作方式的结构类型。而关于函数uint8_t QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi, uint32_t Timeout)的原型内容,也无非是准备并配置好命令,然后命令结构和自动轮询结构一起,配置Quad-SPI控制器和Quad-SPIFlash。所以现在主要值得研究的是新出现的QSPI_AutoPollingTypeDef结构。且看结构原型:


呵呵!每个成员所代表的意义就如上了,这就还需要另外的证据,如下:


寄存器就不分析了!!且看它的功能配置讲解即可。嘿嘿!证据就充足了。那么关于其在函数中的配置也就没必要再多解释了,再看看设置函数的原型,其实调用的HAL库,但是不管它,我们只要知道他的作用即可,如下:


嘿嘿!函数功能是在阻塞模式下配置QSPI自动轮询方式,并且可配置超时机制。关于参数就不在多说,看注释和配置就可知。现在基本就明白了,这一步的工作主要是配置STM32F7Quad-SPI控制器接口也Quad-SPIFlash的通信机制。嘿嘿!这一步就基本完事了。

E.配置虚拟存储周期

说实话,这一步的配置,如果没有仔细的研究研究STM32F7系列MCU的参考手册(关于Quad-SPI部分),还真不太理解这是啥意思,“虚拟”!呵呵!有点意思!先看看函数原型吧!这也是初始化配置的最后一步配置(第5步),如下图:



貌似这个函数要干的事不少啊!整整分为7个步骤来走!而且一眼可以看出来它所要做的事。步骤顺序如:配置命令结构变量并将指令设置为READ_VOL_CFG_REG_CMD(意为读取Quad-SPIFlashvolatile寄存器的值,指令为RDVCR)、发生READ_VOL_CFG_REG_CMD指令给Quad-SPIFlashn25q128a)、接收要读取的数据(RDVCR寄存器的值)、使能对Quad-SPIFlash的写操作、更新Quad-SPIFlashvolatile配置寄存器(修改指令值为WRITE_VOL_CFG_REG_CMD)、发送volatile寄存器写指令WRITE_VOL_CFG_REG_CMDQuad-SPIflash(指令为WRVCR)、最后做数据传输。

从以上步骤解释基本上就可以确定,主要是对Quad-SPIFlashn25q128a)的volatile配置寄存器进行读和写,那么下面看看这个寄存器的解释,通过查找n25q128aDatasheet有:


指令就如上了,那么再看看关于volatile配置寄存器的解释:



寄存器都分解如上了!没什么可解释的,那么还有一个问题是,这操作的到底是啥东东哇!弄了半天意思还是有点模模糊糊的!其实呢,这里操作的是5个阶段中的空周期阶段,如下图:


没错!就是它!那么搞的这么复杂,到底是要干嘛呢?其实在参考手册中已经有了解释,如下图:


所以,在uint8_t QSPI_DummyCyclesCfg(QSPI_HandleTypeDef *hqspi)函数的最后有如下调用:


其所调用的参数hqspi为形参、HAL_QPSI_TIMEOUT_DEFAULT_VALUE为设定超时的时间值、

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

0

主题

113

帖子

46

积分

新手上路

Rank: 1

积分
46
沙发
发表于 2015-10-5 09:52 AM | 只看该作者

注:原帖装不下了!在此做最后补充,如下:

其所调用的参数hqspi为形参、HAL_QPSI_TIMEOUT_DEFAULT_VALUE为设定超时的时间值、reg为未初始化,未操作的一个局部变量,而此函数的目的是发送数据,所以要发送的数据即为reg,而reg在此之前未操作过,其实我认为就是在浪费时间,空周期嘛!发送函数原型为:

有图有真相!不再多说!

到这里,整一个关于Quad-SPIQuad-SPIFlash的初始化配置就基本完成了,这也是本文的重点阐述内容。

总结:本文比较详细的讲解了关于Quad-SPI接口、控制器和Quad-SPIFlash初始化配置的全过程,通过理解配置的过程来比较详细的了解和理解关于Quad-SPI的使用。本文的分析完全是代码跟踪分析,具体关于Quad-SPIFlash的使用现象在下一篇文章呈现。

 

 

声明:以上内容纯属自己理解,如若有错误或者不足,还望赐教!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

368

主题

2017

帖子

4268

积分

论坛元老

Rank: 8Rank: 8

积分
4268
板凳
发表于 2015-10-5 10:15 AM | 只看该作者
牛逼,教主威猛。。。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

38

主题

120

帖子

145

积分

注册会员

Rank: 2

积分
145
地板
发表于 2015-10-5 12:51 PM | 只看该作者
棒棒哒。。。
回复 支持 反对

使用道具 举报

0

主题

73

帖子

6

积分

新手上路

Rank: 1

积分
6
5#
发表于 2015-10-6 04:36 PM | 只看该作者
咳咳!请叫我阁主!哈哈!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则