【STM32H7教程】第78章 STM32H7的QSPI总线基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第78章 STM32H7的QSPI总线基础知识和HAL库API
本章节为大家讲解QSP(Quad Serial peripheral interface)总线的基础知识和对应的HAL库API。
目录
第78章 STM32H7的QSPI总线基础知识和HAL库API
78.1 初学者重要提示
78.2 QSPI总线基础知识
78.2.1 QSPI总线的硬件框图
78.2.2 QSPI的时钟源
78.2.3 QSPI 命令序列
78.2.4 QSPI的1线,2线,4线,SDR和DDR模式
78.2.5 QSPI间接模式,查询模式和内存映射
78.3 QSPI总线的HAL库用法
78.3.1 QSPI总线结构体QUADSPI_TypeDef
78.3.2 QSPI总线初始化结构体QSPI_InitTypeDef
78.3.3 QSPI总线句柄结构体QSPI_HandleTypeDef
78.3.4 QSPI命令结构体QSPI_CommandTypeDef
78.3.5 QSPI自动查询结构体QSPI_AutoPollingTypeDef
78.3.6 QSPI内存映射结构体QSPI_MemoryMappedTypeDef
78.4 QSPI总线源文件stm32h7xx_hal_qspi.c
78.4.1 函数HAL_QSPI_Init
78.4.2 函数HAL_QSPI_DeInit
78.4.3 函数HAL_QSPI_Command
78.4.4 函数HAL_QSPI_Command_IT
78.4.5 函数HAL_QSPI_AutoPolling
78.4.6 函数HAL_QSPI_AutoPolling_IT
78.4.7 函数HAL_QSPI_Transmit
78.4.8 函数HAL_QSPI_Receive
78.4.9 函数HAL_QSPI_Transmit_DMA
78.4.10 函数HAL_QSPI_Receive_DMA
78.4.11 函数HAL_QSPI_MemoryMapped
78.5 总结
78.1 初学者重要提示
- QSPI接口,可以做1线,2线或者4线使用。
- 注意,QSPI接口不分主从,QSPI仅用于主控。
- 可以单独使用BANK1外接一个Flash,也可以单独使用BANK2外接一个Flash,不可以BANK1和BANK2同时独立使用。但可以两个BANK合起来做双BANK(也称双Flash,即dual flash)使用,即8线模式。
- STM32H7可用的单线,双线,四线和八线SPI QSPI OctaFlash等涵盖各大厂商,含性能测评:http://www.armbbs.cn/forum.php?mod=viewthread&tid=89669
- QSPI Flash应用笔记:http://www.armbbs.cn/forum.php?mod=viewthread&tid=99489
78.2 QSPI总线基础知识
78.2.1 QSPI总线的硬件框图
认识一个外设,最好的方式就是看它的框图,方便我们快速的了解QSPI的基本功能,然后再看手册了解细节。
BANK1外接Flash
BANK1和BANK2都外接Flash:
通过这个框图,我们可以得到如下信息:
- quadspi_ker_ck时钟输入
内核时钟。
- quadspi_hclk时钟输入
为寄存器提供时钟。
- quadspi_it输出信号
QSPI全局中断。
- quadspi_ft_trg输出信号
MDMA的QSPI FIFO阀值触发信号
- quadspi_tc_trg输出信号
MDMA的QSPI传输完成触发信号
- CLK
为外接Flash提供的时钟。为外接的两块Flash同时提供时钟。
- BK1_IO0/SO
在2线或者4线模式中作为双向IO,1线模式作为单向输出,供Flash1使用。
- BK1_IO1/SI
在2线或者4线模式中作为双向IO,1线模式作为单向输入,供Flash1使用。
- BK1_IO2
在4线模式中作为双向IO,供Flash1使用。
- BK1_IO3
在4线模式中作为双向IO,供Flash1使用。
- BK12_IO0/SO
在2线或者4线模式中作为双向IO,1线模式作为单向输出,供Flash2使用。
- BK2_IO1/SI
在2线或者4线模式中作为双向IO,1线模式作为单向输入,供Flash2使用。
- BK2_IO2
在4线模式中作为双向IO,供Flash2使用。
- BK2_IO3
在4线模式中作为双向IO,供Flash2使用。
- BK1_nCS
片选信号,低电平有效,供Flash1使用。如果工作在双bank模式下,也可用于Flash2。
- BK2_nCS
片选信号,低电平有效,供Flash1使用。如果工作在双bank模式下,也可用于Flash1。
78.2.2 QSPI的时钟源
这个知识点在初学的时候容易忽视,所以我们这里整理下。
STM32H7主频在400MHz下,QSPI的最高时钟是200MHz,可以选择的时钟源如下:
我们的程序中默认是采用的HCLK3作为QSPI时钟。
(QSPI Flash应用笔记:http://www.armbbs.cn/forum.php?mod=viewthread&tid=99489 )
78.2.3 QSPI 命令序列
QSPI的命令序列主要包括以下几个阶段:指令阶段、地址阶段、交替字节阶段、空指令阶段和数据这五个阶段,任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。
- 指令阶段
在此阶段,将命令(8位指令)发送到Flash,允许发送任何值。用户可以简单地将所需的命令写在指令字段中。根据软件和硬件配置,可以1线,2线或者4线方式发送。在某些只发送地址的案例中,指令阶段可以跳过。
- 地址阶段
在此阶段,将地址发送到Flash,从指定的地址读取或写入数据。 地址阶段是完全可配置的,允许发送1、2、3或4个字节的地址。在间接模式和自动轮询模式下,用户可以简单地将所需的地址写入QUADSPI_AR寄存器。据软件和硬件配置,可以1线,2线或者4线方式方式发送。 在一些不需要地址的情况下,可以跳过地址阶段。
- 交替字节阶段
QSPI接口支持的一个额外阶段,具有更大的灵活性。它是通常用于控制操作模式。交替字节阶段是完全可配置的,并允许发送一,二,三或四字节。
- 空周期阶段
在高速时钟下运行时,此阶段可以确保有足够的“周转”时间从输出模式切换到输入模式。
- 数据阶段
这个阶段实现数据的收发。
78.2.4 QSPI的1线,2线,4线,SDR和DDR模式
这里所说的线是指通信阶段使用的数据线个数。
- 1线(SingleSPI,虽然是一发一收,但属于1线方式)
发送用的数据线BK1_IO0/SO(BK2_IO0/SO),接收用的数据线BK1_IO1/SI(BK2_IO1/SI)
1线模式下,所有线处于的状态:
(1) BK1_IO0 / SO(BK2_IO0 / SO)处于输出模式。
(2) BK1_IO1 / SI(BK2_IO1 / SI)处于输入模式(高阻抗)。
(3) BK1_IO2(BK2_IO2)处于输出模式并强制置0。
(4) BK1_IO3(BK2_IO3)处于输出模式并强制置1。
- 2线(Dual-SPI)
同时使用BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1)做输入输出。
2线模式下,所有线处于的状态:
(1) BK1_IO0 (BK2_IO0 )和BK1_IO1(BK2_IO1)读取时处于输入(高阻)。其它情况下为输出。
(2) BK1_IO2(BK2_IO2)处于输出模式并强制置0。
(3) BK1_IO3(BK2_IO3)处于输出模式并强制置1
- 4线(Quad-SPI)
同时使用BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1),BK1_IO2(BK2_IO2),BK1_IO3(BK2_IO3)做输入输出。
4线模式下,当读取数据时,所有线处于输入(高阻),其它情况作为输出。
- SDR
在 SDR 模式下,当QSPI驱动BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1),BK1_IO2(BK2_IO2),BK1_IO3(BK2_IO3)信号时,这些信号仅在 CLK的下降沿发生转变。
- DDR
在 SDR 模式下,当QSPI驱动BK1_IO0(BK2_IO0),BK1_IO1(BK2_IO1),BK1_IO2(BK2_IO2),BK1_IO3(BK2_IO3)信号时,这些信号在CLK的上升沿和下降沿发生转变。
- 双BANK(双Flash)
双闪存就是将QSPI的两个BANK分别接一个QSPI Flash,然后时钟公用,片选公用(也可以不公用),从而实现8线模式。
78.2.5 QSPI间接模式,查询模式和内存映射
QSPI支持在以下三种模式下工作:
1、 间接模式:
这里所谓的间接模式是指寄存器方式访问外设,就跟我们操作串口外设一样。间接模式主要用于以下场合:
- 用于读取,写入,擦除和配置QSPI Flash。
- 如果不需要AHB总线访问 QSPI Flash(在内存映射模式用)。
- CPU或者DMA通过QSPI数据寄存器执行所有操作。
在间接模式下,所有操作均通过QSPI寄存器执行,含读取和写入操作都由软件管理。 QSPI接口类似于经典的SPI接口。传输的数据通过数据寄存器与FIFO。在在此模式下,可以从大容量的外部Flash读取数据或向外部Flash写入数据,可以支持到4GB容量。
如果进行擦除或编程操作,则必须使用间接模式,并且所有操作必须由软件处理。在这种情况下,建议使用状态轮询模式,然后轮询闪存内部的状态寄存器以了解何时编程或擦除操作完成。
2、 状态轮询模式
在以下情况下使用状态轮询模式:
- 读取QSPI Flash状态寄存器。
- 在操作结束时自动轮询状态寄存器。
可以自动轮询内存中的指定寄存器并减轻CPU负担,例如检查擦除操作何时完成。QSPI也支持定期查询QSPI Flash,并且可以屏蔽返回的数据位,将所选位与所需值进行比较,结果比较可以通过下面两种方式处理:
- AND与操作模式:如果所有选定位都匹配,则产生中断。
- OR或操作模式:如果所选位之一匹配,则产生中断
发生匹配时,QSPI可以自动停止。
3、 内存映射模式
在以下情况下使用内存映射模式:
- 用于阅读操作。
- 像使用内部Flash一样使用外部QSPI Flash, 任何AHB总线主控都可以自主读取数据。
- 用于从外部QSPI Flash执行代码。
QSPI接口能够管理多达256 MB的内存,在内存映射模式下地址范围是0x9000 0000到0x9FFF FFFF。
78.3 QSPI总线的HAL库用法
78.3.1 QSPI总线结构体QUADSPI_TypeDef
QSPI总线相关的寄存器是通过HAL库中的结构体QUADSPI_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:
typedef struct
{__IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */__IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */__IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */__IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */__IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */__IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */__IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */__IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */__IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */__IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */__IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */__IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */__IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */
} QUADSPI_TypeDef;
这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。
__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:
#define __O volatile /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
下面我们看下QSPI的定义,在stm32h743xx.h文件。
#define QSPI_BASE ((uint32_t)0x90000000) /*!< Base address of : QSPI memories accessible over AXI */#define PERIPH_BASE (0x40000000UL)
#define D1_AHB1PERIPH_BASE (PERIPH_BASE + 0x12000000)#define QSPI_R_BASE (D1_AHB1PERIPH_BASE + 0x5000)
#define DLYB_QSPI_BASE (D1_AHB1PERIPH_BASE + 0x6000)#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) <----- 展开这个宏,(FLASH_TypeDef *)0x52005000
#define DLYB_QUADSPI ((DLYB_TypeDef *) DLYB_QSPI_BASE)
我们访问QSPI的CR寄存器可以采用这种形式:QUADSPI->CR= 0。
78.3.2 QSPI总线初始化结构体QSPI_InitTypeDef
下面是QSPI总线的初始化结构体:
typedef struct
{uint32_t ClockPrescaler; uint32_t FifoThreshold; uint32_t SampleShifting; uint32_t FlashSize; uint32_t ChipSelectHighTime; uint32_t ClockMode; uint32_t FlashID; uint32_t DualFlash;
}QSPI_InitTypeDef;
下面将结构体成员逐一做个说明:
- ClockPrescaler
设置时钟分频,参数范围0到255。特别注意,这里是针对HCLK3作为QSPI时钟来说的。
- FifoThreshold
用于设置FIFO阀值,仅用于间接模式,参数范围1到32,单位字节。
- SampleShifting
QUADSPI在FLASH驱动信号后可以选择再过半个CLK周期后才对FLASH数据采样,这有利于推迟数据采样。支持的参数如下:
#define QSPI_SAMPLE_SHIFTING_NONE ((uint32_t)0x00000000U)
#define QSPI_SAMPLE_SHIFTING_HALFCYCLE ((uint32_t)QUADSPI_CR_SSHIFT)
- FlashSize
Flash大小是2^(FlashSize + 1),单位字节。
间接模式下,最大支持的Flash大小是4GB,内存映射模式,最大支持256MB。
#define SPI_POLARITY_LOW (0x00000000UL)
#define SPI_POLARITY_HIGH SPI_CFG2_CPOL
- ChipSelectHighTime
命令之间的CS片选至少保持的高电平时钟周期ChipSelectHighTime+1。支持的参数如下:
#define QSPI_CS_HIGH_TIME_1_CYCLE ((uint32_t)0x00000000U)
#define QSPI_CS_HIGH_TIME_2_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_0)
#define QSPI_CS_HIGH_TIME_3_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_1)
#define QSPI_CS_HIGH_TIME_4_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_0 | QUADSPI_DCR_CSHT_1)
#define QSPI_CS_HIGH_TIME_5_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_2)
#define QSPI_CS_HIGH_TIME_6_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_2 | QUADSPI_DCR_CSHT_0)
#define QSPI_CS_HIGH_TIME_7_CYCLE ((uint32_t)QUADSPI_DCR_CSHT_2 | QUADSPI_DCR_CSHT_1)
#define QSPI_CS_HIGH_TIME_8_CYCLE ((uint32_t)QUADSPI_DCR_CSHT)
- FlashID
用于选择要操作的BANK,即用BANK1还是BANK2操作Flash。
#define QSPI_FLASH_ID_1 ((uint32_t)0x00000000)
#define QSPI_FLASH_ID_2 ((uint32_t)QUADSPI_CR_FSEL)
- DualFlash
用于选择是否使用双BANK。
#define QSPI_DUALFLASH_ENABLE ((uint32_t)QUADSPI_CR_DFM) /*!<Dual-flash mode enabled*/
#define QSPI_DUALFLASH_DISABLE ((uint32_t)0x00000000) /*!<Dual-flash mode disabled*/
78.3.3 QSPI总线句柄结构体QSPI_HandleTypeDef
下面是QSPI总线的初始化结构体:
typedef struct
{QUADSPI_TypeDef *Instance; /* QSPI registers base address */QSPI_InitTypeDef Init; /* QSPI communication parameters */uint8_t *pTxBuffPtr; /* Pointer to QSPI Tx transfer Buffer */__IO uint32_t TxXferSize; /* QSPI Tx Transfer size */__IO uint32_t TxXferCount; /* QSPI Tx Transfer Counter */uint8_t *pRxBuffPtr; /* Pointer to QSPI Rx transfer Buffer */__IO uint32_t RxXferSize; /* QSPI Rx Transfer size */__IO uint32_t RxXferCount; /* QSPI Rx Transfer Counter */MDMA_HandleTypeDef *hmdma; /* QSPI Rx/Tx MDMA Handle parameters */__IO HAL_LockTypeDef Lock; /* Locking object */__IO HAL_QSPI_StateTypeDef State; /* QSPI communication state */__IO uint32_t ErrorCode; /* QSPI Error code */uint32_t Timeout; /* Timeout for the QSPI memory access */
}QSPI_HandleTypeDef;
下面将结构体成员做个说明:
- QUADSPI_TypeDef *Instance
这个参数是寄存器的例化,方便操作寄存器,比如使能QUADSPI。
SET_BIT(QUADSPI ->CR, QUADSPI_CR_EN)。
- QSPI_InitTypeDef Init
这个参数是用户接触最多的,在本章节3.2小节已经进行了详细说明。
- MDMA_HandleTypeDef *hmdma
用于QSPI句柄关联MDMA句柄,方便操作调用。
- 其它参数
其它参数基本都是在函数内部调用,用户基本不用管。
78.3.4 QSPI命令结构体QSPI_CommandTypeDef
下面是QSPI总线的命名结构体:
typedef struct
{uint32_t Instruction; uint32_t Address; uint32_t AlternateBytes; uint32_t AddressSize; uint32_t AlternateBytesSize; uint32_t DummyCycles; uint32_t InstructionMode; uint32_t AddressMode; uint32_t AlternateByteMode; uint32_t DataMode; uint32_t NbData; uint32_t DdrMode; uint32_t DdrHoldHalfCycle; uint32_t SIOOMode;
}QSPI_CommandTypeDef;
下面将结构体成员逐一做个说明:
- Instruction
设置要发送的指令,参数范围0x00到0xFF。
- Address
设置要发送的地址,地址由是1个字节到4个字节来表示,参数范围0x0 到 0xFFFFFFFF。
- AddressSize
地址大小,即表示此地址需要的字节数,支持的参数如下:
#define QSPI_ADDRESS_8_BITS ((uint32_t)0x00000000) /*!<8-bit address*/
#define QSPI_ADDRESS_16_BITS ((uint32_t)QUADSPI_CCR_ADSIZE_0) /*!<16-bit address*/
#define QSPI_ADDRESS_24_BITS ((uint32_t)QUADSPI_CCR_ADSIZE_1) /*!<24-bit address*/
#define QSPI_ADDRESS_32_BITS ((uint32_t)QUADSPI_CCR_ADSIZE) /*!<32-bit address*/
- AlternateBytesSize
交替字节大小,支持的参数如下:
#define QSPI_ALTERNATE_BYTES_8_BITS ((uint32_t)0x00000000) /*!<8-bit alternate bytes*/
#define QSPI_ALTERNATE_BYTES_16_BITS ((uint32_t)QUADSPI_CCR_ABSIZE_0) /*!<16-bit alternate bytes*/
#define QSPI_ALTERNATE_BYTES_24_BITS ((uint32_t)QUADSPI_CCR_ABSIZE_1) /*!<24-bit alternate bytes*/
#define QSPI_ALTERNATE_BYTES_32_BITS ((uint32_t)QUADSPI_CCR_ABSIZE) /*!<32-bit alternate bytes*/
- DummyCycles
执行空周期个数,参数范围0到31:
- InstructionMode
指令阶段需要几线模式:
#define QSPI_INSTRUCTION_NONE ((uint32_t)0x00000000) /*!<No instruction*/
#define QSPI_INSTRUCTION_1_LINE ((uint32_t)QUADSPI_CCR_IMODE_0) /*!<Instruction on a single line*/
#define QSPI_INSTRUCTION_2_LINES ((uint32_t)QUADSPI_CCR_IMODE_1) /*!<Instruction on two lines*/
#define QSPI_INSTRUCTION_4_LINES ((uint32_t)QUADSPI_CCR_IMODE) /*!<Instruction on four lines*/
- AddressMode
地址阶段需要几线模式:
#define QSPI_ADDRESS_NONE ((uint32_t)0x00000000) /*!<No address*/
#define QSPI_ADDRESS_1_LINE ((uint32_t)QUADSPI_CCR_ADMODE_0) /*!<Address on a single line*/
#define QSPI_ADDRESS_2_LINES ((uint32_t)QUADSPI_CCR_ADMODE_1) /*!<Address on two lines*/
#define QSPI_ADDRESS_4_LINES ((uint32_t)QUADSPI_CCR_ADMODE) /*!<Address on four lines*/
- AlternateByteMode
交替字节阶段需要几线模式:
#define QSPI_ALTERNATE_BYTES_NONE ((uint32_t)0x00000000) /*!<No alternate bytes*/
#define QSPI_ALTERNATE_BYTES_1_LINE ((uint32_t)QUADSPI_CCR_ABMODE_0) /*!<Alternate bytes on a single line*/
#define QSPI_ALTERNATE_BYTES_2_LINES ((uint32_t)QUADSPI_CCR_ABMODE_1) /*!<Alternate bytes on two lines*/
#define QSPI_ALTERNATE_BYTES_4_LINES ((uint32_t)QUADSPI_CCR_ABMODE) /*!<Alternate bytes on four lines*/
- DataMode
数据阶段需要几线模式:
#define QSPI_DATA_NONE ((uint32_t)0X00000000) /*!<No data*/
#define QSPI_DATA_1_LINE ((uint32_t)QUADSPI_CCR_DMODE_0) /*!<Data on a single line*/
#define QSPI_DATA_2_LINES ((uint32_t)QUADSPI_CCR_DMODE_1) /*!<Data on two lines*/
#define QSPI_DATA_4_LINES ((uint32_t)QUADSPI_CCR_DMODE) /*!<Data on four lines*/
- NbData
要传输的数据大小,参数范围0 到 0xFFFFFFFF,如果设置为0表示不定长,直到存储器末尾。
- DdrMode
用于设置是否使能DDR模式。数据阶段,交替字节阶段和数据传输阶段可以使用DDR模式。支持的参数如下:
#define QSPI_DDR_MODE_DISABLE ((uint32_t)0x00000000) /*!<Double data rate mode disabled*/
#define QSPI_DDR_MODE_ENABLE ((uint32_t)QUADSPI_CCR_DDRM) /*!<Double data rate mode enabled*/
- DdrHoldHalfCycle
DDR模式下,用于设置延迟半个时钟周期再做数据输出。
#define QSPI_DDR_HHC_ANALOG_DELAY ((uint32_t)0x00000000)
#define QSPI_DDR_HHC_HALF_CLK_DELAY ((uint32_t)QUADSPI_CCR_DHHC)
- SIOOMode
设置仅发送一次指令还是每次操作都发送指令,支持的参数如下:
#define QSPI_SIOO_INST_EVERY_CMD ((uint32_t)0x00000000)
#define QSPI_SIOO_INST_ONLY_FIRST_CMD ((uint32_t)QUADSPI_CCR_SIOO)
78.3.5 QSPI自动查询结构体QSPI_AutoPollingTypeDef
下面是QSPI总线自动查询结构体:
typedef struct
{uint32_t Match; uint32_t Mask; uint32_t Interval; uint32_t StatusBytesSize; uint32_t MatchMode; uint32_t AutomaticStop;
}QSPI_AutoPollingTypeDef;
下面将结构体成员逐一做个说明:
- Match
参数成员Mask屏蔽了状态寄存器的某些位后,状态寄存器的值与此参数成员值做匹配。参数范围0x0 到 0xFFFFFFFF。
- Mask
用于设置屏蔽位,比如Mask = 0x01,表示仅保留bit0的数值,其它bit忽略。参数范围0x0 到 0xFFFFFFFF。
- Interval
指定自动轮询阶段两次读取之间的时钟周期数。参数范围0 到 0xFFFF。
- StatusBytesSize
用于设置状态寄存器大小,参数范围1到4个字节。
- MatchMode
参数成员Mask屏蔽了状态寄存器的某些位后,状态寄存器完全与参数成员Match一样(与操作的含义)或者任意一个bit的值与参数成员Match中一个bit的值一样(或操作的含义),比如Mask = 0x01,Match=0x00,MatchMode=与操作,表示不断查询状态寄存器bit0,等待其为0。
MatchMode支持的参数成员如下:
#define QSPI_MATCH_MODE_AND ((uint32_t)0x00000000) /*!<AND match mode between unmasked bits*/
#define QSPI_MATCH_MODE_OR ((uint32_t)QUADSPI_CR_PMM) /*!<OR match mode between unmasked bits*/
- AutomaticStop
当与参数成员Match匹配时,自动停止检测。
78.3.6 QSPI内存映射结构体QSPI_MemoryMappedTypeDef
下面是QSPI总线的内存映射结构体:
typedef struct
{uint32_t TimeOutPeriod; uint32_t TimeOutActivation;
}QSPI_MemoryMappedTypeDef;
下面将结构体成员逐一做个说明:
- TimeOutPeriod
FIFO满时,释放芯片选择之前要等待的时钟周期数。参数范围0到0xFFFF。
- TimeOutActivation
指定是否启用超时计数器以释放芯片选择,支持的参数成员如下:
#define QSPI_TIMEOUT_COUNTER_DISABLE ((uint32_t)0x00000000)
#define QSPI_TIMEOUT_COUNTER_ENABLE ((uint32_t)QUADSPI_CR_TCEN)
78.4 QSPI总线源文件stm32h7xx_hal_qspi.c
此文件涉及到的函数较多,这里把几个常用的函数做个说明:
- HAL_QSPI_Init
- HAL_QSPI_DeInit
- HAL_QSPI_Command
- HAL_QSPI_Command_IT
- HAL_QSPI_AutoPolling
- HAL_QSPI_AutoPolling_IT
- HAL_QSPI_Transmit
- HAL_QSPI_Receive
- HAL_QSPI_Transmit_DMA
- HAL_QSPI_Receive_DMA
- HAL_QSPI_MemoryMapped
78.4.1 函数HAL_QSPI_Init
函数原型:
HAL_StatusTypeDef HAL_QSPI_Init(QSPI_HandleTypeDef *hqspi)
{HAL_StatusTypeDef status;uint32_t tickstart = HAL_GetTick();/* 检测句柄是否有效 */if(hqspi == NULL){return HAL_ERROR;}/* 检查参数是否有效 */assert_param(IS_QSPI_ALL_INSTANCE(hqspi->Instance));assert_param(IS_QSPI_CLOCK_PRESCALER(hqspi->Init.ClockPrescaler));assert_param(IS_QSPI_FIFO_THRESHOLD(hqspi->Init.FifoThreshold));assert_param(IS_QSPI_SSHIFT(hqspi->Init.SampleShifting));assert_param(IS_QSPI_FLASH_SIZE(hqspi->Init.FlashSize));assert_param(IS_QSPI_CS_HIGH_TIME(hqspi->Init.ChipSelectHighTime));assert_param(IS_QSPI_CLOCK_MODE(hqspi->Init.ClockMode));assert_param(IS_QSPI_DUAL_FLASH_MODE(hqspi->Init.DualFlash));if (hqspi->Init.DualFlash != QSPI_DUALFLASH_ENABLE ){assert_param(IS_QSPI_FLASH_ID(hqspi->Init.FlashID));}if(hqspi->State == HAL_QSPI_STATE_RESET){#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)/* 复位状态,设置默认的回调 */hqspi->ErrorCallback = HAL_QSPI_ErrorCallback;hqspi->AbortCpltCallback = HAL_QSPI_AbortCpltCallback;hqspi->FifoThresholdCallback = HAL_QSPI_FifoThresholdCallback;hqspi->CmdCpltCallback = HAL_QSPI_CmdCpltCallback;hqspi->RxCpltCallback = HAL_QSPI_RxCpltCallback;hqspi->TxCpltCallback = HAL_QSPI_TxCpltCallback;hqspi->StatusMatchCallback = HAL_QSPI_StatusMatchCallback;hqspi->TimeOutCallback = HAL_QSPI_TimeOutCallback;if(hqspi->MspInitCallback == NULL){hqspi->MspInitCallback = HAL_QSPI_MspInit;}/* 初始化底层硬件 */hqspi->MspInitCallback(hqspi);
#else/* 初始化: GPIO, CLOCK */HAL_QSPI_MspInit(hqspi);
#endif/* 配置QSPI内存访问默认的溢出时间 */HAL_QSPI_SetTimeout(hqspi, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);}/* 配置QSPI FIFO阀值 */MODIFY_REG(hqspi->Instance->CR, QUADSPI_CR_FTHRES,((hqspi->Init.FifoThreshold - 1U) << QUADSPI_CR_FTHRES_Pos));/* 等BUSY标志复位 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);if(status == HAL_OK){/* 配置QSPI时钟分频和采样延迟 */
MODIFY_REG(hqspi->Instance->CR, (QUADSPI_CR_PRESCALER | QUADSPI_CR_SSHIFT | QUADSPI_CR_FSEL |QUADSPI_CR_DFM),((hqspi->Init.ClockPrescaler << QUADSPI_CR_PRESCALER_Pos) |hqspi->Init.SampleShifting | hqspi->Init.FlashID | hqspi->Init.DualFlash));/* 配置QSPI Flash大小,CS片选高电平时间和时钟模式 */MODIFY_REG(hqspi->Instance->DCR, (QUADSPI_DCR_FSIZE | QUADSPI_DCR_CSHT | QUADSPI_DCR_CKMODE),((hqspi->Init.FlashSize << QUADSPI_DCR_FSIZE_Pos) |hqspi->Init.ChipSelectHighTime | hqspi->Init.ClockMode));/* 时钟QSPI外设 */__HAL_QSPI_ENABLE(hqspi);/* 设置QSPI无错误代码 */hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;/* 初始化QSPI状态就绪 */hqspi->State = HAL_QSPI_STATE_READY;}/* 返回状态信息 */return status;
}
函数描述:
此函数用于初始化QSPI。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数,详见本章3.3小节。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。
注意事项:
- 函数HAL_QSPI_MspInit用于初始化QSPI的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
- 如果形参hqspi的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量QSPI_HandleTypeDef SpiHandle。
对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_QSPI_STATE_RESET = 0x00U。
解决办法有三
方法1:用户自己初始化QSPI和涉及到的GPIO等。
方法2:定义QSPI_HandleTypeDef QspiHandle为全局变量。
方法3:下面的方法
if(HAL_QSPI_DeInit(&QspiHandle) != HAL_OK)
{Error_Handler();
}
if(HAL_QSPI_Init(&QspiHandle) != HAL_OK)
{Error_Handler();
}
使用举例:
/*
*********************************************************************************************************
* 函 数 名: bsp_InitQSPI_W25Q256
* 功能说明: QSPI Flash硬件初始化,配置基本参数
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitQSPI_W25Q256(void)
{/* 复位QSPI */QSPIHandle.Instance = QUADSPI;if (HAL_QSPI_DeInit(&QSPIHandle) != HAL_OK){Error_Handler(__FILE__, __LINE__);}/* 设置时钟速度,QSPI clock = 200MHz / (ClockPrescaler+1) = 100MHz */QSPIHandle.Init.ClockPrescaler = 1; /* 设置FIFO阀值,范围1 - 32 */QSPIHandle.Init.FifoThreshold = 32; /* QUADSPI在FLASH驱动信号后过半个CLK周期才对FLASH驱动的数据采样。在外部信号延迟时,这有利于推迟数据采样。*/QSPIHandle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; /*Flash大小是2^(FlashSize + 1) = 2^25 = 32MB *///QSPI_FLASH_SIZE - 1; 需要扩大一倍,否则内存映射方位最后1个地址时,会异常。QSPIHandle.Init.FlashSize = QSPI_FLASH_SIZE; /* 命令之间的CS片选至少保持2个时钟周期的高电平 */QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;/*MODE0: 表示片选信号空闲期间,CLK时钟信号是低电平MODE3: 表示片选信号空闲期间,CLK时钟信号是高电平*/QSPIHandle.Init.ClockMode = QSPI_CLOCK_MODE_0;/* QSPI有两个BANK,这里使用的BANK1 */QSPIHandle.Init.FlashID = QSPI_FLASH_ID_1;/* V7开发板仅使用了BANK1,这里是禁止双BANK */QSPIHandle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;/* 初始化配置QSPI */if (HAL_QSPI_Init(&QSPIHandle) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.4.2 函数HAL_QSPI_DeInit
函数原型:
HAL_StatusTypeDef HAL_QSPI_DeInit(QSPI_HandleTypeDef *hqspi)
{/* 检测QSPI句柄 */if(hqspi == NULL){return HAL_ERROR;}/* 禁止QSPI外设时钟 */__HAL_QSPI_DISABLE(hqspi);#if (USE_HAL_QSPI_REGISTER_CALLBACKS == 1)if(hqspi->MspDeInitCallback == NULL){hqspi->MspDeInitCallback = HAL_QSPI_MspDeInit;}/* 复位硬件底层 */hqspi->MspDeInitCallback(hqspi);
#else/* 复位: GPIO, CLOCK, NVIC... */HAL_QSPI_MspDeInit(hqspi);
#endif/* 设置无错误 Set QSPI error code to none */hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;/* 设置QSPI状态为复位 */hqspi->State = HAL_QSPI_STATE_RESET;return HAL_OK;
}
函数描述:
用于复位QSPI总线初始化。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。
78.4.3 函数HAL_QSPI_Command
函数原型:
HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout)
{HAL_StatusTypeDef status;uint32_t tickstart = HAL_GetTick();/* 检测参数 */assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE){assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));}assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));if (cmd->AddressMode != QSPI_ADDRESS_NONE){assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));}assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE){assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));}assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;/* 更新QSPI状态 */hqspi->State = HAL_QSPI_STATE_BUSY;/* 等待BUSY标志复位 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, Timeout);if (status == HAL_OK){/* 配置QSPI */QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);if (cmd->DataMode == QSPI_DATA_NONE){/* 没有数据阶段时,配置完成后立即开始传输,所以请等到TC标志设置并返回到空闲状态 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_TC, SET, tickstart, Timeout);if (status == HAL_OK){__HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TC);/* QSPI就绪 */hqspi->State = HAL_QSPI_STATE_READY;}}else{/* QSPI就绪 */hqspi->State = HAL_QSPI_STATE_READY;}}}else{status = HAL_BUSY;}/* 解锁 */__HAL_UNLOCK(hqspi);/* 返回函数状态 */return status;
}
函数描述:
此函数主要用于为QSPI Flash发送操作命令,查询方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。
- 第3个参数是溢出时间,单位HAL库时间基准,一般我们设置的是1ms。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_WriteEnable
* 功能说明: 写使能
* 形 参: hqspi QSPI_HandleTypeDef句柄。
* 返 回 值: 无
*********************************************************************************************************
*/
static void QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi)
{QSPI_CommandTypeDef sCommand = {0};/* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 *//* 写使能 */sCommand.Instruction = WRITE_ENABLE_CMD; /* 写使能指令 */sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */sCommand.DataMode = QSPI_DATA_NONE; /* 无需数据 */sCommand.DummyCycles = 0; /* 空周期 */if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.4.4 函数HAL_QSPI_Command_IT
函数原型:
HAL_StatusTypeDef HAL_QSPI_Command_IT(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd)
{HAL_StatusTypeDef status;uint32_t tickstart = HAL_GetTick();/* 检测参数 */assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE){assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));}assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));if (cmd->AddressMode != QSPI_ADDRESS_NONE){assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));}assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE){assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));}assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;/* QSPI忙 */hqspi->State = HAL_QSPI_STATE_BUSY;/* 等待BUSY标志复位 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);if (status == HAL_OK){if (cmd->DataMode == QSPI_DATA_NONE){/* 清除中断 */__HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TE | QSPI_FLAG_TC);}/* 调用所有配置函数 */QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);if (cmd->DataMode == QSPI_DATA_NONE){/* 无数据阶段,配置完毕后,立即开始传输,所以机会TC和TE中断 *//* 解锁 /__HAL_UNLOCK(hqspi);/* 使能TE(Transfer Error)和TC中断 */__HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TE | QSPI_IT_TC);}else{/* 更正QSPI状态 Update QSPI state */hqspi->State = HAL_QSPI_STATE_READY;/* 上锁 */__HAL_UNLOCK(hqspi);}}else{/* 解锁 */__HAL_UNLOCK(hqspi);}}else{status = HAL_BUSY;/* 解锁 */__HAL_UNLOCK(hqspi);}/* 返回状态 */return status;
}
函数描述:
此函数主要用于为QSPI Flash发送操作命令,中断方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_WriteEnable
* 功能说明: 写使能
* 形 参: hqspi QSPI_HandleTypeDef句柄。
* 返 回 值: 无
*********************************************************************************************************
*/
static void QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi)
{QSPI_CommandTypeDef sCommand = {0};/* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 *//* 写使能 */sCommand.Instruction = WRITE_ENABLE_CMD; /* 写使能指令 */sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */sCommand.DataMode = QSPI_DATA_NONE; /* 无需数据 */sCommand.DummyCycles = 0; /* 空周期 */if (HAL_QSPI_Command_IT(&QSPIHandle, &sCommand) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.4.5 函数HAL_QSPI_AutoPolling
函数原型:
HAL_StatusTypeDef HAL_QSPI_AutoPolling(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, QSPI_AutoPollingTypeDef *cfg, uint32_t Timeout)
{HAL_StatusTypeDef status;uint32_t tickstart = HAL_GetTick();/* 检查参数 */assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE){assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));}assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));if (cmd->AddressMode != QSPI_ADDRESS_NONE){assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));}assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE){assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));}assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));assert_param(IS_QSPI_INTERVAL(cfg->Interval));assert_param(IS_QSPI_STATUS_BYTES_SIZE(cfg->StatusBytesSize));assert_param(IS_QSPI_MATCH_MODE(cfg->MatchMode));/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;/* 更新状态 */hqspi->State = HAL_QSPI_STATE_BUSY_AUTO_POLLING;/* 等待BUSY复位标志 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, Timeout);if (status == HAL_OK){/* 配置QSPI匹配位 */WRITE_REG(hqspi->Instance->PSMAR, cfg->Match);/* 配置QSPI屏蔽位 */WRITE_REG(hqspi->Instance->PSMKR, cfg->Mask);/* 配置查询时间间隔 */WRITE_REG(hqspi->Instance->PIR, cfg->Interval);/* 配置匹配模式,使能自动停(否则阻塞方式无限等待) */MODIFY_REG(hqspi->Instance->CR, (QUADSPI_CR_PMM | QUADSPI_CR_APMS),(cfg->MatchMode | QSPI_AUTOMATIC_STOP_ENABLE));/* 调用配置函数 */cmd->NbData = cfg->StatusBytesSize;QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_AUTO_POLLING);/* 等待SM标志 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_SM, SET, tickstart, Timeout);if (status == HAL_OK){__HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_SM);/* 更新状态 */hqspi->State = HAL_QSPI_STATE_READY;}}}else{status = HAL_BUSY;}/* 解锁 */__HAL_UNLOCK(hqspi);/* 返回状态 */return status;
}
函数描述:
用于状态标志查询,函数采用的查询方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。
- 第3个参数是QSPI_AutoPollingTypeDef类型结构体变量,详见本章3.5小节。
- 第4个参数是溢出时间,单位HAL库时间基准,单位1ms。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_AutoPollingMemReady
* 功能说明: 等待QSPI Flash就绪,主要用于Flash擦除和页编程时使用
* 形 参: hqspi QSPI_HandleTypeDef句柄
* 返 回 值: 无
*********************************************************************************************************
*/
static void QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi)
{QSPI_CommandTypeDef sCommand = {0};QSPI_AutoPollingTypeDef sConfig = {0};/* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 *//* 读取状态*/sCommand.Instruction = READ_STATUS_REG_CMD; /* 读取状态命令 */sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */sCommand.DataMode = QSPI_DATA_1_LINE; /* 1线数据 */sCommand.DummyCycles = 0; /* 无需空周期 *//* 屏蔽位设置的bit0,匹配位等待bit0为0,即不断查询状态寄存器bit0,等待其为0 */sConfig.Mask = 0x01;sConfig.Match = 0x00;sConfig.MatchMode = QSPI_MATCH_MODE_AND;sConfig.StatusBytesSize = 1;sConfig.Interval = 0x10;sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;if (HAL_QSPI_AutoPolling(&QSPIHandle, &sCommand, &sConfig, 10000) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.4.6 函数HAL_QSPI_AutoPolling_IT
函数原型:
HAL_StatusTypeDef HAL_QSPI_AutoPolling_IT(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, QSPI_AutoPollingTypeDef *cfg)
{HAL_StatusTypeDef status;uint32_t tickstart = HAL_GetTick();/* 检查参数 */assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE){assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));}assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));if (cmd->AddressMode != QSPI_ADDRESS_NONE){assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));}assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE){assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));}assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));assert_param(IS_QSPI_INTERVAL(cfg->Interval));assert_param(IS_QSPI_STATUS_BYTES_SIZE(cfg->StatusBytesSize));assert_param(IS_QSPI_MATCH_MODE(cfg->MatchMode));assert_param(IS_QSPI_AUTOMATIC_STOP(cfg->AutomaticStop));/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;/* 更新状态 */hqspi->State = HAL_QSPI_STATE_BUSY_AUTO_POLLING;/* 等BUSY标志复位 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);if (status == HAL_OK){/* 配置匹配值 */WRITE_REG(hqspi->Instance->PSMAR, cfg->Match);/* 配置屏蔽值 */WRITE_REG(hqspi->Instance->PSMKR, cfg->Mask);/* 配置查询间隔 */WRITE_REG(hqspi->Instance->PIR, cfg->Interval);/* 配置匹配模式和自动停止位 */MODIFY_REG(hqspi->Instance->CR, (QUADSPI_CR_PMM | QUADSPI_CR_APMS),(cfg->MatchMode | cfg->AutomaticStop));/* 清标志 */__HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TE | QSPI_FLAG_SM);/* 调用配置函数 */cmd->NbData = cfg->StatusBytesSize;QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_AUTO_POLLING);/* 解锁 */__HAL_UNLOCK(hqspi);/* 使能SM(status match)和TE(Transfer Error)标志 */__HAL_QSPI_ENABLE_IT(hqspi, (QSPI_IT_SM | QSPI_IT_TE));}else{/* 解锁 */__HAL_UNLOCK(hqspi);}}else{status = HAL_BUSY;/* 解锁 */__HAL_UNLOCK(hqspi);}/* 返回函数状态 */return status;
}
函数描述:
用于状态标志查询,函数采用的中断方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。
- 第3个参数是QSPI_AutoPollingTypeDef类型结构体变量,详见本章3.5小节。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_AutoPollingMemReady
* 功能说明: 等待QSPI Flash就绪,主要用于Flash擦除和页编程时使用
* 形 参: hqspi QSPI_HandleTypeDef句柄
* 返 回 值: 无
*********************************************************************************************************
*/
static void QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi)
{QSPI_CommandTypeDef sCommand = {0};QSPI_AutoPollingTypeDef sConfig = {0};/* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 *//* 读取状态*/sCommand.Instruction = READ_STATUS_REG_CMD; /* 读取状态命令 */sCommand.AddressMode = QSPI_ADDRESS_NONE; /* 无需地址 */sCommand.DataMode = QSPI_DATA_1_LINE; /* 1线数据 */sCommand.DummyCycles = 0; /* 无需空周期 *//* 屏蔽位设置的bit0,匹配位等待bit0为0,即不断查询状态寄存器bit0,等待其为0 */sConfig.Mask = 0x01;sConfig.Match = 0x00;sConfig.MatchMode = QSPI_MATCH_MODE_AND;sConfig.StatusBytesSize = 1;sConfig.Interval = 0x10;sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;if (HAL_QSPI_AutoPolling_IT(&QSPIHandle, &sCommand, &sConfig) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.4.7 函数HAL_QSPI_Transmit
函数原型:
HAL_StatusTypeDef HAL_QSPI_Transmit(QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout)
{HAL_StatusTypeDef status = HAL_OK;uint32_t tickstart = HAL_GetTick();__IO uint32_t *data_reg = &hqspi->Instance->DR;/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;if(pData != NULL ){/* 更新状态 */hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_TX;/* 配置发送 */hqspi->TxXferCount = READ_REG(hqspi->Instance->DLR) + 1U;hqspi->TxXferSize = READ_REG(hqspi->Instance->DLR) + 1U;hqspi->pTxBuffPtr = pData;/* 配置QSPI,间接模式 */MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);while(hqspi->TxXferCount > 0U){/* 等待FT标志 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_FT, SET, tickstart, Timeout);if (status != HAL_OK){break;}*((__IO uint8_t *)data_reg) = *hqspi->pTxBuffPtr;hqspi->pTxBuffPtr++;hqspi->TxXferCount--;}if (status == HAL_OK){/* 等待TC标志 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_TC, SET, tickstart, Timeout);if (status == HAL_OK){/* 清楚传输完成 */__HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TC);}}/* 更新 */hqspi->State = HAL_QSPI_STATE_READY;}else{hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;status = HAL_ERROR;}}else{status = HAL_BUSY;}/* 解锁 */__HAL_UNLOCK(hqspi);return status;
}
函数描述:
此函数用于QSPI接口数据发送,函数采用查询方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是要发送的数据地址。
- 第3个参数是溢出时间,单位HAL库时间基准,我们一般设置的是1ms。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_WriteBuffer
* 功能说明: 页编程,页大小256字节,任意页都可以写入
* 形 参: _pBuf : 数据源缓冲区;
* _uiWriteAddr :目标区域首地址,即页首地址,比如0, 256, 512等。
* _usWriteSize :数据个数,不能超过页面大小,范围1 - 256。
* 返 回 值: 1:成功, 0:失败
*********************************************************************************************************
*/
uint8_t QSPI_WriteBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize)
{QSPI_CommandTypeDef sCommand={0};/* 写使能 */QSPI_WriteEnable(&QSPIHandle); /* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD; /* 仅发送一次命令 */ /* 写序列配置 */sCommand.Instruction = QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速写入命令 */sCommand.DummyCycles = 0; /* 不需要空周期 */sCommand.AddressMode = QSPI_ADDRESS_1_LINE; /* 4线地址方式 */sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据方式 */sCommand.NbData = _usWriteSize; /* 写数据大小 */ sCommand.Address = _uiWriteAddr; /* 写入地址 */if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK){//return 0;Error_Handler(__FILE__, __LINE__);}/* 启动传输 */if (HAL_QSPI_Transmit(&QSPIHandle, _pBuf, 10000) != HAL_OK){//return 0;Error_Handler(__FILE__, __LINE__);}QSPI_AutoPollingMemReady(&QSPIHandle); return 1;
}
78.4.8 函数HAL_QSPI_Receive
函数原型:
HAL_StatusTypeDef HAL_QSPI_Receive(QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout)
{HAL_StatusTypeDef status = HAL_OK;uint32_t tickstart = HAL_GetTick();uint32_t addr_reg = READ_REG(hqspi->Instance->AR);__IO uint32_t *data_reg = &hqspi->Instance->DR;/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;if(pData != NULL ){/* 更新状态 */hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_RX;/* 配置接收 */hqspi->RxXferCount = READ_REG(hqspi->Instance->DLR) + 1U;hqspi->RxXferSize = READ_REG(hqspi->Instance->DLR) + 1U;hqspi->pRxBuffPtr = pData;/* 配置QSPI,间接模式 */MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_READ);/* 启动传输 */WRITE_REG(hqspi->Instance->AR, addr_reg);while(hqspi->RxXferCount > 0U){/* 等FT和TC标志 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, (QSPI_FLAG_FT | QSPI_FLAG_TC), SET, tickstart, Timeout);if (status != HAL_OK){break;}*hqspi->pRxBuffPtr = *((__IO uint8_t *)data_reg);hqspi->pRxBuffPtr++;hqspi->RxXferCount--;}if (status == HAL_OK){/* 等待TC标志 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_TC, SET, tickstart, Timeout);if (status == HAL_OK){/* 清楚传输完成bit */__HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TC);}}/* 更新QSPI状态 */hqspi->State = HAL_QSPI_STATE_READY;}else{hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;status = HAL_ERROR;}}else{status = HAL_BUSY;}/* 解锁 */__HAL_UNLOCK(hqspi);return status;
}
函数描述:
此函数用于QSPI接口数据接收,函数采用查询方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是要接收的数据地址。
- 第3个参数是溢出时间,单位HAL库时间基准,我们一般设置的是1ms。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_ReadBuffer
* 功能说明: 连续读取若干字节,字节个数不能超出芯片容量。
* 形 参: _pBuf : 数据源缓冲区。
* _uiReadAddr :起始地址。
* _usSize :数据个数, 可以大于PAGE_SIZE, 但是不能超出芯片总容量。
* 返 回 值: 无
*********************************************************************************************************
*/
void QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize)
{QSPI_CommandTypeDef sCommand = {0};/* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输要发指令 */ /* 读取数据 */sCommand.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速读取命令 */sCommand.DummyCycles = 6; /* 空周期 */sCommand.AddressMode = QSPI_ADDRESS_4_LINES; /* 4线地址 */sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据 */ sCommand.NbData = _uiSize; /* 读取的数据大小 */ sCommand.Address = _uiReadAddr; /* 读取数据的起始地址 */ if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK){Error_Handler(__FILE__, __LINE__);}/* 读取 */if (HAL_QSPI_Receive(&QSPIHandle, _pBuf, 10000) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.4.9 函数HAL_QSPI_Transmit_DMA
函数原型:
HAL_StatusTypeDef HAL_QSPI_Transmit_DMA(QSPI_HandleTypeDef *hqspi, uint8_t *pData)
{HAL_StatusTypeDef status = HAL_OK;uint32_t data_size = (READ_REG(hqspi->Instance->DLR) + 1U);/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){/* 无错误 */hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;if(pData != NULL ){/* 配置发送字节数 */hqspi->TxXferCount = data_size;/* 设置QSPI状态 */hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_TX;/* 清除中断标志 */__HAL_QSPI_CLEAR_FLAG(hqspi, (QSPI_FLAG_TE | QSPI_FLAG_TC));/* 配置发送 */hqspi->TxXferSize = hqspi->TxXferCount;hqspi->pTxBuffPtr = pData;/* 配置间接写模式 */MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_WRITE);/* 设置MDMA发送完回调 */hqspi->hmdma->XferCpltCallback = QSPI_DMATxCplt;/* 设置MDMA错误回调 */hqspi->hmdma->XferErrorCallback = QSPI_DMAError;/* 设置MDMA终止传输回调为NULL */hqspi->hmdma->XferAbortCallback = NULL;/* MDMA的目的地址是QSPI DR寄存器,设置禁止递增 */MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) ,MDMA_DEST_INC_DISABLE);/* 更新MDMA源地址配置 */if (hqspi->hmdma->Init.SourceDataSize == MDMA_SRC_DATASIZE_BYTE){MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) , MDMA_SRC_INC_BYTE);}else if (hqspi->hmdma->Init.SourceDataSize == MDMA_SRC_DATASIZE_HALFWORD){MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) ,MDMA_SRC_INC_HALFWORD);}else if (hqspi->hmdma->Init.SourceDataSize == MDMA_SRC_DATASIZE_WORD){MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) , MDMA_SRC_INC_WORD);}else{/* 配置错误 */hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;status = HAL_ERROR;}/* 使能QSPI MDMA发送 */if (HAL_MDMA_Start_IT(hqspi->hmdma, (uint32_t)pData, (uint32_t)&hqspi->Instance->DR,hqspi->TxXferSize, 1) == HAL_OK){/* 解锁 */__HAL_UNLOCK(hqspi);/* 使能QSPI传输错误中断 */__HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TE);/* 使能MDMA传输 */SET_BIT(hqspi->Instance->CR, QUADSPI_CR_DMAEN);}else{status = HAL_ERROR;hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;hqspi->State = HAL_QSPI_STATE_READY;/* 解锁 */__HAL_UNLOCK(hqspi);}}else{hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;status = HAL_ERROR;/* 解锁 */__HAL_UNLOCK(hqspi);}}else{status = HAL_BUSY;/* 解锁 */__HAL_UNLOCK(hqspi);}return status;
}
函数描述:
此函数用于QSPI接口数据发送,函数采用DMA方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是要接收的数据地址。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_WriteBuffer
* 功能说明: 页编程,页大小256字节,任意页都可以写入
* 形 参: _pBuf : 数据源缓冲区;
* _uiWriteAddr :目标区域首地址,即页首地址,比如0, 256, 512等。
* _usWriteSize :数据个数,不能超过页面大小,范围1 - 256。
* 返 回 值: 1:成功, 0:失败
*********************************************************************************************************
*/
uint8_t QSPI_WriteBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize)
{QSPI_CommandTypeDef sCommand={0};/* 写使能 */QSPI_WriteEnable(&QSPIHandle); /* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_ONLY_FIRST_CMD; /* 仅发送一次命令 */ /* 写序列配置 */sCommand.Instruction = QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速写入命令 */sCommand.DummyCycles = 0; /* 不需要空周期 */sCommand.AddressMode = QSPI_ADDRESS_1_LINE; /* 4线地址方式 */sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据方式 */sCommand.NbData = _usWriteSize; /* 写数据大小 */ sCommand.Address = _uiWriteAddr; /* 写入地址 */if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK){//return 0;Error_Handler(__FILE__, __LINE__);}/* 启动传输 */if (HAL_QSPI_Transmit_DMA(&QSPIHandle, _pBuf) != HAL_OK){//return 0;Error_Handler(__FILE__, __LINE__);}QSPI_AutoPollingMemReady(&QSPIHandle); return 1;
}
78.4.10 函数HAL_QSPI_Receive_DMA
函数原型:
HAL_StatusTypeDef HAL_QSPI_Receive_DMA(QSPI_HandleTypeDef *hqspi, uint8_t *pData)
{HAL_StatusTypeDef status = HAL_OK;uint32_t addr_reg = READ_REG(hqspi->Instance->AR);uint32_t data_size = (READ_REG(hqspi->Instance->DLR) + 1U);/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){/* 无错误 */hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;if(pData != NULL ){/* 配置接收 */hqspi->RxXferCount = data_size;/* 更新状态 */hqspi->State = HAL_QSPI_STATE_BUSY_INDIRECT_RX;/* 清除中断标志 */__HAL_QSPI_CLEAR_FLAG(hqspi, (QSPI_FLAG_TE | QSPI_FLAG_TC));/* 配置接收 Configure */hqspi->RxXferSize = hqspi->RxXferCount;hqspi->pRxBuffPtr = pData;/* 设置接收完成中断 */hqspi->hmdma->XferCpltCallback = QSPI_DMARxCplt;/* 设置MDMA错误回调 */hqspi->hmdma->XferErrorCallback = QSPI_DMAError;/* 置空MDMA终止回调 */hqspi->hmdma->XferAbortCallback = NULL;/* 接收模式下,MDMA的源地址是QSPI DR,要禁止地址自增 */MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_SINC | MDMA_CTCR_SINCOS) , MDMA_SRC_INC_DISABLE);/* 更新目的地址配置 */if (hqspi->hmdma->Init.DestDataSize == MDMA_DEST_DATASIZE_BYTE){MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) , MDMA_DEST_INC_BYTE);}else if (hqspi->hmdma->Init.DestDataSize == MDMA_DEST_DATASIZE_HALFWORD){MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) ,
MDMA_DEST_INC_HALFWORD);}else if (hqspi->hmdma->Init.DestDataSize == MDMA_DEST_DATASIZE_WORD){MODIFY_REG(hqspi->hmdma->Instance->CTCR, (MDMA_CTCR_DINC | MDMA_CTCR_DINCOS) , MDMA_DEST_INC_WORD);}else{/* 配置错误 */hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;status = HAL_ERROR;}/* 配置CCR寄存器,间接读 */MODIFY_REG(hqspi->Instance->CCR, QUADSPI_CCR_FMODE, QSPI_FUNCTIONAL_MODE_INDIRECT_READ);/* 启动传输 */WRITE_REG(hqspi->Instance->AR, addr_reg);/* 使能MDMA */if (HAL_MDMA_Start_IT(hqspi->hmdma, (uint32_t)&hqspi->Instance->DR, (uint32_t)pData,hqspi->RxXferSize, 1) == HAL_OK){/* 解锁 */__HAL_UNLOCK(hqspi);/* 使能传输错误中断 */__HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TE);/* 使能MDMA传输 */SET_BIT(hqspi->Instance->CR, QUADSPI_CR_DMAEN);}else{status = HAL_ERROR;hqspi->ErrorCode |= HAL_QSPI_ERROR_DMA;hqspi->State = HAL_QSPI_STATE_READY;/* 解锁 */__HAL_UNLOCK(hqspi);}}else{hqspi->ErrorCode |= HAL_QSPI_ERROR_INVALID_PARAM;status = HAL_ERROR;/* 解锁 */__HAL_UNLOCK(hqspi);}}else{status = HAL_BUSY;/* 解锁 */__HAL_UNLOCK(hqspi);}return status;
}
函数描述:
此函数用于QSPI接口数据接收,函数采用DMA方式。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是要接收的数据地址。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_ReadBuffer
* 功能说明: 连续读取若干字节,字节个数不能超出芯片容量。
* 形 参: _pBuf : 数据源缓冲区。
* _uiReadAddr :起始地址。
* _usSize :数据个数, 可以大于PAGE_SIZE, 但是不能超出芯片总容量。
* 返 回 值: 无
*********************************************************************************************************
*/
void QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize)
{QSPI_CommandTypeDef sCommand = {0};/* 基本配置 */sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */sCommand.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输要发指令 */ /* 读取数据 */sCommand.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD; /* 32bit地址的4线快速读取命令 */sCommand.DummyCycles = 6; /* 空周期 */sCommand.AddressMode = QSPI_ADDRESS_4_LINES; /* 4线地址 */sCommand.DataMode = QSPI_DATA_4_LINES; /* 4线数据 */ sCommand.NbData = _uiSize; /* 读取的数据大小 */ sCommand.Address = _uiReadAddr; /* 读取数据的起始地址 */ if (HAL_QSPI_Command(&QSPIHandle, &sCommand, 10000) != HAL_OK){Error_Handler(__FILE__, __LINE__);}/* 读取 */if (HAL_QSPI_Receive_DMA(&QSPIHandle, _pBuf) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.4.11 函数HAL_QSPI_MemoryMapped
函数原型:
HAL_StatusTypeDef HAL_QSPI_MemoryMapped(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, QSPI_MemoryMappedTypeDef *cfg)
{HAL_StatusTypeDef status;uint32_t tickstart = HAL_GetTick();/* 检查参数 */assert_param(IS_QSPI_INSTRUCTION_MODE(cmd->InstructionMode));if (cmd->InstructionMode != QSPI_INSTRUCTION_NONE){assert_param(IS_QSPI_INSTRUCTION(cmd->Instruction));}assert_param(IS_QSPI_ADDRESS_MODE(cmd->AddressMode));if (cmd->AddressMode != QSPI_ADDRESS_NONE){assert_param(IS_QSPI_ADDRESS_SIZE(cmd->AddressSize));}assert_param(IS_QSPI_ALTERNATE_BYTES_MODE(cmd->AlternateByteMode));if (cmd->AlternateByteMode != QSPI_ALTERNATE_BYTES_NONE){assert_param(IS_QSPI_ALTERNATE_BYTES_SIZE(cmd->AlternateBytesSize));}assert_param(IS_QSPI_DUMMY_CYCLES(cmd->DummyCycles));assert_param(IS_QSPI_DATA_MODE(cmd->DataMode));assert_param(IS_QSPI_DDR_MODE(cmd->DdrMode));assert_param(IS_QSPI_DDR_HHC(cmd->DdrHoldHalfCycle));assert_param(IS_QSPI_SIOO_MODE(cmd->SIOOMode));assert_param(IS_QSPI_TIMEOUT_ACTIVATION(cfg->TimeOutActivation));/* 上锁 */__HAL_LOCK(hqspi);if(hqspi->State == HAL_QSPI_STATE_READY){hqspi->ErrorCode = HAL_QSPI_ERROR_NONE;/* 更新状态 */hqspi->State = HAL_QSPI_STATE_BUSY_MEM_MAPPED;/* 等待BUSY标志复位 */status = QSPI_WaitFlagStateUntilTimeout(hqspi, QSPI_FLAG_BUSY, RESET, tickstart, hqspi->Timeout);if (status == HAL_OK){/* 配置QSPI CR寄存器溢出时间计数 */MODIFY_REG(hqspi->Instance->CR, QUADSPI_CR_TCEN, cfg->TimeOutActivation);if (cfg->TimeOutActivation == QSPI_TIMEOUT_COUNTER_ENABLE){assert_param(IS_QSPI_TIMEOUT_PERIOD(cfg->TimeOutPeriod));/* 配置溢出时间 */WRITE_REG(hqspi->Instance->LPTR, cfg->TimeOutPeriod);/* 清楚中断标志 */__HAL_QSPI_CLEAR_FLAG(hqspi, QSPI_FLAG_TO);/* 使能QSPI溢出中断 */__HAL_QSPI_ENABLE_IT(hqspi, QSPI_IT_TO);}/* 调用配置函数 */QSPI_Config(hqspi, cmd, QSPI_FUNCTIONAL_MODE_MEMORY_MAPPED);}}else{status = HAL_BUSY;}/* 解锁 */__HAL_UNLOCK(hqspi);/* 返回状态 */return status;
}
函数描述:
用于QSPI内存映射设置。
函数参数:
- 第1个参数是QSPI_HandleTypeDef类型结构体指针变量,详见本章3.3小节。
- 第2个参数是QSPI_CommandTypeDef类型结构体变量,详见本章3.4小节。
- 第3个参数是QSPI_MemoryMappedTypeDef类型结构体变量,详见本章3.6小节。
- 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
使用举例:
/*
*********************************************************************************************************
* 函 数 名: QSPI_MemoryMapped
* 功能说明: QSPI内存映射,地址 0x90000000
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void QSPI_MemoryMapped(void)
{QSPI_CommandTypeDef s_command = {0};QSPI_MemoryMappedTypeDef s_mem_mapped_cfg = {0};/* 基本配置 */s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 1线方式发送指令 */ s_command.AddressSize = QSPI_ADDRESS_32_BITS; /* 32位地址 */s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */s_command.DdrMode = QSPI_DDR_MODE_DISABLE; /* W25Q256JV不支持DDR */s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; /* DDR模式,数据输出延迟 */s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次传输都发指令 *//* 全部采用4线 */s_command.Instruction = QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD; /* 快速读取命令 */s_command.AddressMode = QSPI_ADDRESS_4_LINES; /* 4个地址线 */s_command.DataMode = QSPI_DATA_4_LINES; /* 4个数据线 */s_command.DummyCycles = 6; /* 空周期 *//* 关闭溢出计数 */s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;s_mem_mapped_cfg.TimeOutPeriod = 0;if (HAL_QSPI_MemoryMapped(&QSPIHandle, &s_command, &s_mem_mapped_cfg) != HAL_OK){Error_Handler(__FILE__, __LINE__);}
}
78.5 总结
本章节就为大家讲解这么多,要熟练掌握QSPI总线的查询,中断和DMA方式的API用法。
【STM32H7教程】第78章 STM32H7的QSPI总线基础知识和HAL库API相关推荐
- 【STM32H7教程】第87章 STM32H7的SDMMC总线基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第87章 STM32H7的SDMMC总线基础知识和 ...
- 【STM32H7教程】第72章 STM32H7的SPI总线基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第72章 STM32H7的SPI总线基础知识和HA ...
- 【STM32H7教程】第91章 STM32H7的FDCAN总线基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第91章 STM32H7的FDCAN总线基础知识和 ...
- 【STM32H7教程】第70章 STM32H7的内部Flash基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第70章 STM32H7的内部Flash基础知识和 ...
- 【STM32H7教程】第29章 STM32H7的USART串口基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第29章 STM32H7的USART串口基础知识和 ...
- 【STM32H7教程】第65章 STM32H7的低功耗串口LPUART基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第65章 STM32H7的低功耗串口LPUART基 ...
- 【STM32H7教程】第55章 STM32H7的图形加速器DMA2D的基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第55章 STM32H7的图形加速器DMA2D的基 ...
- 【STM32H7教程】第63章 STM32H7的高分辨率定时器HRTIM基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第63章 STM32H7的高分辨率定时器HRTIM ...
- 【STM32F429开发板用户手册】第40章 STM32F429的LCD控制器LTDC基础知识和HAL库API
最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第40章 STM32F429的LCD控制器LTDC基础 ...
最新文章
- 2018.07.17 洛谷P1368 工艺(最小表示法)
- linux 系统监控和进程管理
- 集体备课模板_幼儿教师资格证面试试讲万能模板和历年真题,看完轻松过面试!...
- 201671010128 2017-10-08《Java程序设计》之Lambda与内部类
- linux查看磁盘挂载dntf,从Windows设置Linux服务器的公钥认证(ppk私钥)
- 多项式加减 List
- LIS (nlogn)的算法
- Ubuntu 14.04安装和卸载搜狗拼音输入法
- 绝缘端子行业调研报告 - 市场现状分析与发展前景预测
- applet mysql_applet数字签名_java applet读取mysql_软件数字签名(5)
- 前端 docker + gitlab CI 的持续集成(二)
- 基于IOS的仿微博系统
- android图片添加文字,Android给图片加文字和图片水印
- (SWAT-1)SWAT进行流域提取
- 2020 — 只争朝夕,不负韶华
- error Target dll has been cancelled debugger aborted
- 基于Python的拉勾网的模拟登录获取cookie
- 信诺计算机怎么算一次函数,excel用一次函数进行计算的方法步骤
- 精密测量仪器的气源维护知识
- 【C语言基础练习】百钱买百鸡问题。母鸡3元钱一只,小鸡1元钱三只,问100元钱要刚好买100只鸡,编程实现母鸡和小鸡各多少只?
热门文章
- MySQL 占用cpu超过100%,怎么搞?
- ToB 服务的交付能力优化
- java初级程序员简历上不能写,但是不能不会的项目!
- r语言 精美rda图_R语言RDA分析结果环境因子丢失
- airbnbs收入预测数据平台
- 【Vivado使用】从0开始 综合后生成门级网表
- 车万翔:ChatGPT只是“搬运工”而不是“创造者”,但NLP学术界确实非常危急!...
- oppo--软件测试工程师岗位面试总结(二)
- MS2108 RGB to USB是一款数字视频和音频采集芯片,内部集成USB 2 0 Device控制器、数据收发模块、数字视频输入处理模块、I2S输入处理模块、音视频处理
- 平面曲线曲率的计算-MATLAB