16.HAL库之SPI和QSPI

本文详细介绍了SPI(串行外设接口)和QSPI(队列串行外围接口)的原理与应用。SPI是一种高速全双工同步通信总线,仅占用四根线,适用于多种设备。QSPI是在SPI基础上的增强,支持队列传输机制,适用于更广泛的场景。文章还提供了STM32F746的SPI与QSPI驱动程序实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.SPI协议(以RN8302为例)

SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线(SCSN,SCLK,SDI,SDO).

总结传输流程:

a.写时序

SCSN拉低,开启通讯。主机依次写入地址,命令,数据(高位在前,低位在后),CS校验。主机在SCLK下降沿将数据通过SDI写入从机。最后SCSN拉高结束通讯。

b.读时序

SCSN拉低开启通讯,主机先写入地址+命令共两个字节,随后在SCLK下降沿读取从机从SDO输出的数据(高位在前,低位在后)。最后SCSN拉高结束通讯。

驱动程序编写:头文件中需定义管脚控制命令,寄存器地址,芯片命令。c文件中依次编写读写字节,读写帧数据,读写寄存器,芯片初始化等函数。

RN8302具体驱动程序见: https://download.youkuaiyun.com/download/weixin_42480952/10772976

主机(STM32F746)只需配置SPI接口即可,具体流程为:开启时钟,GPIO管脚配置,SPI配置。

2.QSPI

QSPI是Queued SPI的简写,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。在SPI协议的基础上,Motorola公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即QSPI协议)。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。共有SNCS,SCLK,BK0,BK1,BK2,BK3共6根接线,可以实现多种通信模式。

主机需进行QSPI接口的配置,QSPI协议主要通过发送命令来进行通讯,因此主机的配置包括管脚配置,QSPI初始化。

QUADSPI通过命令和FLASH通讯,命令包括:指令+地址+交替字节+空周期+数据五个阶段,并且各阶段可省略。

 

单线SPI模式:BK_IO0即SO,BK_IO1即SI,加上NCS和SCLK,类似于SPI模式。

双线SPI模式:BK_IO0和BK_IO1实现数据的双入双出。

四线SPI模式:BK_IO0,BK_IO1,BK_IO2,BK_IO3四根线实现数据的传输。

SDR模式:QSPI驱动IO0~IO3在SCLK的下降沿进行数据变化。

DDR模式:在该模式下,指令阶段在SCLK下降沿发送数据,而在地址,交替字节,数据阶段在SCLK上升沿和下降沿均发送数据。

双闪存模式:使用两个外部SPI四线,可将flash扩大一倍。

QSPI配置:

时钟使能,管脚定义,QSPI配置,flash初始化,QSPI读/写函数定义。

void Qspi_Config()
{
	__HAL_RCC_QSPI_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOF_CLK_ENABLE();
	
	Flash_GPIO.Pin=GPIO_PIN_2;
	Flash_GPIO.Mode=GPIO_MODE_AF_PP;
	Flash_GPIO.Speed=GPIO_SPEED_FREQ_MEDIUM;
	Flash_GPIO.Pull=GPIO_NOPULL;
	Flash_GPIO.Alternate=GPIO_AF9_QUADSPI;
	HAL_GPIO_Init(GPIOB,&Flash_GPIO);   //QSPI_CLK, PB2
	
	Flash_GPIO.Pin=GPIO_PIN_6;
	Flash_GPIO.Mode=GPIO_MODE_AF_PP;
	Flash_GPIO.Speed=GPIO_SPEED_FREQ_MEDIUM;
	Flash_GPIO.Pull=GPIO_PULLUP;
	Flash_GPIO.Alternate=GPIO_AF10_QUADSPI;
	HAL_GPIO_Init(GPIOB,&Flash_GPIO);   //QSPI_NCS, PB6
	
	Flash_GPIO.Pin=GPIO_PIN_8;
	Flash_GPIO.Mode=GPIO_MODE_AF_PP;
	Flash_GPIO.Speed=GPIO_SPEED_FREQ_MEDIUM;
	Flash_GPIO.Pull=GPIO_NOPULL;
	Flash_GPIO.Alternate=GPIO_AF10_QUADSPI;
	HAL_GPIO_Init(GPIOF,&Flash_GPIO);   //QSPI_IO0, PF8
	
	Flash_GPIO.Pin=GPIO_PIN_9;
	Flash_GPIO.Mode=GPIO_MODE_AF_PP;
	Flash_GPIO.Speed=GPIO_SPEED_FREQ_MEDIUM;
	Flash_GPIO.Pull=GPIO_NOPULL;
	Flash_GPIO.Alternate=GPIO_AF10_QUADSPI;
	HAL_GPIO_Init(GPIOF,&Flash_GPIO);   //QSPI_IO1, PF9
	
	Flash_GPIO.Pin=GPIO_PIN_7;
	Flash_GPIO.Mode=GPIO_MODE_AF_PP;
	Flash_GPIO.Speed=GPIO_SPEED_FREQ_MEDIUM;
	Flash_GPIO.Pull=GPIO_NOPULL;
	Flash_GPIO.Alternate=GPIO_AF9_QUADSPI;
	HAL_GPIO_Init(GPIOF,&Flash_GPIO);   //QSPI_IO2, PF7
	
	Flash_GPIO.Pin=GPIO_PIN_6;
	Flash_GPIO.Mode=GPIO_MODE_AF_PP;
	Flash_GPIO.Speed=GPIO_SPEED_FREQ_MEDIUM;
	Flash_GPIO.Pull=GPIO_NOPULL;
	Flash_GPIO.Alternate=GPIO_AF9_QUADSPI;
	HAL_GPIO_Init(GPIOF,&Flash_GPIO);   //QSPI_IO3, PF6
	
  QSPI_Flash.Instance=QUADSPI;
	QSPI_Flash.Init.ClockPrescaler=2;
	QSPI_Flash.Init.FifoThreshold=4;
	QSPI_Flash.Init.SampleShifting=QSPI_SAMPLE_SHIFTING_HALFCYCLE;
	QSPI_Flash.Init.FlashSize=25;
	QSPI_Flash.Init.ChipSelectHighTime=QSPI_CS_HIGH_TIME_8_CYCLE;
	QSPI_Flash.Init.ClockMode=QSPI_CLOCK_MODE_0;
	HAL_QSPI_Init(&QSPI_Flash);
}

 

