mbed-os
Dependents: cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more
Diff: targets/TARGET_ublox/TARGET_HI2110/serial_api.c
- Revision:
- 0:b74591d5ab33
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_ublox/TARGET_HI2110/serial_api.c Mon Dec 11 17:54:04 2017 +0000 @@ -0,0 +1,819 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 u-blox + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* The serial driver connects UART HW to mbed and also associates the UART + * HW with physical pins. Any physical pin can be linked to any UART, + * however the mbed serial port initialisation API makes no mention of + * which UART HW is to be used (only the pins) and hence the driver needs + * to make some decisions for itself. + * + * There are two and a half UARTs on the chip: UART0, UART1 and a + * lower-power, receive-only UART that is clocked from 32 kHz and can + * therefore be awake while the rest of the chip is sleeping peacefully. + * This provides maximal power saving, however the LP UART can only run + * at 9600 bits/s (which is quite sufficient for all NB-IoT needs). + * + * So, if the baud rate is 9600 the driver code configures the LP UART + * for Rx and UART0 for Tx. If the baud rate is not 9600 then it configures + * UART0 for both Rx and Tx. Unless... the Tx pin is the pin UART1_TX (it + * is an mbed convention to use the Tx pin), which is p6, in which case UART1 + * is configured instead. This latter is not the normal case as this pin + * is intended to be used as a GPIO. + * + * If the baud rate is changed the driver reconfigures to match. + * + * TODO: implement asynchronous and flow control APIs. + */ + +#include "mbed_assert.h" +#include "serial_api.h" +#include "pinmap.h" + +#include "cmsis.h" + +/* ---------------------------------------------------------------- + * MACROS + * ----------------------------------------------------------------*/ + +/* Registers banks for the standard UARTs */ +#define UART0_REG (*(volatile uart_ctrl_t *) UART0_BASE) +#define UART1_REG (*(volatile uart_ctrl_t *) UART1_BASE) + +/* Masks for the UART control bits in the reset and clock enable registers */ +#define UART0_CTRL (1 << 3) +#define UART1_CTRL (1 << 4) +#define UARTLP_CTRL (1 << 6) + +/* Convert number of data bits to register values */ +#define MIN_NUM_UART_DATA_BITS 5 +#define MAX_NUM_UART_DATA_BITS 8 +#define REGISTER_DATA_BITS(x) ((x) - MIN_NUM_UART_DATA_BITS) + +/* Number of stop bits */ +#define NUM_UART_STOP_BITS_1 1 +#define NUM_UART_STOP_BITS_2 2 + +/* ---------------------------------------------------------------- + * TYPES + * ----------------------------------------------------------------*/ + +/* Enum to identify the interrupt to the UART handler */ +typedef enum { + IRQ_UART_ID_0_AND_LP, + IRQ_UART_ID_1, + NUM_IRQ_IDS +} irq_uart_id_t; + +/* ---------------------------------------------------------------- + * GLOBAL VARIABLES + * ----------------------------------------------------------------*/ + +/* The IRQ configuration variables, set up and named by mbed */ +static uint32_t serial_irq_ids[NUM_IRQ_IDS] = {0}; +static uart_irq_handler irq_handler = NULL; + +/* RTX needs these */ +int stdio_uart_inited = 0; +serial_t stdio_uart; + +/* ---------------------------------------------------------------- + * FUNCTION PROTOTYPES + * ----------------------------------------------------------------*/ + +static void init_config(serial_t *obj); +static void deinit_config(serial_t *obj); +static void set_baud(serial_t *obj, uint32_t baud_rate); +static void irq_enable(serial_t *obj); +static void irq_disable(serial_t *obj); + +/* ---------------------------------------------------------------- + * NON-API FUNCTIONS + * ----------------------------------------------------------------*/ + +/* Initialise the given serial config by setting the pin functions + * and then resetting the relevant HW */ +static void init_config(serial_t *obj) +{ + uint32_t x; + + switch (obj->config) { + case SERIAL_CONFIG_UARTLP_RX_UART0_TX: + { + pin_function(obj->rx_pin, PIN_FUNCTION_LP_UART); + pin_function(obj->tx_pin, PIN_FUNCTION_UART0_TXD); + CLKEN_REG_BITSET = UARTLP_CTRL | UART0_CTRL; + obj->reg_base = &UART0_REG; + obj->index = IRQ_UART_ID_0_AND_LP; + /* Reset the LPUART and UART0 HW */ + /* NOTE: RESET_REG_BITTOG doesn't have the desired + * effect, need to use BITSET and then BITCLR */ + RESET_REG_BITSET |= 1ul << 6; + RESET_REG_BITCLR |= 1ul << 6; + RESET_REG_BITSET |= 1ul << 3; + RESET_REG_BITCLR |= 1ul << 3; + } + break; + case SERIAL_CONFIG_UART0_RX_UART0_TX: + { + pin_function(obj->rx_pin, PIN_FUNCTION_UART0_RXD); + pin_function(obj->tx_pin, PIN_FUNCTION_UART0_TXD); + CLKEN_REG_BITSET = UART0_CTRL; + obj->reg_base = &UART0_REG; + obj->index = IRQ_UART_ID_0_AND_LP; + /* Reset the UART0 HW */ + RESET_REG_BITSET |= 1ul << 3; + RESET_REG_BITCLR |= 1ul << 3; + } + break; + case SERIAL_CONFIG_UART1_RX_UART1_TX: + { + pin_function(obj->rx_pin, PIN_FUNCTION_UART1_RXD); + pin_function(obj->tx_pin, PIN_FUNCTION_UART1_TXD); + CLKEN_REG_BITSET = UART1_CTRL; + obj->reg_base = &UART1_REG; + obj->index = IRQ_UART_ID_1; + /* Reset the UART1 HW */ + RESET_REG_BITSET |= 1ul << 4; + RESET_REG_BITCLR |= 1ul << 4; + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + + /* Tickle the UART control register to make sure it is updated */ + x = obj->reg_base->UARTLCR_H; + obj->reg_base->UARTLCR_H = x; + + /* Set the FIFO. The meaning of the three FIFO interrupt-level + * bits are as follows: + * + * 0 = 1/8 full + * 1 = 1/4 full + * 2 = 1/2 full + * 3 = 3/4 full + * 4 = 7/8 full + * + * Set up the Rx FIFO to be used fully (but we will also set + * a timeout to get immediate notice) and also the Tx FIFO + * to be fully used. */ + obj->reg_base->UARTIFLS = (obj->reg_base->UARTIFLS & ~(0x07 << 0)) | (4 << 0); + obj->reg_base->UARTIFLS = (obj->reg_base->UARTIFLS & ~(0x07 << 3)) | (4 << 3); + obj->reg_base->UARTLCR_H |= 1 << 4; + + /* Enable for Tx and Rx (TODO: add CTS when we add flow control) */ + obj->reg_base->UARTCR |= (1 << 8) | (1 << 9); + + /* Now enable it */ + obj->reg_base->UARTCR |= 1 << 0; + + obj->format_set = false; + obj->baud_rate = 0; + obj->irq_rx_setting = IRQ_NOT_SET; + obj->irq_tx_setting = IRQ_NOT_SET; +} + +/* Release a serial port */ +static void deinit_config(serial_t *obj) +{ + pin_function(obj->rx_pin, PIN_FUNCTION_UNCLAIMED); + pin_function(obj->tx_pin, PIN_FUNCTION_UNCLAIMED); + + /* Disable UART */ + obj->reg_base->UARTCR &= ~(1 << 0); + + /* Flush transmit FIFO */ + obj->reg_base->UARTLCR_H = 0; + + /* Disable everything */ + obj->reg_base->UARTCR = 0; + + switch (obj->config) { + case SERIAL_CONFIG_UARTLP_RX_UART0_TX: + { + CLKEN_REG_BITCLR = UARTLP_CTRL | UART0_CTRL; + LP_UART_CTRL &= ~(0xF << 20); /* Disable all LP interrupts */ + } + break; + case SERIAL_CONFIG_UART0_RX_UART0_TX: + { + CLKEN_REG_BITCLR = UART0_CTRL; + } + break; + case SERIAL_CONFIG_UART1_RX_UART1_TX: + { + CLKEN_REG_BITCLR = UART1_CTRL; + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + + obj->config = MAX_NUM_SERIAL_CONFIGS; + obj->reg_base = NULL; +} + +/* Set the baud rate for either of the two (non-LP) UARTS */ +static void set_baud(serial_t *obj, uint32_t baud_rate) +{ + uint32_t baud_rate_divider_i; + uint32_t baud_rate_divider_f; + uint32_t remainder; + uint32_t core_clock; + uint32_t x; + + /* Baud rate divider calculation: + * + * The integer part is: clock / (16 * baud) + * + * The fractional part is: round (decimal_part * 64), + * ...where decimal part is, for example, 0.085 + * + * decimal_part is: remainder / (16 * baud), + * ...where: remainder = core_clock % (baud * 16), + * + * So the fractional part becomes: + * round (decimal_part * 64) = round (remainder * 64 / (16 * baud)) = round (remainder * 4 / baud) + */ + + /* Get the mean clock frequency */ + core_clock = (CLK_FREQ_HIGHTARGET >> 1) + (CLK_FREQ_LOWTARGET >> 1); + /* Work out the actual clock frequency */ + core_clock = (core_clock * CLOCKS_REFERENCE_CLOCK_FREQ) / (((CLK_FREQ_NREFCLKS + 1) & 0xFFFF) * (CLK_GATE_SYS & 0xFF)); + baud_rate_divider_i = core_clock / (baud_rate << 4); + remainder = core_clock % (baud_rate << 4); + baud_rate_divider_f = ((remainder << 3) / baud_rate) >> 1; + /* Round it */ + baud_rate_divider_f += ((remainder << 3) / baud_rate) & 1; + + /* Disable UART while writing to control registers */ + obj->reg_base->UARTCR &= ~(1 << 0); + + obj->reg_base->UARTIBRD = baud_rate_divider_i; + obj->reg_base->UARTFBRD = baud_rate_divider_f; + + /* Make IBRD and FBRD update */ + x = obj->reg_base->UARTLCR_H; + obj->reg_base->UARTLCR_H = x; + + /* Now enable the UART again */ + obj->reg_base->UARTCR |= 1 << 0; +} + +/* Set the NVIC bits */ +static void irq_enable(serial_t *obj) +{ + switch (obj->config) { + case SERIAL_CONFIG_UARTLP_RX_UART0_TX: + { + NVIC_EnableIRQ(UART0_IRQn); + NVIC_EnableIRQ(LPUART_IRQn); + } + break; + case SERIAL_CONFIG_UART0_RX_UART0_TX: + { + NVIC_EnableIRQ(UART0_IRQn); + } + break; + case SERIAL_CONFIG_UART1_RX_UART1_TX: + { + NVIC_EnableIRQ(UART1_IRQn); + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } +} + +/* Unset the NVIC bits */ +static void irq_disable(serial_t *obj) +{ + switch (obj->config) { + case SERIAL_CONFIG_UARTLP_RX_UART0_TX: + { + NVIC_DisableIRQ(UART0_IRQn); + NVIC_DisableIRQ(LPUART_IRQn); + } + break; + case SERIAL_CONFIG_UART0_RX_UART0_TX: + { + NVIC_DisableIRQ(UART0_IRQn); + } + break; + case SERIAL_CONFIG_UART1_RX_UART1_TX: + { + NVIC_DisableIRQ(UART1_IRQn); + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } +} + +/* UART0 IRQ */ +void IRQ7_UART0_Handler() +{ + uint32_t id = serial_irq_ids[IRQ_UART_ID_0_AND_LP]; + + /* Check Rx and Rx Timeout bit */ + if (UART0_REG.UARTMIS & ((1 << 4) | (1 << 6))) { + if (id != 0) { + irq_handler(id, RxIrq); + /* Reading the character clears the interrupt, + * no way to protect against another arriving + * while processing one */ + } + } + /* Check Tx bit */ + if (UART0_REG.UARTMIS & (1 << 5)) { + if (id != 0) { + irq_handler(id, TxIrq); + } + /* Not sure what clears the interrupt so clear it explicitly */ + NVIC_ClearPendingIRQ(UART1_IRQn); + } +} + +/* UART1 IRQ */ +void IRQ8_UART1_Handler() +{ + uint32_t id = serial_irq_ids[IRQ_UART_ID_1]; + + /* Check Rx and Rx Timeout bit */ + if (UART1_REG.UARTMIS & ((1 << 4) | (1 << 6))) { + if (id != 0) { + irq_handler(id, RxIrq); + } + /* Reading the character clears the interrupt, + * no way to protect against another arriving + * while processing one */ + } + /* Check Tx bit */ + if (UART1_REG.UARTMIS & (1 << 5)) { + if (id != 0) { + irq_handler(id, TxIrq); + } + /* Not sure what clears the interrupt so clear it explicitly */ + NVIC_ClearPendingIRQ(UART1_IRQn); + } +} + +/* LP UART IRQ, receive only */ +void IRQ16_LPUART_Handler() +{ + uint32_t id = serial_irq_ids[IRQ_UART_ID_0_AND_LP]; + + if (id != 0) { + irq_handler(id, RxIrq); + + /* Another character might have arrived while + * we are processing the last, so + * check status bits 8 to 10 again and pend + * interrupt if there's something there */ + if (((LP_UART_STATUS >> 8) & 0x07) != 0) { + NVIC_SetPendingIRQ(LPUART_IRQn); + } else { + LP_UART_CTRL |= 1 << 27; /* Clear the interrupt */ + } + } +} + +/* ---------------------------------------------------------------- + * MBED API CALLS: SETUP FUNCTIONS + * ----------------------------------------------------------------*/ + +void serial_init(serial_t *obj, PinName tx, PinName rx) +{ + uint32_t clock = CLK_FREQ_DIG_CLKS; + + /* There are two and a half UARTs on the chip. The normal + * configuration is to use the LP_UART for Rx and UART0 for + * Tx. This gives maximal power saving in that the chip can + * wake up on receipt of data. However, this only works if the + * data rate is 9600 because that's the only data rate that + * the 32 kHz (i.e. RTC) clock driving the LP UART can support. + * + * So, if the data rate is 9600, use the LP_UART/UART0 + * combination, otherwise use UART0 for both Rx and Tx. However, + * we don't know the data rate at this point so assume LP_UART + * (as this works at the default baud rate) and we can change + * our minds later. + * + * There is another serial port, UART1, which is normally used + * by the modem processor to send out debug. We only initialise + * that here if the Tx pin is UART1_TX. */ + + /* Wait for the clock to be stable */ + while ((clock < CLK_FREQ_LOWTARGET) || (clock > CLK_FREQ_HIGHTARGET)) { + clock = CLK_FREQ_DIG_CLKS; + } + + if (tx == UART1_TX) { + /* Use UART1 for Rx and Tx */ + obj->config = SERIAL_CONFIG_UART1_RX_UART1_TX; + } else { + /* Use LP_UART for Rx, UART0 for Tx */ + obj->config = SERIAL_CONFIG_UARTLP_RX_UART0_TX; + } + + obj->rx_pin = rx; + obj->tx_pin = tx; + init_config(obj); + + /* TODO: set rx pin Pull mode ? */ + + /* set default baud rate and format */ + serial_baud(obj, 9600); + serial_format(obj, 8, ParityNone, 1); + + if (tx == UART0_TX) { + /* The UART0 pins are the stdio pins */ + stdio_uart_inited = 1; + stdio_uart = *obj; + } +} + +void serial_free(serial_t *obj) +{ + if (obj->tx_pin == UART0_TX) { + stdio_uart_inited = 0; + } + + serial_irq_ids[obj->index] = 0; + + /* Release the port HW */ + deinit_config(obj); +} + +void serial_baud(serial_t *obj, int baudrate) +{ + bool switch_port_config = false; + bool format_set = obj->format_set; + uint8_t stop_bits = obj->format.stop_bits; + uint8_t data_bits = obj->format.data_bits; + SerialParity parity = (SerialParity) obj->format.parity; + irq_setting_t irq_rx_setting = obj->irq_rx_setting; + irq_setting_t irq_tx_setting = obj->irq_tx_setting; + + if ((obj->config == SERIAL_CONFIG_UARTLP_RX_UART0_TX) && (baudrate != 9600)) { + /* If we were on LP UART but the baud rate is not 9600 then + * switch to the standard UART (as the LP UART can't go any higher + * because it's clocked from 32 kHz) */ + deinit_config(obj); + obj->config = SERIAL_CONFIG_UART0_RX_UART0_TX; + init_config(obj); + switch_port_config = true; + } else if ((obj->config == SERIAL_CONFIG_UART0_RX_UART0_TX) && (baudrate == 9600)) { + /* If we were on UART0 but the baud rate is 9600 then switch to the + * LP UART for receive */ + deinit_config(obj); + obj->config = SERIAL_CONFIG_UARTLP_RX_UART0_TX; + init_config(obj); + switch_port_config = true; + } + + /* Disable UART while writing to control registers */ + obj->reg_base->UARTCR &= ~(1 << 0); + + if (switch_port_config) { + /* If the port was switched, switch the port configuration also */ + if (format_set) { + /* This serial port has been previously set up so switch the + * settings across to this new configuration */ + serial_format(obj, data_bits, parity, stop_bits); + } + if (irq_rx_setting != IRQ_NOT_SET) { + /* Do the same for Rx interrupts, if they were set */ + serial_irq_set(obj, RxIrq, (irq_rx_setting == IRQ_ON)); + } + if (irq_tx_setting != IRQ_NOT_SET) { + /* Do the same for Tx interrupts, if they were set */ + serial_irq_set(obj, TxIrq, (irq_tx_setting == IRQ_ON)); + } + } + + switch (obj->config) { + case SERIAL_CONFIG_UARTLP_RX_UART0_TX: + { + /* Set LP UART to 9600 (numerator 75 (0x4B), denominator 256 (00 == 256)) */ + LP_UART_CTRL = (LP_UART_CTRL & ~0xFFFF) | 0x004B; + set_baud(obj, baudrate); + } + break; + case SERIAL_CONFIG_UART0_RX_UART0_TX: + case SERIAL_CONFIG_UART1_RX_UART1_TX: + { + set_baud(obj, baudrate); + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + + /* Enable the UART again */ + obj->reg_base->UARTCR |= 1 << 0; + + obj->baud_rate = baudrate; +} + +void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) +{ + bool lp_also = false; + + MBED_ASSERT(data_bits >= MIN_NUM_UART_DATA_BITS); + MBED_ASSERT(data_bits <= MAX_NUM_UART_DATA_BITS); + MBED_ASSERT(stop_bits >= NUM_UART_STOP_BITS_1); + MBED_ASSERT(stop_bits <= NUM_UART_STOP_BITS_2); + + /* The LP UART is different to UARTs 0 and 1 so deal with it + * explicitly when required */ + if (obj->config == SERIAL_CONFIG_UARTLP_RX_UART0_TX) { + lp_also = true; + } + + /* Disable UART while writing to control registers */ + obj->reg_base->UARTCR &= ~(1 << 0); + + /* Set data bits (bits 5 and 6 of the UART0/1 register, bits 18 and 19 of the LP UART register) */ + obj->reg_base->UARTLCR_H = (obj->reg_base->UARTLCR_H & ~(0x03 << 5)) | (REGISTER_DATA_BITS(data_bits) << 5); + if (lp_also) { + LP_UART_CTRL = (LP_UART_CTRL & ~(0x03 << 18)) | (REGISTER_DATA_BITS(data_bits) << 18); + } + obj->format.data_bits = (uint8_t) data_bits; + + /* Set stop bits (bit 7 of the UART0/1 register) (there is no such setting for the LP UART) */ + if (stop_bits == NUM_UART_STOP_BITS_1) { + /* Clear 2-stop-bits bit */ + obj->reg_base->UARTLCR_H &= ~(1 << 7); + } else { + /* Set 2-stop-bits bit */ + obj->reg_base->UARTLCR_H |= 1 << 7; + } + obj->format.stop_bits = (uint8_t) stop_bits; + + /* Set parity */ + switch (parity) { + case ParityNone: + { + /* Disable parity */ + obj->reg_base->UARTLCR_H &= ~0x02; + if (lp_also) + { + LP_UART_CTRL &= ~(1 << 24); + } + } + break; + case ParityOdd: + { + /* Set even bit and enable parity */ + obj->reg_base->UARTLCR_H = (obj->reg_base->UARTLCR_H | (1 << 3)) | (1 << 2); + if (lp_also) + { + LP_UART_CTRL |= (1 << 24) | (1 << 25); + } + } + break; + case ParityEven: + { + /* Clear even bit and enable parity */ + obj->reg_base->UARTLCR_H = (obj->reg_base->UARTLCR_H & ~(1 << 3)) | (1 << 2); + if (lp_also) + { + LP_UART_CTRL &= ~(1 << 25); + LP_UART_CTRL |= 1 << 24; + } + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + + /* Enable the UART again */ + obj->reg_base->UARTCR |= 1 << 0; + + obj->format.parity = (uint8_t) parity; + obj->format_set = true; +} + +/* ---------------------------------------------------------------- + * MBED API CALLS: INTERRUPT FUNCTIONS + * ----------------------------------------------------------------*/ + +void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) +{ + irq_handler = handler; + serial_irq_ids[obj->index] = id; +} + +void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) +{ + bool lp_also = false; + + if (obj->config == SERIAL_CONFIG_UARTLP_RX_UART0_TX) { + lp_also = true; + } + + /* Disable UART while writing to control registers */ + obj->reg_base->UARTCR &= ~(1 << 0); + + if (enable) { + switch (irq) { + case RxIrq: + { + /* Bit 4 for Rx and bit 6 for Rx Timeout */ + obj->reg_base->UARTIMSC |= (1 << 4) | (1 << 6); + if (lp_also) + { + /* "Word Received" IRQ */ + LP_UART_CTRL |= 1 << 23; + } + obj->irq_rx_setting = IRQ_ON; + irq_enable(obj); + } + break; + case TxIrq: + { + /* Bit 5 */ + obj->reg_base->UARTIMSC |= 1 << 5; + obj->irq_tx_setting = IRQ_ON; + irq_enable(obj); + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + } else { + switch (irq) { + case RxIrq: + { + /* Bit 4 for Rx and bit 6 for Rx Timeout */ + obj->reg_base->UARTIMSC &= ~((1 << 4) | (1 << 6)); + if (lp_also) + { + /* "Word Received" IRQ */ + LP_UART_CTRL &= ~(1 << 23); + } + obj->irq_rx_setting = IRQ_OFF; + } + break; + case TxIrq: + { + /* Bit 5 */ + obj->reg_base->UARTIMSC &= ~(1 << 5); + obj->irq_tx_setting = IRQ_OFF; + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + + if ((obj->irq_rx_setting == IRQ_OFF) && (obj->irq_tx_setting == IRQ_OFF)) { + irq_disable(obj); + } + } + + /* Enable the UART again */ + obj->reg_base->UARTCR |= 1 << 0; +} + +/* ---------------------------------------------------------------- + * MBED API CALLS: TRANSMIT AND RECEIVE FUNCTIONS + * ----------------------------------------------------------------*/ + +int serial_getc(serial_t *obj) +{ + uint8_t data = 0; + + /* Block until there is data to read */ + while (!serial_readable(obj)) {} + + /* Read the data */ + switch (obj->config) { + case SERIAL_CONFIG_UARTLP_RX_UART0_TX: + { + data = (uint8_t) LP_UART_DATA; + } + break; + case SERIAL_CONFIG_UART0_RX_UART0_TX: + case SERIAL_CONFIG_UART1_RX_UART1_TX: + { + data = (uint8_t) obj->reg_base->UARTDR; + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + + return (int) data; +} + +void serial_putc(serial_t *obj, int c) +{ + /* Block until there is room to write */ + while (!serial_writable(obj)) {} + + /* Write the data */ + obj->reg_base->UARTDR = (uint8_t) c; +} + +int serial_readable(serial_t *obj) +{ + bool readable = false; + + switch (obj->config) { + case SERIAL_CONFIG_UARTLP_RX_UART0_TX: + { + /* Check the status register, bits 8 to 10 indicate + * the number of Rx bytes (make sure it's the status + * register not the data register as a read from that + * register would clear the Rx interrupt) */ + readable = (((LP_UART_STATUS >> 8) & 0x07) != 0); + } + break; + case SERIAL_CONFIG_UART0_RX_UART0_TX: + case SERIAL_CONFIG_UART1_RX_UART1_TX: + { + /* Check the Rx FIFO Empty bit */ + readable = ((obj->reg_base->UARTFR & (1 << 4)) != (1 << 4)); + } + break; + default: + { + MBED_ASSERT(false); + } + break; + } + + return (int) readable; +} + +int serial_writable(serial_t *obj) +{ + /* Check the "UART TX FIFO full" bit: + * only if this is 0 can we transmit */ + return (obj->reg_base->UARTFR & (1 << 5)) != (1 << 5); +} + +void serial_break_set(serial_t *obj) +{ + /* Disable UART while writing to control registers */ + obj->reg_base->UARTCR &= ~(1 << 0); + + /* Set bit 1 of the line control register */ + obj->reg_base->UARTLCR_H |= 1 << 0; + + /* Enable the UART again */ + obj->reg_base->UARTCR |= 1 << 0; +} + +void serial_break_clear(serial_t *obj) +{ + /* Disable UART while writing to control registers */ + obj->reg_base->UARTCR &= ~(1 << 0); + + /* Clear bit 1 of the line control register */ + obj->reg_base->UARTLCR_H &= ~(1 << 0); + + /* Enable the UART again */ + obj->reg_base->UARTCR |= 1 << 0; +}