SPI Master DLPS

This document introduces two SPI communication samples. The sample1 demonstrates how SPI master sends data by GDMA mode in DLPS situation, which wakes up the system using a software timer. The sample2 demonstrates how SPI sends and reads data by polling in DLPS situation, which wakes up the system using a wake-up pin. In both examples, SPI is configured as a master, and the direction is full-duplex. The chip sends data to the SPI slave.

Requirements

For hardware requirements, please refer to the Requirements.

Wiring

Sample 1 Wiring

Connect P2_1 (master SCK) to SCK of SPI slave device, connect P2_2 (master MOSI) to MOSI of SPI slave device, connect P2_3 (master MISO) to MISO of SPI slave device, and connect P0_1 (master CS) to CS of SPI slave device. The hardware connection of SPI sample code is shown in the figure below.

../../../_images/SPI_Demo_10_2_Hardware_Connection_Diagram.png

SPI Sample Code Hardware Connection Diagram

Sample 2 Wiring

Connect P1_2 (master SCK) to SCK of SPI slave device, connect P1_3 (master MOSI) to MOSI of SPI slave device, connect P1_4 (master MISO) to MISO of SPI slave device, and connect P1_5 (master CS) to CS of SPI slave device. The hardware connection of SPI sample code is shown in the figure below.

../../../_images/SPI_Demo_1_1_Hardware_Connection_Diagram.png

SPI Sample Code Hardware Connection Diagram

Configurations

  1. The following macros can be configured to modify pin definitions for sample1.

    • #define SPI0_SCK P2_1

    • #define SPI0_MOSI P2_2

    • #define SPI0_MISO P2_3

    • #define SPI0_CS P0_1

  2. The following macros can be configured to modify pin definitions for sample2.

    • #define PIN_SPI0_SCK P1_2

    • #define PIN_SPI0_MOSI P1_3

    • #define PIN_SPI0_MISO P1_4

    • #define PIN_SPI0_CS P1_5

    • #define WAKE_UP_PIN P0_0

  3. The entry function are as follows, call this function in main() to run this sample code. For more details, please refer to the Initialization.

    For sample 1, use the following entry function:

    spi_master_tx_dma_dlps_demo();
    

    For sample 2, use the following entry function:

    spi_dlps_demo();
    

Building and Downloading

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

Experimental Verification

Sample 1 Verification

  1. Press the Reset button on the EVB, the device enters DLPS and prints log.

    spi_dlps_enter
    
  2. After being awakened by the timer every 1 second, the data in the array SPI_TX_Buf is sent to SPI slave device. When completing the transmission, it enters the GDMA interrupt and prints log.

    spi_dlps_exit
    timer_callback
    spi_master_tx_dma_handler! tx_len_all 1000
    
  3. When SPI TX FIFO level is equal to or below its threshold value, trigger the SPI_INT_TXE interrupt and prints log. Then the chip can enter DLPS again.

    spi_tx_handler: SPI TX FIFO Empty
    

Sample 2 Verification

  1. Press the Reset button on the EVB.

  2. The data in the array SPI_WriteBuf is sent to SPI slave device. If the slave device sends the same data to the chip, the chip stores the received data in array SPI_ReadBuf and prints the data in Debug Analyzer.

    SPI_DemoCode: SPI_ReadBuf[0] 0x9f
    SPI_DemoCode: SPI_ReadBuf[1] 0x2
    SPI_DemoCode: SPI_ReadBuf[2] 0x0
    SPI_DemoCode: SPI_ReadBuf[3] 0x0
    
  3. After the transmission is complete, the system is in idle state, it will enter DLPS mode. Observe the entering DLPS message displayed in the Debug Analyzer.

    DlpsEnterCallback: enter dlps
    
  4. Pull down the wake-up pin to wake up the system. Observe the exiting DLPS message displayed in the Debug Analyzer.

    DlpsExitCallback
    
  5. After exiting DLPS, enter the system wake up event, then print the SPI regiter and repeat the step2 to write and read data.

Code Overview

Source Code Directory

For both samples, please refer to the Source Code Directory for the project directory.

Sample 1 source code:

  • Source code directory: sdk\src\sample\io_demo\spi\dlps\spi_master_tx_dma_dlps_demo.c .

Sample 2 source code:

  • Source code directory: sdk\src\sample\io_demo\spi\dlps\spi_dlps_demo.c .