void QSPI_Read(uint8_t *data,uint32_t addr, uint32_t size)
{
	QSPI_CommandTypeDef flash_command;
	flash_command.InstructionMode=QSPI_INSTRUCTION_1_LINE;
	flash_command.Instruction=0x13;
	flash_command.Address=addr;
	flash_command.AddressMode=QSPI_ADDRESS_1_LINE;
	flash_command.AddressSize=QSPI_ADDRESS_32_BITS;
	flash_command.AlternateByteMode=QSPI_ALTERNATE_BYTES_NONE;
	flash_command.DataMode=QSPI_DATA_1_LINE;
	flash_command.DummyCycles=0;
	flash_command.NbData=size;
	flash_command.DdrMode=QSPI_DDR_MODE_DISABLE;
	flash_command.DdrHoldHalfCycle=QSPI_DDR_HHC_ANALOG_DELAY;
	flash_command.SIOOMode=QSPI_SIOO_INST_EVERY_CMD;
	HAL_QSPI_Command(&QSPI_Flash,&flash_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
  HAL_QSPI_Receive(&QSPI_Flash,data,1000); //读取的数据放入*data中,共size个字节
  QSPI_AutoPollingMemReady();
}

void QSPI_WritePage(uint8_t *pData,uint32_t WriteAddr, uint32_t Size)
{
	QSPI_CommandTypeDef flash_command;
	QSPI_WriteEnable();
	
	flash_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  flash_command.Instruction       = 0x12;
  flash_command.AddressMode       = QSPI_ADDRESS_1_LINE;
  flash_command.AddressSize       = QSPI_ADDRESS_32_BITS;
	flash_command.Address           = WriteAddr;
  flash_command.NbData            = Size;
  flash_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  flash_command.DataMode          = QSPI_DATA_1_LINE;
  flash_command.DummyCycles       = 0;
  flash_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
  flash_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  flash_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
	
	HAL_QSPI_Command(&QSPI_Flash, &flash_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
  HAL_QSPI_Transmit(&QSPI_Flash, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
	QSPI_AutoPollingMemReady();
}

 

 

### STM32H743 HAL QUADSPI 使用教程 #### 配置QUADSPI外设 为了在STM32H743上使用QUADSPI接口,首先需要配置QUADSPI外设。这可以通过STM32CubeMX工具完成,该工具简化了初始化过程并自动生成必要的初始化代码[^2]。 ```c // 初始化结构体定义 QUADSPI_HandleTypeDef hqspi; void MX_QUADSPI_Init(void) { hqspi.Instance = QUADSPI; hqspi.Init.ClockPrescaler = 1; hqspi.Init.FifoThreshold = 4; hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; hqspi.Init.FlashSize = 22; hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_6_CYCLE; hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0; if (HAL_QSPI_Init(&hqspi) != HAL_OK) { Error_Handler(); } } ``` 这段代码展示了如何设置QUADSPI的主要参数,包括时钟分频器、FIFO阈值等,并调用`HAL_QSPI_Init()`函数来应用这些设置。 #### 编写读取写入命令 对于具体的读写操作,则需构建相应的指令序列并通过`HAL_QSPI_Command()`发送给外部存储设备。这里给出一个简单的例子用于读取Flash ID: ```c uint32_t ReadQuadSpiId() { uint8_t reg[3]; // 命令结构体初始化 QSPI_CommandTypeDef sCommand; memset(&sCommand, 0, sizeof(QSPI_CommandTypeDef)); sCommand.Instruction = READ_JEDEC_ID_CMD; // 指令码 sCommand.AddressSize = QSPI_ADDRESS_SIZE_24_BITS; sCommand.AlternateBytesSize = QSPI_ALTERNATE_BYTES_SIZE_8_BITS; sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; if(HAL_QSPI_Command(&hqspi,&sCommand,QPSI_TIMEOUT)!= HAL_OK){ return ERROR; } // 接收数据到reg数组中 if(HAL_QSPI_Read(&hqspi,(uint8_t*)reg,sizeof(reg),QPSI_TIMEOUT)!= HAL_OK){ return ERROR; } return ((uint32_t)(reg[0]) << 16 | (uint32_t)(reg[1]) << 8 | (uint32_t)(reg[2])); } ``` 此函数实现了向外部闪存芯片发出JEDEC标准的读ID请求,并解析返回的数据形成完整的ID号[^4]。 #### 测试QUADSPI功能 最后可以编写一段测试程序验证上述实现是否正常工作。例如,在主循环里调用上面提到的功能函数打印出获取到的结果即可作为初步检验手段之一。 ```c int main(void) { /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_QUADSPI_Init(); while (1) { printf("Flash ID: %X\n",ReadQuadSpiId()); HAL_Delay(1000); } } ``` 通过这种方式能够确认硬件连接无误且软件逻辑正确执行,从而为进一步的应用开发打下良好基础[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值