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 :-/