DLPS Mode Initialization (Sample 1)

  1. Call io_dlps_register() to initialize IO store/restore and do not need to worry about which IO peripheral requires specific handling.

  2. Call power_check_cb_register() to register inquiry callback function to DLPS framework. This function will be called each time before entering DLPS to decide whether DLPS is allowed to enter. DLPS will be disallowed if any inquiry callback function returns false. Function io_dlps_check will be executed before entering DLPS.

  3. Call io_dlps_register_enter_cb() to register callbacks to DLPS enter stage. Function spi_dlps_enter will be executed while entering DLPS.

  4. Call io_dlps_register_exit_cb() to register callbacks to DLPS exit stage. Function spi_dlps_exit will be executed while exiting from DLPS:

    1. Set allowedSystemEnterDlps to false to not allow the system to enter DLPS mode.

    2. Call spi_master_tx_dma_init to reinitialize the GDMA peripheral.

  5. Call bt_power_mode_set() to set Bluetooth MAC deep sleep mode.

  6. Call power_mode_set() to switch the system to DLPS mode.

Software Timer Initialization (Sample 1)

  1. Call os_timer_create() to create a software timer with a period value of 1 seconds and reload mode.

  2. Call os_timer_start() to start the software timer after setting the system to DLPS mode.

SPI TX DMA Initialization Flow (Sample 1)

The initialization flow for peripherals can refer to Initialization Flow.

The SPI TX DMA initialization flow requires first initializing the SPI peripheral, followed by TX DMA initialization.

SPI Initialization Flow

The SPI initialization flow can refer to SPI Initialization Flow Chart.

  1. Call Pad_Config() and Pinmux_Config() to initialize the pin.

    static void board_spi_init(void)
    {
       Pinmux_Config(SPI0_SCK, SPI0_CLK_MASTER);
       Pinmux_Config(SPI0_MOSI, SPI0_MO_MASTER);
       Pinmux_Config(SPI0_MISO, SPI0_MI_MASTER);
       Pinmux_Config(SPI0_CS, SPI0_SS_N_0_MASTER);
    
       Pad_Config(SPI0_SCK, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_HIGH);
       Pad_Config(SPI0_MOSI, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_HIGH);
       Pad_Config(SPI0_MISO, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_HIGH);
       Pad_Config(SPI0_CS, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_HIGH);
    }
    
  2. Call RCC_PeriphClockCmd() to enable the SPI clock and function.

  3. Initialize the SPI peripheral:

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

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

    3. Call SPI_Init() to initialize the SPI peripheral, SPI0_HS is seleted.

    SPI Initialization Parameters

    SPI Hardware Parameters

    Setting in the SPI_InitStructure

    SPI

    Direction

    SPI_InitTypeDef::SPI_Direction

    SPI_Direction_FullDuplex

    Device Role (SPI Master or SPI Slave)

    SPI_InitTypeDef::SPI_Mode

    SPI_Mode_Master

    Data Frame Size

    SPI_InitTypeDef::SPI_DataSize

    SPI_DataSize_8b

    Clock Polarity

    SPI_InitTypeDef::SPI_CPOL

    SPI_CPOL_Low

    Clock Phase

    SPI_InitTypeDef::SPI_CPHA

    SPI_CPHA_1Edge

    Clock Div

    SPI_InitTypeDef::SPI_BaudRatePrescaler

    100

    Frame Format

    SPI_InitTypeDef::SPI_FrameFormat

    SPI_Frame_Motorola

    Transmit FIFO Threshold Level

    SPI_InitTypeDef::SPI_TxThresholdLevel

    0

    RX Water Level

    SPI_InitTypeDef::SPI_TxWaterlevel

    7

  4. Call RamVectorTableUpdate() to register the SPI interrupt handler.

  5. Call SPI_Cmd() to enable SPI.

TX DMA Initialization Flow

