GDMA Single Block

This sample demonstrates how to use SPI to transfer data via GDMA.

The sample uses full-duplex mode for communication, with SPI communicating with external flash via GDMA to perform data reading, writing, erasing, and other 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 configure macros to choose whether to use software-controlled CS line communication, modify pin settings, and adjust GDMA channel settings and other parameters. For detailed 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#.

Hardware Introduction

The W25Q128 is a NOR Flash chip with SPI interface, which supports the standard Serial Peripheral Interface (SPI), Dual/Quad I/O SPI as well as 2-clocks instruction cycle Quad Peripheral Interface (QPI).

Additionally, the device supports JEDEC standard manufacturer and device ID and SFDP Register, a 64-bit Unique Serial Number and three 256-byte Security Registers.

Additional details can be found in the application guide for this device. In this example the device is used as a slave for SPI communication test.

Configurations

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

    #define CONFIG_SPI_SW_SIM_CS                   1            /*< Set this macro to 1 to enable software control CS. */
    
  2. The following macros can be configured to modify pin definitions.

    #define SPI_SCK_PIN                     P4_0
    #define SPI_MOSI_PIN                    P4_1
    #define SPI_MISO_PIN                    P4_2
    #define SPI_CS_PIN                      P4_3
    
  3. The following macros can be configured to modify the GDMA TX and GDMA RX channel settings.

    // Set the following macros to modify the SPI TX GDMA Channel configurations.
    #define GDMA_TX_CHANNEL_NUM                 2
    #define GDMA_TX_CHANNEL                     GDMA_Channel2
    #define GDMA_TX_CHANNEL_IRQN                GDMA0_Channel2_IRQn
    #define GDMA_TX_CHANNEL_HANDLER             GDMA0_Channel2_Handler
    
    // Set the following macros to modify the SPI RX GDMA Channel configurations.
    #define GDMA_RX_CHANNEL_NUM                 4
    #define GDMA_RX_CHANNEL                     GDMA_Channel4
    #define GDMA_RX_CHANNEL_IRQN                GDMA0_Channel4_IRQn
    #define GDMA_RX_CHANNEL_HANDLER             GDMA0_Channel4_Handler
    

Building and Downloading

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

