Polling

This sample demonstrates communication with external flash using the SPI polling mode.

In the sample, communication is conducted using the polling method, employing both full-duplex mode and EEPROM mode to perform data reading, erasing, and writing operations on the external flash.

The sample uses W25Q128 as the external flash to communicate with SPI. For an introduction to this hardware, see Hardware Introduction.

Users can select the SPI communication mode through macro configuration, choose whether to use software to control the CS line communication, modify pin settings, and other parameters. For specific macro configurations, see Configurations.

Note

The purpose of using software to control the CS line is to ensure that SPI does not experience data flow interruption during transmission. If a Tx underflow occurs during SPI transmission, it may cause the CS line to be pulled high, leading to communication issues. Using software control for the CS line ensures that the CS line will not be pulled high until the current communication is complete, ensuring the integrity of the communication.

Requirements

For requirements, please refer to the Requirements.

Wiring

EVB is connected to W25Q128 module: connect P4_0(SCK) to CLK, P4_1(MISO) to DO, P4_2(MOSI) to DI, P4_3(CS) to CS#.

Note

For module description of W25Q128, please refer to W25Q128 hardware introduction

Configurations

  1. The following macro can be configured to modify whether to use software control for the CS line.

    #define SPI_CONFIG_GPIO_SIM_CS                   1                 /*< Set this macro to 1 to enable software control CS. */
    
  2. The following macro can be configured to modify the SPI communication mode to full-duplex or EEPROM mode.

    #define SPI_MODE_FULLDUPLEX             0
    #define SPI_MODE_EEPROM                 3
    
    #define SPI_MODE                        SPI_MODE_FULLDUPLEX    /*< Set this macro to change the SPI transfer mode. */
    
  3. The following macro can be configured to modify the pin definitions.

    #define SPI_SCK_PIN                     P4_0
    #define SPI_MISO_PIN                    P4_1
    #define SPI_MOSI_PIN                    P4_2
    #define SPI_CS_PIN                      P4_3
    

Building and Downloading

For building and downloading, please refer to the Building and Downloading.

Experimental Verification

  1. When the IC resets, observe the following log within the Debug Analyzer.

    Start spi polling test!
    
  2. SPI transfer mode configuration:

    1. If SPI_MODE is configured as SPI_MODE_FULLDUPLEX, print the following log

      SPI is set to FullDuplex Mode!
      
    2. If SPI_MODE is configured as SPI_MODE_EEPROM, print the following log

      SPI is set to EEPROM Mode!
      
  3. Read external flash ID.

    1. Read DEVICE_ID, the first three bytes are dummy read, the last byte is DEVICE_ID.

      flash id = 000000ff
      flash id = 000000ff
      flash id = 000000ff
      flash id = 00000017
      
    2. Read MF_DEVICE_ID, the first three bytes are dummy read, the last two bytes are DEVICE_ID.

      flash id = 000000ff
      flash id = 000000ff
      flash id = 000000ff
      flash id = 000000ef
      flash id = 00000017
      
    3. Read JEDEC_ID.

      flash id = 000000ef
      flash id = 00000040
      flash id = 00000018
      
  4. Erase the data at the specified address in the Flash, and read the erased data (all FF).

    spi_demo: spi_flash_sector_erase done
    spi_demo: after erase read_data[0] = 0x000000ff
    ...
    spi_demo: after erase read_data[99] = 0x000000ff
    
  5. Write data at the specified address in the Flash, and then read the data.

    1. In Fullduplex mode, since four bytes of data are sent, the first four bytes of the received data are FF (invalid data), and the fifth byte onwards is valid data.

      spi_demo: after write read_data[0] = 0x000000ff
      spi_demo: after write read_data[1] = 0x000000ff
      spi_demo: after write read_data[2] = 0x000000ff
      spi_demo: after write read_data[3] = 0x000000ff
      spi_demo: after write read_data[4] = 0x0000000a
      ...
      spi_demo: after write read_data[99] = 0x00000069
      
    2. In EEPROM mode, data is not received when it is sent, so all data received is valid information.

      spi_demo: after write read_data[0] = 0x0000000a
      spi_demo: after write read_data[1] = 0x0000000b
      ...
      spi_demo: after write read_data[99] = 0x0000006d
      

