当前位置:首页 > 技术 > Cortex-M3 > 正文内容

STM32CubeMX系列教程19:Quad-SPI

watrt2年前 (2017-12-17)Cortex-M3270
一.Quad-SPI简介

        在第十章和第十一章中,我们介绍了标准的SPI总线,SPI由四根线控制,NSS为片选,SCK为时钟信号线。MISO,MOSI为数据线,一根作为输入,一根作为输出。


        Quad-SPI,即四线SPI,由此可知其数据线比标准的SPI接口要多,最多支持四条数据线同时传输。

连接单、双或四(条数据线) SPI Flash 存储介质。Quad-SPI总共有6根控制线:CS为片选,CLK为时钟信号线。IO0~IO3为数据线,可以发送数据也可以接收数据。


二.Quad-SPI 命令序列

QUADSPI 通过命令与 Flash 通信 每条命令包括指令、地址、交替字节、空指令和数据这五个阶段 任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。
nCS 在每条指令开始前下降,在每条指令完成后再次上升。

指令阶段
        这一阶段将发送一条8位指令到flash,指定待执行的类型。指令可以单线,双线或四线传输。

地址阶段
        在地址阶段,将1-4字节发送到flash,指示操作地址,地址阶段可一次发送 1 位(在单线 SPI 模式中通过 IO0)、2 位(在双线 SPI 模式中通过 IO0/IO1 )或 4 位(在四线 SPI 模式中通过 IO0/IO1/IO2/IO3)。

交替字节阶段
        在交替字节阶段,将 1-4 字节发送到 Flash,一般用于控制操作模式。可以通过可以单线,双线或四线传输。
空指令周期阶段
        在空指令周期阶段,给定的 1-31个周期内不发送或接收任何数据,目的是当采用更高的时钟频率时,给 Flash 留出准备数据阶段的时间。
数据阶段        在数据阶段,可从 Flash 接收或向其发送任意数量的字节。数据阶段如果发送数据IO口为输出,如果是接收数据则IO切换为输入,一次可发送 1 位(在单线 SPI 模式中通过 IO0)、2 位(在双线 SPI 模式中通过 IO0/IO1 )或 4 位(在四线 SPI 模式中通过 IO0/IO1/IO2/IO3)。

三.新建工程

    复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置,QuadSPI模式选择Bank1四线SPI。


此时QUADSPI对应的IO口会被选中。

QUADSPI配置如下。时钟分频设置为2,故QSPI时钟 = 216M / (2+ 1) = 72MHz
FIFO深度设置为4个字节,配置QSPI Flash 驱动信号后过半个CLK 周期才对Flash 驱动的数据采样。
Flash Size设置外部存储器大小,本实验以W25Q128FV芯片为例,大小为16MB(使用24位寻址),则设置为23。


四.应用程序

生成报告以及代码,编译程序。在quadspi.c文件中可以看到初始化函数。在stm32f7xx_hal_qspic.h头文件中可以看到QSPI的操作函数。分别对应轮询,中断和DMA三种控制方式。
下面为W25QXX的驱动文件。下载并添加进工程中。(操作方式参照第十一章)

在main.c文件中添加头文件

/* USER CODE BEGIN Includes */
#include <string.h>
#include "stm32746g_qspi.h"
/* USER CODE END Includes */</string.h>

声明变量: s_command为QSPI命令结构体,配置命令;pData存储读取ID值,rData,wData作为读写数据缓存

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
QSPI_CommandTypeDef s_command;
uint8_t pData[3];
     
uint8_t wData[0x100];
uint8_t rData[0x100];
 
uint32_t i;
/* USER CODE END PV */


在main函数中添加应用程序,第一部分初始化W25Q128FV芯片,第二部分分别用单线,双线和四线三种模式读设备ID。第三部分则是读写擦除芯片操作实验。

