一、芯片管脚和MCU管脚、时钟
时钟配置:
二、生成QSPI的代码
代码里面挺多备注了,不懂得再去刷一刷正点原子的视频吧,结合手册来看,来源都是正点原子的代码。
头文件qspi.h
#ifndef __QSPI_H
#define __QSPI_H
#include "stm32f7xx_hal.h"
//QSPI功能配置
u8 QSPI_Init(void);
//QSPI 底层驱动 引脚配置 时钟使能
void HAL_QSPI_MspInit(QSPI_HandleTypeDef* hqspi);
//QSPI发送命令
void QSPI_Send_CMD(u32 Instruction,u32 Address,u32 DummyCycles,u32 InstructionMode ,
u32 AddressMode , u32 AddressSize ,u32 DataMode);
//发送数据给W25Q256
u8 QSPI_Transmit(u8*buf,u32 datalen);
//接受W25Q256数据到数组中
u8 QSPI_Receive(u8*buf,u32 datalen);
//关闭QSPI通讯
void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* hqspi);
#endif
#include "qspi.h"
QSPI_HandleTypeDef hqspi;//QSPI句柄
/**
* @brief QSPI功能配置
QSPI_BK2_NCS PC11
QSPI_BK2_IO0 PE7
QSPI_BK2_IO1 PE8
QSPI_BK2_IO2 PE9
QSPI_BK2_IO3 PE10
QSPI_BK2_CLK PB2
* @param None
* @retval None
*/
u8 QSPI_Init(void)
{
hqspi.Instance = QUADSPI;//QSPI
//时钟预分频系数,在QUADSPI_CR控制寄存器的PRESCALER位[31:24]
//FCLK=FAHB/(ClockPrescaler+1) QSPI挂载在AHB总线上
//范围0~255 QSPI分频比,W25Q256的最大频率为104M,这里的AHB频率为216M
hqspi.Init.ClockPrescaler = 2;//QSPI频率=216/(2+1)=72M
hqspi.Init.FifoThreshold = 4;//FIFO阈值 CR中FTHRES[4:0] FIFO中写入4+1个字节后,FTF标志置1
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; //采样移位CR4 1=半个周期之后再采样 (DDR模式下,必须设置为0)
//POSITION_VAL (0X2000000)获取VAL值的最高位位数
//Flash字节数=2^(FlashSize+1)
//POSITION_VAL(0X2000000)=26 2^26=67108864 字节=64MB 计算结果错误,难道是POSITION_VAL(0X2000000)=25???
hqspi.Init.FlashSize = POSITION_VAL(0X2000000)-1; //DCR[20:16] Flash大小32MB=256Mb
//片选高电平时间为四个时钟(13.8*4=55.2ns) 手册里面的tSHSL参数
//发送Flash命令之间必须保持高电平的最少CLK周期数
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE;//片选高电平时间 DCR 10:8
//模式设置 DCR0
//nCS为高电平(片选释放)时,CLK必须保持低电平,这称为模式0 QSPI_CLOCK_MODE_3
//nCS为高电平(片选释放)时,CLK必须保持高电平,这称为模式3 QSPI_CLOCK_MODE_0
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;//模式0
//选择FLASH CR7 FSEL
//0选择FLASH1 1选择FLASH2 DFM=1时忽略该位,因为DFM时双闪存模式
hqspi.Init.FlashID = QSPI_FLASH_ID_2;//第一片Flash
//双闪存模式 CR6 DFM 0禁止 1使能
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;//禁止双闪存模式
if (HAL_QSPI_Init(&hqspi) != HAL_OK)//QSI初始化失败
{
return 1;//初始化失败
}else
{
HAL_QSPI_MspInit(&hqspi);
return 0;//初始化成功
}
}
/**
* @brief QSPI 底层驱动 引脚配置 时钟使能
*此函数会被QSPI_Init(void)函数调用
* @param hqspi: QSPI handle pointer
* @retval None
*/
void HAL_QSPI_MspInit(QSPI_HandleTypeDef* hqspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {
0};
if(hqspi->Instance==QUADSPI)
{
/* USER CODE BEGIN QUADSPI_MspInit 0 */
/* USER CODE END QUADSPI_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_QSPI_CLK_ENABLE();//使能时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**QUADSPI GPIO Configuration
PB2 ------> QUADSPI_CLK
PE7 ------> QUADSPI_BK2_IO0
PE8 ------> QUADSPI_BK2_IO1
PE9 ------> QUADSPI_BK2_IO2
PE10 ------> QUADSPI_BK2_IO3
PC11 ------> QUADSPI_BK2_NCS
*/
//PB2 时钟管脚
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//PE7、8、9、10 IO0、1、2、3
//数据管脚
GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
//片选管脚
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN QUADSPI_MspInit 1 */
/* USER CODE END QUADSPI_MspInit 1 */
}
}
//QSPI发送命令
void QSPI_Send_CMD(u32 Instruction,u32 Address,u32 DummyCycles,u32 InstructionMode ,
u32 AddressMode , u32 AddressSize ,u32 DataMode)
{
QSPI_CommandTypeDef CmdHandler;
CmdHandler.Instruction=Instruction;//指令
CmdHandler.Address=Address;//地址
CmdHandler.DummyCycles=DummyCycles;//设置空指令周期数
CmdHandler.InstructionMode=InstructionMode;//指令模式
CmdHandler.AddressMode=AddressMode;//地址模式
CmdHandler.AddressSize=AddressSize;//地址长度
CmdHandler.DataMode=DataMode;//数据模式
CmdHandler