Code Overview

This section introduces the code and process description for initialization and corresponding function implementation in the sample.

Source Code Directory

The directory for project file and source code are as follows:

  • Project directory: sdk\samples\peripheral\spi\polling\proj

  • Source code directory: sdk\samples\peripheral\spi\polling\src

Initialization

The initialization flow for peripherals can refer to Initialization Flow in General Introduction.

  1. Call Pad_Config() and Pinmux_Config() to configure the PAD and PINMUX of the corresponding pins.

    void board_spi_init(void)
    {
       Pad_Config(SPI_SCK_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_MOSI_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_MISO_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
       Pad_Config(SPI_CS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    
       Pinmux_Deinit(SPI_SCK_PIN);
       Pinmux_Deinit(SPI_MOSI_PIN);
       Pinmux_Deinit(SPI_MISO_PIN);
       Pinmux_Deinit(SPI_CS_PIN);
    
       Pinmux_Config(SPI_SCK_PIN, SPI_CLK_MASTER);
       Pinmux_Config(SPI_MOSI_PIN, SPI_MO_MASTER);
       Pinmux_Config(SPI_MISO_PIN, SPI_MI_MASTER);
    #if (SPI_CONFIG_GPIO_SIM_CS == 1)
       Pinmux_Config(SPI_CS_PIN, DWGPIO);
    #else
       Pinmux_Config(SPI_CS_PIN, SPI_CSN_0_MASTER);
    #endif
    }
    
  2. Call RCC_PeriphClockCmd() to enable the SPI clock.

  3. Initialize the SPI peripheral:

    1. Define the SPI_InitTypeDef type SPI_InitStruct, and call SPI_StructInit() to pre-fill SPI_InitStruct with default values.

    2. Modify the SPI_InitStruct parameters as needed. The SPI initialization parameter configuration is shown in the table below.

    3. Call SPI_Init() to initialize the SPI peripheral.

SPI Initialization Parameters

SPI Hardware Parameters

Setting in the SPI_InitStruct

SPI

SPI Transfer Mode

SPI_InitTypeDef::SPI_Direction

SPI_Direction_FullDuplex (SPI_MODE is set to SPI_MODE_FULLDUPLEX), SPI_Direction_EEPROM (SPI_MODE is set to SPI_MODE_EEPROM)

SPI Data Size

SPI_InitTypeDef::SPI_DataSize

SPI_DataSize_8b

Clock Mode - CPOL

SPI_InitTypeDef::SPI_CPOL

SPI_CPOL_High

Clock Mode - CPHA

SPI_InitTypeDef::SPI_CPHA

SPI_CPHA_2Edge

Clock Prescaler

SPI_InitTypeDef::SPI_BaudRatePrescaler

20

Frame Format

SPI_InitTypeDef::SPI_FrameFormat

SPI_Frame_Motorola

  1. Call SPI_Cmd() to enable SPI peripheral.

Functional Implementation

The flow of SPI communication in polling mode is shown in the diagram:

Here should be SPI polling flow

SPI polling mode flow

  1. According to the hardware information and communication protocol of W25Q128, the length and content of data that SPI needs to send vary during different operations. Additionally, the logic for reading and sending differs in various SPI communication modes.

  2. Taking read external flash ID information as an example, the program needs to separately read external flash DEVICE_ID / MF_DEVICE_ID / JEDEC_ID information.

    1. Prepare the data content to be sent. Modify the first byte of the data to be sent according to the different instruction and the required data length based on the type of ID to be read.

    2. If the macro SPI_MODE is configured as SPI_MODE_EEPROM:

      1. Before sending data, call SPI_SetReadLen() to set the data length to be read.

      2. Since the send and receive logic in EEPROM communication mode are conducted separately, after setting the receive length, only call SPI_SendBuffer() to send a one-byte instruction data, and subsequently call SPI_ReceiveData() function in a loop for data reception.

    3. If the macro SPI_MODE is configured as SPI_MODE_FULLDUPLEX:

      1. Call SPI_SendBuffer() to send data. As full-duplex mode communication involves simultaneous sending and receiving, in addition to the instruction data, you need to send the number of bytes required by the receive logic to maintain the continuous SCK signal.

      2. After sending data, call SPI_ReceiveData() to read the data replied from the device side. In full-duplex mode, the first byte read corresponds to invalid data replying to the sent instruction byte, so it can be omitted from storage in the array.

    4. If the macro SPI_CONFIG_GPIO_SIM_CS is configured as 1, then before and after each communication, call GPIO_ResetBits() and GPIO_SetBits() to respectively pull down and pull up the CS line.

    void spi_flash_read_id(Flash_ID_Type vFlashIdType, uint8_t *pFlashId)
    {
    #if (SPI_CONFIG_GPIO_SIM_CS == 1)
       GPIO_ResetBits(GPIO_GetPort(SPI_CS_PIN), GPIO_GetPin(SPI_CS_PIN));
    #endif
       uint8_t send_buf[4] = {SPI_FLASH_JEDEC_ID, 0, 0, 0};
       uint8_t recv_len = 3;
    
       switch (vFlashIdType)
       {
       ...
       }
    
    #if (SPI_MODE_EEPROM == SPI_MODE)
       SPI_SetReadLen(FLASH_SPI, recv_len);
       SPI_SendBuffer(FLASH_SPI, send_buf, 1);
    #elif (SPI_MODE_FULLDUPLEX == SPI_MODE)
       SPI_SendBuffer(FLASH_SPI, send_buf, recv_len + 1);
       while (SPI_GetFlagState(FLASH_SPI, SPI_FLAG_RFNE) == RESET){}
       SPI_ReceiveData(FLASH_SPI);//dummy read data
    #endif
    
       *pFlashId++ = recv_len;
       uint8_t idx = 1;
       while (recv_len--)
       {
          while (RESET == SPI_GetFlagState(FLASH_SPI, SPI_FLAG_RFNE)){}
          *pFlashId++ = SPI_ReceiveData(FLASH_SPI);
       }
    
    #if (SPI_CONFIG_GPIO_SIM_CS == 1)
       GPIO_SetBits(GPIO_GetPort(SPI_CS_PIN), GPIO_GetPin(SPI_CS_PIN));
    #endif
    }
    
  3. During a write operation, under this communication logic, only data needs to be sent without receiving data. At this time, the EEPROM mode cannot support this scenario, so it is necessary to call SPI_ChangeDirection() to switch the communication mode to Tx Only mode for sending data. After the data is sent, call SPI_ChangeDirection() again to switch the mode back to EEPROM mode.

    SPI_ChangeDirection(FLASH_SPI, SPI_Direction_TxOnly);
    SPI_SendBuffer(FLASH_SPI, send_buf, 4);
    while (SPI_GetFlagState(FLASH_SPI, SPI_FLAG_BUSY) == SET){}
    SPI_ChangeDirection(FLASH_SPI, SPI_Direction_EEPROM);
    ...
    
  1. In the main function, call spi_flash_read_id to read the ID information of the external flash. Call spi_flash_sector_erase to erase the data at the specified address. After erasing, call spi_flash_read to read the data at the corresponding address to check if the erasure was successful. Call spi_flash_page_write to write data to the specified address, and after writing, read the data at that address again to check if the write was successful.

    void spi_demo(void)
    {
       ...
       while (flash_id_type < 3)
       {
          spi_flash_read_id((Flash_ID_Type)flash_id_type, flash_id);
          ...
          flash_id_type++;
          memset(flash_id, 0, sizeof(flash_id));
       }
    
       spi_flash_sector_erase(0x001000);
    
       spi_flash_read(SPI_FLASH_FAST_READ, 0x001000, read_data, 105);
       ...
    
       spi_flash_page_write(0x001000, write_data, 100);
       spi_flash_read(SPI_FLASH_FAST_READ, 0x001000, read_data, 105);
       ...
    }