RT-Thread Studio configures QSPI and SFUD

1 Introduction

This time, the STM32F767 series board of Punctual Atom is used, which is different from the SPI on F1 and F4, and QSPI is added to F7. The W25Q256FV type Flash on board also realizes communication read and write through QSPI. There are many examples on the Internet about RT-Thread opening SPI and using SFUD for FLASH reading and writing, but most of them are based on ENV, because this time I use RT-Thread's newly launched Studio, and I am configuring QSPI and SFUD process China also stepped on a lot of pits to record the learning and configuration process.

Reference materials:
SFUD Github original website
RT-Thread document center
IOT-OS RT-Thread (9)-SPI device object management and SFUD management framework
Rtthread study notes (9) RT-Thread Studio opens SPI1 bus, external flash (W25Q64 ) SFUD as a slave device
| A serial Flash universal driver library

2 overview

2.1 SPI and QSPI

SPI (Serial Peripheral Interface, serial peripheral interface) is a high-speed, full-duplex, synchronous communication bus, often used for short-distance communication, generally using 4 wires for communication
Insert picture description here

  • MOSI-master output / slave input data line (SPI Bus Master Output/Slave Input).
  • MISO-master input/slave output data line (SPI Bus Master Input/Slave Output).
  • SCLK-serial clock line (Serial Clock), the master device outputs the clock signal to the slave device.
  • CS-Slave selection line (Chip select). Also called SS, CSB, CSN, EN, etc., the master device outputs chip select signals to the slave device

The specific working mode is as follows:
the clock of the slave device is provided by the master device through SCLK, and MOSI and MISO complete data transmission based on this pulse. The working sequence mode of SPI is determined by the phase relationship between CPOL (Clock Polarity) and CPHA (Clock Phase). CPOL determines the idle level state of the clock, CPOL=0 means the idle state is low level, and CPOL=1 means the idle state is high level. CPHA means sampling on the first edge, CPHA=0 means sampling on the first clock change edge, corresponding to the red line in the figure below, CPHA=1 means sampling on the second clock change edge, corresponding to the figure below The blue line in.
Insert picture description here
QSPI is the abbreviation of Queued SPI, an extension of the SPI interface launched by Motorola, and is more widely used than SPI. On the basis of the SPI protocol, Motorola has enhanced its functions, increased the queue transmission mechanism, and introduced the queue serial peripheral interface protocol (ie QSPI protocol). QSPI is a dedicated communication interface that connects single, dual or quad (data lines) SPI Flash storage media.

The interface can work in the following three modes:

① Indirect mode: use QSPI register to perform all operations

② Status polling mode: Periodically read the external Flash status register, and an interrupt will be generated when the flag is set to 1 (such as erasing or programming is completed, an interrupt will be generated)

③ Memory mapping mode: external Flash is mapped to the address space of the microcontroller, so the system regards it as internal memory

When the dual flash mode is adopted, two Quad-SPI Flash will be accessed at the same time, and the throughput and capacity can be doubled.

It can be simply understood as an advanced version of SPI.

2.2 SFUD

SFUD is an open source serial SPI Flash universal driver library. Since there are most types of serial Flash in the market, and the specifications and commands of each Flash are different, SFUD is designed to solve these differences in Flash, so that our products can support Flash of different brands and specifications, and improve the Flash The reusability and extensibility of the functional software can also avoid the risk of Flash out of stock or discontinuation of the product.

  • Main features: support SPI/QSPI interface, object-oriented (support multiple Flash objects at the same time), flexible tailoring, strong scalability, support for 4-byte addresses
  • Resource occupation
    • Standard occupation: RAM: 0.2KB ROM: 5.5KB
    • Minimum occupation: RAM: 0.1KB ROM: 3.6KB

Open source library address: https://github.com/armink/SFUD

2.3 W25Q256FV

The following excerpts are selected from the Punctual Atom Hal library manual
Insert picture description here

3 transplantation process

Please carefully read the help documents given by the SFUD source code and the official RT-Thread manual before transplanting.

3.1 Open related components

First, enter the software package center under RT-Thread Settings under the selected project folder , as shown in the figure below, select and enable the two functions of SPI and SFUD,
Insert picture description here
and then select more configurations in the lower right corner or the left arrow symbol on the far right in the figure , Enter the interface, the configuration process is as follows, after the configuration is complete, as shown in the figure,
component → device driver → use SPI bus / device driver (check)use QSPI mode (check)use serial Flash universal driver ( SFUD) (check)check the first three options
Insert picture description here
, save after opening to update, and exit this interface after the update is complete.

