mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
Diff: targets/TARGET_GigaDevice/TARGET_GD32E10X/serial_api.c
- Revision:
- 189:f392fc9709a3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_GigaDevice/TARGET_GD32E10X/serial_api.c Wed Feb 20 22:31:08 2019 +0000 @@ -0,0 +1,1072 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 GigaDevice Semiconductor Inc. + * + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ +#include "mbed_assert.h" +#include "serial_api.h" + +#if DEVICE_SERIAL + +#include "cmsis.h" +#include "pinmap.h" +#include "mbed_error.h" +#include <string.h> +#include "PeripheralPins.h" + +#define USART_NUM (5) + +static uint32_t serial_irq_ids[USART_NUM] = {0}; +static rcu_periph_enum usart_clk[USART_NUM] = {RCU_USART0, RCU_USART1, RCU_USART2, RCU_UART3, RCU_UART4}; +static IRQn_Type usart_irq_n[USART_NUM] = {USART0_IRQn, USART1_IRQn, USART2_IRQn, UART3_IRQn, UART4_IRQn}; + +static uart_irq_handler irq_handler; + +int stdio_uart_inited = 0; +serial_t stdio_uart; + +#if DEVICE_SERIAL_ASYNCH +#define GET_SERIAL_S(obj) (&((obj)->serial)) +#else +#define GET_SERIAL_S(obj) (obj) +#endif /* DEVICE_SERIAL_ASYNCH */ + +/** Initialize the USART peripheral. + * + * @param obj_s The serial object + */ +static void usart_init(struct serial_s *obj_s) +{ + if (obj_s->index >= USART_NUM) { + return; + } + + /* USART configuration */ + usart_deinit(obj_s->uart); + usart_word_length_set(obj_s->uart, obj_s->databits); + usart_baudrate_set(obj_s->uart, obj_s->baudrate); + usart_stop_bit_set(obj_s->uart, obj_s->stopbits); + usart_parity_config(obj_s->uart, obj_s->parity); +#if DEVICE_SERIAL_FC + if (obj_s->hw_flow_ctl == USART_HWCONTROL_NONE) { + usart_hardware_flow_cts_config(obj_s->uart, USART_CTS_DISABLE); + usart_hardware_flow_rts_config(obj_s->uart, USART_RTS_DISABLE); + } else if (obj_s->hw_flow_ctl == USART_HWCONTROL_RTS) { + usart_hardware_flow_cts_config(obj_s->uart, USART_CTS_DISABLE); + usart_hardware_flow_rts_config(obj_s->uart, USART_RTS_ENABLE); + } else if (obj_s->hw_flow_ctl == USART_HWCONTROL_CTS) { + usart_hardware_flow_cts_config(obj_s->uart, USART_CTS_ENABLE); + usart_hardware_flow_rts_config(obj_s->uart, USART_RTS_DISABLE); + } else if (obj_s->hw_flow_ctl == USART_HWCONTROL_RTS_CTS) { + usart_hardware_flow_cts_config(obj_s->uart, USART_CTS_ENABLE); + usart_hardware_flow_rts_config(obj_s->uart, USART_RTS_ENABLE); + } +#endif /* DEVICE_SERIAL_FC */ + usart_receive_config(obj_s->uart, USART_RECEIVE_ENABLE); + usart_transmit_config(obj_s->uart, USART_TRANSMIT_ENABLE); + usart_enable(obj_s->uart); +} + +/** Initialize the serial peripheral. It sets the default parameters for serial + * peripheral, and configures its specifieds pins. + * + * @param obj The serial object + * @param tx The TX pin name + * @param rx The RX pin name + */ +void serial_init(serial_t *obj, PinName tx, PinName rx) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + UARTName uart_tx = (UARTName)pinmap_peripheral(tx, PinMap_UART_TX); + UARTName uart_rx = (UARTName)pinmap_peripheral(rx, PinMap_UART_RX); + + p_obj->uart = (UARTName)pinmap_merge(uart_tx, uart_rx); + MBED_ASSERT(p_obj->uart != (UARTName)NC); + + /* enable UART peripheral clock */ + if (p_obj->uart == UART_0) { + p_obj->index = 0; + rcu_periph_clock_enable(usart_clk[p_obj->index]); + } else if (p_obj->uart == UART_1) { + p_obj->index = 1; + rcu_periph_clock_enable(usart_clk[p_obj->index]); + } else if (p_obj->uart == UART_2) { + p_obj->index = 2; + rcu_periph_clock_enable(usart_clk[p_obj->index]); + } else if (p_obj->uart == UART_3) { + p_obj->index = 3; + rcu_periph_clock_enable(usart_clk[p_obj->index]); + } else if (p_obj->uart == UART_4) { + p_obj->index = 4; + rcu_periph_clock_enable(usart_clk[p_obj->index]); + } + + /* configurte the pins */ + pinmap_pinout(tx, PinMap_UART_TX); + pinmap_pinout(rx, PinMap_UART_RX); + + /* default UART parameters */ + p_obj->baudrate = 9600U; + p_obj->databits = USART_WL_8BIT; + p_obj->stopbits = USART_STB_1BIT; + p_obj->parity = USART_PM_NONE; + +#if DEVICE_SERIAL_FC + p_obj->hw_flow_ctl = USART_HWCONTROL_NONE; +#endif /* DEVICE_SERIAL_FC */ + + p_obj->pin_tx = tx; + p_obj->pin_rx = rx; + + p_obj->tx_state = OP_STATE_BUSY; + p_obj->rx_state = OP_STATE_BUSY; + + usart_init(p_obj); + + p_obj->tx_state = OP_STATE_READY; + p_obj->rx_state = OP_STATE_READY; + + if (p_obj->uart == STDIO_UART) { + stdio_uart_inited = 1; + memcpy(&stdio_uart, obj, sizeof(serial_t)); + } +} + +/** Release the serial peripheral, not currently invoked. It requires further + * resource management. + * + * @param obj The serial object + */ +void serial_free(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + rcu_periph_enum rcu_periph = usart_clk[p_obj->index]; + + /* reset USART and disable clock */ + usart_deinit(p_obj->uart); + rcu_periph_clock_disable(rcu_periph); + + serial_irq_ids[p_obj->index] = 0; + + /* reset the GPIO state */ + pin_function(p_obj->pin_tx, MODE_IN_FLOATING); + pin_function(p_obj->pin_rx, MODE_IN_FLOATING); +} + +/** Configure the baud rate + * + * @param obj The serial object + * @param baudrate The baud rate to be configured + */ +void serial_baud(serial_t *obj, int baudrate) +{ + uint16_t uen_flag = 0U; + struct serial_s *p_obj = GET_SERIAL_S(obj); + + /* store the UEN flag */ + uen_flag = USART_CTL0(p_obj->uart) & USART_CTL0_UEN; + + /* disable the USART first */ + usart_disable(p_obj->uart); + + usart_baudrate_set(p_obj->uart, baudrate); + + p_obj->baudrate = baudrate; + + /* restore the UEN flag */ + if (RESET != uen_flag) { + usart_enable(p_obj->uart); + } +} + +/** Configure the format. Set the number of bits, parity and the number of stop bits + * + * @param obj The serial object + * @param data_bits The number of data bits + * @param parity The parity + * @param stop_bits The number of stop bits + */ +void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) +{ + uint16_t uen_flag = 0U; + struct serial_s *p_obj = GET_SERIAL_S(obj); + + /* store the UEN flag */ + uen_flag = USART_CTL0(p_obj->uart) & USART_CTL0_UEN; + + /* disable the UART clock first */ + usart_disable(p_obj->uart); + + /* configurate the UART parity */ + switch (parity) { + case ParityOdd: + p_obj->parity = USART_PM_ODD; + usart_parity_config(p_obj->uart, USART_PM_ODD); + break; + + case ParityEven: + p_obj->parity = USART_PM_EVEN; + usart_parity_config(p_obj->uart, USART_PM_EVEN); + break; + + case ParityForced0: + case ParityForced1: + default: + p_obj->parity = USART_PM_NONE; + usart_parity_config(p_obj->uart, USART_PM_NONE); + break; + } + + if (p_obj->parity == USART_PM_NONE) { + if (data_bits == 9) { + usart_word_length_set(p_obj->uart, USART_WL_9BIT); + } else if (data_bits == 8) { + usart_word_length_set(p_obj->uart, USART_WL_8BIT); + } else if (data_bits == 7) { + return; + } + } else { + if (data_bits == 9) { + return; + } else if (data_bits == 8) { + usart_word_length_set(p_obj->uart, USART_WL_9BIT); + } else if (data_bits == 7) { + usart_word_length_set(p_obj->uart, USART_WL_8BIT); + } + } + + if (stop_bits == 2) { + usart_stop_bit_set(p_obj->uart, USART_STB_2BIT); + } else { + usart_stop_bit_set(p_obj->uart, USART_STB_1BIT); + } + + /* restore the UEN flag */ + if (RESET != uen_flag) { + usart_enable(p_obj->uart); + } +} + +/** The serial interrupt handler registration + * + * @param obj The serial object + * @param handler The interrupt handler which will be invoked when the interrupt fires + * @param id The SerialBase object + */ +void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + irq_handler = handler; + serial_irq_ids[p_obj->index] = id; +} + +/** This function handles USART interrupt handler + * + * @param usart_index The index of UART + * @param usart_periph The UART peripheral + */ +static void usart_irq(int usart_index, uint32_t usart_periph) +{ + if (serial_irq_ids[usart_index] != 0) { + if (usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_TC) != RESET) { + usart_interrupt_flag_clear(usart_periph, USART_INT_FLAG_TC); + irq_handler(serial_irq_ids[usart_index], TxIrq); + } + + if (usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_RBNE) != RESET) { + usart_interrupt_flag_clear(usart_periph, USART_INT_FLAG_RBNE); + irq_handler(serial_irq_ids[usart_index], RxIrq); + } + + if (usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_ERR_ORERR) != RESET) { + /* clear ORERR error flag by reading USART DATA register */ + USART_DATA(usart_periph); + } + + if (usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_ERR_NERR) != RESET) { + /* clear NERR error flag by reading USART DATA register */ + USART_DATA(usart_periph); + } + + if (usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_ERR_FERR) != RESET) { + /* clear FERR error flag by reading USART DATA register */ + USART_DATA(usart_periph); + } + + if (usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_PERR) != RESET) { + /* clear PERR error flag by reading USART DATA register */ + USART_DATA(usart_periph); + } + } +} + +/** This function handles USART0 interrupt handler + * + */ +static void usart0_irq(void) +{ + usart_irq(0, USART0); +} + +/** This function handles USART1 interrupt handler + * + */ +static void usart1_irq(void) +{ + usart_irq(1, USART1); +} + +/** This function handles USART2 interrupt handler + * + */ +static void usart2_irq(void) +{ + usart_irq(2, USART2); +} + +/** This function handles USART3 interrupt handler + * + */ +static void uart3_irq(void) +{ + usart_irq(3, UART3); +} + +/** This function handles USART4 interrupt handler + * + */ +static void uart4_irq(void) +{ + usart_irq(4, UART4); +} + +/** Configure serial interrupt. This function is used for word-approach + * + * @param obj The serial object + * @param irq The serial IRQ type (RX or TX) + * @param enable Set to non-zero to enable events, or zero to disable them + */ +void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + IRQn_Type irq_n = (IRQn_Type)0; + uint32_t vector = 0; + + if (p_obj->uart == USART0) { + irq_n = USART0_IRQn; + vector = (uint32_t)&usart0_irq; + } else if (p_obj->uart == USART1) { + irq_n = USART1_IRQn; + vector = (uint32_t)&usart1_irq; + } else if (p_obj->uart == USART2) { + irq_n = USART2_IRQn; + vector = (uint32_t)&usart2_irq; + } else if (p_obj->uart == UART3) { + irq_n = UART3_IRQn; + vector = (uint32_t)&uart3_irq; + } else if (p_obj->uart == UART4) { + irq_n = UART4_IRQn; + vector = (uint32_t)&uart4_irq; + } + + if (enable) { + if (irq == RxIrq) { + /* Rx IRQ */ + usart_interrupt_enable(p_obj->uart, USART_INT_RBNE); + } else { + /* Tx IRQ */ + usart_interrupt_enable(p_obj->uart, USART_INT_TBE); + } + + NVIC_SetVector(irq_n, vector); + NVIC_EnableIRQ(irq_n); + } else { + if (irq == RxIrq) { + /* Rx IRQ */ + usart_interrupt_disable(p_obj->uart, USART_INT_RBNE); + } else { + /* Tx IRQ */ + usart_interrupt_disable(p_obj->uart, USART_INT_TBE); + } + } +} + +/** Get character. This is a blocking call, waiting for a character + * + * @param obj The serial object + */ +int serial_getc(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + while (!serial_readable(obj)); + return (int)(usart_data_receive(p_obj->uart) & BITS(0, 7 + (p_obj->databits >> 12))); +} + +/** Send a character. This is a blocking call, waiting for a peripheral to be available + * for writing + * + * @param obj The serial object + * @param c The character to be sent + */ +void serial_putc(serial_t *obj, int c) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + while (!serial_writable(obj)); + usart_data_transmit(p_obj->uart, (int)((c) & BITS(0, 7 + (p_obj->databits >> 12)))); +} + +/** Check if the serial peripheral is readable + * + * @param obj The serial object + * @return Non-zero value if a character can be read, 0 if nothing to read + */ +int serial_readable(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + return (usart_flag_get(p_obj->uart, USART_FLAG_RBNE) != RESET) ? 1 : 0; +} + +/** Check if the serial peripheral is writable + * + * @param obj The serial object + * @return Non-zero value if a character can be written, 0 otherwise. + */ +int serial_writable(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + return (usart_flag_get(p_obj->uart, USART_FLAG_TBE) != RESET) ? 1 : 0; +} + +/** Clear the serial peripheral + * + * @param obj The serial object + */ +void serial_clear(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + p_obj->tx_count = 0U; + p_obj->rx_count = 0U; +} + +/** Set the break + * + * @param obj The serial object + */ +void serial_break_set(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + usart_send_break(p_obj->uart); +} + +/** Clear the break + * + * @param obj The serial object + */ +void serial_break_clear(serial_t *obj) +{ + /* do nothing */ +} + +/** Configure the TX pin for UART function. + * + * @param tx The pin name used for TX + */ +void serial_pinout_tx(PinName tx) +{ + pinmap_pinout(tx, PinMap_UART_TX); +} + +#if DEVICE_SERIAL_ASYNCH +/** + * Enable the serial events + * + * @param obj The serial object + * @param event The events to be configured + */ +static void serial_event_enable(serial_t *obj, int event) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + p_obj->events |= event; + +} + +/** + * Disable the serial events + * + * @param obj The serial object + * @param event The events to be configured + */ +static void serial_event_disable(serial_t *obj, int event) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + p_obj->events &= ~event; +} + +/** + * Preprocess the USART tx interrupt + * + * @param obj_s The serial object + * @param pData Pointer to tx buffer + * @param Size Size of tx buffer + * @return Returns the status + */ +static gd_status_enum usart_tx_interrupt_preprocess(struct serial_s *obj_s, uint8_t *pData, uint16_t Size) +{ + if (obj_s->tx_state == OP_STATE_READY) { + if ((pData == NULL) || (Size == 0U)) { + return GD_ERROR; + } + + obj_s->tx_buffer_ptr = pData; + obj_s->tx_count = Size; + obj_s->error_code = USART_ERROR_CODE_NONE; + obj_s->tx_state = OP_STATE_BUSY_TX; + + usart_interrupt_enable(obj_s->uart, USART_INT_TBE); + + return GD_OK; + } else { + return GD_BUSY; + } +} + +/** + * Preprocess the USART rx interrupt + * + * @param obj_s The serial object + * @param pData Pointer to rx buffer + * @param Size Size of rx buffer + * @return Returns the status + */ +static gd_status_enum usart_rx_interrupt_preprocess(struct serial_s *obj_s, uint8_t *pData, uint16_t Size) +{ + if (obj_s->rx_state == OP_STATE_READY) { + if ((pData == NULL) || (Size == 0U)) { + return GD_ERROR; + } + + obj_s->rx_buffer_ptr = pData; + obj_s->rx_size = Size; + obj_s->rx_count = Size; + obj_s->error_code = USART_ERROR_CODE_NONE; + obj_s->rx_state = OP_STATE_BUSY_RX; + + usart_interrupt_enable(obj_s->uart, USART_INT_PERR); + usart_interrupt_enable(obj_s->uart, USART_INT_ERR); + usart_interrupt_enable(obj_s->uart, USART_INT_RBNE); + + return GD_OK; + } else { + return GD_BUSY; + } +} + +/** Begin asynchronous TX transfer. The used buffer is specified in the serial object, + * tx_buff + * + * @param obj The serial object + * @param tx The transmit buffer + * @param tx_length The number of bytes to transmit + * @param tx_width Deprecated argument + * @param handler The serial handler + * @param event The logical OR of events to be registered + * @param hint A suggestion for how to use DMA with this transfer + * @return Returns number of data transfered, otherwise returns 0 + */ +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) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + IRQn_Type irq = usart_irq_n[p_obj->index]; + + if ((tx_length == 0) || (tx_width != 8)) { + return 0; + } + + if (serial_tx_active(obj)) { + /* some transmit is in progress */ + return 0; + } + + obj->tx_buff.buffer = (void *)tx; + obj->tx_buff.length = tx_length; + obj->tx_buff.pos = 0; + + /* disable all events first */ + serial_event_disable(obj, SERIAL_EVENT_TX_ALL); + /* enable the specific event */ + serial_event_enable(obj, event); + + /* enable interrupt */ + /* clear pending IRQ */ + NVIC_ClearPendingIRQ(irq); + /* disable the IRQ first */ + NVIC_DisableIRQ(irq); + /* set the priority and vector */ + NVIC_SetPriority(irq, 1); + NVIC_SetVector(irq, (uint32_t)handler); + /* enable IRQ */ + NVIC_EnableIRQ(irq); + + if (usart_tx_interrupt_preprocess(p_obj, (uint8_t *)tx, tx_length) != GD_OK) { + return 0; + } + + return tx_length; +} + +/** Begin asynchronous RX transfer (enable interrupt for data collecting) + * The used buffer is specified in the serial object - rx_buff + * + * @param obj The serial object + * @param rx The receive buffer + * @param rx_length The number of bytes to receive + * @param rx_width Deprecated argument + * @param handler The serial handler + * @param event The logical OR of events to be registered + * @param handler The serial handler + * @param char_match A character in range 0-254 to be matched + * @param hint A suggestion for how to use DMA with this transfer + */ +void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t event, uint8_t char_match, DMAUsage hint) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + IRQn_Type irq = usart_irq_n[p_obj->index]; + + if ((rx_length == 0) || (rx_width != 8)) { + return; + } + + /* disable all events first */ + serial_event_disable(obj, SERIAL_EVENT_RX_ALL); + /* enable the specific event */ + serial_event_enable(obj, event); + + obj->char_match = char_match; + + if (serial_rx_active(obj)) { + /* some reception is in progress */ + return; + } + + obj->rx_buff.buffer = rx; + obj->rx_buff.length = rx_length; + obj->rx_buff.pos = 0; + + /* enable interrupt */ + /* clear pending IRQ */ + NVIC_ClearPendingIRQ(irq); + /* disable the IRQ first */ + NVIC_DisableIRQ(irq); + /* set the priority(higher than Tx) and vector */ + NVIC_SetPriority(irq, 0); + NVIC_SetVector(irq, (uint32_t)handler); + /* enable IRQ */ + NVIC_EnableIRQ(irq); + + usart_rx_interrupt_preprocess(p_obj, (uint8_t *)rx, rx_length); +} + +/** Attempts to determine if the serial peripheral is already in use for TX + * + * @param obj The serial object + * @return Non-zero if the RX transaction is ongoing, 0 otherwise + */ +uint8_t serial_tx_active(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + return ((p_obj->tx_state == OP_STATE_BUSY_TX) ? 1 : 0); +} + +/** Attempts to determine if the serial peripheral is already in use for RX + * + * @param obj The serial object + * @return Non-zero if the RX transaction is ongoing, 0 otherwise + */ +uint8_t serial_rx_active(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + return ((p_obj->rx_state == OP_STATE_BUSY_RX) ? 1 : 0); +} + +/** Handle the serial rx interrupt + * + * @param obj_s The serial object + * @return Returns the status + */ +static gd_status_enum usart_rx_interrupt(struct serial_s *obj_s) +{ + uint16_t *temp; + + if (obj_s->rx_state == OP_STATE_BUSY_RX) { + if (obj_s->databits == USART_WL_9BIT) { + temp = (uint16_t *) obj_s->rx_buffer_ptr; + if (obj_s->parity == USART_PM_NONE) { + /* 9-bit data, none parity bit */ + *temp = (uint16_t)(USART_DATA(obj_s->uart) & (uint16_t)0x01FF); + obj_s->rx_buffer_ptr += 2U; + } else { + /* 9-bit data, with parity bit */ + *temp = (uint16_t)(USART_DATA(obj_s->uart) & (uint16_t)0x00FF); + obj_s->rx_buffer_ptr += 1U; + } + } else { + if (obj_s->parity == USART_PM_NONE) { + /* 8-bit data, none parity bit */ + *obj_s->rx_buffer_ptr++ = (uint8_t)(USART_DATA(obj_s->uart) & (uint8_t)0x00FF); + } else { + /* 8-bit data, with parity bit */ + *obj_s->rx_buffer_ptr++ = (uint8_t)(USART_DATA(obj_s->uart) & (uint8_t)0x007F); + } + } + + if (--obj_s->rx_count == 0U) { + usart_interrupt_disable(obj_s->uart, USART_INT_RBNE); + usart_interrupt_disable(obj_s->uart, USART_INT_PERR); + usart_interrupt_disable(obj_s->uart, USART_INT_ERR); + + obj_s->rx_state = OP_STATE_READY; + } + + return GD_OK; + } else { + return GD_BUSY; + } +} + +/** Handle the serial tx interrupt + * + * @param obj_s The serial object + * @return Returns the status + */ +static gd_status_enum usart_tx_interrupt(struct serial_s *obj_s) +{ + uint16_t *temp; + + if (obj_s->tx_state == OP_STATE_BUSY_TX) { + if (obj_s->databits == USART_WL_9BIT) { + temp = (uint16_t *) obj_s->tx_buffer_ptr; + USART_DATA(obj_s->uart) = (uint16_t)(*temp & (uint16_t)0x01FF); + if (obj_s->parity == USART_PM_NONE) { + obj_s->tx_buffer_ptr += 2U; + } else { + obj_s->tx_buffer_ptr += 1U; + } + } else { + USART_DATA(obj_s->uart) = (uint8_t)(*obj_s->tx_buffer_ptr++ & (uint8_t)0x00FF); + } + + if (--obj_s->tx_count == 0U) { + /* disable USART_INT_TBE interrupt */ + usart_interrupt_disable(obj_s->uart, USART_INT_TBE); + + /* enable USART_INT_TC interrupt */ + usart_interrupt_enable(obj_s->uart, USART_INT_TC); + } + + return GD_OK; + } else { + return GD_BUSY; + } +} + +/** Handle the serial tx complete interrupt + * + * @param obj_s The serial object + */ +static void usart_tx_complete_interrupt(struct serial_s *obj_s) +{ + usart_interrupt_disable(obj_s->uart, USART_INT_TC); + + obj_s->tx_state = OP_STATE_READY; +} + +/** Handle all the serial interrupt request + * + * @param obj_s The serial object + */ +static void usart_irq_handler(struct serial_s *obj_s) +{ + uint32_t err_flags = 0U; + + /* no error occurs */ + err_flags = (USART_STAT0(obj_s->uart) & (uint32_t)(USART_FLAG_PERR | USART_FLAG_FERR | USART_FLAG_ORERR | USART_FLAG_NERR)); + if (err_flags == RESET) { + /* check whether USART is in receiver mode or not */ + if (usart_interrupt_flag_get(obj_s->uart, USART_INT_FLAG_RBNE) != RESET) { + usart_rx_interrupt(obj_s); + + return; + } + } + + if (usart_interrupt_flag_get(obj_s->uart, USART_INT_FLAG_TBE) != RESET) { + usart_tx_interrupt(obj_s); + return; + } + + if (usart_interrupt_flag_get(obj_s->uart, USART_INT_FLAG_TC) != RESET) { + usart_tx_complete_interrupt(obj_s); + return; + } +} + +/** The asynchronous TX and RX handler. + * + * @param obj The serial object + * @return Returns event flags if an RX transfer termination condition was met; otherwise returns 0 + */ +int serial_irq_handler_asynch(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + volatile uint8_t i = 0; + volatile int return_val = 0; + uint8_t *p_buf = (uint8_t *)(obj->rx_buff.buffer); + + if (usart_interrupt_flag_get(p_obj->uart, USART_INT_FLAG_PERR) != RESET) { + /* clear PERR error flag by reading USART DATA register */ + USART_DATA(p_obj->uart); + + return_val |= (SERIAL_EVENT_RX_PARITY_ERROR & p_obj->events); + p_obj->error_code |= USART_ERROR_CODE_PERR; + } + + if (usart_interrupt_flag_get(p_obj->uart, USART_INT_FLAG_ERR_FERR) != RESET) { + /* clear FERR error flag by reading USART DATA register */ + USART_DATA(p_obj->uart); + + return_val |= (SERIAL_EVENT_RX_FRAMING_ERROR & p_obj->events); + p_obj->error_code |= USART_ERROR_CODE_FERR; + } + + if (usart_interrupt_flag_get(p_obj->uart, USART_INT_FLAG_ERR_ORERR) != RESET) { + /* clear ORERR error flag by reading USART DATA register */ + USART_DATA(p_obj->uart); + + return_val |= (SERIAL_EVENT_RX_OVERRUN_ERROR & p_obj->events); + p_obj->error_code |= USART_ERROR_CODE_ORERR; + } + + if (return_val & (SERIAL_EVENT_RX_PARITY_ERROR | SERIAL_EVENT_RX_FRAMING_ERROR | + SERIAL_EVENT_RX_OVERRUN_ERROR)) { + return return_val; + } + + if (usart_interrupt_flag_get(p_obj->uart, USART_INT_FLAG_TC) != RESET) { + if ((p_obj->events & SERIAL_EVENT_TX_COMPLETE) != 0) { + return_val |= (SERIAL_EVENT_TX_COMPLETE & p_obj->events); + } + } + + usart_irq_handler(p_obj); + + if (p_obj->rx_size != 0) { + obj->rx_buff.pos = p_obj->rx_size - p_obj->rx_count; + } + + if ((p_obj->rx_count == 0) && (obj->rx_buff.pos >= (obj->rx_buff.length - 1))) { + return_val |= (SERIAL_EVENT_RX_COMPLETE & p_obj->events); + } + + if (p_obj->events & SERIAL_EVENT_RX_CHARACTER_MATCH) { + if (p_buf != NULL) { + for (i = 0; i < obj->rx_buff.pos; i++) { + if (p_buf[i] == obj->char_match) { + obj->rx_buff.pos = i; + return_val |= (SERIAL_EVENT_RX_CHARACTER_MATCH & p_obj->events); + serial_rx_abort_asynch(obj); + break; + } + } + } + } + + return return_val; +} + +/** Abort the ongoing TX transaction. It disables the enabled interupt for TX and + * flushes the TX hardware buffer if TX FIFO is used + * + * @param obj The serial object + */ +void serial_tx_abort_asynch(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + usart_interrupt_disable(p_obj->uart, USART_INT_TC); + usart_interrupt_disable(p_obj->uart, USART_INT_TBE); + + usart_flag_clear(p_obj->uart, USART_FLAG_TC); + + p_obj->tx_count = 0; + p_obj->tx_state = OP_STATE_READY; +} + +/** Abort the ongoing RX transaction. It disables the enabled interrupt for RX and + * flushes the RX hardware buffer if RX FIFO is used + * + * @param obj The serial object + */ +void serial_rx_abort_asynch(serial_t *obj) +{ + struct serial_s *p_obj = GET_SERIAL_S(obj); + + /* disable interrupts */ + usart_interrupt_disable(p_obj->uart, USART_INT_RBNE); + usart_interrupt_disable(p_obj->uart, USART_INT_PERR); + usart_interrupt_disable(p_obj->uart, USART_INT_ERR); + + /* clear USART_FLAG_RBNE flag */ + usart_flag_clear(p_obj->uart, USART_FLAG_RBNE); + + /* clear errors flag by reading USART STATx register and then USART DATA register */ + usart_flag_get(p_obj->uart, USART_FLAG_PERR); + usart_flag_get(p_obj->uart, USART_FLAG_FERR); + usart_flag_get(p_obj->uart, USART_FLAG_ORERR); + USART_DATA(p_obj->uart); + + /* reset rx transfer count */ + p_obj->rx_count = 0; + + /* reset rx state */ + p_obj->rx_state = OP_STATE_READY; +} + +#endif /* DEVICE_SERIAL_ASYNCH */ + +#if DEVICE_SERIAL_FC +/** Configure the serial for the flow control. It sets flow control in the hardware + * if a serial peripheral supports it, otherwise software emulation is used. + * + * @param obj The serial object + * @param type The type of the flow control. Look at the available FlowControl types. + * @param rxflow The TX pin name + * @param txflow The RX pin name + */ +void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow) +{ + uint16_t uen_flag = 0U; + struct serial_s *p_obj = GET_SERIAL_S(obj); + + /* store the UEN flag */ + uen_flag = USART_CTL0(p_obj->uart) & USART_CTL0_UEN; + + UARTName uart_rts = (UARTName)pinmap_peripheral(rxflow, PinMap_UART_RTS); + UARTName uart_cts = (UARTName)pinmap_peripheral(txflow, PinMap_UART_CTS); + + p_obj->uart = (UARTName)pinmap_merge(uart_cts, uart_rts); + MBED_ASSERT(p_obj->uart != (UARTName)NC); + + /* disable USART to modify CTS/RTS configuration */ + usart_disable(p_obj->uart); + + if (type == FlowControlNone) { + p_obj->hw_flow_ctl = USART_HWCONTROL_NONE; + usart_hardware_flow_cts_config(p_obj->uart, USART_CTS_DISABLE); + usart_hardware_flow_rts_config(p_obj->uart, USART_RTS_DISABLE); + } + + if (type == FlowControlRTS) { + MBED_ASSERT(uart_rts != (UARTName)NC); + p_obj->hw_flow_ctl = USART_HWCONTROL_RTS; + p_obj->pin_rts = rxflow; + pinmap_pinout(rxflow, PinMap_UART_RTS); + usart_hardware_flow_cts_config(p_obj->uart, USART_CTS_DISABLE); + usart_hardware_flow_rts_config(p_obj->uart, USART_RTS_ENABLE); + } + + if (type == FlowControlCTS) { + MBED_ASSERT(uart_cts != (UARTName)NC); + p_obj->hw_flow_ctl = USART_HWCONTROL_CTS; + p_obj->pin_cts = txflow; + pinmap_pinout(txflow, PinMap_UART_CTS); + usart_hardware_flow_rts_config(p_obj->uart, USART_RTS_DISABLE); + usart_hardware_flow_cts_config(p_obj->uart, USART_CTS_ENABLE); + } + + if (type == FlowControlRTSCTS) { + MBED_ASSERT(uart_rts != (UARTName)NC); + MBED_ASSERT(uart_cts != (UARTName)NC); + p_obj->hw_flow_ctl = USART_HWCONTROL_RTS_CTS; + p_obj->pin_rts = rxflow; + p_obj->pin_cts = txflow; + pinmap_pinout(txflow, PinMap_UART_CTS); + pinmap_pinout(rxflow, PinMap_UART_RTS); + usart_hardware_flow_cts_config(p_obj->uart, USART_CTS_ENABLE); + usart_hardware_flow_rts_config(p_obj->uart, USART_RTS_ENABLE); + } + + /* restore the UEN flag */ + if (RESET != uen_flag) { + usart_enable(p_obj->uart); + } +} + +#endif /* DEVICE_SERIAL_FC */ + +#if DEVICE_SLEEP +/** Check whether the serial is in busy state + * + * @return 0: all the serial is free to use, 1: some serial is in busy in transfer + */ +int serial_busy_state_check(void) +{ +#if defined(USART0) + if ((USART_CTL0(USART0) & USART_CTL0_UEN) && !(USART_STAT0(USART0) & USART_STAT0_TC)) { + return 1; + } +#endif + +#if defined(USART1) + if ((USART_CTL0(USART1) & USART_CTL0_UEN) && !(USART_STAT0(USART1) & USART_STAT0_TC)) { + return 1; + } +#endif + +#if defined(USART2) + if ((USART_CTL0(USART2) & USART_CTL0_UEN) && !(USART_STAT0(USART2) & USART_STAT0_TC)) { + return 1; + } +#endif + +#if defined(UART3) + if ((USART_CTL0(UART3) & USART_CTL0_UEN) && !(USART_STAT0(UART3) & USART_STAT0_TC)) { + return 1; + } +#endif + +#if defined(UART4) + if ((USART_CTL0(UART4) & USART_CTL0_UEN) && !(USART_STAT0(UART4) & USART_STAT0_TC)) { + return 1; + } +#endif + + /* no serial is in busy state */ + return 0; +} +#endif /* DEVICE_SLEEP */ + +#endif /* DEVICE_SERIAL */