mirror of https://github.com/ARMmbed/mbed-os.git
414 lines
15 KiB
C
414 lines
15 KiB
C
/*
|
|
* Copyright (c) 2020 SparkFun Electronics
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#if DEVICE_SERIAL
|
|
|
|
#include "serial_api.h"
|
|
|
|
#include "mbed_assert.h"
|
|
#include "PeripheralPins.h"
|
|
|
|
// globals
|
|
int stdio_uart_inited = 0;
|
|
serial_t stdio_uart;
|
|
bool value = false;
|
|
|
|
// interrupt variables
|
|
static uart_irq_handler irq_handler;
|
|
static ap3_uart_control_t ap3_uart_control[AM_REG_UART_NUM_MODULES];
|
|
|
|
// forward declarations
|
|
extern void am_uart_isr(void);
|
|
extern void am_uart1_isr(void);
|
|
void uart_configure_pin_function(PinName pin, UARTName uart, const PinMap *map);
|
|
|
|
/**
|
|
* \defgroup hal_GeneralSerial Serial Configuration Functions
|
|
*
|
|
* # Defined behavior
|
|
* * ::serial_init initializes the ::serial_t
|
|
* * ::serial_init sets the default parameters for serial peripheral (9600 bps, 8N1 format)
|
|
* * ::serial_init configures the specified pins
|
|
* * ::serial_free releases the serial peripheral
|
|
* * ::serial_baud configures the baud rate
|
|
* * at least 9600 bps the baud rate must be supported
|
|
* * ::serial_format configures the transmission format (number of bits, parity and the number of stop bits)
|
|
* * at least 8N1 format must be supported
|
|
* * ::serial_irq_handler registers the interrupt handler which will be invoked when the interrupt fires.
|
|
* * ::serial_irq_set enables or disables the serial RX or TX IRQ.
|
|
* * If `RxIrq` is enabled by ::serial_irq_set, ::serial_irq_handler will be invoked whenever
|
|
* Receive Data Register Full IRQ is generated.
|
|
* * If `TxIrq` is enabled by ::serial_irq_set, ::serial_irq_handler will be invoked whenever
|
|
* Transmit Data Register Empty IRQ is generated.
|
|
* * If the interrupt condition holds true, when the interrupt is enabled with ::serial_irq_set,
|
|
* the ::serial_irq_handler is called instantly.
|
|
* * ::serial_getc returns the character from serial buffer.
|
|
* * ::serial_getc is a blocking call (waits for the character).
|
|
* * ::serial_putc sends a character.
|
|
* * ::serial_putc is a blocking call (waits for a peripheral to be available).
|
|
* * ::serial_readable returns non-zero value if a character can be read, 0 otherwise.
|
|
* * ::serial_writable returns non-zero value if a character can be written, 0 otherwise.
|
|
* * ::serial_clear clears the ::serial_t RX/TX buffers
|
|
* * ::serial_break_set sets the break signal.
|
|
* * ::serial_break_clear clears the break signal.
|
|
* * ::serial_pinout_tx configures the TX pin as an output (to be used in half-duplex mode).
|
|
* * ::serial_set_flow_control configures serial flow control.
|
|
* * ::serial_set_flow_control sets flow control in the hardware if a serial peripheral supports it,
|
|
* otherwise software emulation is used.
|
|
* * ::serial_tx_asynch starts the serial asynchronous transfer.
|
|
* * ::serial_tx_asynch writes `tx_length` bytes from the `tx` to the bus.
|
|
* * ::serial_tx_asynch must support 8 data bits
|
|
* * The callback given to ::serial_tx_asynch is invoked when the transfer completes.
|
|
* * ::serial_tx_asynch specifies the logical OR of events to be registered.
|
|
* * The ::serial_tx_asynch function may use the `DMAUsage` hint to select the appropriate async algorithm.
|
|
* * ::serial_rx_asynch starts the serial asynchronous transfer.
|
|
* * ::serial_rx_asynch reads `rx_length` bytes to the `rx` buffer.
|
|
* * ::serial_rx_asynch must support 8 data bits
|
|
* * The callback given to ::serial_rx_asynch is invoked when the transfer completes.
|
|
* * ::serial_rx_asynch specifies the logical OR of events to be registered.
|
|
* * The ::serial_rx_asynch function may use the `DMAUsage` hint to select the appropriate async algorithm.
|
|
* * ::serial_rx_asynch specifies a character in range 0-254 to be matched, 255 is a reserved value.
|
|
* * If SERIAL_EVENT_RX_CHARACTER_MATCH event is not registered, the `char_match` is ignored.
|
|
* * The SERIAL_EVENT_RX_CHARACTER_MATCH event is set in the callback when SERIAL_EVENT_RX_CHARACTER_MATCH event is
|
|
* registered AND `char_match` is present in the received data.
|
|
* * ::serial_tx_active returns non-zero if the TX transaction is ongoing, 0 otherwise.
|
|
* * ::serial_rx_active returns non-zero if the RX transaction is ongoing, 0 otherwise.
|
|
* * ::serial_irq_handler_asynch returns event flags if a transfer termination condition was met, otherwise returns 0.
|
|
* * ::serial_irq_handler_asynch takes no longer than one packet transfer time (packet_bits / baudrate) to execute.
|
|
* * ::serial_tx_abort_asynch aborts the ongoing TX transaction.
|
|
* * ::serial_tx_abort_asynch disables the enabled interupt for TX.
|
|
* * ::serial_tx_abort_asynch flushes the TX hardware buffer if TX FIFO is used.
|
|
* * ::serial_rx_abort_asynch aborts the ongoing RX transaction.
|
|
* * ::serial_rx_abort_asynch disables the enabled interupt for RX.
|
|
* * ::serial_rx_abort_asynch flushes the TX hardware buffer if RX FIFO is used.
|
|
* * Correct operation guaranteed when interrupt latency is shorter than one packet transfer time (packet_bits / baudrate)
|
|
* if the flow control is not used.
|
|
* * Correct operation guaranteed regardless of interrupt latency if the flow control is used.
|
|
*
|
|
* # Undefined behavior
|
|
* * Calling ::serial_init multiple times on the same `serial_t` without ::serial_free.
|
|
* * Passing invalid pin to ::serial_init, ::serial_pinout_tx.
|
|
* * Calling any function other than ::serial_init on am uninitialized or freed `serial_t`.
|
|
* * Passing an invalid pointer as `obj` to any function.
|
|
* * Passing an invalid pointer as `handler` to ::serial_irq_handler, ::serial_tx_asynch, ::serial_rx_asynch.
|
|
* * Calling ::serial_tx_abort while no async TX transfer is being processed.
|
|
* * Calling ::serial_rx_abort while no async RX transfer is being processed.
|
|
* * Devices behavior is undefined when the interrupt latency is longer than one packet transfer time
|
|
* (packet_bits / baudrate) if the flow control is not used.
|
|
* @{
|
|
*/
|
|
|
|
void serial_init(serial_t *obj, PinName tx, PinName rx)
|
|
{
|
|
// determine the UART to use
|
|
UARTName uart_tx = (UARTName)pinmap_peripheral(tx, serial_tx_pinmap());
|
|
UARTName uart_rx = (UARTName)pinmap_peripheral(rx, serial_rx_pinmap());
|
|
UARTName uart = (UARTName)pinmap_merge(uart_tx, uart_rx);
|
|
MBED_ASSERT((int)uart != NC);
|
|
obj->serial.uart_control = &ap3_uart_control[uart];
|
|
obj->serial.uart_control->inst = uart;
|
|
|
|
// config uart pins
|
|
pinmap_config(tx, serial_tx_pinmap());
|
|
pinmap_config(rx, serial_rx_pinmap());
|
|
|
|
if (!obj->serial.uart_control->handle) {
|
|
// if handle uninitialized this is first time set up
|
|
// ensure that HAL queueing is disabled (we want to use the FIFOs directly)
|
|
obj->serial.uart_control->cfg.pui8RxBuffer = NULL;
|
|
obj->serial.uart_control->cfg.pui8TxBuffer = NULL;
|
|
obj->serial.uart_control->cfg.ui32RxBufferSize = 0;
|
|
obj->serial.uart_control->cfg.ui32TxBufferSize = 0;
|
|
|
|
obj->serial.uart_control->cfg.ui32FifoLevels = AM_HAL_UART_RX_FIFO_7_8;
|
|
|
|
// start UART instance
|
|
MBED_ASSERT(am_hal_uart_initialize(uart, &(obj->serial.uart_control->handle)) == AM_HAL_STATUS_SUCCESS);
|
|
MBED_ASSERT(am_hal_uart_power_control(obj->serial.uart_control->handle, AM_HAL_SYSCTRL_WAKE, false) == AM_HAL_STATUS_SUCCESS);
|
|
MBED_ASSERT(am_hal_uart_configure_fifo(obj->serial.uart_control->handle, &(obj->serial.uart_control->cfg), false) == AM_HAL_STATUS_SUCCESS);
|
|
|
|
// set default format
|
|
serial_format(obj, 8, ParityNone, 1);
|
|
}
|
|
}
|
|
|
|
void serial_free(serial_t *obj)
|
|
{
|
|
// nothing to do unless resources are allocated for members of the serial_s serial member of obj
|
|
// assuming mbed handles obj and its members
|
|
}
|
|
|
|
void serial_baud(serial_t *obj, int baudrate)
|
|
{
|
|
obj->serial.uart_control->cfg.ui32BaudRate = (uint32_t)baudrate;
|
|
MBED_ASSERT(am_hal_uart_configure_fifo(obj->serial.uart_control->handle, &(obj->serial.uart_control->cfg), false) == AM_HAL_STATUS_SUCCESS);
|
|
}
|
|
|
|
void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits)
|
|
{
|
|
uint32_t am_hal_data_bits = 0;
|
|
switch (data_bits) {
|
|
case 5:
|
|
am_hal_data_bits = AM_HAL_UART_DATA_BITS_5;
|
|
break;
|
|
case 6:
|
|
am_hal_data_bits = AM_HAL_UART_DATA_BITS_6;
|
|
break;
|
|
case 7:
|
|
am_hal_data_bits = AM_HAL_UART_DATA_BITS_7;
|
|
break;
|
|
case 8:
|
|
am_hal_data_bits = AM_HAL_UART_DATA_BITS_8;
|
|
break;
|
|
default:
|
|
MBED_ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
uint32_t am_hal_parity = AM_HAL_UART_PARITY_NONE;
|
|
switch (parity) {
|
|
case ParityNone:
|
|
am_hal_parity = AM_HAL_UART_PARITY_NONE;
|
|
break;
|
|
case ParityOdd:
|
|
am_hal_parity = AM_HAL_UART_PARITY_ODD;
|
|
break;
|
|
case ParityEven:
|
|
am_hal_parity = AM_HAL_UART_PARITY_EVEN;
|
|
break;
|
|
default: // fall-through intentional after default
|
|
case ParityForced1:
|
|
case ParityForced0:
|
|
MBED_ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
uint32_t am_hal_stop_bits = 0;
|
|
switch (stop_bits) {
|
|
case 1:
|
|
am_hal_stop_bits = AM_HAL_UART_ONE_STOP_BIT;
|
|
break;
|
|
case 2:
|
|
am_hal_stop_bits = AM_HAL_UART_TWO_STOP_BITS;
|
|
break;
|
|
default:
|
|
MBED_ASSERT(0);
|
|
}
|
|
|
|
obj->serial.uart_control->cfg.ui32DataBits = (uint32_t)am_hal_data_bits;
|
|
obj->serial.uart_control->cfg.ui32Parity = (uint32_t)am_hal_parity;
|
|
obj->serial.uart_control->cfg.ui32StopBits = (uint32_t)am_hal_stop_bits;
|
|
MBED_ASSERT(am_hal_uart_configure_fifo(obj->serial.uart_control->handle, &(obj->serial.uart_control->cfg), false) == AM_HAL_STATUS_SUCCESS);
|
|
}
|
|
|
|
void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id)
|
|
{
|
|
irq_handler = handler;
|
|
obj->serial.uart_control->serial_irq_id = id;
|
|
}
|
|
|
|
void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable)
|
|
{
|
|
MBED_ASSERT(obj->serial.uart_control->handle != NULL);
|
|
if (enable) {
|
|
switch (irq) {
|
|
case RxIrq:
|
|
MBED_ASSERT(am_hal_uart_interrupt_enable(obj->serial.uart_control->handle, AM_HAL_UART_INT_RX) == AM_HAL_STATUS_SUCCESS);
|
|
break;
|
|
case TxIrq:
|
|
MBED_ASSERT(am_hal_uart_interrupt_enable(obj->serial.uart_control->handle, AM_HAL_UART_INT_TXCMP) == AM_HAL_STATUS_SUCCESS);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// NVIC_SetVector(uart_irqs[obj->serial.index], vector);
|
|
NVIC_EnableIRQ((IRQn_Type)(UART0_IRQn + obj->serial.uart_control->inst));
|
|
} else { // disable
|
|
switch (irq) {
|
|
case RxIrq:
|
|
MBED_ASSERT(am_hal_uart_interrupt_disable(obj->serial.uart_control->handle, AM_HAL_UART_INT_RX) == AM_HAL_STATUS_SUCCESS);
|
|
break;
|
|
case TxIrq:
|
|
MBED_ASSERT(am_hal_uart_interrupt_disable(obj->serial.uart_control->handle, AM_HAL_UART_INT_TXCMP) == AM_HAL_STATUS_SUCCESS);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int serial_getc(serial_t *obj)
|
|
{
|
|
MBED_ASSERT(obj->serial.uart_control != NULL);
|
|
|
|
uint8_t rx_c = 0x00;
|
|
volatile uint32_t bytes_read = 0x00;
|
|
am_hal_uart_transfer_t am_hal_uart_xfer_read_single = {
|
|
.ui32Direction = AM_HAL_UART_READ,
|
|
.pui8Data = (uint8_t *) &rx_c,
|
|
.ui32NumBytes = 1,
|
|
.ui32TimeoutMs = 0,
|
|
.pui32BytesTransferred = (uint32_t *) &bytes_read,
|
|
};
|
|
|
|
do {
|
|
am_hal_uart_transfer(obj->serial.uart_control->handle, &am_hal_uart_xfer_read_single);
|
|
} while (bytes_read == 0);
|
|
|
|
return (int)rx_c;
|
|
}
|
|
|
|
void serial_putc(serial_t *obj, int c)
|
|
{
|
|
MBED_ASSERT(obj->serial.uart_control != NULL);
|
|
|
|
volatile uint32_t bytes_sent = 0;
|
|
am_hal_uart_transfer_t am_hal_uart_xfer_write_single = {
|
|
.ui32Direction = AM_HAL_UART_WRITE,
|
|
.pui8Data = (uint8_t *)(&c),
|
|
.ui32NumBytes = 1,
|
|
.ui32TimeoutMs = 0,
|
|
.pui32BytesTransferred = (uint32_t *) &bytes_sent,
|
|
};
|
|
|
|
do {
|
|
am_hal_uart_transfer(obj->serial.uart_control->handle, &am_hal_uart_xfer_write_single);
|
|
} while (bytes_sent == 0);
|
|
}
|
|
|
|
int serial_readable(serial_t *obj)
|
|
{
|
|
MBED_ASSERT(obj->serial.uart_control != NULL);
|
|
return !(UARTn(obj->serial.uart_control->inst)->FR_b.RXFE);
|
|
}
|
|
|
|
int serial_writable(serial_t *obj)
|
|
{
|
|
MBED_ASSERT(obj->serial.uart_control != NULL);
|
|
return !(UARTn(obj->serial.uart_control->inst)->FR_b.TXFF);
|
|
}
|
|
|
|
void serial_clear(serial_t *obj)
|
|
{
|
|
// todo:
|
|
MBED_ASSERT(0);
|
|
}
|
|
|
|
void serial_break_set(serial_t *obj)
|
|
{
|
|
MBED_ASSERT(obj->serial.uart_control != NULL);
|
|
UARTn(obj->serial.uart_control->inst)->LCRH |= UART0_LCRH_BRK_Msk;
|
|
}
|
|
|
|
void serial_break_clear(serial_t *obj)
|
|
{
|
|
MBED_ASSERT(obj->serial.uart_control != NULL);
|
|
UARTn(obj->serial.uart_control->inst)->LCRH &= ~UART0_LCRH_BRK_Msk;
|
|
}
|
|
|
|
void serial_pinout_tx(PinName tx)
|
|
{
|
|
// todo: vestigial?
|
|
MBED_ASSERT(0);
|
|
}
|
|
|
|
#if DEVICE_SERIAL_FC
|
|
|
|
void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow)
|
|
{
|
|
// todo:
|
|
MBED_ASSERT(0);
|
|
}
|
|
|
|
void serial_set_flow_control_direct(serial_t *obj, FlowControl type, const serial_fc_pinmap_t *pinmap)
|
|
{
|
|
// todo:
|
|
MBED_ASSERT(0);
|
|
}
|
|
#endif
|
|
|
|
const PinMap *serial_tx_pinmap(void)
|
|
{
|
|
return PinMap_UART_TX;
|
|
}
|
|
|
|
const PinMap *serial_rx_pinmap(void)
|
|
{
|
|
return PinMap_UART_RX;
|
|
}
|
|
|
|
#if DEVICE_SERIAL_FC
|
|
|
|
const PinMap *serial_cts_pinmap(void)
|
|
{
|
|
return PinMap_UART_CTS;
|
|
}
|
|
|
|
const PinMap *serial_rts_pinmap(void)
|
|
{
|
|
return PinMap_UART_RTS;
|
|
}
|
|
#endif
|
|
|
|
static inline void uart_irq(uint32_t instance)
|
|
{
|
|
void *handle = ap3_uart_control[instance].handle;
|
|
MBED_ASSERT(handle != NULL);
|
|
|
|
// check flags
|
|
uint32_t status = 0x00;
|
|
MBED_ASSERT(am_hal_uart_interrupt_status_get(handle, &status, true) == AM_HAL_STATUS_SUCCESS);
|
|
MBED_ASSERT(am_hal_uart_interrupt_clear(handle, status) == AM_HAL_STATUS_SUCCESS);
|
|
|
|
if (ap3_uart_control[instance].serial_irq_id != 0) {
|
|
if (status & AM_HAL_UART_INT_TXCMP) { // for transmit complete
|
|
if (irq_handler) {
|
|
irq_handler(ap3_uart_control[instance].serial_irq_id, TxIrq);
|
|
}
|
|
}
|
|
if (status & AM_HAL_UART_INT_RX) { // for receive complete
|
|
if (irq_handler) {
|
|
irq_handler(ap3_uart_control[instance].serial_irq_id, RxIrq);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void am_uart_isr(void)
|
|
{
|
|
uart_irq(UART_0);
|
|
}
|
|
|
|
extern void am_uart1_isr(void)
|
|
{
|
|
uart_irq(UART_1);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/** @}*/
|