From 31e1a7ed32404d80ce55b6cb8f60bff1a9a8390e Mon Sep 17 00:00:00 2001 From: Glenn Ergeerts Date: Fri, 17 Mar 2017 15:27:19 +0100 Subject: [PATCH] UART driver for ezr32 now uses DMA for TX. Console implementation dumps the whole fifo is the HAL support UART TX using DMA, otherwise it resort to ending small chunks at once (as before) --- stack/framework/components/console/console.c | 16 ++++-- stack/framework/hal/CMakeLists.txt | 2 + .../hal/chips/ezr32lg/CMakeLists.txt | 5 +- .../hal/chips/ezr32lg/ezr32lg_uart.c | 54 ++++++++++++++++--- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/stack/framework/components/console/console.c b/stack/framework/components/console/console.c index 168a38b47..7a0b6d288 100644 --- a/stack/framework/components/console/console.c +++ b/stack/framework/components/console/console.c @@ -8,6 +8,7 @@ #include "fifo.h" #include "scheduler.h" #include "console.h" +#include "hal_defs.h" #ifdef FRAMEWORK_CONSOLE_ENABLED @@ -21,20 +22,27 @@ static uint8_t console_tx_buffer[CONSOLE_TX_FIFO_SIZE]; static fifo_t console_tx_fifo; static void flush_console_tx_fifo() { + uint8_t len = fifo_get_size(&console_tx_fifo); +#ifdef HAL_UART_USE_DMA_TX + // when using DMA we transmit the whole FIFO at once + uint8_t buffer[CONSOLE_TX_FIFO_SIZE]; + fifo_pop(&console_tx_fifo, buffer, len); + uart_send_bytes(uart, buffer, len); +#else // only send small chunks over uart each invocation, to make sure // we don't interfer with critical stack timings. // When there is still data left in the fifo this will be rescheduled // with lowest prio uint8_t chunk[TX_FIFO_FLUSH_CHUNK_SIZE]; - uint8_t depth = fifo_get_size(&console_tx_fifo); - if(depth < 10) { - fifo_pop(&console_tx_fifo, chunk, depth); - uart_send_bytes(uart, chunk, depth); + if(len < 10) { + fifo_pop(&console_tx_fifo, chunk, len); + uart_send_bytes(uart, chunk, len); } else { fifo_pop(&console_tx_fifo, chunk, TX_FIFO_FLUSH_CHUNK_SIZE); uart_send_bytes(uart, chunk, TX_FIFO_FLUSH_CHUNK_SIZE); sched_post_task_prio(&flush_console_tx_fifo, MIN_PRIORITY); } +#endif } void console_init(void) { diff --git a/stack/framework/hal/CMakeLists.txt b/stack/framework/hal/CMakeLists.txt index 873b6291d..debd6c1f8 100644 --- a/stack/framework/hal/CMakeLists.txt +++ b/stack/framework/hal/CMakeLists.txt @@ -29,6 +29,7 @@ include(${PROJECT_SOURCE_DIR}/cmake/hal_macros.cmake) # HAL parameters (might be forcefully overruled by chip, which is why HAL_HEADER_DEFINE() is only called after adding chips) SET(HAL_RADIO_USE_HW_CRC "FALSE" CACHE BOOL "Enable/Disable the use of HW CRC") +SET(HAL_UART_USE_DMA_TX "FALSE" CACHE BOOL "Enable/Disable the use of DMA for UART TX") #note: this does not include any chip code. #see note in 'chips' directory in the CMakeLists.txt in the 'chips' directory @@ -40,6 +41,7 @@ ADD_SUBDIRECTORY("platforms") #Generate the 'hal_defs.h' HAL_HEADER_DEFINE(BOOL HAL_RADIO_INCLUDE_TIMESTAMP) HAL_HEADER_DEFINE(BOOL HAL_RADIO_USE_HW_CRC) +HAL_HEADER_DEFINE(BOOL HAL_UART_USE_DMA_TX) HAL_BUILD_SETTINGS_FILE() diff --git a/stack/framework/hal/chips/ezr32lg/CMakeLists.txt b/stack/framework/hal/chips/ezr32lg/CMakeLists.txt index 29195b37c..f978829b4 100644 --- a/stack/framework/hal/chips/ezr32lg/CMakeLists.txt +++ b/stack/framework/hal/chips/ezr32lg/CMakeLists.txt @@ -24,6 +24,8 @@ ENDIF() SET(LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/CMSIS/device/linker/ezr32lg.ld" CACHE FILEPATH "") +SET(HAL_UART_USE_DMA_TX "TRUE" CACHE BOOL "Enable/Disable the use of DMA for UART TX" FORCE) + IF(${PLATFORM_BUILD_BOOTLOADABLE_VERSION}) SET(LINKER_SCRIPT_BOOTLOADABLE "${CMAKE_CURRENT_SOURCE_DIR}/CMSIS/device/linker/ezr32lg_bootloader.ld" CACHE FILEPATH "") ENDIF() @@ -132,5 +134,6 @@ ADD_LIBRARY (${CHIP_LIBRARY_NAME} OBJECT usb/src/em_usbtimer.c emdrv/gpiointerrupt/src/gpiointerrupt.c emdrv/spidrv/src/spidrv.c - emdrv/dmadrv/src/dmadrv.c + emdrv/dmadrv/src/dmadrv.c + emdrv/dmadrv/inc/dmadrv.h ) diff --git a/stack/framework/hal/chips/ezr32lg/ezr32lg_uart.c b/stack/framework/hal/chips/ezr32lg/ezr32lg_uart.c index 38b4a9bd0..334994395 100644 --- a/stack/framework/hal/chips/ezr32lg/ezr32lg_uart.c +++ b/stack/framework/hal/chips/ezr32lg/ezr32lg_uart.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "hwgpio.h" #include "hwuart.h" #include @@ -37,6 +38,7 @@ #include "ezr32lg_pins.h" #include "platform.h" +#include "hal_defs.h" #define UARTS 4 // 2 UARTs + 3 USARTs @@ -149,6 +151,10 @@ struct uart_handle { CMU_Clock_TypeDef clock; uart_irq_t irq; uart_pins_t* pins; +#ifdef HAL_UART_USE_DMA_TX + unsigned int dma_channel_tx; + DMADRV_PeripheralSignal_t dma_req_signal_tx; +#endif }; // private storage of handles, pointers to these records are passed around @@ -157,25 +163,37 @@ static uart_handle_t handle[UARTS] = { .idx = 0, .channel = UART0, .clock = cmuClock_UART0, - .irq = { .tx = UART0_TX_IRQn, .rx = UART0_RX_IRQn } + .irq = { .tx = UART0_TX_IRQn, .rx = UART0_RX_IRQn }, +#ifdef HAL_UART_USE_DMA_TX + .dma_req_signal_tx = DMAREQ_UART0_TXBL +#endif }, { .idx = 1, .channel = UART1, .clock = cmuClock_UART1, - .irq = { .tx = UART1_TX_IRQn, .rx = UART1_RX_IRQn } + .irq = { .tx = UART1_TX_IRQn, .rx = UART1_RX_IRQn }, +#ifdef HAL_UART_USE_DMA_TX + .dma_req_signal_tx = DMAREQ_UART1_TXBL +#endif }, { .idx = 2, .channel = USART1, .clock = cmuClock_USART1, - .irq = { .tx = USART1_TX_IRQn, .rx = USART1_RX_IRQn } + .irq = { .tx = USART1_TX_IRQn, .rx = USART1_RX_IRQn }, +#ifdef HAL_UART_USE_DMA_TX + .dma_req_signal_tx = DMAREQ_USART1_TXBL +#endif }, { .idx = 3, .channel = USART2, .clock = cmuClock_USART2, - .irq = { .tx = USART2_TX_IRQn, .rx = USART2_RX_IRQn } + .irq = { .tx = USART2_TX_IRQn, .rx = USART2_RX_IRQn }, +#ifdef HAL_UART_USE_DMA_TX + .dma_req_signal_tx = DMAREQ_USART2_TXBL +#endif } }; @@ -216,6 +234,14 @@ uart_handle_t* uart_init(uint8_t idx, uint32_t baudrate, uint8_t pins) { USART_Enable(handle[idx].channel, usartEnable); +#ifdef HAL_UART_USE_DMA_TX + // DMADRV is used for allocating a channel. We need to use DMADRV for housekeeping, since ezradio driver also uses this for allocating a channel. + Ecode_t e = DMADRV_Init(); + assert(e == ECODE_EMDRV_DMADRV_OK || e == ECODE_EMDRV_DMADRV_ALREADY_INITIALIZED); // can be already initialized for example by EZRDRV + + e = DMADRV_AllocateChannel(&(handle[idx].dma_channel_tx), NULL); assert(e == ECODE_EMDRV_DMADRV_OK); +#endif + return &handle[idx]; } @@ -278,11 +304,24 @@ void uart_send_bytes(uart_handle_t* uart, void const *data, size_t length) { }; int ret = USBD_Write( 0x81, (void*) tempData, length, NULL); } +#else +#ifdef HAL_UART_USE_DMA_TX + DMADRV_MemoryPeripheral( + uart->dma_channel_tx, + uart->dma_req_signal_tx, + (void*)&uart->channel->TXDATA, + data, + true, + length, + dmadrvDataSize1, + NULL, + NULL); #else for(size_t i=0; iSTATUS & UART_STATUS_RXDATAV) { + assert((USART_IntGet(handle[0].channel) & UART_IF_RXOF) == 0); handler[0](USART_Rx(handle[0].channel)); USART_IntClear(handle[0].channel, UART_IF_RXDATAV); }