SPI Slave DLPS

This document introduces two SPI communication samples. The two sample demonstrates how SPI slave receives data by GDMA mode in DLPS situation.

Sample 1 uses a GPIO pin to wake up the system. Users can wake up the system to receive data by pulling the pin high, and return to DLPS mode by pulling the pin low.

Sample 2 wakes up the system using the SPI CS pin, CS pin will pull low when master device sends data. And add software timer to allow system enter DLPS. It should be noted that before sending each packet of data, the master needs to send an extra dummy data to wake up the slave. Since the wake-up takes time, the data used for wake-up will be lost.

In both examples, SPI is configured as a slave, and the direction is full-duplex. The chip reads data from the master when exit DLPS.

Requirements

For hardware requirements, please refer to the Requirements.

Wiring

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

../../../_images/SPI_Demo_12_1_Hardware_Connection_Diagram.png

SPI Sample Code Hardware Connection Diagram

Configurations

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

    • #define PIN_SPI_SCK P2_1

    • #define PIN_SPI_MOSI P2_2

    • #define PIN_SPI_MISO P2_3

    • #define PIN_SPI_CS P0_1

  2. 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_dlps_slave_rx_dma_gpio_demo();
    

    For sample 2, use the following entry function:

    spi_dlps_slave_rx_dma_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.

  2. After initialization is complete, the system is in idle state, it will enter DLPS mode. Observe the entering DLPS message displayed in the Debug Analyzer.

    spi_dlps_enter
    
  3. Pull up the wake-up pin to wake up the system. Observe the exiting DLPS message displayed in the Debug Analyzer.

    spi_dlps_exit
    
  4. Enter gpio interrupt and send message to enable DMA receive data. Once the SPI receives the data, it will print the following message in the Debug Analyzer.

    spi_slave_gpio_cb: gpio_level 1
    spi_slave_rx_dma_handler! rx_len_all 6
    spi_slave_rx_dma_handler: read_buf[0] = 0x%x
    ...
    spi_slave_rx_dma_handler: read_buf[5] = 0x%x
    
  5. Pull down the wake-up pin to allow the system re-enter DLPS mode.

    spi_slave_gpio_cb: gpio_level 0
    spi_dlps_enter
    

Sample 2 Verification

  1. Press the Reset button on the EVB.

  2. After initialization is complete, the system is in idle state, it will enter DLPS mode. Observe the entering DLPS message displayed in the Debug Analyzer.

    spi_dlps_enter
    
  3. When the master sends data, CS is pulled low and the system will be awakened. Observe the exiting DLPS message displayed in the Debug Analyzer.

    spi_dlps_exit
    
  4. After exiting dlps, slave enables DMA to receive data. When completing the transmission, it enters the GDMA interrupt and prints log.

    spi_slave_rx_dma_handler! rx_len_all 6
    spi_slave_rx_dma_handler: read_buf[0] = 0x%x
    ...
    spi_slave_rx_dma_handler: read_buf[5] = 0x%x
    
  5. When the software timer expires, the system is allowed to re-enter DLPS mode.

    timer_callback
    

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_dlps_slave_rx_dma_gpio_demo.c .

Sample 2 source code:

  • Source code directory: sdk\src\sample\io_demo\spi\dlps\spi_dlps_slave_rx_dma_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:

    1. Call System_WakeUpPinEnable() to to enable the wake-up function of PIN_GPIO_WAKEUP.

  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. Call System_WakeUpInterruptValue() to check if the wake-up was triggered by the PIN_GPIO_WAKEUP.

    2. If the wake-up was triggered by the current PIN_GPIO_WAKEUP, call the interrupt function of the GPIO.

    3. Then set allowedSystemEnterDlps to false to not allow the system to enter DLPS mode.

    4. Then call spi_slave_rx_dma_init to reinitialize the GDMA peripheral, and call spi_dma_rx_data to enable receive data.

  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.

GPIO Interrupt Initialization (Sample 1)

  1. Call hal_gpio_init() to enable GPIO clock.

  2. Call hal_gpio_int_init() to initialize GPIO interrupt.

  3. Call hal_gpio_set_debounce_time() to set GPIO debounce time.

  4. Call hal_gpio_init_pin() to initialize the GPIO peripheral and call hal_gpio_set_up_irq() to initialize interrupt-related parameters. The GPIO initialization parameters are configured as shown in the table below.

GPIO Input0 Initialization Parameters

GPIO Hardware Parameters

GPIO

PIN Number

PIN_GPIO_WAKEUP

GPIO Type

GPIO_TYPE_CORE

GPIO Mode

GPIO_DIR_INPUT

GPIO Pull Value

GPIO_PULL_DOWN

Interrupt Type

GPIO_IRQ_EDGE

Interrupt Polarity

GPIO_IRQ_ACTIVE_HIGH

Debounce Enable