If it is configured through ENV, you need to download the source program from the source URL and copy the files inside (core source files include: sfud.c, sfud_sfdp.c, sfud_port.c), but RT-Thread Studio will automatically complete the configuration for you , This is also the power of this editor, but if you are not familiar with this software and related source code configuration, you will feel clouded when you first get started. After the configuration is complete, SFUD is under the path "rt-thread → components → drivers → spi". Mainly sfud folder and spi_flash_sfud.c, spi_flash_sfud.h files.
Insert picture description here
Let's ignore SFUD, and configure QSPI first.

3.2 QSPI configuration

Open the board.h file (under the drivers path) and find the QSPI configuration information as follows:
Insert picture description here
First, add in the board.h file: #define BSP_USING_QSPI (configure just below the text in the above figure).

Then open CubeMx for relevant configuration to generate code. Determine the pins that need to be turned on as follows:
Insert picture description here
Note that the W25Q256 series is a 32M byte serial Flash chip, 32M = 32 1024 1024 = 2^25, so the configuration Flash Size should be 25-1=24. The configuration is as follows:
Insert picture description here
Insert picture description here
According to the official statement, the generated program should be placed in board.c, and then the corresponding initialization function should be placed in the main file. But this will make the program a bit bloated and complicated and inconvenient to check, so we add two new folders inc and src under the application file, create a new header file qspi.h in inc, and create a new source file qspi.c under src to store The corresponding code. In the subsequent programming process, inc and src can also be used to store other header files and source files (remember to include these two paths in the workspace to find the corresponding files and compile correctly). The internal procedures are as follows:

/* qspi.h 文件内容*/
#ifndef APPLICATIONS_INC_QSPI_H_
#define APPLICATIONS_INC_QSPI_H_

#include <board.h>

void HAL_QSPI_MspInit(QSPI_HandleTypeDef* hqspi);
void MX_QUADSPI_Init(void);


#endif /* APPLICATIONS_INC_QSPI_H_ */
/* qspi.c 文件内容*/
#include "qspi.h"

QSPI_HandleTypeDef hqspi;

/**
* @brief QSPI MSP Initialization
* This function configures the hardware resources used in this example
* @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_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**QUADSPI GPIO Configuration
    PF6     ------> QUADSPI_BK1_IO3
    PF7     ------> QUADSPI_BK1_IO2
    PF8     ------> QUADSPI_BK1_IO0
    PF9     ------> QUADSPI_BK1_IO1
    PB2     ------> QUADSPI_CLK
    PB6     ------> QUADSPI_BK1_NCS
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* USER CODE BEGIN QUADSPI_MspInit 1 */

  /* USER CODE END QUADSPI_MspInit 1 */
  }

}

/**
  * @brief QUADSPI Initialization Function
  * @param None
  * @retval None
  */