Experimental Verification

  1. After initialization is complete, SPI sends commands to the external flash via GDMA TX to perform operations such as data reading, writing, and erasing. At the same time, it reads the response data from the external flash via GDMA RX.

  2. When the data transmission of the GDMA TX channel is complete, log information will be printed.

    gdma tx data finish!
    
  3. When the GDMA RX channel data transfer is completed, it will print the transfer completion statement, the length of the received data and the data content.

    1. When flash id is read, the following will be printed: the first byte is dummy read, and the last three bytes are JEDEC_ID contents.

      data_len = 4
      dma rx data[0] = 000000ff
      dma rx data[1] = 000000ef
      dma rx data[2] = 00000040
      dma rx data[3] = 00000018
      
    2. When the write operation is enabled, the following is printed: the first byte is dummy read.

      data_len = 1
      dma rx data[0] = 000000ff
      
    3. When the erase operation is performed, the following is printed: the four bytes corresponding to the write operation are dummy read.

      data_len = 4
      dma rx data[0] = 000000ff
      dma rx data[1] = 000000ff
      dma rx data[2] = 000000ff
      dma rx data[3] = 000000ff
      
    4. After the erase is completed, read the data under this address and print the following: Since the data under this address is erased, all the data read is FF and the first 5 bytes are dummy read.

      data_len = 105
      dma rx data[0] = 000000ff
      dma rx data[1] = 000000ff
      ...
      dma rx data[104] = 000000ff
      
    5. Execute a data write operation, writing 100 pieces of data such as {a, b, …} into the specified memory. The following is printed: the first 4 bytes corresponding to the write operation are the dummy read of the instruction, and the last 100 bytes are the dummy read of the data.

      data_len = 104
      dma rx data[0] = 000000ff
      ...
      dma rx data[103] = 000000ff
      
    6. After writing the data, the data is read again, and the content of the read data is printed: the first 5 bytes are dummy read, and the last 100 bytes are the read data.

      data_len = 105
      dma rx data[0] = 000000ff
      dma rx data[1] = 000000ff
      dma rx data[2] = 000000ff
      dma rx data[3] = 000000ff
      dma rx data[4] = 000000ff
      dma rx data[5] = 0000000a
      dma rx data[6] = 0000000b
      dma rx data[7] = 0000000c
      ...
      dma rx data[104] = 0000006d
      

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\gdma_singleblock\proj

  • Source code directory: sdk\samples\peripheral\spi\gdma_singleblock\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. If CONFIG_SPI_SW_SIM_CS is set to 1, the CS pin needs to be configured to SW mode.

    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);
    
        Pinmux_Deinit(P4_0);
        Pinmux_Deinit(P4_1);
        Pinmux_Deinit(P4_2);
        Pinmux_Deinit(P4_3);
    
        Pinmux_Config(SPI_SCK_PIN, SPI0_CLK_MASTER);
        Pinmux_Config(SPI_MOSI_PIN, SPI0_MO_MASTER);
        Pinmux_Config(SPI_MISO_PIN, SPI0_MI_MASTER);
    
    #if (CONFIG_SPI_SW_SIM_CS == 1)
        Pad_Config(SPI_CS_PIN, PAD_SW_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    #else
        Pad_Config(SPI_CS_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_ENABLE, PAD_OUT_HIGH);
        Pinmux_Config(SPI_CS_PIN, SPI0_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 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

TX GDMA Enable

SPI_InitTypeDef::SPI_TxDmaEn

ENABLE

RX GDMA Enable

SPI_InitTypeDef::SPI_RxDmaEn

ENABLE

TX Waterlevel

SPI_InitTypeDef::SPI_TxWaterlevel

31

RX Waterlevel

SPI_InitTypeDef::SPI_RxWaterlevel

0

Note

When using GDMA for SPI transmission, it is recommended to set the TX Waterlevel to SPI_TX_FIFO_SIZE - MSize, and the RX Waterlevel to MSize - 1.

  1. Call RCC_PeriphClockCmd() to enable the GDMA clock.

  2. Initialize the GDMA peripheral:

    1. Define a GDMA_InitTypeDef type GDMA_InitStruct, and call GDMA_StructInit() to pre-fill GDMA_InitStruct with default values.

    2. Modify the GDMA_InitStruct parameters as needed. The initialization parameters for the GDMA TX and RX channels are configured as shown in the table below. Call GDMA_Init() to initialize the GDMA peripheral.

    3. Configure the GDMA total transfer complete interrupt GDMA_INT_Transfer and NVIC. For NVIC configurations, refer to Interrupt Configuration.

GDMA Initialization Parameters

GDMA Hardware Parameters

Setting in the GDMA_InitStruct

GDMA TX Channel

GDMA RX Channel

Channel Num

GDMA_InitTypeDef::GDMA_ChannelNum

2

4

Transfer Direction

GDMA_InitTypeDef::GDMA_DIR

GDMA_DIR_MemoryToPeripheral

GDMA_DIR_PeripheralToMemory

Buffer Size

GDMA_InitTypeDef::GDMA_BufferSize

-

-

Source Address Increment or Decrement

GDMA_InitTypeDef::GDMA_SourceInc

DMA_SourceInc_Inc

DMA_SourceInc_Fix

Destination Address Increment or Decrement

GDMA_InitTypeDef::GDMA_DestinationInc

DMA_DestinationInc_Fix

DMA_DestinationInc_Inc

Source Data Size

GDMA_InitTypeDef::GDMA_SourceDataSize

GDMA_DataSize_Byte

GDMA_DataSize_Byte

Destination Data Size

GDMA_InitTypeDef::GDMA_DestinationDataSize

GDMA_DataSize_Byte

GDMA_DataSize_Byte

Source Burst Transaction Length

GDMA_InitTypeDef::GDMA_SourceMsize

GDMA_Msize_1

GDMA_Msize_1

Destination Burst Transaction Length

GDMA_InitTypeDef::GDMA_DestinationMsize

GDMA_Msize_1

GDMA_Msize_1

Source Address

GDMA_InitTypeDef::GDMA_SourceAddr

GDMA_Send_Buffer

SPI0->SPI_DR

Destination Address

GDMA_InitTypeDef::GDMA_DestinationAddr

SPI0->SPI_DR

GDMA_Recv_Buffer

Source Handshake

GDMA_InitTypeDef::GDMA_SourceHandshake

-

GDMA_Handshake_SPI0_RX

Destination Handshake

GDMA_InitTypeDef::GDMA_DestHandshake

GDMA_Handshake_SPI0_TX

-

Functional Implementation

  1. According to the hardware information and communication protocol of the W25Q128, the length and content of data that SPI needs to send vary for different operations. Hence, it is necessary to set the GDMA data length and content before each communication begins. Call GDMA_SetBufferSize() to set the Block Size for the corresponding GDMA channel. Since SPI is set for full-duplex communication, the same Block Size needs to be set for both the GDMA TX channel and the GDMA RX channel.

  2. After the settings are completed, enable the SPI, GDMA TX, and GDMA RX channels. When the GDMA TX and GDMA RX channels have finished transmission, the corresponding GDMA interrupt will be triggered. In the GDMA RX interrupt, the data information replied by the external flash will be printed, and within the respective interrupt, the isDMATxDone and isDMARxDone will be set to true.

  3. When both flag bits are found to be set to true, it indicates that the communication is complete, and the next communication process can begin.

  4. If CONFIG_SPI_SW_SIM_CS is configured to 1, it is necessary to call pull_cs_down to pull down and pull up the CS line at the beginning and end of each communication, respectively.

    memset(GDMA_Send_Buffer, 0, sizeof(GDMA_Send_Buffer) / sizeof(GDMA_Send_Buffer[0]));
    memset(GDMA_Recv_Buffer, 0, sizeof(GDMA_Recv_Buffer) / sizeof(GDMA_Recv_Buffer[0]));
    
    GDMA_Send_Buffer[0] = SPI_FLASH_JEDEC_ID;
    GDMA_SetBufferSize(GDMA_TX_CHANNEL, 4);
    GDMA_SetBufferSize(GDMA_RX_CHANNEL, 4);
    
    GDMA_INTConfig(GDMA_TX_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE);
    GDMA_INTConfig(GDMA_RX_CHANNEL_NUM, GDMA_INT_Transfer, ENABLE);
    
    pull_cs_down(true);
    
    SPI_Cmd(SPI0, ENABLE);
    GDMA_Cmd(GDMA_RX_CHANNEL_NUM, ENABLE);
    GDMA_Cmd(GDMA_TX_CHANNEL_NUM, ENABLE);
    
    DBG_DIRECT("after dma cmd enable");
    
    //wait for read id finish!
    while ((isDMATxDone != true) || (isDMARxDone != true))
    {
        DBG_DIRECT("isDMATxDone = %d", isDMATxDone);
        DBG_DIRECT("isDMARxDone = %d", isDMARxDone);
        platform_delay_ms(1000);
    }
    
    isDMATxDone = false;
    isDMARxDone = false;
    
    pull_cs_down(false);
    ...
    
  5. 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.

    spi_flash_read_id();
    //sector erase
    spi_flash_sector_erase(0x001000);
    
    spi_flash_read(SPI_FLASH_FAST_READ, 0x001000, read_data, 100);
    
    spi_flash_page_write(0x001000, write_data, 100);
    
    spi_flash_read(SPI_FLASH_FAST_READ, 0x001000, read_data, 100);