mirror of https://github.com/ARMmbed/mbed-os.git
1105 lines
31 KiB
C
1105 lines
31 KiB
C
/* 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);
|
|
}
|
|
|
|
const PinMap *serial_tx_pinmap()
|
|
{
|
|
return PinMap_UART_TX;
|
|
}
|
|
|
|
const PinMap *serial_rx_pinmap()
|
|
{
|
|
return PinMap_UART_RX;
|
|
}
|
|
|
|
const PinMap *serial_cts_pinmap()
|
|
{
|
|
#if !DEVICE_SERIAL_FC
|
|
static const PinMap PinMap_UART_CTS[] = {
|
|
{NC, NC, 0}
|
|
};
|
|
#endif
|
|
|
|
return PinMap_UART_CTS;
|
|
}
|
|
|
|
const PinMap *serial_rts_pinmap()
|
|
{
|
|
#if !DEVICE_SERIAL_FC
|
|
static const PinMap PinMap_UART_RTS[] = {
|
|
{NC, NC, 0}
|
|
};
|
|
#endif
|
|
|
|
return PinMap_UART_RTS;
|
|
}
|
|
|
|
#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 */
|