void MX_QUADSPI_Init(void)
{
    
    

  /* USER CODE BEGIN QUADSPI_Init 0 */

  /* USER CODE END QUADSPI_Init 0 */

  /* USER CODE BEGIN QUADSPI_Init 1 */

  /* USER CODE END QUADSPI_Init 1 */
  /* QUADSPI parameter configuration*/
  hqspi.Instance = QUADSPI;
  hqspi.Init.ClockPrescaler = 2;
  hqspi.Init.FifoThreshold = 4;
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 24;
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE;
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
  hqspi.Init.FlashID = QSPI_FLASH_ID_1;
  hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
  if (HAL_QSPI_Init(&hqspi) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /* USER CODE BEGIN QUADSPI_Init 2 */

  /* USER CODE END QUADSPI_Init 2 */

}

Correspondingly, since there is no function definition in the board.c file, we need to declare the relevant function in board.h to make the entire program call normally. The configuration is as follows:
Insert picture description here
At this time, the terminal is enabled after the program is compiled and downloaded into the microcontroller. And call the list_device command to see that the internal qspi bus has been turned on
Insert picture description here

3.3 SFUD configuration

Of course, it is not enough to turn on the bus. We have to mount the device on the bus to use it normally.

Under normal circumstances, after the above operations, our internal configuration in the rtconfig.h file should be as follows (because I have enabled other functions here, so it may be different, but roughly too much QSPI and SFUD configuration should be similar of)

/* Device Drivers */

#define RT_USING_DEVICE_IPC
#define RT_PIPE_BUFSZ 512
#define RT_USING_SERIAL
#define RT_SERIAL_RB_BUFSZ 64
#define RT_USING_HWTIMER
#define RT_USING_PIN
#define RT_USING_PWM
#define RT_USING_SPI
#define RT_USING_QSPI
#define RT_USING_SFUD
#define RT_SFUD_USING_SFDP
#define RT_SFUD_USING_FLASH_INFO_TABLE
#define RT_SFUD_USING_QSPI
#define RT_SFUD_SPI_MAX_HZ 50000000

Let me briefly talk about it here. If it is transplanted inside Studio, you will find that there is no sfud_port.c file. The related function RT-Thread has been integrated for you and implemented in spi_flash_sfud.c, so no configuration is required. Even the related functions of initializing QSPI have been implemented for you under the drv_qspi.c file.

Regarding the more detailed internal implementation process and porting of SFUD, please read the official documents on Github carefully. Many details are described in it.

Note that the related chip information has been defined in sfud_flash_def.h, mainly as follows:

#ifdef SFUD_USING_FLASH_INFO_TABLE
/* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
 * then the SFUD will find the flash chip information by this table. You can add other flash to here then
 *  notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
 * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
 */
#define SFUD_FLASH_CHIP_TABLE                                                                                       \
{                                                                                                                   \
    {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81},      \
    {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
    {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
    {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
    {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                      \
    {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
    {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
    {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
    {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
    {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},                     \
    {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
}
#endif /* SFUD_USING_FLASH_INFO_TABLE */

#ifdef SFUD_USING_QSPI
/* This table saves flash read-fast instructions in QSPI mode, 
 * SFUD can use this table to select the most appropriate read instruction for flash.
 * | mf_id | type_id | capacity_id | qspi_read_mode |
 */
#define SFUD_FLASH_EXT_INFO_TABLE                                                                  \
{                                                                                                  \
    /* W25Q40BV */                                                                                 \
    {
    
    SFUD_MF_ID_WINBOND, 0x40, 0x13, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* W25Q80JV */                                                                                 \
    {
    
    SFUD_MF_ID_WINBOND, 0x40, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* W25Q16BV */                                                                                 \
    {
    
    SFUD_MF_ID_WINBOND, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* W25Q32BV */                                                                                 \
    {
    
    SFUD_MF_ID_WINBOND, 0x40, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* W25Q64JV */                                                                                 \
    {
    
    SFUD_MF_ID_WINBOND, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
    /* W25Q128JV */                                                                                \
    {
    
    SFUD_MF_ID_WINBOND, 0x40, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
    /* W25Q256FV */                                                                                \
    {
    
    SFUD_MF_ID_WINBOND, 0x40, 0x19, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO},     \
    /* EN25Q32B */                                                                                 \
    {
    
    SFUD_MF_ID_EON, 0x30, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_IO},                             \
    /* S25FL216K */                                                                                \
    {
    
    SFUD_MF_ID_CYPRESS, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT},                                 \
    /* A25L080 */                                                                                  \
    {
    
    SFUD_MF_ID_AMIC, 0x30, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO},                            \
    /* A25LQ64 */                                                                                  \
    {
    
    SFUD_MF_ID_AMIC, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_IO},                    \
    /* MX25L3206E and KH25L3206E */                                                                \
    {
    
    SFUD_MF_ID_MICRONIX, 0x20, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT},                                \
    /* GD25Q64B */                                                                                 \
    {
    
    SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT},                              \
}
#endif /* SFUD_USING_QSPI */

Look carefully to see if there is any chip you are using inside. Basically most of the chips should be inside.

You can also configure the device table yourself. Modify the macro definition of SFUD_FLASH_DEVICE_TABLE in sfud_cfg.h

enum {
    
    
    SFUD_W25Q256FV_DEVICE_INDEX = 0,
};

#define SFUD_FLASH_DEVICE_TABLE                                                 \
{                                                                               \
    [SFUD_W25Q256FV_DEVICE_INDEX] = {.name = "W25Q256FV", .spi.name = "qspi1"}  \
}

Then we add the slave device driver. In the inc and src under the application path, we add flash.h and flash.c files respectively. The internal procedures are as follows:

#include "board.h"
#include "drv_qspi.h"
#include "rtdevice.h"

#define QSPI_BUS_NAME       "qspi1"
#define QSPI_DEVICE_NAME    "qspi10"
#define W25Q_FLASH_NAME     "W25Q256FV"

#define QSPI_CS_PIN         GET_PIN(B, 6)	//此处注意引脚区分,用你自己的

static int rt_hw_qspi_flash_with_sfud_init(void)
{
    
    
    if (stm32_qspi_bus_attach_device(QSPI_BUS_NAME, QSPI_DEVICE_NAME, (rt_uint32_t)QSPI_CS_PIN, 4, RT_NULL, RT_NULL) != RT_EOK)
        return -RT_ERROR;

#ifdef  RT_USING_SFUD
    if (rt_sfud_flash_probe(W25Q_FLASH_NAME, QSPI_DEVICE_NAME) == RT_NULL)
        return -RT_ERROR;
#endif

    return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_qspi_flash_with_sfud_init);

After compiling and downloading, it was found that the 32M chip was successfully read. We typed list_device in msh and got the result shown in the figure below (please ignore pwm1 and pwm8, which are my other drivers).
Insert picture description here
Use the sf command to view the device and
Insert picture description here
then transplant it Success! For more detailed commands, please check the official RT document

Please point out any errors in time

Guess you like

Origin blog.csdn.net/moumde/article/details/108002454