NRF52: serial_api: Use polling for putc

There are scenarios where putc is called within a critical section, e.g
to log ASSERTs in early initialization code. The interrupts being
disabled here prevents the handlers for the UARTE from executing.
This breaks the tx_in_progress flag based approach. The tx_in_progress
never gets reset. Poll on the TXDRDY instead.

It can be recreated with a simple program as shown here:

*************** Current Behavior ****************
++ MbedOS Error Info ++
Error Status: 0x80FF0100 Code: 256 Module: 255
Error Message: F

************** With Fix *************************

++ MbedOS Error Info ++
Error Status: 0x80FF0100 Code: 256 Module: 255
Error Message: Fatal Run-time error
Location: 0x2C0A9
Error Value: 0x0
Current Thread: Id: 0x20005520 Entry: 0x30EBF StackSize: 0x1000 StackMem: 0x20004520 SP: 0x20005490
For more info, visit: https://armmbed.github.io/mbedos-error/?error=0x80FF0100
-- MbedOS Error Info --
nrf failure at .\main.cpp:22
***************************************************
pull/8479/head
Naveen Kaje 2018-09-05 07:52:08 -05:00 committed by adbridge
parent 455b44bd0f
commit 099d0500ef
2 changed files with 40 additions and 21 deletions

View File

@ -1439,6 +1439,7 @@ int serial_getc(serial_t *obj)
*/
void serial_putc(serial_t *obj, int character)
{
bool done = false;
MBED_ASSERT(obj);
#if DEVICE_SERIAL_ASYNCH
@ -1449,35 +1450,20 @@ void serial_putc(serial_t *obj, int character)
int instance = uart_object->instance;
/**
* tx_in_progress acts like a mutex to ensure only one transmission can be active at a time.
* The flag is modified using the atomic compare-and-set function.
*/
bool mutex = false;
do {
uint8_t expected = 0;
uint8_t desired = 1;
mutex = core_util_atomic_cas_u8((uint8_t *) &nordic_nrf5_uart_state[instance].tx_in_progress, &expected, desired);
} while (mutex == false);
/* Take ownership and configure UART if necessary. */
nordic_nrf5_serial_configure(obj);
/* 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 ENDTX event and enable interrupts. */
nrf_uarte_event_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_ENDTX);
nrf_uarte_int_enable(nordic_nrf5_uart_register[instance], NRF_UARTE_INT_ENDTX_MASK);
nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance], NRF_UARTE_TASK_STARTTX);
/* Trigger DMA transfer. */
nrf_uarte_task_trigger(nordic_nrf5_uart_register[instance],
NRF_UARTE_TASK_STARTTX);
do {
done = nrf_uarte_event_extra_check(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY);
} while(done == false);
nrf_uarte_event_extra_clear(nordic_nrf5_uart_register[instance], NRF_UARTE_EVENT_TXDRDY);
}
/** Check if the serial peripheral is readable

View File

@ -228,6 +228,25 @@ __STATIC_INLINE void nrf_uarte_event_clear(NRF_UARTE_Type * p_reg, nrf_uarte_eve
*/
__STATIC_INLINE bool nrf_uarte_event_check(NRF_UARTE_Type * p_reg, nrf_uarte_event_t event);
/**
* @brief Function for checking the state of a specific extra UARTE event.
*
* @param[in] p_reg Pointer to the peripheral registers structure.
* @param[in] event Event to check.
*
* @retval True if event is set, False otherwise.
*/
__STATIC_INLINE bool nrf_uarte_event_extra_check(NRF_UARTE_Type * p_reg, uint32_t event);
/**
* @brief Function for clearing a specific extra UARTE event.
*
* @param[in] p_reg Pointer to the peripheral registers structure.
* @param[in] event Extra event to clear.
*/
__STATIC_INLINE void nrf_uarte_event_extra_clear(NRF_UARTE_Type * p_reg, uint32_t event);
/**
* @brief Function for returning the address of a specific UARTE event register.
*
@ -456,11 +475,25 @@ __STATIC_INLINE void nrf_uarte_event_clear(NRF_UARTE_Type * p_reg, nrf_uarte_eve
}
__STATIC_INLINE void nrf_uarte_event_extra_clear(NRF_UARTE_Type * p_reg, uint32_t event)
{
*((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event)) = 0x0UL;
#if __CORTEX_M == 0x04
volatile uint32_t dummy = *((volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event));
(void)dummy;
#endif
}
__STATIC_INLINE bool nrf_uarte_event_check(NRF_UARTE_Type * p_reg, nrf_uarte_event_t event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event);
}
__STATIC_INLINE bool nrf_uarte_event_extra_check(NRF_UARTE_Type * p_reg, uint32_t event)
{
return (bool)*(volatile uint32_t *)((uint8_t *)p_reg + (uint32_t)event);
}
__STATIC_INLINE uint32_t nrf_uarte_event_address_get(NRF_UARTE_Type * p_reg,
nrf_uarte_event_t event)
{