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

No slave select on SPIM xfer using PPI and GPIOTE

I have a custom board that has an MPU9250 attached as a SPI device. The MPU can generate data ready interrupts. I have set the MPU to generate data ready 4x per second which is about the slowest it can run.

I am using SDK 12.2.0 and I have modified the GPIOTE example under SDK->perpherals. I have set up a PPI channel to start the SPIM xfer on a LOTOHI event of the interrupt pin. I have a second PPI channel configured to toggle a LED attached to another GPIO port when the SPIM_END event occurs.

I have the PPI configured correctly ASFAIK because my LED is toggling, and I can see the interrupts happening on a scope connected to the input pin.

The SPIM is configured to do mulitple XFERs with no increment. (At this slow rate I can respond to the data fast enough) I have a callback defined that does a LOG_HEXDUMP of my rx_buffer and it reads all FFs for the first three transfers and then all 00s thereafter. When I connect a scope to the SPIM pins, my slave select never goes low on the xfers, but the clock is active. Thoughts as to what I'm missing?

I've attached my main.c where everything happens for the sake of this example.

main.c

  • So I tried the suggestion from Petter Myhre in the comments to my original question, and used the fork to trigger both the slave select toggle task and the SPI start task. My initial concern was that slave select would need time to be low before the clock started, so I made the slave select task the main task in the PPI, and the SPI start the forked task. This works exactly as intended. My SPI end event triggers the SS toggle task to raise the select again. I have attached the changed functions from my main above for anyone interested.

    static void ppi_setup()
    {
        uint32_t int_evt_addr;
        uint32_t cs_task_addr;
        uint32_t spi_evt_addr;
        uint32_t spi_task_addr;
        uint32_t gpiote_task_addr;
        ret_code_t err_code;
        nrf_drv_gpiote_out_config_t config_led_out = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false); // start low             
        nrf_drv_gpiote_out_config_t config_cs_out = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true); // Start high
    
        err_code = nrf_drv_gpiote_out_init(GPIO_OUTPUT_PIN_NUMBER, &config_led_out);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_gpiote_out_init(SPI0_CONFIG_SS_PIN, &config_cs_out);
        APP_ERROR_CHECK(err_code);
    
        // Setup the input interrupt pin
        nrf_drv_gpiote_in_config_t config_in = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
        err_code = nrf_drv_gpiote_in_init(MPU_INT_PIN, &config_in, NULL); // No handler function
        APP_ERROR_CHECK(err_code);
        
        // allocate PPI channels for hardware
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_int);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_drv_ppi_channel_alloc(&ppi_channel_toggle);
        APP_ERROR_CHECK(err_code);
    
        int_evt_addr = nrf_drv_gpiote_in_event_addr_get(MPU_INT_PIN);
        cs_task_addr = nrf_drv_gpiote_out_task_addr_get(SPI0_CONFIG_SS_PIN);
        spi_task_addr = nrf_drv_spi_start_task_get(&m_spi_instance);
        err_code = nrf_drv_ppi_channel_assign(ppi_channel_int, int_evt_addr, cs_task_addr);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_fork_assign(ppi_channel_int, spi_task_addr);
        APP_ERROR_CHECK(err_code);
    
        spi_evt_addr = nrf_drv_spi_end_event_get(&m_spi_instance);
        gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(GPIO_OUTPUT_PIN_NUMBER);
    
        err_code = nrf_drv_ppi_channel_assign(ppi_channel_toggle, spi_evt_addr, cs_task_addr);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_fork_assign(ppi_channel_toggle, gpiote_task_addr);
        APP_ERROR_CHECK(err_code);
    }
    
    void transfer_enable()
    {
        ret_code_t err_code;
        // end to front
        nrf_drv_gpiote_out_task_enable(GPIO_OUTPUT_PIN_NUMBER);
        nrf_drv_gpiote_out_task_enable(SPI0_CONFIG_SS_PIN);
        err_code = nrf_drv_ppi_channel_enable(ppi_channel_toggle);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(ppi_channel_int);
        APP_ERROR_CHECK(err_code);
        nrf_drv_gpiote_in_event_enable(MPU_INT_PIN,true);
    }
    
    void transfer_disable()
    {
        ret_code_t err_code;
        
        nrf_drv_gpiote_in_event_disable(MPU_INT_PIN);
        err_code = nrf_drv_ppi_channel_disable(ppi_channel_int);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_disable(ppi_channel_toggle);
        APP_ERROR_CHECK(err_code);
        nrf_drv_gpiote_out_task_disable(GPIO_OUTPUT_PIN_NUMBER);
        nrf_drv_gpiote_out_task_disable(SPI0_CONFIG_SS_PIN);
    }
    
  • Great that you got it working, and thanks for sharing :)

  • Rusty thanks for this. I used part of your code to transfer more than 255 bytes with spi master devzone.nordicsemi.com/.../

Related