This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

The problem of SPI communication initiated by external interrupt using PPI

Hi, Nordicsemi

At present, although SPI uses easy DMA when reading and sending data, in the actual use scenario, I still use gpiote to detect the interrupt pin of the sensor, and polling the flag bit of the interrupt signal in the main program to trigger reading data. In this way, when polling to read data, it is bound to be interrupted by the Bluetooth protocol stack, resulting in the read sensor data is not continuous. It's just the continuous and complete data that customers need.

So,

1. Can I use PPI module to trigger SPI read / write task with gpiote interrupt event as trigger condition?

2. If you can, can you briefly describe the process or provide simple routines?

3. If PPI + gpiote is used to trigger SPI read-write, can it solve the problem of SPI read-write discontinuity?

4. If PPI + gpiote is used to trigger SPI read / write, will it affect the part that I didn't want to be triggered in this way?

I look forward to hearing from you soon.

Best regards!

June6

  • Can I use PPI module to trigger SPI read / write task with gpiote interrupt event as trigger condition?

     Yes. 

    If you can, can you briefly describe the process or provide simple routines?

     You must first initialize the SPIM peripheral driver and configure the proper settings for your transfer, ie. frequency , pointer to rx/tx buffers, length of buffers, SPI polarity, etc. The standard SPIM driver API has all that you need. 

    Then you connect the GPIOTE IN_EVENT to the SPIM TASKS_START. Use the PPI example as a reference for how to connect an event to a task. 
    Use nrfx_spim_start_task_get to get the address of the SPIM TASKS_START. 

    If PPI + gpiote is used to trigger SPI read-write, can it solve the problem of SPI read-write discontinuity?

    Yes, but you need to process the buffers at some point. The SPIM is double-buffered so that you can prepare the next buffer immediately after the STARTED event has fired. 

    If PPI + gpiote is used to trigger SPI read / write, will it affect the part that I didn't want to be triggered in this way?

     I don't know what you don't want to get triggered. You need to elaborate. 

  • When there is an external interrupt signal, I only need to perform the operation of reading one register of the external sensor, and I do not want to affect the other parts. Does this mean I need to turn off PPI and clear all event and task addresses when I don't want to do this?

    And, is the module used by the SPI in the peripheral routine a SPIM module?

  • At present, I have used PPI to associate gpiote event address with SPI task start address. But I still have a little doubt. 

    The program code is as follows

    static void in_pin_handler(nrf_drv_gpiote_pin_t pin,
                               nrf_gpiote_polarity_t action) {
      if (pin == MCU_GPIO_INT)
    }
    
    ret_code_t gpiote_init(ppi_addr_t *addr) {
      ret_code_t err_code;
    
      if (!nrf_drv_gpiote_is_init()) {
        err_code = nrf_drv_gpiote_init();
        APP_ERROR_CHECK(err_code);
      }
      nrf_drv_gpiote_in_config_t in_config_0 = GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
      in_config_0.pull = NRF_GPIO_PIN_PULLUP;
    
      /* When configuration conversion occurs, no callback is generated and PPI is
       * used instead */
      err_code = nrf_drv_gpiote_in_init(MCU_GPIO_INT, &in_config_0, in_pin_handler);
      APP_ERROR_CHECK(err_code);
    
      nrf_drv_gpiote_in_event_enable(MCU_GPIO_INT, true);
      addr->gpiote_evt_addr = nrf_drv_gpiote_in_event_addr_get(MCU_GPIO_INT);
    
      return NRF_SUCCESS;
    }
    
    static void spi_event_handler(nrf_drv_spi_evt_t const *p_event,
                                         void *p_context) {
      spi_xfer_done = true;
    }
    
    uint32_t spi_init(ppi_addr_t *addr) {
      if (!spi_init_flag) {
        nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
        spi_config.ss_pin = SPI_CS_PIN;
        spi_config.miso_pin = SPI_MISO_PIN;
        spi_config.mosi_pin = SPI_MOSI_PIN;
        spi_config.sck_pin = SPI_SCK_PIN;
    
        APP_ERROR_CHECK(
            nrf_drv_spi_init(&spi_0, &spi_config, spi_event_handler, NULL));
    
        x = addr->spi_task_addr = nrf_drv_spi_start_task_get(&spi_0);
    
        spi_init_flag = true;
    
        return 0;
      } else {
        // TODO Define return value
        return 1;
      }
    }
    
    uint32_t ppi_init(ppi_addr_t *addr) {
      uint32_t err_code;
      uint32_t evt_addr, task_addr;
      nrf_ppi_channel_t ppi_channel;
    
      err_code = nrf_drv_ppi_init();
      APP_ERROR_CHECK(err_code);
    
      evt_addr = addr->gpiote_evt_addr;
      task_addr = addr->spi_task_addr;
    
      err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
      APP_ERROR_CHECK(err_code);
    
      err_code = nrf_drv_ppi_channel_assign(ppi_channel, evt_addr, task_addr);
      APP_ERROR_CHECK(err_code);
    
      // Enable configured PPI channel
      err_code = nrf_drv_ppi_channel_enable(ppi_channel);
      APP_ERROR_CHECK(err_code);
    
      return NRF_SUCCESS;
    }
    
    int main(void) {
      APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
      NRF_LOG_DEFAULT_BACKENDS_INIT();
      lfclk_init();
      timers_init();
      UNUSED_VARIABLE(nrf_mem_init());
      spi_init(&ppi_addr);
      gpiote_init(&ppi_addr);
      ppi_init(&ppi_addr);
      NRF_LOG_INFO("SPI example started.");
    
      while (1) {
        loop_operation();
        NRF_LOG_FLUSH();
      }
    }

    I found that as long as there is an external interrupt signal, SPI will generate an event handler. Can this indicate that the association has been successful?

    Next, when there is an external interrupt signal, how to let the hardware read data by SPI without the participation of MCU?

  • Hi, haakonsh.

    Now I have added a part of the program as follows

    #define BUFFER_SIZE  4
    
    typedef struct ArrayList {
      uint8_t buffer[BUFFER_SIZE];
    } ArrayList_type;
    
    SPI0ArrayList[0].buffer[0] = 0x0F;
    nrf_spim_tx_buffer_set(spi_0.u.spim.p_reg, SPI0ArrayList[0].buffer, 1);
    nrf_spim_rx_buffer_set(spi_0.u.spim.p_reg, SPI0ArrayList[1].buffer, 2);
    nrf_spim_event_clear(spi_0.u.spim.p_reg, NRF_SPIM_EVENT_END);
    nrf_spim_tx_list_enable(spi_0.u.spim.p_reg);
    nrf_spim_rx_list_enable(spi_0.u.spim.p_reg);

    In this way, when receiving the gpiote external interrupt, can we read the sensor's 0x0f register and save the read data to SPI0ArrayList[1].buffer?

Related