added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
Diff: targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/serial_api.c
- Revision:
- 50:a417edff4437
- Parent:
- 22:9c52de9bc1d7
- Child:
- 52:4ce9155acc4d
--- a/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/serial_api.c Wed Jan 13 12:45:11 2016 +0000 +++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/serial_api.c Fri Jan 15 07:45:16 2016 +0000 @@ -34,6 +34,7 @@ #include "mbed_assert.h" #include "serial_api.h" +#include "serial_api_HAL.h" #include <string.h> #include <stdbool.h> @@ -68,7 +69,7 @@ #endif /* Store IRQ id for each UART */ -static uint32_t serial_irq_ids[SERIAL_NUM_UARTS] = { 0 }; +static uint32_t serial_irq_ids[MODULES_SIZE_SERIAL] = { 0 }; /* Interrupt handler from mbed common */ static uart_irq_handler irq_handler; /* Keep track of incoming DMA IRQ's */ @@ -78,52 +79,64 @@ int stdio_uart_inited = 0; serial_t stdio_uart; -static void uart_irq(UARTName, int, SerialIrq); -uint8_t serial_get_index(serial_t *obj); -void serial_enable(serial_t *obj, uint8_t enable); -void serial_enable_pins(serial_t *obj, uint8_t enable); -IRQn_Type serial_get_rx_irq_index(serial_t *obj); -IRQn_Type serial_get_tx_irq_index(serial_t *obj); -CMU_Clock_TypeDef serial_get_clock(serial_t *obj); +static void uart_irq(UARTName, SerialIrq); +static uint8_t serial_get_index(serial_t *obj); +static void serial_enable(serial_t *obj, uint8_t enable); +static void serial_enable_pins(serial_t *obj, uint8_t enable); +static IRQn_Type serial_get_rx_irq_index(serial_t *obj); +static IRQn_Type serial_get_tx_irq_index(serial_t *obj); +static CMU_Clock_TypeDef serial_get_clock(serial_t *obj); +static void serial_dmaSetupChannel(serial_t *obj, bool tx_nrx); +static void serial_rx_abort_asynch_intern(serial_t *obj, int unblock_sleep); +static void serial_tx_abort_asynch_intern(serial_t *obj, int unblock_sleep); +static void serial_block_sleep(serial_t *obj); +static void serial_unblock_sleep(serial_t *obj); +static void serial_leuart_baud(serial_t *obj, int baudrate); /* ISRs for RX and TX events */ #ifdef UART0 -static void uart0_rx_irq() { uart_irq(UART_0, 0, RxIrq); } -static void uart0_tx_irq() { uart_irq(UART_0, 0, TxIrq); USART_IntClear((USART_TypeDef*)UART_0, USART_IFC_TXC);} +static void uart0_rx_irq() { uart_irq(UART_0, RxIrq); } +static void uart0_tx_irq() { uart_irq(UART_0, TxIrq); USART_IntClear((USART_TypeDef*)UART_0, USART_IFC_TXC);} #endif #ifdef UART1 -static void uart1_rx_irq() { uart_irq(UART_1, 1, RxIrq); } -static void uart1_tx_irq() { uart_irq(UART_1, 1, TxIrq); USART_IntClear((USART_TypeDef*)UART_1, USART_IFC_TXC);} +static void uart1_rx_irq() { uart_irq(UART_1, RxIrq); } +static void uart1_tx_irq() { uart_irq(UART_1, TxIrq); USART_IntClear((USART_TypeDef*)UART_1, USART_IFC_TXC);} #endif #ifdef USART0 -static void usart0_rx_irq() { uart_irq(USART_0, 2, RxIrq); } -static void usart0_tx_irq() { uart_irq(USART_0, 2, TxIrq); USART_IntClear((USART_TypeDef*)USART_0, USART_IFC_TXC);} +static void usart0_rx_irq() { uart_irq(USART_0, RxIrq); } +static void usart0_tx_irq() { uart_irq(USART_0, TxIrq); USART_IntClear((USART_TypeDef*)USART_0, USART_IFC_TXC);} #endif #ifdef USART1 -static void usart1_rx_irq() { uart_irq(USART_1, 3, RxIrq); } -static void usart1_tx_irq() { uart_irq(USART_1, 3, TxIrq); USART_IntClear((USART_TypeDef*)USART_1, USART_IFC_TXC);} +static void usart1_rx_irq() { uart_irq(USART_1, RxIrq); } +static void usart1_tx_irq() { uart_irq(USART_1, TxIrq); USART_IntClear((USART_TypeDef*)USART_1, USART_IFC_TXC);} #endif #ifdef USART2 -static void usart2_rx_irq() { uart_irq(USART_2, 4, RxIrq); } -static void usart2_tx_irq() { uart_irq(USART_2, 4, TxIrq); USART_IntClear((USART_TypeDef*)USART_2, USART_IFC_TXC);} +static void usart2_rx_irq() { uart_irq(USART_2, RxIrq); } +static void usart2_tx_irq() { uart_irq(USART_2, TxIrq); USART_IntClear((USART_TypeDef*)USART_2, USART_IFC_TXC);} #endif #ifdef LEUART0 static void leuart0_irq() { - if(LEUART_IntGetEnabled(LEUART0) && (LEUART_IF_RXDATAV | LEUART_IF_FERR | LEUART_IFC_PERR | LEUART_IF_RXOF)) { - uart_irq(LEUART_0, 5, RxIrq); - } else { - uart_irq(LEUART_0, 5, TxIrq); + if(LEUART_IntGetEnabled(LEUART0) & (LEUART_IF_RXDATAV | LEUART_IF_FERR | LEUART_IF_PERR | LEUART_IF_RXOF)) { + uart_irq(LEUART_0, RxIrq); + } + + if(LEUART_IntGetEnabled(LEUART0) & (LEUART_IF_TXC | LEUART_IF_TXBL | LEUART_IF_TXOF)) { + uart_irq(LEUART_0, TxIrq); + LEUART_IntClear(LEUART0, LEUART_IFC_TXC); } } #endif #ifdef LEUART1 static void leuart1_irq() { - if(LEUART_IntGetEnabled(LEUART1) && (LEUART_IF_RXDATAV | LEUART_IF_FERR | LEUART_IFC_PERR | LEUART_IF_RXOF)) { - uart_irq(LEUART_1, 6, RxIrq); - } else { - uart_irq(LEUART_1, 6, TxIrq); + if(LEUART_IntGetEnabled(LEUART1) & (LEUART_IF_RXDATAV | LEUART_IF_FERR | LEUART_IF_PERR | LEUART_IF_RXOF)) { + uart_irq(LEUART_1, RxIrq); + } + + if(LEUART_IntGetEnabled(LEUART1) & (LEUART_IF_TXC | LEUART_IF_TXBL | LEUART_IF_TXOF)) { + uart_irq(LEUART_1, TxIrq); + LEUART_IntClear(LEUART1, LEUART_IFC_TXC); } } #endif @@ -133,78 +146,124 @@ * * @param obj pointer to serial object */ -static void uart_init(serial_t *obj) +static void uart_init(serial_t *obj, uint32_t baudrate, SerialParity parity, int stop_bits) { if(LEUART_REF_VALID(obj->serial.periph.leuart)) { LEUART_Init_TypeDef init = LEUART_INIT_DEFAULT; + if (stop_bits == 2) { + init.stopbits = leuartStopbits2; + } else { + init.stopbits = leuartStopbits1; + } + + switch (parity) { + case ParityOdd: + case ParityForced0: + init.parity = leuartOddParity; + break; + case ParityEven: + case ParityForced1: + init.parity = leuartEvenParity; + break; + default: /* ParityNone */ + init.parity = leuartNoParity; + break; + } + init.enable = leuartDisable; init.baudrate = 9600; init.databits = leuartDatabits8; - init.parity = leuartNoParity; - init.stopbits = leuartStopbits1; #ifdef LEUART_USING_LFXO init.refFreq = LEUART_LF_REF_FREQ; #else init.refFreq = LEUART_REF_FREQ; #endif LEUART_Init(obj->serial.periph.leuart, &init); + + if (baudrate != 9600) { + serial_baud(obj, baudrate); + } } else { USART_InitAsync_TypeDef init = USART_INITASYNC_DEFAULT; + if (stop_bits == 2) { + init.stopbits = usartStopbits2; + } else { + init.stopbits = usartStopbits1; + } + switch (parity) { + case ParityOdd: + case ParityForced0: + init.parity = usartOddParity; + break; + case ParityEven: + case ParityForced1: + init.parity = usartEvenParity; + break; + default: /* ParityNone */ + init.parity = usartNoParity; + break; + } + init.enable = usartDisable; - init.baudrate = 9600; + init.baudrate = baudrate; init.oversampling = usartOVS16; init.databits = usartDatabits8; - init.parity = usartNoParity; - init.stopbits = usartStopbits1; - - /* Determine the reference clock, because the correct clock is not set up at init time */ init.refFreq = REFERENCE_FREQUENCY; USART_InitAsync(obj->serial.periph.uart, &init); } } +/** +* Get index of serial object, relating it to the physical peripheral. +* +* @param obj pointer to serial peripheral (= base address of periph) +* @return internal index of U(S)ART peripheral +*/ +static inline uint8_t serial_pointer_get_index(uint32_t serial_ptr) +{ + uint8_t index = 0; +#ifdef UART0 + if (serial_ptr == UART_0) return index; + index++; +#endif +#ifdef UART1 + if (serial_ptr == UART_1) return index; + index++; +#endif +#ifdef USART0 + if (serial_ptr == USART_0) return index; + index++; +#endif +#ifdef USART1 + if (serial_ptr == USART_1) return index; + index++; +#endif +#ifdef USART2 + if (serial_ptr == USART_2) return index; + index++; +#endif +#ifdef LEUART0 + if (serial_ptr == LEUART_0) return index; + index++; +#endif +#ifdef LEUART1 + if (serial_ptr == LEUART_1) return index; + index++; +#endif + return 0; +} /** * Get index of serial object, relating it to the physical peripheral. * -* @param obj pointer to serial object +* @param obj pointer to serial object (mbed object) * @return internal index of U(S)ART peripheral */ -inline uint8_t serial_get_index(serial_t *obj) +static inline uint8_t serial_get_index(serial_t *obj) { - switch ((uint32_t)obj->serial.periph.uart) { -#ifdef UART0 - case UART_0: - return 0; -#endif -#ifdef UART1 - case UART_1: - return 1; -#endif -#ifdef USART0 - case USART_0: - return 2; -#endif -#ifdef USART1 - case USART_1: - return 3; -#endif -#ifdef USART2 - case USART_2: - return 4; -#endif -#ifdef LEUART0 - case LEUART_0: - return 5; -#endif -#ifdef LEUART1 - case LEUART_1: - return 6; -#endif - } - return 0; + return serial_pointer_get_index((uint32_t)obj->serial.periph.uart); } /** @@ -213,7 +272,7 @@ * @param obj pointer to serial object * @return internal NVIC RX IRQ index of U(S)ART peripheral */ -inline IRQn_Type serial_get_rx_irq_index(serial_t *obj) +static inline IRQn_Type serial_get_rx_irq_index(serial_t *obj) { switch ((uint32_t)obj->serial.periph.uart) { #ifdef UART0 @@ -256,7 +315,7 @@ * @param obj pointer to serial object * @return internal NVIC TX IRQ index of U(S)ART peripheral */ -inline IRQn_Type serial_get_tx_irq_index(serial_t *obj) +static inline IRQn_Type serial_get_tx_irq_index(serial_t *obj) { switch ((uint32_t)obj->serial.periph.uart) { #ifdef UART0 @@ -349,11 +408,18 @@ /* Get location */ uint32_t uart_tx_loc = pin_location(tx, PinMap_UART_TX); uint32_t uart_rx_loc = pin_location(rx, PinMap_UART_RX); + +#if defined(_SILICON_LABS_32B_PLATFORM_1) /* Check that pins are used by same location for the given UART */ obj->serial.location = pinmap_merge(uart_tx_loc, uart_rx_loc); MBED_ASSERT(obj->serial.location != (uint32_t)NC); +#else + obj->serial.location_tx = uart_tx_loc; + obj->serial.location_rx = uart_rx_loc; +#endif /* Store pins in object for easy disabling in serial_free() */ + //TODO: replace all usages with AF_USARTx_TX_PORT(location) macro to save 8 bytes from struct obj->serial.rx_pin = rx; obj->serial.tx_pin = tx; @@ -407,57 +473,123 @@ } } -void serial_enable_pins(serial_t *obj, uint8_t enable) +static void serial_enable_pins(serial_t *obj, uint8_t enable) { if (enable) { /* Configure GPIO pins*/ - pin_mode(obj->serial.rx_pin, Input); - /* 0x10 sets DOUT. Prevents false start. */ - pin_mode(obj->serial.tx_pin, PushPull | 0x10); + if(obj->serial.rx_pin != NC) { + pin_mode(obj->serial.rx_pin, Input); + } + /* Set DOUT first to prevent glitches */ + if(obj->serial.tx_pin != NC) { + GPIO_PinOutSet((GPIO_Port_TypeDef)(obj->serial.tx_pin >> 4 & 0xF), obj->serial.tx_pin & 0xF); + pin_mode(obj->serial.tx_pin, PushPull); + } } else { - pin_mode(obj->serial.rx_pin, Disabled); - pin_mode(obj->serial.tx_pin, Disabled); + if(obj->serial.rx_pin != NC) { + pin_mode(obj->serial.rx_pin, Disabled); + } + if(obj->serial.tx_pin != NC) { + pin_mode(obj->serial.tx_pin, Disabled); + } } } + void serial_init(serial_t *obj, PinName tx, PinName rx) { + uint32_t baudrate; + uint32_t uart_for_stdio = false; + serial_preinit(obj, tx, rx); if(LEUART_REF_VALID(obj->serial.periph.leuart)) { // Set up LEUART clock tree #ifdef LEUART_USING_LFXO //set to use LFXO + CMU_ClockEnable(cmuClock_CORELE, true); CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO); - CMU_ClockEnable(cmuClock_CORELE, true); #else //set to use high-speed clock +#ifdef _SILICON_LABS_32B_PLATFORM_2 + CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_HFCLKLE); +#else CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_CORELEDIV2); #endif +#endif } CMU_ClockEnable(serial_get_clock(obj), true); - /* Configure UART for async operation */ - uart_init(obj); + /* Limitations of board controller: CDC port only supports 115kbaud */ + if(((tx == STDIO_UART_TX) || (rx == STDIO_UART_RX)) + && (obj->serial.periph.uart == (USART_TypeDef*)STDIO_UART ) + ) { + baudrate = 115200; + uart_for_stdio = true; + } else { + baudrate = 9600; + } - /* Limitations of board controller: CDC port only supports 115kbaud */ - if((tx == STDIO_UART_TX) && (rx == STDIO_UART_RX) && (obj->serial.periph.uart == (USART_TypeDef*)STDIO_UART )) { - serial_baud(obj, 115200); - } + /* Configure UART for async operation */ + uart_init(obj, baudrate, ParityNone, 1); /* Enable pins for UART at correct location */ if(LEUART_REF_VALID(obj->serial.periph.leuart)) { - obj->serial.periph.leuart->ROUTE = LEUART_ROUTE_RXPEN | LEUART_ROUTE_TXPEN | (obj->serial.location << _LEUART_ROUTE_LOCATION_SHIFT); +#ifdef _LEUART_ROUTE_LOCATION_SHIFT + obj->serial.periph.leuart->ROUTE = (obj->serial.location << _LEUART_ROUTE_LOCATION_SHIFT); + if(tx != (uint32_t)NC) { + obj->serial.periph.leuart->ROUTE |= LEUART_ROUTE_TXPEN; + } + if(rx != (uint32_t)NC) { + obj->serial.periph.leuart->ROUTE |= LEUART_ROUTE_RXPEN; + } +#else + if(obj->serial.location_tx != NC) { + obj->serial.periph.leuart->ROUTELOC0 = (obj->serial.periph.leuart->ROUTELOC0 & (~_LEUART_ROUTELOC0_TXLOC_MASK)) | (obj->serial.location_tx << _LEUART_ROUTELOC0_TXLOC_SHIFT); + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_TXPEN_MASK)) | LEUART_ROUTEPEN_TXPEN; + } else { + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_TXPEN_MASK)); + } + if(obj->serial.location_rx != NC) { + obj->serial.periph.leuart->ROUTELOC0 = (obj->serial.periph.leuart->ROUTELOC0 & (~_LEUART_ROUTELOC0_RXLOC_MASK)) | (obj->serial.location_rx << _LEUART_ROUTELOC0_RXLOC_SHIFT); + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_RXPEN_MASK)) | LEUART_ROUTEPEN_RXPEN; + } else { + obj->serial.periph.leuart->CMD = LEUART_CMD_RXBLOCKEN; + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_RXPEN_MASK)); + } +#endif obj->serial.periph.leuart->IFC = LEUART_IFC_TXC; obj->serial.periph.leuart->CTRL |= LEUART_CTRL_RXDMAWU | LEUART_CTRL_TXDMAWU; } else { - obj->serial.periph.uart->ROUTE = USART_ROUTE_RXPEN | USART_ROUTE_TXPEN | (obj->serial.location << _USART_ROUTE_LOCATION_SHIFT); +#ifdef _USART_ROUTE_LOCATION_SHIFT + obj->serial.periph.uart->ROUTE = (obj->serial.location << _USART_ROUTE_LOCATION_SHIFT); + if(tx != (uint32_t)NC) { + obj->serial.periph.uart->ROUTE |= USART_ROUTE_TXPEN; + } + if(rx != (uint32_t)NC) { + obj->serial.periph.uart->ROUTE |= USART_ROUTE_RXPEN; + } +#else + if(obj->serial.location_tx != NC) { + obj->serial.periph.uart->ROUTELOC0 = (obj->serial.periph.uart->ROUTELOC0 & (~_USART_ROUTELOC0_TXLOC_MASK)) | (obj->serial.location_tx << _USART_ROUTELOC0_TXLOC_SHIFT); + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_TXPEN_MASK)) | USART_ROUTEPEN_TXPEN; + } else { + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_TXPEN_MASK)); + } + if(obj->serial.location_rx != NC) { + obj->serial.periph.uart->ROUTELOC0 = (obj->serial.periph.uart->ROUTELOC0 & (~_USART_ROUTELOC0_RXLOC_MASK)) | (obj->serial.location_rx << _USART_ROUTELOC0_RXLOC_SHIFT); + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_RXPEN_MASK)) | USART_ROUTEPEN_RXPEN; + } else { + obj->serial.periph.uart->CMD = USART_CMD_RXBLOCKEN; + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_RXPEN_MASK)); + } +#endif obj->serial.periph.uart->IFC = USART_IFC_TXC; } /* If this is the UART to be used for stdio, copy it to the stdio_uart struct */ - if (obj->serial.periph.uart == (USART_TypeDef*)STDIO_UART ) { + if(uart_for_stdio) { stdio_uart_inited = 1; memcpy(&stdio_uart, obj, sizeof(serial_t)); } @@ -465,7 +597,6 @@ serial_enable_pins(obj, true); serial_enable(obj, true); - obj->serial.dmaOptionsTX.dmaChannel = -1; obj->serial.dmaOptionsTX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC; @@ -474,7 +605,17 @@ } -void serial_enable(serial_t *obj, uint8_t enable) +void serial_free(serial_t *obj) +{ + if( LEUART_REF_VALID(obj->serial.periph.leuart) ) { + LEUART_Enable(obj->serial.periph.leuart, leuartDisable); + } else { + USART_Enable(obj->serial.periph.uart, usartDisable); + } + serial_enable_pins(obj, false); +} + +static void serial_enable(serial_t *obj, uint8_t enable) { if(LEUART_REF_VALID(obj->serial.periph.leuart)) { if (enable) { @@ -498,53 +639,86 @@ void serial_baud(serial_t *obj, int baudrate) { if(LEUART_REF_VALID(obj->serial.periph.leuart)) { -#ifdef LEUART_USING_LFXO - - /* check if baudrate is within allowed range */ - MBED_ASSERT(baudrate >= (LEUART_LF_REF_FREQ >> 7)); - - if(baudrate > (LEUART_LF_REF_FREQ >> 1)){ - /* check if baudrate is within allowed range */ - MBED_ASSERT((baudrate <= (LEUART_HF_REF_FREQ >> 1)) && (baudrate > (LEUART_HF_REF_FREQ >> 10))); - - CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_CORELEDIV2); - uint8_t divisor = 1; + serial_leuart_baud(obj, baudrate); + } else { + USART_BaudrateAsyncSet(obj->serial.periph.uart, REFERENCE_FREQUENCY, (uint32_t)baudrate, usartOVS16); + } +} - if(baudrate > (LEUART_HF_REF_FREQ >> 7)){ - divisor = 1; - }else if(baudrate > (LEUART_HF_REF_FREQ >> 8)){ - divisor = 2; - }else if(baudrate > (LEUART_HF_REF_FREQ >> 9)){ - divisor = 4; - }else{ - divisor = 8; - } - CMU_ClockDivSet(serial_get_clock(obj), divisor); - LEUART_BaudrateSet(obj->serial.periph.leuart, LEUART_HF_REF_FREQ/divisor, (uint32_t)baudrate); - }else{ - CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO); - CMU_ClockDivSet(serial_get_clock(obj), 1); - LEUART_BaudrateSet(obj->serial.periph.leuart, LEUART_LF_REF_FREQ, (uint32_t)baudrate); - } +/** + * Set LEUART baud rate + * Calculate whether LF or HF clock should be used. + */ +static void serial_leuart_baud(serial_t *obj, int baudrate) +{ +#ifdef LEUART_USING_LFXO + /* check if baudrate is within allowed range */ +#if defined(_SILICON_LABS_32B_PLATFORM_2) + // P2 has 9 bits + 5 fractional bits in LEUART CLKDIV register + MBED_ASSERT(baudrate >= (LEUART_LF_REF_FREQ >> 9)); #else - /* check if baudrate is within allowed range */ - MBED_ASSERT((baudrate > (LEUART_REF_FREQ >> 10)) && (baudrate <= (LEUART_REF_FREQ >> 1))); + // P1 has 7 bits + 5 fractional bits in LEUART CLKDIV register + MBED_ASSERT(baudrate >= (LEUART_LF_REF_FREQ >> 7)); +#endif + + if(baudrate > (LEUART_LF_REF_FREQ >> 1)){ + // Baudrate is bigger than LFCLK/2 - we need to use the HF clock uint8_t divisor = 1; - if(baudrate > (LEUART_REF_FREQ >> 7)){ + +#if defined(_SILICON_LABS_32B_PLATFORM_2) + /* Check if baudrate is within allowed range: (HFCLK/4096, HFCLK/2] */ + MBED_ASSERT((baudrate <= (LEUART_HF_REF_FREQ >> 1)) && (baudrate > (LEUART_HF_REF_FREQ >> 12))); + + CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_HFCLKLE); + + if(baudrate > (LEUART_HF_REF_FREQ >> 9)){ divisor = 1; - }else if(baudrate > (LEUART_REF_FREQ >> 8)){ + }else if(baudrate > (LEUART_HF_REF_FREQ >> 10)){ divisor = 2; - }else if(baudrate > (LEUART_REF_FREQ >> 9)){ + }else if(baudrate > (LEUART_HF_REF_FREQ >> 11)){ divisor = 4; }else{ divisor = 8; } - CMU_ClockDivSet(serial_get_clock(obj), divisor); - LEUART_BaudrateSet(obj->serial.periph.leuart, LEUART_REF_FREQ/divisor, (uint32_t)baudrate); +#else // P1 + /* Check if baudrate is within allowed range */ + MBED_ASSERT((baudrate <= (LEUART_HF_REF_FREQ >> 1)) && (baudrate > (LEUART_HF_REF_FREQ >> 10))); + + CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_CORELEDIV2); + + if(baudrate > (LEUART_HF_REF_FREQ >> 7)){ + divisor = 1; + }else if(baudrate > (LEUART_HF_REF_FREQ >> 8)){ + divisor = 2; + }else if(baudrate > (LEUART_HF_REF_FREQ >> 9)){ + divisor = 4; + }else{ + divisor = 8; + } #endif - } else { - USART_BaudrateAsyncSet(obj->serial.periph.uart, REFERENCE_FREQUENCY, (uint32_t)baudrate, usartOVS16); + CMU_ClockDivSet(serial_get_clock(obj), divisor); + LEUART_BaudrateSet(obj->serial.periph.leuart, LEUART_HF_REF_FREQ/divisor, (uint32_t)baudrate); + }else{ + CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO); + CMU_ClockDivSet(serial_get_clock(obj), 1); + LEUART_BaudrateSet(obj->serial.periph.leuart, LEUART_LF_REF_FREQ, (uint32_t)baudrate); } +#else + /* check if baudrate is within allowed range */ + MBED_ASSERT((baudrate > (LEUART_REF_FREQ >> 10)) && (baudrate <= (LEUART_REF_FREQ >> 1))); + uint8_t divisor = 1; + if(baudrate > (LEUART_REF_FREQ >> 7)){ + divisor = 1; + }else if(baudrate > (LEUART_REF_FREQ >> 8)){ + divisor = 2; + }else if(baudrate > (LEUART_REF_FREQ >> 9)){ + divisor = 4; + }else{ + divisor = 8; + } + CMU_ClockDivSet(serial_get_clock(obj), divisor); + LEUART_BaudrateSet(obj->serial.periph.leuart, LEUART_REF_FREQ/divisor, (uint32_t)baudrate); +#endif } /** @@ -587,7 +761,28 @@ LEUART_Init(obj->serial.periph.leuart, &init); /* Re-enable pins for UART at correct location */ - obj->serial.periph.leuart->ROUTE = LEUART_ROUTE_RXPEN | LEUART_ROUTE_TXPEN | (obj->serial.location << _LEUART_ROUTE_LOCATION_SHIFT); +#ifdef _LEUART_ROUTE_LOCATION_SHIFT + obj->serial.periph.leuart->ROUTE = (obj->serial.location << _LEUART_ROUTE_LOCATION_SHIFT); + if(obj->serial.tx_pin != (uint32_t)NC) { + obj->serial.periph.leuart->ROUTE |= LEUART_ROUTE_TXPEN; + } + if(obj->serial.rx_pin != (uint32_t)NC) { + obj->serial.periph.leuart->ROUTE |= LEUART_ROUTE_RXPEN; + } +#else + if(obj->serial.location_tx != NC) { + obj->serial.periph.leuart->ROUTELOC0 = (obj->serial.periph.leuart->ROUTELOC0 & (~_LEUART_ROUTELOC0_TXLOC_MASK)) | (obj->serial.location_tx << _LEUART_ROUTELOC0_TXLOC_SHIFT); + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_TXPEN_MASK)) | LEUART_ROUTEPEN_TXPEN; + } else { + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_TXPEN_MASK)); + } + if(obj->serial.location_rx != NC) { + obj->serial.periph.leuart->ROUTELOC0 = (obj->serial.periph.leuart->ROUTELOC0 & (~_LEUART_ROUTELOC0_RXLOC_MASK)) | (obj->serial.location_rx << _LEUART_ROUTELOC0_RXLOC_SHIFT); + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_RXPEN_MASK)) | LEUART_ROUTEPEN_RXPEN; + } else { + obj->serial.periph.leuart->ROUTEPEN = (obj->serial.periph.leuart->ROUTEPEN & (~_LEUART_ROUTEPEN_RXPEN_MASK)); + } +#endif /* Re-enable interrupts */ if(was_enabled != 0) { @@ -632,7 +827,28 @@ USART_InitAsync(obj->serial.periph.uart, &init); /* Re-enable pins for UART at correct location */ - obj->serial.periph.uart->ROUTE = USART_ROUTE_RXPEN | USART_ROUTE_TXPEN | (obj->serial.location << _USART_ROUTE_LOCATION_SHIFT); +#ifdef _USART_ROUTE_LOCATION_SHIFT + obj->serial.periph.uart->ROUTE = (obj->serial.location << _USART_ROUTE_LOCATION_SHIFT); + if(obj->serial.tx_pin != (uint32_t)NC) { + obj->serial.periph.uart->ROUTE |= USART_ROUTE_TXPEN; + } + if(obj->serial.rx_pin != (uint32_t)NC) { + obj->serial.periph.uart->ROUTE |= USART_ROUTE_RXPEN; + } +#else + if(obj->serial.location_tx != NC) { + obj->serial.periph.uart->ROUTELOC0 = (obj->serial.periph.uart->ROUTELOC0 & (~_USART_ROUTELOC0_TXLOC_MASK)) | (obj->serial.location_tx << _USART_ROUTELOC0_TXLOC_SHIFT); + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_TXPEN_MASK)) | USART_ROUTEPEN_TXPEN; + } else { + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_TXPEN_MASK)); + } + if(obj->serial.location_rx != NC) { + obj->serial.periph.uart->ROUTELOC0 = (obj->serial.periph.uart->ROUTELOC0 & (~_USART_ROUTELOC0_RXLOC_MASK)) | (obj->serial.location_rx << _USART_ROUTELOC0_RXLOC_SHIFT); + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_RXPEN_MASK)) | USART_ROUTEPEN_RXPEN; + } else { + obj->serial.periph.uart->ROUTEPEN = (obj->serial.periph.uart->ROUTEPEN & (~_USART_ROUTEPEN_RXPEN_MASK)); + } +#endif /* Re-enable interrupts */ if(was_enabled != 0) { @@ -764,8 +980,9 @@ /** * Generic ISR for all UARTs, both TX and RX */ -static void uart_irq(UARTName name, int index, SerialIrq irq) +static void uart_irq(UARTName name, SerialIrq irq) { + uint8_t index = serial_pointer_get_index((uint32_t)name); if (serial_irq_ids[index] != 0) { /* Pass interrupt on to mbed common handler */ irq_handler(serial_irq_ids[index], irq); @@ -852,8 +1069,10 @@ * need to use serial_writable(). */ if(LEUART_REF_VALID(obj->serial.periph.leuart)) { LEUART_Tx(obj->serial.periph.leuart, (uint8_t)(c)); + while (!(obj->serial.periph.leuart->STATUS & LEUART_STATUS_TXC)); } else { USART_Tx(obj->serial.periph.uart, (uint8_t)(c)); + while (!(obj->serial.periph.uart->STATUS & USART_STATUS_TXC)); } } @@ -940,6 +1159,8 @@ } } +#ifndef LDMA_PRESENT + /****************************************** * static void serial_setupDmaChannel(serial_t *obj, bool tx_nrx) * @@ -1041,9 +1262,9 @@ DMA_CfgChannel(obj->serial.dmaOptionsRX.dmaChannel, &channelConfig); } - +} -} +#endif /* LDMA_PRESENT */ /****************************************** * static void serial_dmaTrySetState(DMA_OPTIONS_t *obj, DMAUsage requestedState) @@ -1097,6 +1318,8 @@ } } +#ifndef LDMA_PRESENT + static void serial_dmaActivate(serial_t *obj, void* cb, void* buffer, int length, bool tx_nrx) { DMA_CfgDescr_TypeDef channelConfig; @@ -1104,7 +1327,7 @@ if(tx_nrx) { // Set DMA callback obj->serial.dmaOptionsTX.dmaCallback.cbFunc = serial_dmaTransferComplete; - obj->serial.dmaOptionsTX.dmaCallback.userPtr = cb; + obj->serial.dmaOptionsTX.dmaCallback.userPtr = NULL; // Set up configuration structure channelConfig.dstInc = dmaDataIncNone; @@ -1113,9 +1336,23 @@ channelConfig.arbRate = dmaArbitrate1; channelConfig.hprot = 0; + // Clear TXC + if(LEUART_REF_VALID(obj->serial.periph.leuart)) { + LEUART_IntClear(obj->serial.periph.leuart, LEUART_IFC_TXC); + } else { + USART_IntClear(obj->serial.periph.uart, USART_IFC_TXC); + } + + // Set callback and enable TXC. This will fire once the + // serial transfer finishes + NVIC_SetVector(serial_get_tx_irq_index(obj), (uint32_t)cb); + serial_irq_set(obj, TxIrq, true); + DMA_CfgDescr(obj->serial.dmaOptionsTX.dmaChannel, true, &channelConfig); if(LEUART_REF_VALID(obj->serial.periph.leuart)) { - // Activate TX + // Activate TX and clear TX buffer (note that clear must be done + // separately and before TXEN or DMA will die on some platforms) + obj->serial.periph.leuart->CMD = LEUART_CMD_CLEARTX; obj->serial.periph.leuart->CMD = LEUART_CMD_TXEN; while(obj->serial.periph.leuart->SYNCBUSY & LEUART_SYNCBUSY_CMD); @@ -1128,6 +1365,8 @@ // Kick off TX DMA DMA_ActivateBasic(obj->serial.dmaOptionsTX.dmaChannel, true, false, (void*) &(obj->serial.periph.uart->TXDATA), buffer, length - 1); } + + } else { // Set DMA callback obj->serial.dmaOptionsRX.dmaCallback.cbFunc = serial_dmaTransferComplete; @@ -1144,7 +1383,8 @@ if(LEUART_REF_VALID(obj->serial.periph.leuart)) { // Activate RX and clear RX buffer - obj->serial.periph.leuart->CMD = LEUART_CMD_RXEN | LEUART_CMD_CLEARRX; + obj->serial.periph.leuart->CMD = LEUART_CMD_CLEARRX; + obj->serial.periph.leuart->CMD = LEUART_CMD_RXEN; while(obj->serial.periph.leuart->SYNCBUSY & LEUART_SYNCBUSY_CMD); // Kick off RX DMA @@ -1159,6 +1399,112 @@ } } +#endif + + +#ifdef LDMA_PRESENT + +static void serial_dmaSetupChannel(serial_t *obj, bool tx_nrx) +{ +} + +static void serial_dmaActivate(serial_t *obj, void* cb, void* buffer, int length, bool tx_nrx) +{ + LDMA_PeripheralSignal_t dma_periph; + + obj->serial.dmaOptionsRX.dmaCallback.userPtr = cb; + + if( tx_nrx ) { + volatile void *target_addr; + + // Clear TXC + if(LEUART_REF_VALID(obj->serial.periph.leuart)) { + LEUART_IntClear(obj->serial.periph.leuart, LEUART_IFC_TXC); + } else { + USART_IntClear(obj->serial.periph.uart, USART_IFC_TXC); + } + + switch((uint32_t)(obj->serial.periph.uart)) { +#ifdef USART0 + case USART_0: + dma_periph = ldmaPeripheralSignal_USART0_TXBL; + target_addr = &USART0->TXDATA; + obj->serial.periph.uart->CMD = USART_CMD_TXEN | USART_CMD_CLEARTX; + break; +#endif +#ifdef USART1 + case USART_1: + dma_periph = ldmaPeripheralSignal_USART1_TXBL; + target_addr = &USART1->TXDATA; + obj->serial.periph.uart->CMD = USART_CMD_TXEN | USART_CMD_CLEARTX; + break; +#endif +#ifdef LEUART0 + case LEUART_0: + dma_periph = ldmaPeripheralSignal_LEUART0_TXBL; + target_addr = &LEUART0->TXDATA; + obj->serial.periph.leuart->CMD = LEUART_CMD_CLEARTX; + obj->serial.periph.leuart->CMD = LEUART_CMD_TXEN; + while(obj->serial.periph.leuart->SYNCBUSY & LEUART_SYNCBUSY_CMD); + break; +#endif + default: + MBED_ASSERT(0); + while(1); + break; + } + + // Set callback and enable TXC. This will fire once the + // serial transfer finishes + NVIC_SetVector(serial_get_tx_irq_index(obj), (uint32_t)cb); + serial_irq_set(obj, TxIrq, true); + + // Start DMA transfer + LDMA_TransferCfg_t xferConf = LDMA_TRANSFER_CFG_PERIPHERAL(dma_periph); + LDMA_Descriptor_t desc = LDMA_DESCRIPTOR_SINGLE_M2P_BYTE(buffer, target_addr, length); + LDMAx_StartTransfer(obj->serial.dmaOptionsTX.dmaChannel, &xferConf, &desc, serial_dmaTransferComplete, NULL); + + } else { + volatile const void *source_addr; + + switch((uint32_t)(obj->serial.periph.uart)) { +#ifdef USART0 + case USART_0: + dma_periph = ldmaPeripheralSignal_USART0_RXDATAV; + source_addr = &USART0->RXDATA; + obj->serial.periph.uart->CMD = USART_CMD_RXEN | USART_CMD_CLEARRX; + break; +#endif +#ifdef USART1 + case USART_1: + dma_periph = ldmaPeripheralSignal_USART1_RXDATAV; + source_addr = &USART1->RXDATA; + obj->serial.periph.uart->CMD = USART_CMD_RXEN | USART_CMD_CLEARRX; + break; +#endif +#ifdef LEUART0 + case LEUART_0: + dma_periph = ldmaPeripheralSignal_LEUART0_RXDATAV; + source_addr = &LEUART0->RXDATA; + obj->serial.periph.leuart->CMD = LEUART_CMD_CLEARRX; + obj->serial.periph.leuart->CMD = LEUART_CMD_RXEN; + while(obj->serial.periph.leuart->SYNCBUSY & LEUART_SYNCBUSY_CMD); + break; +#endif + default: + MBED_ASSERT(0); + while(1); + break; + } + + LDMA_TransferCfg_t xferConf = LDMA_TRANSFER_CFG_PERIPHERAL(dma_periph); + LDMA_Descriptor_t desc = LDMA_DESCRIPTOR_SINGLE_P2M_BYTE(source_addr, buffer, length); + LDMAx_StartTransfer(obj->serial.dmaOptionsRX.dmaChannel, &xferConf, &desc, serial_dmaTransferComplete, cb); + } +} + +#endif /* LDMA_PRESENT */ + /************************************************************************************ * ASYNCHRONOUS HAL * ************************************************************************************/ @@ -1283,7 +1629,7 @@ */ int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t event, DMAUsage hint) { - // Check that a buffer has indeed been set up + // Check that a buffer has indeed been set up MBED_ASSERT(tx != (void*)0); if(tx_length == 0) return 0; @@ -1295,15 +1641,7 @@ serial_tx_enable_event(obj, event, true); // Set up sleepmode -#ifdef LEUART_USING_LFXO - if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){ - blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART); - }else{ - blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); - } -#else - blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); -#endif + serial_block_sleep(obj); // Determine DMA strategy serial_dmaTrySetState(&(obj->serial.dmaOptionsTX), hint, obj, true); @@ -1323,7 +1661,8 @@ if(LEUART_REF_VALID(obj->serial.periph.leuart)) { // Activate TX and clear TX buffer - obj->serial.periph.leuart->CMD = LEUART_CMD_TXEN | LEUART_CMD_CLEARTX; + obj->serial.periph.leuart->CMD = LEUART_CMD_CLEARTX; + obj->serial.periph.leuart->CMD = LEUART_CMD_TXEN; while(obj->serial.periph.leuart->SYNCBUSY & LEUART_SYNCBUSY_CMD); // Enable interrupt @@ -1374,15 +1713,7 @@ obj->char_match = char_match; // Set up sleepmode -#ifdef LEUART_USING_LFXO - if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){ - blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART); - }else{ - blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); - } -#else - blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); -#endif + serial_block_sleep(obj); // Determine DMA strategy // If character match is enabled, we can't use DMA, sadly. We could when using LEUART though, but that support is not in here yet. @@ -1406,7 +1737,8 @@ if(LEUART_REF_VALID(obj->serial.periph.leuart)) { // Activate RX and clear RX buffer - obj->serial.periph.leuart->CMD = LEUART_CMD_RXEN | LEUART_CMD_CLEARRX; + obj->serial.periph.leuart->CMD = LEUART_CMD_CLEARRX; + obj->serial.periph.leuart->CMD = LEUART_CMD_RXEN; while(obj->serial.periph.leuart->SYNCBUSY & LEUART_SYNCBUSY_CMD); // Enable interrupt @@ -1433,20 +1765,10 @@ */ uint8_t serial_tx_active(serial_t *obj) { - switch(obj->serial.dmaOptionsTX.dmaUsageState) { - case DMA_USAGE_TEMPORARY_ALLOCATED: - /* Temporary allocation always means its active, as this state gets cleared afterwards */ - return 1; - case DMA_USAGE_ALLOCATED: - /* Check whether the allocated DMA channel is active by checking the DMA transfer */ - return(DMA_ChannelEnabled(obj->serial.dmaOptionsTX.dmaChannel)); - default: - /* Check whether interrupt for serial TX is enabled */ - if(LEUART_REF_VALID(obj->serial.periph.leuart)) { - return (obj->serial.periph.leuart->IEN & (LEUART_IEN_TXBL)) ? true : false; - } else { - return (obj->serial.periph.uart->IEN & (USART_IEN_TXBL)) ? true : false; - } + if(LEUART_REF_VALID(obj->serial.periph.leuart)) { + return (obj->serial.periph.leuart->IEN & (LEUART_IEN_TXBL|LEUART_IEN_TXC)) ? true : false; + } else { + return (obj->serial.periph.uart->IEN & (USART_IEN_TXBL|USART_IEN_TXC)) ? true : false; } } @@ -1463,7 +1785,13 @@ return 1; case DMA_USAGE_ALLOCATED: /* Check whether the allocated DMA channel is active by checking the DMA transfer */ - return(DMA_ChannelEnabled(obj->serial.dmaOptionsRX.dmaChannel)); +#ifndef LDMA_PRESENT + return DMA_ChannelEnabled(obj->serial.dmaOptionsRX.dmaChannel); +#else + // LDMA_TransferDone does not work since the CHDONE bits get cleared, + // so just check if the channel is enabled + return LDMA->CHEN & (1 << obj->serial.dmaOptionsRX.dmaChannel); +#endif default: /* Check whether interrupt for serial TX is enabled */ if(LEUART_REF_VALID(obj->serial.periph.leuart)) { @@ -1497,10 +1825,11 @@ /* Last byte has been put in TX, set up TXC interrupt */ LEUART_IntDisable(obj->serial.periph.leuart, LEUART_IEN_TXBL); LEUART_IntEnable(obj->serial.periph.leuart, LEUART_IEN_TXC); + while (obj->serial.periph.leuart->SYNCBUSY); } }else if (obj->serial.periph.leuart->IF & LEUART_IF_TXC){ /* Last byte has been successfully transmitted. Stop the procedure */ - serial_tx_abort_asynch(obj); + serial_tx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; } } else { @@ -1517,7 +1846,7 @@ } } else if (obj->serial.periph.uart->IF & USART_IF_TXC) { /* Last byte has been successfully transmitted. Stop the procedure */ - serial_tx_abort_asynch(obj); + serial_tx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; } } @@ -1542,21 +1871,21 @@ if(LEUART_IntGetEnabled(obj->serial.periph.leuart) & LEUART_IF_PERR) { /* Parity error has occurred, and we are notifying. */ LEUART_IntClear(obj->serial.periph.leuart, LEUART_IFC_PERR); - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_RX_PARITY_ERROR; } if(LEUART_IntGetEnabled(obj->serial.periph.leuart) & LEUART_IF_FERR) { /* Framing error has occurred, and we are notifying */ LEUART_IntClear(obj->serial.periph.leuart, LEUART_IFC_FERR); - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_RX_FRAMING_ERROR; } if(LEUART_IntGetEnabled(obj->serial.periph.leuart) & LEUART_IF_RXOF) { /* RX buffer overflow has occurred, and we are notifying */ LEUART_IntClear(obj->serial.periph.leuart, LEUART_IFC_RXOF); - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_RX_OVERFLOW; } @@ -1570,7 +1899,7 @@ if((buf[obj->rx_buff.pos] == obj->char_match) && (obj->serial.events & SERIAL_EVENT_RX_CHARACTER_MATCH)) event |= SERIAL_EVENT_RX_CHARACTER_MATCH; - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return event & obj->serial.events; } else { /* There's still space in the receive buffer */ @@ -1592,7 +1921,7 @@ } if(aborting) { - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return event & obj->serial.events; } } @@ -1603,21 +1932,21 @@ if(USART_IntGetEnabled(obj->serial.periph.uart) & USART_IF_PERR) { /* Parity error has occurred, and we are notifying. */ USART_IntClear(obj->serial.periph.uart, USART_IFC_PERR); - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_RX_PARITY_ERROR; } if(USART_IntGetEnabled(obj->serial.periph.uart) & USART_IF_FERR) { /* Framing error has occurred, and we are notifying */ USART_IntClear(obj->serial.periph.uart, USART_IFC_FERR); - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_RX_FRAMING_ERROR; } if(USART_IntGetEnabled(obj->serial.periph.uart) & USART_IF_RXOF) { /* RX buffer overflow has occurred, and we are notifying */ USART_IntClear(obj->serial.periph.uart, USART_IFC_RXOF); - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return SERIAL_EVENT_RX_OVERFLOW; } @@ -1631,7 +1960,7 @@ if((buf[obj->rx_buff.pos] == obj->char_match) && (obj->serial.events & SERIAL_EVENT_RX_CHARACTER_MATCH)) event |= SERIAL_EVENT_RX_CHARACTER_MATCH; - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return event & obj->serial.events; } else { /* There's still space in the receive buffer */ @@ -1653,7 +1982,7 @@ } if(aborting) { - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); return event & obj->serial.events; } } @@ -1671,37 +2000,37 @@ */ int serial_irq_handler_asynch(serial_t *obj) { + uint32_t txc_int; + + if(LEUART_REF_VALID(obj->serial.periph.leuart)) { + txc_int = LEUART_IntGetEnabled(obj->serial.periph.leuart) & LEUART_IF_TXC; + } else { + txc_int = USART_IntGetEnabled(obj->serial.periph.uart) & USART_IF_TXC; + } + /* First, check if we're running in DMA mode */ - if(serial_dma_irq_fired[obj->serial.dmaOptionsRX.dmaChannel]) { + if( (obj->serial.dmaOptionsRX.dmaChannel != -1) && + serial_dma_irq_fired[obj->serial.dmaOptionsRX.dmaChannel]) { /* Clean up */ serial_dma_irq_fired[obj->serial.dmaOptionsRX.dmaChannel] = false; - serial_rx_abort_asynch(obj); + serial_rx_abort_asynch_intern(obj, 1); /* Notify CPP land of RX completion */ return SERIAL_EVENT_RX_COMPLETE & obj->serial.events; - } else if (serial_dma_irq_fired[obj->serial.dmaOptionsTX.dmaChannel]) { + } else if (txc_int && (obj->serial.dmaOptionsTX.dmaChannel != -1) && + serial_dma_irq_fired[obj->serial.dmaOptionsTX.dmaChannel]) { if(LEUART_REF_VALID(obj->serial.periph.leuart)) { - if(obj->serial.periph.leuart->IEN & LEUART_IEN_TXC){ - LEUART_IntDisable(obj->serial.periph.leuart,LEUART_IEN_TXC); - /* Clean up */ - serial_dma_irq_fired[obj->serial.dmaOptionsTX.dmaChannel] = false; - serial_tx_abort_asynch(obj); - /* Notify CPP land of completion */ - return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; - }else{ - LEUART_IntEnable(obj->serial.periph.leuart,LEUART_IEN_TXC); - } + /* Clean up */ + serial_dma_irq_fired[obj->serial.dmaOptionsTX.dmaChannel] = false; + serial_tx_abort_asynch_intern(obj, 1); + /* Notify CPP land of completion */ + return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; }else{ - if(obj->serial.periph.uart->IEN & USART_IEN_TXC){ - USART_IntDisable(obj->serial.periph.uart,USART_IEN_TXC); - /* Clean up */ - serial_dma_irq_fired[obj->serial.dmaOptionsTX.dmaChannel] = false; - serial_tx_abort_asynch(obj); - /* Notify CPP land of completion */ - return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; - }else{ - USART_IntEnable(obj->serial.periph.uart,USART_IEN_TXC); - } + /* Clean up */ + serial_dma_irq_fired[obj->serial.dmaOptionsTX.dmaChannel] = false; + serial_tx_abort_asynch_intern(obj, 1); + /* Notify CPP land of completion */ + return SERIAL_EVENT_TX_COMPLETE & obj->serial.events; } } else { /* Check the NVIC to see which interrupt we're running from @@ -1733,43 +2062,96 @@ */ void serial_tx_abort_asynch(serial_t *obj) { - /* Stop transmitter */ - //obj->serial.periph.uart->CMD |= USART_CMD_TXDIS; + serial_tx_abort_asynch_intern(obj, 0); +} + +static void serial_tx_abort_asynch_intern(serial_t *obj, int unblock_sleep) +{ + // Transmitter should be disabled here but there are multiple issues + // making that quite difficult. + // + // - Disabling the transmitter when using DMA on platforms prior to + // Pearl can cause the UART to leave the line low, generating a break + // condition until the next transmission begins. + // + // - On (at least) Pearl, once TXC interrupt has fired it will take some time + // (some tens of microsec) for TXC to be set in STATUS. If we turn off + // the transmitter before this, bad things will happen. + // + // - On (at least) Pearl, when using TX DMA it is possible for the USART + // status to be: TXENS TXBL TXIDLE = 1, TXBUFCNT = 0, but TXC = 0. + // + // All in all, the logic was so fragile it's best to leave it out. /* Clean up */ switch(obj->serial.dmaOptionsTX.dmaUsageState) { case DMA_USAGE_ALLOCATED: /* stop DMA transfer */ +#ifndef LDMA_PRESENT DMA_ChannelEnable(obj->serial.dmaOptionsTX.dmaChannel, false); +#else + LDMA_StopTransfer(obj->serial.dmaOptionsTX.dmaChannel); +#endif break; case DMA_USAGE_TEMPORARY_ALLOCATED: /* stop DMA transfer and release channel */ +#ifndef LDMA_PRESENT DMA_ChannelEnable(obj->serial.dmaOptionsTX.dmaChannel, false); +#else + LDMA_StopTransfer(obj->serial.dmaOptionsTX.dmaChannel); +#endif dma_channel_free(obj->serial.dmaOptionsTX.dmaChannel); obj->serial.dmaOptionsTX.dmaChannel = -1; obj->serial.dmaOptionsTX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC; break; default: - /* stop interrupting */ - if(LEUART_REF_VALID(obj->serial.periph.leuart)) { - LEUART_IntDisable(obj->serial.periph.leuart, LEUART_IEN_TXC); - LEUART_IntClear(obj->serial.periph.leuart, LEUART_IFC_TXC); - } else { - USART_IntDisable(obj->serial.periph.uart, USART_IEN_TXC); - USART_IntClear(obj->serial.periph.uart, USART_IFC_TXC); - } break; } + /* stop interrupting */ + if(LEUART_REF_VALID(obj->serial.periph.leuart)) { + LEUART_IntDisable(obj->serial.periph.leuart, LEUART_IEN_TXBL); + LEUART_IntDisable(obj->serial.periph.leuart, LEUART_IEN_TXC); + LEUART_IntClear(obj->serial.periph.leuart, LEUART_IFC_TXC); + } else { + USART_IntDisable(obj->serial.periph.uart, USART_IEN_TXBL); + USART_IntDisable(obj->serial.periph.uart, USART_IEN_TXC); + USART_IntClear(obj->serial.periph.uart, USART_IFC_TXC); + } + /* Say that we can stop using this emode */ + if(unblock_sleep) + serial_unblock_sleep(obj); +} + + +static void serial_unblock_sleep(serial_t *obj) +{ + if( obj->serial.sleep_blocked > 0 ) { +#ifdef LEUART_USING_LFXO + if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){ + unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART); + }else{ + unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); + } +#else + unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); +#endif + obj->serial.sleep_blocked--; + } +} + +static void serial_block_sleep(serial_t *obj) +{ + obj->serial.sleep_blocked++; #ifdef LEUART_USING_LFXO if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){ - unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART); + blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART); }else{ - unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); + blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); } #else - unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); + blockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); #endif } @@ -1780,18 +2162,36 @@ */ void serial_rx_abort_asynch(serial_t *obj) { + serial_rx_abort_asynch_intern(obj, 0); +} + +static void serial_rx_abort_asynch_intern(serial_t *obj, int unblock_sleep) +{ /* Stop receiver */ - obj->serial.periph.uart->CMD |= USART_CMD_RXDIS; + if(LEUART_REF_VALID(obj->serial.periph.leuart)) { + obj->serial.periph.leuart->CMD = LEUART_CMD_RXDIS; + while(obj->serial.periph.leuart->SYNCBUSY & LEUART_SYNCBUSY_CMD); + } else { + obj->serial.periph.uart->CMD = USART_CMD_RXDIS; + } /* Clean up */ switch(obj->serial.dmaOptionsRX.dmaUsageState) { case DMA_USAGE_ALLOCATED: /* stop DMA transfer */ +#ifndef LDMA_PRESENT DMA_ChannelEnable(obj->serial.dmaOptionsRX.dmaChannel, false); +#else + LDMA_StopTransfer(obj->serial.dmaOptionsRX.dmaChannel); +#endif break; case DMA_USAGE_TEMPORARY_ALLOCATED: /* stop DMA transfer and release channel */ +#ifndef LDMA_PRESENT DMA_ChannelEnable(obj->serial.dmaOptionsRX.dmaChannel, false); +#else + LDMA_StopTransfer(obj->serial.dmaOptionsRX.dmaChannel); +#endif dma_channel_free(obj->serial.dmaOptionsRX.dmaChannel); obj->serial.dmaOptionsRX.dmaChannel = -1; obj->serial.dmaOptionsRX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC; @@ -1814,15 +2214,8 @@ } /* Say that we can stop using this emode */ -#ifdef LEUART_USING_LFXO - if(LEUART_REF_VALID(obj->serial.periph.leuart) && (LEUART_BaudrateGet(obj->serial.periph.leuart) <= (LEUART_LF_REF_FREQ/2))){ - unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE_LEUART); - }else{ - unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); - } -#else - unblockSleepMode(SERIAL_LEAST_ACTIVE_SLEEPMODE); -#endif + if( unblock_sleep ) + serial_unblock_sleep(obj); } #endif //DEVICE_SERIAL_ASYNCH