false

  1. Call hal_gpio_register_isr_callback() to register gpio interrupt callback.

  2. Call hal_gpio_irq_enable() to enable gpio interrupt.

SPI RX DMA Initialization Flow (Sample 1&2)

The initialization flow for peripherals can refer to Initialization Flow.

The SPI RX DMA initialization flow requires first initializing the SPI peripheral, followed by RX 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(PIN_SPI_SCK, SPI_CLK_SLAVE);
       Pinmux_Config(PIN_SPI_MOSI, SPI_MO_SLAVE);
       Pinmux_Config(PIN_SPI_MISO, SPI_MI_SLAVE);
       Pinmux_Config(PIN_SPI_CS, SPI_SS_N_0_SLAVE);
    
       Pad_Config(PIN_SPI_SCK, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE,
                   PAD_OUT_HIGH);
       Pad_Config(PIN_SPI_MOSI, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE,
                   PAD_OUT_HIGH);
       Pad_Config(PIN_SPI_MISO, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE,
                   PAD_OUT_HIGH);
       Pad_Config(PIN_SPI_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_Slave

    Data Frame Size

    SPI_InitTypeDef::SPI_DataSize

    SPI_DataSize_8b

    Clock Polarity

    SPI_InitTypeDef::SPI_CPOL

    SPI_CPOL_High

    Clock Phase

    SPI_InitTypeDef::SPI_CPHA

    SPI_CPHA_1Edge

    RX Water Level

    SPI_InitTypeDef::SPI_RxWaterlevel

    7

  4. Call SPI_Cmd() to enable SPI.

RX DMA Initialization Flow

The SPI RX DMA initialization flow can refer to SPI RX 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_SLAVE_RX_DMA_CHANNEL_NUM

    Transfer Direction

    GDMA_InitTypeDef::GDMA_DIR

    GDMA_DIR_PeripheralToMemory

    Buffer Size

    GDMA_InitTypeDef::GDMA_BufferSize

    TEST_SIZE

    Source Address Increment or Decrement

    GDMA_InitTypeDef::GDMA_SourceInc

    DMA_SourceInc_Fix

    Destination Address Increment or Decrement

    GDMA_InitTypeDef::GDMA_DestinationInc

    DMA_DestinationInc_Inc

    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

    SPI0->DR

    Destination Address

    GDMA_InitTypeDef::GDMA_DestinationAddr

    read_buf

    Source Handshake

    GDMA_InitTypeDef::GDMA_SourceHandshake

    DMA_Handshake_SPI_SLAVE_RX

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

  5. Call NVIC_Init() to enable NVIC of RX 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 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:

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

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

  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. Call System_WakeUpInterruptValue() to check if the wake-up was triggered by the PIN_SPI_CS.

    2. Set allowedSystemEnterDlps to false to not allow the system to enter DLPS mode, and start the software timer.

    3. Call spi_slave_rx_dma_init to reinitialize the GDMA peripheral, and call spi_dma_rx_data to enable receive data.

  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 2)

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

  2. Call os_timer_start() to start the software timer after exiting from DLPS mode.

Functional Implementation

GPIO Interrupt Handle (Sample 1)

The interrupt handler function spi_slave_gpio_cb prints interrupt information.

  1. When detecting a high level of P1_1 triggers a GPIO interrupt. Set allowedSystemEnterDlps to false to not allow the system to enter DLPS mode. Then send message to enable DMA to receive data.

  2. When detecting a low level of P1_1 triggers a GPIO interrupt. Set allowedSystemEnterDlps to true to allow the system to enter DLPS mode.

static void spi_slave_gpio_cb(uint32_t context)
{
   uint8_t pin_index = (uint32_t)context;
   uint8_t event = IO_DEMO_EVENT_SPI_RX;
   T_GPIO_LEVEL gpio_level = hal_gpio_get_input_level(pin_index);
   IO_PRINT_INFO1("spi_slave_gpio_cb: gpio_level %d", gpio_level);

   if (gpio_level == GPIO_LEVEL_HIGH)
   {
      allowedSystemEnterDlps = false;
      hal_gpio_irq_change_polarity(PIN_GPIO_WAKEUP, GPIO_IRQ_ACTIVE_LOW);

      if (os_msg_send(io_queue_handle, &event, 0) == false)
      {
            IO_PRINT_ERROR0("spi_slave_gpio_cb: Send Queue Error");
      }
   }
   else
   {
      allowedSystemEnterDlps = true;
      hal_gpio_irq_change_polarity(PIN_GPIO_WAKEUP, GPIO_IRQ_ACTIVE_HIGH);
   }
}

SPI RX DMA Interrupt Handle (Sample 1&2)

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.

Software Timer Callback Handle (Sample 2)

When the software timer expires, the software timer callback function will be executed. Set allowedSystemEnterDlps to true to allow the system to re-enter DLPS mode.