From 28c63ec0f3d31c3def30713966dc6aec60f35c68 Mon Sep 17 00:00:00 2001 From: Naveen Kaje Date: Wed, 5 Sep 2018 07:52:08 -0500 Subject: [PATCH] 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 *************************************************** --- .../TARGET_NRF5x/TARGET_NRF52/serial_api.c | 28 ++++------------ .../drivers_nrf/hal/nrf_uarte.h | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/serial_api.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/serial_api.c index ab3b33f2905..cfbc1ec7f95 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/serial_api.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/serial_api.c @@ -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 diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/drivers_nrf/hal/nrf_uarte.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/drivers_nrf/hal/nrf_uarte.h index 49a5c4e5d45..2576a7770c4 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/drivers_nrf/hal/nrf_uarte.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/drivers_nrf/hal/nrf_uarte.h @@ -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) {