Fix serial_putc bug in NRF52 family

serial_putc didn't work when called with interrupts disabled.
pull/6547/head
Marcus Chang 2018-03-27 16:30:45 -07:00
parent 3663494bd9
commit cafa8ae752
1 changed files with 91 additions and 17 deletions

View File

@ -562,9 +562,6 @@ static void nordic_nrf5_uart_event_handler_rxdrdy(int instance)
*/
static void nordic_nrf5_uart_event_handler_endtx(int instance)
{
/* Set Tx done. */
nordic_nrf5_uart_state[instance].tx_in_progress = 0;
/* Check if callback handler and Tx event mask is set. */
uart_irq_handler callback = (uart_irq_handler) nordic_nrf5_uart_state[instance].owner->handler;
uint32_t mask = nordic_nrf5_uart_state[instance].owner->mask;
@ -611,6 +608,12 @@ static void nordic_nrf5_uart_event_handler_endrx_asynch(int instance)
*/
static void nordic_nrf5_uart_event_handler_endtx_asynch(int instance)
{
/* Disable ENDTX interrupt. */
nordic_nrf5_uart_register[instance]->INTEN &= ~NRF_UARTE_INT_ENDTX_MASK;
/* Clear ENDTX event. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
/* Set Tx done and reset Tx mode to be not asynchronous. */
nordic_nrf5_uart_state[instance].tx_in_progress = 0;
nordic_nrf5_uart_state[instance].tx_asynch = false;
@ -673,21 +676,28 @@ static void nordic_nrf5_uart_event_handler(int instance)
nordic_nrf5_uart_event_handler_rxdrdy(instance);
}
/* Tx single character has been sent. */
if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY)) {
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY);
/* In non-async transfers this will generate and interrupt if callback and mask is set. */
if (!nordic_nrf5_uart_state[instance].tx_asynch) {
nordic_nrf5_uart_event_handler_endtx(instance);
}
}
/* Tx DMA buffer has been sent. */
if (nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX))
{
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
#if DEVICE_SERIAL_ASYNCH
/* Call appropriate event handler based on current mode. */
/* Call async event handler in async mode. */
if (nordic_nrf5_uart_state[instance].tx_asynch) {
nordic_nrf5_uart_event_handler_endtx_asynch(instance);
} else
#endif
{
nordic_nrf5_uart_event_handler_endtx(instance);
}
#endif
}
}
@ -895,10 +905,6 @@ static void nordic_nrf5_serial_configure(serial_t *obj)
/* Configure common setting. */
nordic_nrf5_uart_configure_object(obj);
/* Clear Tx event and enable Tx interrupts. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
nordic_nrf5_uart_register[instance]->INTEN |= NRF_UARTE_INT_ENDTX_MASK;
/* Set new owner. */
nordic_nrf5_uart_state[instance].owner = uart_object;
uart_object->update = false;
@ -1060,6 +1066,11 @@ void serial_init(serial_t *obj, PinName tx, PinName rx)
uart_object->hwfc = NRF_UART_HWFC_DISABLED;
}
/* The STDIO object is stored in this file. Set the flag once initialized. */
if (obj == &stdio_uart) {
stdio_uart_inited = 1;
}
/* Initializing the serial object does not make it the owner of an instance.
* Only when the serial object is being used will the object take ownership
* over the instance.
@ -1335,6 +1346,21 @@ void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable)
uart_object->mask &= ~type;
}
/* Enable TXDRDY event. */
if ((type == NORDIC_TX_IRQ) && enable) {
/* Clear Tx ready event and enable Tx ready interrupts. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[uart_object->instance], NRF_UARTE_EVENT_TXDRDY);
nordic_nrf5_uart_register[uart_object->instance]->INTEN |= NRF_UARTE_INT_TXDRDY_MASK;
/* Disable TXDRDY event. */
} else if ((type == NORDIC_TX_IRQ) && !enable) {
/* Disable Tx ready interrupts and clear Tx ready event. */
nordic_nrf5_uart_register[uart_object->instance]->INTEN &= ~NRF_UARTE_INT_TXDRDY_MASK;
nrf_uarte_event_clear(nordic_nrf5_uart_register[uart_object->instance], NRF_UARTE_EVENT_TXDRDY);
}
}
/** Get character. This is a blocking call, waiting for a character
@ -1412,15 +1438,41 @@ void serial_putc(serial_t *obj, int character)
/* Take ownership and configure UART if necessary. */
nordic_nrf5_serial_configure(obj);
/**
* The UARTE module can generate two different Tx events: TXDRDY when each character has
* been transmitted and ENDTX when the entire buffer has been sent.
*
* For the blocking serial_putc, TXDRDY interrupts are enabled and only used for the
* single character TX IRQ callback handler. The ENDTX event does not generate an interrupt
* but is caught using a busy-wait loop. Once ENDTX has been generated we disable TXDRDY
* interrupts again.
*/
/* Arm Tx DMA buffer. */
nordic_nrf5_uart_state[instance].tx_data = character;
nrf_uarte_tx_buffer_set(nordic_nrf5_uart_register[instance],
&nordic_nrf5_uart_state[instance].tx_data,
1);
/* Clear TXDRDY event and enable TXDRDY interrupts. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY);
nordic_nrf5_uart_register[instance]->INTEN |= NRF_UARTE_INT_TXDRDY_MASK;
/* Clear ENDTX event. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
/* Trigger DMA transfer. */
nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
NRF_UARTE_TASK_STARTTX);
/* Busy-wait until the ENDTX event occurs. */
while (!nrf_uarte_event_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX));
/* Disable TXDRDY event again. */
nordic_nrf5_uart_register[instance]->INTEN &= ~NRF_UARTE_INT_TXDRDY_MASK;
/* Release mutex. As the owner this call is safe. */
nordic_nrf5_uart_state[instance].tx_in_progress = 0;
}
/** Check if the serial peripheral is readable
@ -1574,6 +1626,20 @@ int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx
nordic_nrf5_uart_state[instance].tx_asynch = true;
nordic_nrf5_serial_configure(obj);
/**
* The UARTE module can generate two different Tx events: TXDRDY when each
* character has been transmitted and ENDTX when the entire buffer has been sent.
*
* For the async serial_tx_async, TXDRDY interrupts are disabled completely. ENDTX
* interrupts are enabled and used to signal the completion of the async transfer.
*
* The ENDTX interrupt is diabled immediately after it is fired in the ISR.
*/
/* Clear Tx event and enable Tx interrupts. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
nordic_nrf5_uart_register[instance]->INTEN |= NRF_UARTE_INT_ENDTX_MASK;
/* Set Tx DMA buffer. */
nrf_uarte_tx_buffer_set(nordic_nrf5_uart_register[obj->serial.instance],
buffer,
@ -1708,16 +1774,24 @@ void serial_tx_abort_asynch(serial_t *obj)
/* Transmission might be in progress. Disable interrupts to prevent ISR from firing. */
core_util_critical_section_enter();
int instance = obj->serial.instance;
/* Disable ENDTX interrupts. */
nordic_nrf5_uart_register[instance]->INTEN &= ~NRF_UARTE_INT_ENDTX_MASK;
/* Clear ENDTX event. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
/* Reset Tx flags. */
nordic_nrf5_uart_state[obj->serial.instance].tx_in_progress = 0;
nordic_nrf5_uart_state[obj->serial.instance].tx_asynch = false;
nordic_nrf5_uart_state[instance].tx_in_progress = 0;
nordic_nrf5_uart_state[instance].tx_asynch = false;
/* Force reconfiguration. */
obj->serial.update = true;
nordic_nrf5_serial_configure(obj);
/* Trigger STOP task. */
nrf_uarte_task_trigger(nordic_nrf5_uart_register[obj->serial.instance],
nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
NRF_UARTE_TASK_STOPTX);
/* Enable interrupts again. */