I can't seem to find the answer here - sorry if it has already been answered. We were having an issue recently where the root cause ended up being that we were trying to do blocking and non-blocking transactions via SPIM (using nrfx). Once we performed a non-blocking transaction, the next blocking transaction would fail. The issue was that the interrupt was enabled for a non-blocking SPI transfer, and then when a blocking transfer next occurs the interrupt still happens and clears the done event. The subsequent while loop - that checks for the EVENTS_END - got stuck as the event was cleared by the interrupt.
So my question is - is this expected? Is the intent of the nrfx_spim module that you will only ever use one mode or the other? It seems unlikely since I can pass a flag to change modes.
Our fix for this was to change this code:
static nrfx_err_t spim_xfer(NRF_SPIM_Type * p_spim, spim_control_block_t * p_cb, nrfx_spim_xfer_desc_t const * p_xfer_desc, uint32_t flags) { nrfx_err_t err_code; // EasyDMA requires that transfer buffers are placed in Data RAM region; // signal error if they are not. if ((p_xfer_desc->p_tx_buffer != NULL && !nrfx_is_in_ram(p_xfer_desc->p_tx_buffer)) || (p_xfer_desc->p_rx_buffer != NULL && !nrfx_is_in_ram(p_xfer_desc->p_rx_buffer))) { p_cb->transfer_in_progress = false; err_code = NRFX_ERROR_INVALID_ADDR; NRFX_LOG_WARNING("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); return err_code; } #if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED) p_cb->tx_length = 0; p_cb->rx_length = 0; #endif nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length); nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, p_xfer_desc->rx_length); #if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED) if (p_spim == NRF_SPIM3) { anomaly_198_enable(p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length); } #endif nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END); spim_list_enable_handle(p_spim, flags); if (!(flags & NRFX_SPIM_FLAG_HOLD_XFER)) { nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START); } #if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED) if (flags & NRFX_SPIM_FLAG_HOLD_XFER) { nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED); p_cb->tx_length = p_xfer_desc->tx_length; p_cb->rx_length = p_xfer_desc->rx_length; nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, 0); nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, 0); nrf_spim_int_enable(p_spim, NRF_SPIM_INT_STARTED_MASK); } #endif if (!p_cb->handler) { while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END)){} #if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED) if (p_spim == NRF_SPIM3) { anomaly_198_disable(); } #endif if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED) { #if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) if (!p_cb->use_hw_ss) #endif { if (p_cb->ss_active_high) { nrf_gpio_pin_clear(p_cb->ss_pin); } else { nrf_gpio_pin_set(p_cb->ss_pin); } } } } else { spim_int_enable(p_spim, !(flags & NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER)); } err_code = NRFX_SUCCESS; NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); return err_code; }
to this:
static nrfx_err_t spim_xfer(NRF_SPIM_Type * p_spim, spim_control_block_t * p_cb, nrfx_spim_xfer_desc_t const * p_xfer_desc, uint32_t flags) { nrfx_err_t err_code; // EasyDMA requires that transfer buffers are placed in Data RAM region; // signal error if they are not. if ((p_xfer_desc->p_tx_buffer != NULL && !nrfx_is_in_ram(p_xfer_desc->p_tx_buffer)) || (p_xfer_desc->p_rx_buffer != NULL && !nrfx_is_in_ram(p_xfer_desc->p_rx_buffer))) { p_cb->transfer_in_progress = false; err_code = NRFX_ERROR_INVALID_ADDR; NRFX_LOG_WARNING("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); return err_code; } #if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED) p_cb->tx_length = 0; p_cb->rx_length = 0; #endif nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length); nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, p_xfer_desc->rx_length); #if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED) if (p_spim == NRF_SPIM3) { anomaly_198_enable(p_xfer_desc->p_tx_buffer, p_xfer_desc->tx_length); } #endif nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_END); bool async = p_cb->handler && (flags & NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER) == 0; spim_int_enable(p_spim, async); spim_list_enable_handle(p_spim, flags); if (!(flags & NRFX_SPIM_FLAG_HOLD_XFER)) { nrf_spim_task_trigger(p_spim, NRF_SPIM_TASK_START); } #if NRFX_CHECK(NRFX_SPIM_NRF52_ANOMALY_109_WORKAROUND_ENABLED) if (flags & NRFX_SPIM_FLAG_HOLD_XFER) { nrf_spim_event_clear(p_spim, NRF_SPIM_EVENT_STARTED); p_cb->tx_length = p_xfer_desc->tx_length; p_cb->rx_length = p_xfer_desc->rx_length; nrf_spim_tx_buffer_set(p_spim, p_xfer_desc->p_tx_buffer, 0); nrf_spim_rx_buffer_set(p_spim, p_xfer_desc->p_rx_buffer, 0); nrf_spim_int_enable(p_spim, NRF_SPIM_INT_STARTED_MASK); } #endif if (!async) { while (!nrf_spim_event_check(p_spim, NRF_SPIM_EVENT_END)){} #if NRFX_CHECK(NRFX_SPIM3_NRF52840_ANOMALY_198_WORKAROUND_ENABLED) if (p_spim == NRF_SPIM3) { anomaly_198_disable(); } #endif if (p_cb->ss_pin != NRFX_SPIM_PIN_NOT_USED) { #if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) if (!p_cb->use_hw_ss) #endif { if (p_cb->ss_active_high) { nrf_gpio_pin_clear(p_cb->ss_pin); } else { nrf_gpio_pin_set(p_cb->ss_pin); } } } } err_code = NRFX_SUCCESS; NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code)); return err_code; }
It's nice that there was an easy workaround, but it kind of screws with our code management to have to track a change to the SDK :-/