nRF52840, SD140, SDK 17.0.2
We're using UARTE to communicate with a peer MCU. We're using hardware flow control (CTS and RTS are enabled) and the baudrate is 115200. The traces are less than 2cm. We do not control any firmware on the peer MCU; it's vendor-supplied. We can tell it to power off, but at other times it is free to power itself off. When it does this, it de-asserts a connected GPIO pin which we have configured to interrupt the nRF52 on any logic change.
Our watchdog is firing in the infinite loop at the bottom of nrfx_uarte_uninit() (line 318 in our nrfx_uarte.c):
void nrfx_uarte_uninit(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
NRF_UARTE_Type * p_reg = p_instance->p_reg;
if (p_cb->handler)
{
interrupts_disable(p_instance);
}
// Make sure all transfers are finished before UARTE is disabled
// to achieve the lowest power consumption.
nrf_uarte_shorts_disable(p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
// Check if there is any ongoing reception.
if (p_cb->rx_buffer_length)
{
nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_RXTO);
nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPRX);
}
nrf_uarte_event_clear(p_reg, NRF_UARTE_EVENT_TXSTOPPED);
nrf_uarte_task_trigger(p_reg, NRF_UARTE_TASK_STOPTX);
// Wait for TXSTOPPED event and for RXTO event, provided that there was ongoing reception.
while (!nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_TXSTOPPED) ||
(p_cb->rx_buffer_length && !nrf_uarte_event_check(p_reg, NRF_UARTE_EVENT_RXTO)))
{}
nrf_uarte_disable(p_reg);
pins_to_default(p_instance);
#if NRFX_CHECK(NRFX_PRS_ENABLED)
nrfx_prs_release(p_reg);
#endif
p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
p_cb->handler = NULL;
NRFX_LOG_INFO("Instance uninitialized: %d.", p_instance->drv_inst_idx);
}
The scenario is this:
1. nRF52 is in a WFE state, woken up into our GPIO ISR by a high-to-low change driven by the peer MCU.
2. nRF52 interprets the hi-to-low pin change as the peer MCU powering off.
3. nRF52 deinits the UARTE block that connects to the peer MCU.
4. nRF52 spins forever in the loop pasted above, and is eventually "rescued" by the watchdog.
I have a few questions related to this that I'd love clarification on:
1. Will the UARTE ever generate the NRF_UARTE_EVENT_RXTO event if the peer is not asserting RTS? That would cause an infinite loop in nrfx_uarte_uninit, since the peer is long-gone and is not asserting TX/RX/CTS/RTS at this point.
2. I found nrfx_uarte_rx_abort() as well (line 577 in my nrfx_uarte.c file):
void nrfx_uarte_rx_abort(nrfx_uarte_t const * p_instance)
{
uarte_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
// Short between ENDRX event and STARTRX task must be disabled before
// aborting transmission.
if (p_cb->rx_secondary_buffer_length != 0)
{
nrf_uarte_shorts_disable(p_instance->p_reg, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
nrf_uarte_task_trigger(p_instance->p_reg, NRF_UARTE_TASK_STOPRX);
NRFX_LOG_INFO("RX transaction aborted.");
}
This code does not clear the internal rx_buffer_length field of the UARTE control block structure; is that a bug? Even if I abort any pending RX, the nrfx_uarte_uninit() code will still spin on the now-useless nonzero rx_buffer_length field.
3. What's the best way to shut down a previously-active UARTE block after the peer has powered off? We don't control its power state; it comes and goes.
4. Do you have any other hypotheses about why nrfx_uarte_uninit() would spin forever?
Thanks,
Bill