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
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. */
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
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
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.
When the data transmission of the GDMA TX channel is complete, log information will be printed.
gdma tx data finish!
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.
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
When the write operation is enabled, the following is printed: the first byte is dummy read.
data_len = 1 dma rx data[0] = 000000ff
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
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
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
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.
Call
Pad_Config()
andPinmux_Config()
to configure the PAD and PINMUX of the corresponding pins. IfCONFIG_SPI_SW_SIM_CS
is set to1
, 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 }
Call
RCC_PeriphClockCmd()
to enable the SPI clock.Initialize the SPI peripheral:
Define the
SPI_InitTypeDef
typeSPI_InitStruct
, and callSPI_StructInit()
to pre-fillSPI_InitStruct
with default values.Modify the
SPI_InitStruct
parameters as needed. The SPI initialization parameter configuration is shown in the table below.Call
SPI_Init()
to initialize the SPI peripheral.
SPI Hardware Parameters |
Setting in the |
SPI |
---|---|---|
SPI Transfer Mode |
||
SPI Data Size |
||
Clock Mode - CPOL |
||
Clock Mode - CPHA |
||
Clock Prescaler |
20 |
|
Frame Format |
||
TX GDMA Enable |
||
RX GDMA Enable |
||
TX Waterlevel |
31 |
|
RX Waterlevel |
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.
Call
RCC_PeriphClockCmd()
to enable the GDMA clock.Initialize the GDMA peripheral:
Define a
GDMA_InitTypeDef
typeGDMA_InitStruct
, and callGDMA_StructInit()
to pre-fillGDMA_InitStruct
with default values.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. CallGDMA_Init()
to initialize the GDMA peripheral.Configure the GDMA total transfer complete interrupt
GDMA_INT_Transfer
and NVIC. For NVIC configurations, refer to Interrupt Configuration.
GDMA Hardware Parameters |
Setting in the |
GDMA TX Channel |
GDMA RX Channel |
---|---|---|---|
Channel Num |
2 |
4 |
|
Transfer Direction |
|||
Buffer Size |
- |
- |
|
Source Address Increment or Decrement |
|||
Destination Address Increment or Decrement |
|||
Source Data Size |
|||
Destination Data Size |
|||
Source Burst Transaction Length |
|||
Destination Burst Transaction Length |
|||
Source Address |
|
|
|
Destination Address |
|
|
|
Source Handshake |
- |
||
Destination Handshake |
- |
Functional Implementation
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.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
andisDMARxDone
will be set totrue
.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.If
CONFIG_SPI_SW_SIM_CS
is configured to1
, it is necessary to callpull_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); ...
In the main function, call
spi_flash_read_id
to read the ID information of the external flash. Callspi_flash_sector_erase
to erase the data at the specified address. After erasing, callspi_flash_read
to read the data at the corresponding address to check if the erasure was successful. Callspi_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);