UARTE RX error and crash when quik insert and remove RX pin

When we try to test the stability of the UARTE peripheral, we find that when inserting and removing the RX pin, the uarte_handler callback function is very likely to goes to NRFX_UARTE_EVT_ERROR state with error_code = 4. We try to reinitialize the UARTE by uninit and re-init it, but then the uarte_handler only goes to NRFX_UARTE_EVT_RX_DOWN state, and cannot receive any data.

In our test case, we send 11 uint8_t data at 1000000 baud rate every 10ms to the nrf52840dk. UARTE instance 1, TX on P0.27, RX on P0.26. with nrfx_uarte.h library. Also we have enable the interal pull up on the RX pin.

The code shows the uarte_handler function, and init/de-init function

// uarte_handler
static void uarte_handler(nrfx_uarte_event_t const *p_event, void *p_context)
{
	nrfx_err_t status;
	(void)status;
	uint8_t index;

	nrfx_uarte_t *p_inst = p_context;

	switch (p_event->type) {
	case NRFX_UARTE_EVT_RX_DONE:
		// NRFX_LOG_INFO("--> RX done");
		index = (++m_rx_buffers.w_pos % RINGBUFF_SIZE);
		status = nrfx_uarte_rx(p_inst, (uint8_t *)(m_rx_buffers.buff + index),
				       sizeof(struct split_msgq_trans));
		NRFX_ASSERT(status == NRFX_SUCCESS);
		m_rx_buffers.w_pos = index;
		if (!bad_crc) {
			k_work_schedule(&uarte_work, K_NO_WAIT);
		} else {
			struct k_work_sync sync;
			k_work_cancel_delayable_sync(&uarte_work, &sync);
			split_uarte_deinit(p_inst);
			split_uart_init(p_inst);
			NRFX_LOG_ERROR("delayable work stopped");
			bad_crc = 0;
		}

		break;
	case NRFX_UARTE_EVT_TX_DONE:
		NRFX_LOG_INFO("--> TX done");
		NRFX_LOG_INFO("--> Bytes transfered: %u", p_event->data.tx.bytes);

		break;

	case NRFX_UARTE_EVT_ERROR:
		NRFX_LOG_ERROR("--> UARTE Error: %d", p_event->data.error.error_mask);
		NRFX_LOG_ERROR("--> Rx bytes: %d", p_event->data.error.rx.bytes);
		struct k_work_sync sync;
		k_work_cancel_delayable_sync(&uarte_work, &sync);
		LOG_DBG("NRFX_UARTE_EVT_ERROR: delayable work stopped");
		split_uarte_deinit(p_inst);
		split_uart_init(p_inst);
		break;

	default:
		break;
	}
}

// Initialize function
static void split_uart_init(nrfx_uarte_t *p_inst)
{
	nrfx_err_t status;
	uint32_t key;
	if (false == uart_status) {
		nrfx_uarte_config_t uarte_config =
			NRFX_UARTE_DEFAULT_CONFIG(UARTE_TX_PIN, UARTE_RX_PIN);
		uarte_config.baudrate = NRF_UARTE_BAUDRATE_1000000;
		uarte_config.p_context = p_inst;
		status = nrfx_uarte_init(p_inst, &uarte_config, uarte_handler);
		NRFX_ASSERT(status == NRFX_SUCCESS);
		// nrfy_gpio_cfg_input(UARTE_RX_PIN,  NRF_GPIO_PIN_PULLUP);
		nrfy_gpio_cfg(UARTE_RX_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
		nrfy_gpio_cfg(UARTE_TX_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
#if defined(__ZEPHYR__)
		IRQ_DIRECT_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_UARTE_INST_GET(UARTE_INST_IDX)),
				   IRQ_PRIO_LOWEST, NRFX_UARTE_INST_HANDLER_GET(UARTE_INST_IDX), 0);
#endif


		status = nrfx_uarte_rx(&uarte_inst, (uint8_t *)m_rx_buffers.buff,
				       sizeof(split_msgq_trans_t));
		NRFX_ASSERT(status == NRFX_SUCCESS);
		key = irq_lock();
		uart_status = true;
		irq_unlock(key);
	}
}

// de-initialize function
static void split_uarte_deinit(nrfx_uarte_t *p_inst)
{
	if (uart_status) {
	uint32_t key = irq_lock();
	uart_status = false;
	nrfx_uarte_uninit(p_inst);
	m_rx_buffers.r_pos = 0;
	m_rx_buffers.w_pos = 0;
	memset(m_rx_buffers.buff, 0, sizeof(m_rx_buffers.buff));
	irq_unlock(key);
	}
}

  • Hi Letian

    Have you tried any of the higher level UART drivers, like the UART async driver? 

    A couple of comments to your implementation: 

    1) I am not sure how well it works to deinit and init the driver in the interrupt directly. Have you tried to schedule this operation using a work item? 

    2) Technically it shouldn't be necessary to do a full re-init of the driver whenever an error occurs. What happens if you simply ignore the error? Will UART operation stop? 

    Best regards
    Torbjørn

  • Hi,

    Thank you for your reply.

    If we ignore the error, the uarte_handler will always goes to NRFX_UARTE_EVT_ERROR after we reconnect RX pin, even we flush the RX buffer. Do we need to manually clear the error after we reach the EVT_ERROR?

    We are currently try to transfer the nrfx_uarte to zephyr uart async API, we will test it as soon as we get there.

    We have organized a separate test file for test the uarte individually, I can send it to you if you want it (I don't know if I can upload it in this post).

    Best regards,

    Letian.

  • Hi Letian

    In non blocking mode it should not be necessary to clear the error, no, this should happen automatically by the driver. 

    What if you try to restart RX when the issue occurs? 

    I made a small wrapper for the UART async driver some time back, in order to provide a simple UART API without relying on dynamic memory allocation. Feel free to have a look for reference:
    https://github.com/too1/ncs-uart-handler

    Best regards
    Torbjørn

Related