The SPI TX DMA initialization flow can refer to SPI TX DMA Initialization Flow Chart.

  1. Call GDMA_channel_request to request a free GDMA channel and register the GDMA interrupt handler.

  2. Call RCC_PeriphClockCmd() to enable the GDMA clock and function.

  3. Initialize the GDMA peripheral:

    1. Define the 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 GDMA initialization parameter configuration is shown in the table below.

    3. Call GDMA_Init() to initialize the GDMA peripheral.

    GDMA Initialization Parameters

    GDMA Hardware Parameters

    Setting in the GDMA_InitStruct

    GDMA

    Channel Num

    GDMA_InitTypeDef::GDMA_ChannelNum

    SPI_MASTER_TX_DMA_CHANNEL_NUM

    Transfer Direction

    GDMA_InitTypeDef::GDMA_DIR

    GDMA_DIR_MemoryToPeripheral

    Buffer Size

    GDMA_InitTypeDef::GDMA_BufferSize

    SPI_DATA_LEN

    Source Address Increment or Decrement

    GDMA_InitTypeDef::GDMA_SourceInc

    DMA_SourceInc_Inc

    Destination Address Increment or Decrement

    GDMA_InitTypeDef::GDMA_DestinationInc

    DMA_DestinationInc_Fix

    Source Data Size

    GDMA_InitTypeDef::GDMA_SourceDataSize

    GDMA_DataSize_Byte

    Destination Data Size

    GDMA_InitTypeDef::GDMA_DestinationDataSize

    GDMA_DataSize_Byte

    Source Burst Transaction Length

    GDMA_InitTypeDef::GDMA_SourceMsize

    GDMA_Msize_1

    Destination Burst Transaction Length

    GDMA_InitTypeDef::GDMA_DestinationMsize

    GDMA_Msize_1

    Source Address

    GDMA_InitTypeDef::GDMA_SourceAddr

    SPI_TX_Buf

    Destination Address

    GDMA_InitTypeDef::GDMA_DestinationAddr

    SPI0->DR

    Destination Handshake

    GDMA_InitTypeDef::GDMA_DestHandshake

    GDMA_Handshake_SPI0_TX

  4. Call GDMA_INTConfig() to enable TX GDMA transfer complete interrupt GDMA_INT_Transfer.

  5. Call NVIC_Init() to enable NVIC of TX GDMA.

DLPS Mode Initialization (Sample 2)

  1. Call io_dlps_register() to initialize IO store/restore and do not need to worry about which IO peripheral requires specific handling.

  2. Call io_dlps_register_enter_cb() to register callbacks to DLPS enter stage. Function DlpsEnterCallback will be executed while entering DLPS:

    1. Call Pad_PullUpOrDownValue() to config WAKE_UP_PIN pull up.

    2. Call System_WakeUpPinEnable() to enable the wake-up function of WAKE_UP_PIN.

  1. Call io_dlps_register_exit_cb() to register callbacks to DLPS exit stage. Function DlpsExitCallback will be executed while exiting from DLPS.

  2. Call bt_power_mode_set() to set Bluetooth MAC deep sleep mode.

  3. Call power_mode_set() to switch the system to DLPS mode.

SPI TX Initialization (Sample 2)

The initialization flow for peripherals can refer to Initialization Flow. More details please refer to TX Polling Initialization Flow.

Functional Implementation

Software Timer Callback Handle (Sample 1)

When the software timer expires, the software timer callback function will be executed:

  1. Call SPI_GDMACmd() to disable and then enable SPI GDMA TX Function.

  2. Call GDMA_Cmd() to enable TX DMA transfers.

  3. Call SPI_INTConfig() to enable TX FIFO empty interrupt SPI_INT_TXE.

SPI TX DMA Interrupt Handle (Sample 1)

When GDMA transfer is completed, transfer complete interrupt is triggered:

  1. Call GDMA_GetTransferLen() to get the GDMA transfer data length.

  2. Call GDMA_INTConfig() to disable GDMA transfer complete interrupt GDMA_INT_Transfer.

  3. Call GDMA_ClearINTPendingBit() to clear GDMA_INT_Transfer interrupt.

  4. Call GDMA_INTConfig() to enable GDMA transfer complete interrupt GDMA_INT_Transfer for next transmission.

SPI TX Interrupt Handle (Sample 1)

When the transmit buffer reaches or goes below the TX FIFO threshold level (SPI_InitTypeDef::SPI_TxThresholdLevel), TX FIFO empty interrupt will be triggered and enters the interrupt handler:

  1. Call SPI_GetINTStatus() to check SPI_INT_TXE interrupt status.

  2. Call SPI_INTConfig() to disable SPI_INT_TXE.

  3. Set allowedSystemEnterDlps to true to allow the system to enter DLPS mode.

Send and Receive Data by Polling (Sample 2)

  1. Call SPI_SendBuffer() to send the data in SPI_WriteBuf to the slave.

  2. Call SPI_GetFlagState() to check SPI_FLAG_BUSY flag state, and wait for the SPI data transfer to complete.

  3. Call SPI_GetRxFIFOLen() to get the data length in RX FIFO.

  4. Call SPI_ReceiveData() to receive data from RX FIFO.