/* USER CODE BEGIN 2 */
    printf("W25Q128FV QuadSPI Test ....\r\n\r\n");
 
    /*##-1- Initialize W25Q128FV  ###########################################*/
    BSP_QSPI_Init();
 
    /*##-2-Read Device ID Test    ###########################################*/
    /* Read Manufacture/Device ID */
    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = READ_ID_CMD;
    s_command.AddressMode       = QSPI_ADDRESS_1_LINE;
    s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
    s_command.Address           = 0x000000;
    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    s_command.DataMode          = QSPI_DATA_1_LINE;
    s_command.DummyCycles       = 0;
    s_command.NbData            = 2;
    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
     
    if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    printf("SPI  I/0 Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);
     
     
    /* Read Manufacture/Device ID Dual I/O*/
    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = DUAL_READ_ID_CMD;
    s_command.AddressMode       = QSPI_ADDRESS_2_LINES;
    s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
    s_command.Address           = 0x000000;
    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_2_LINES;
    s_command.AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS;
    s_command.AlternateBytes    = 0;
    s_command.DataMode          = QSPI_DATA_2_LINES;
    s_command.DummyCycles       = 0;
    s_command.NbData            = 4;
    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
 
    if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    printf("Dual I/O Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);
     
    /* Read Manufacture/Device ID Quad I/O*/
    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = QUAD_READ_ID_CMD;
    s_command.AddressMode       = QSPI_ADDRESS_4_LINES;
    s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
    s_command.Address           = 0x000000;
    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES;
    s_command.AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS;
    s_command.AlternateBytes    = 0x00;
    s_command.DataMode          = QSPI_DATA_4_LINES;
    s_command.DummyCycles       = 4;
    s_command.NbData            = 2;
    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
 
    if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    printf("Quad I/O Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);
 
    /* Read JEDEC ID */
    s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
    s_command.Instruction       = READ_JEDEC_ID_CMD;
    s_command.AddressMode       = QSPI_ADDRESS_NONE;
    s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    s_command.DataMode          = QSPI_DATA_1_LINE;
    s_command.DummyCycles       = 0;
    s_command.NbData            = 3;
    s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
    s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
    s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;
 
    if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        Error_Handler();
    }
    printf("Read JEDEC ID :  0x%2X 0x%2X 0x%2X\r\n\r\n",pData[0],pData[1],pData[2]);
     
     
    /*##-3-QSPI Erase/Write/Read Test    ###########################################*/
    /* fill buffer */
    for(i =0;i<0x100;i ++)
    {
        wData[i] = i;
        rData[i] = 0;
    }
 
    if(BSP_QSPI_Erase_Block(0) == QSPI_OK)
        printf(" QSPI Erase Block ok\r\n");
    else
        Error_Handler();
 
    if(BSP_QSPI_Write(wData,0x00,0x100)== QSPI_OK)
        printf(" QSPI Write ok\r\n");
    else
        Error_Handler();
 
    if(BSP_QSPI_Read(rData,0x00,0x100)== QSPI_OK)
        printf(" QSPI Read ok\r\n\r\n");
    else
        Error_Handler();
 
    printf("QSPI Read Data : \r\n");
    for(i =0;i<0x100;i++)
        printf("0x%02X  ",rData[i]);
    printf("\r\n\r\n");
 
    for(i =0;i<0x100;i++)
        if(rData[i] != wData[i])printf("0x%02X 0x%02X ",wData[i],rData[i]);
    printf("\r\n\r\n");
    /* check date */
    if(memcmp(wData,rData,0x100) == 0 ) 
        printf(" W25Q128FV QuadSPI Test OK\r\n");
    else
        printf(" W25Q128FV QuadSPI Test False\r\n");
  /* USER CODE END 2 */


将W25QXX DataFlash Board模块插入到Open746I开发板I2C1中,编译程序并下载到开发板。打开串口调试助手。设置波特率为115200。串口助手上会显示如下信息。

五.程序讲解

现在以四线读设备ID为例讲解QSPI如何进行一次读写操作。如下为四线读制造商/设备ID命令(94H)


程序中先根据上面时序配置s_command命令结构体,
  • 命令为1线,命令为QUAD_READ_ID_CMD,在w25q128fv.h头文件中宏定义为0x94。

  • 地址为4线,地址长度为24位,地址为0x000000。

  • 交替字节阶段设置为4线,长度为8位,备用字节为0x00。

  • 空指令阶段为4个时钟周期

  • 数据为4线,字节为两个字节。


程序中先通过HAL_QSPI_Command()将命令发送出去,然后通过HAL_QSPI_Receive()命令接收数据。即完成一次读操作,如果是写操作,同样是先配置命令结构体,然后发送命令,最后通过HAL_QSPI_Transmit()命令发送数据。

/* Read Manufacture/Device ID Quad I/O*/
s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
s_command.Instruction       = QUAD_READ_ID_CMD;
s_command.AddressMode       = QSPI_ADDRESS_4_LINES;
s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
s_command.Address           = 0x000000;
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES;
s_command.AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS;
s_command.AlternateBytes    = 0x00;
s_command.DataMode          = QSPI_DATA_4_LINES;
s_command.DummyCycles       = 4;
s_command.NbData            = 2;
s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
    Error_Handler();
}
if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
    Error_Handler();
}
printf("Quad I/O Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);


关于W25Q128fv的读写擦除等驱动函数可以查看stm32746g-qspi.c文件,这里不再详细讲解。


分享给朋友:

相关文章

STM32CubeMX系列教程14:电源控制器(PWR)

STM32CubeMX系列教程14:电源控制器(PWR)

一.低功耗模式介绍        系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗。由用户根据应用选择具体的低功耗模式,以在低功耗、短启动时间和可用唤醒源之间寻求最佳平衡。        当系统断电时,仍然可以通过电池供电保留备份域的数据。备份域中包含RTC实时时钟,4KB备份SRAM以及调压器,调压器为备份域和待机电路以外...

STM32CubeMX系列教程16:RNG和CRC

STM32CubeMX系列教程16:RNG和CRC

一、随机数发生器(RNG)    RNG 处理器是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个 32 位的随机数。    复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置,激活随机数发生器(RNG)。配置RNG时钟为48MHz。RNG没有参数配置。生成报告以及代码,编译程序。在iwdg.c文件中可以看到RNG初始化函数。在stm32f7xx_h...

STM32CubeMX系列教程12:控制器局域网络(CAN)

STM32CubeMX系列教程12:控制器局域网络(CAN)

一.CAN简介        CAN是控制器局域网络(Controller Area Network, CAN)的简称,是由以研发和生产汽车电子产品著称的德国BOSCH公司开发的,是国际上应用最广泛的现场总线之一。        CAN控制器通过组成总线的2根线(CAN-H和CAN-L)的电位差来确定总线的电平,信号是以两线之间的“差分”电压形式出现,总线电平分为显性电平和隐性电平。   ...

STM32CubeMX系列教程2:外部中断(EXIT)

STM32CubeMX系列教程2:外部中断(EXIT)

      这一章我们在前一章GPIO的工程修改。复制GPIO的工程,修改文件夹名。点击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置。PA0管脚重新配置为GPIO_EXIT0模式。 WAKEUP按键已经外部下拉,按下是PA0为高电平。在GPIO配置中配置PA0为上升沿触发。内部既不上拉也不下拉,添加用户标签WAKEUP。在NVIC(嵌套向量中断控制器)中,勾选EXIT Line0 interrupt使能PA0中断。右边两个选项设置抢占优...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。