cc2538: uart: Make it possible to use several UARTs simultaneously

This avoids the limitation of having a single UART available at runtime, without
duplicating code.

Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
This commit is contained in:
Benoît Thébaudeau 2014-04-04 19:33:11 +02:00
parent db754a57d7
commit d93d129da6
12 changed files with 353 additions and 146 deletions

View file

@ -48,88 +48,220 @@
#include <stdint.h>
#include <string.h>
static int (* input_handler)(unsigned char c);
/*---------------------------------------------------------------------------*/
#define UART_RX_PORT_BASE GPIO_PORT_TO_BASE(UART_RX_PORT)
#define UART_RX_PIN_MASK GPIO_PIN_MASK(UART_RX_PIN)
#ifndef UART0_RX_PORT
#define UART0_RX_PORT (-1)
#endif
#ifndef UART0_RX_PIN
#define UART0_RX_PIN (-1)
#endif
#if UART0_RX_PORT >= 0 && UART0_RX_PIN < 0 || \
UART0_RX_PORT < 0 && UART0_RX_PIN >= 0
#error Both UART0_RX_PORT and UART0_RX_PIN must be valid or invalid
#endif
#define UART_TX_PORT_BASE GPIO_PORT_TO_BASE(UART_TX_PORT)
#define UART_TX_PIN_MASK GPIO_PIN_MASK(UART_TX_PIN)
#ifndef UART0_TX_PORT
#define UART0_TX_PORT (-1)
#endif
#ifndef UART0_TX_PIN
#define UART0_TX_PIN (-1)
#endif
#if UART0_TX_PORT >= 0 && UART0_TX_PIN < 0 || \
UART0_TX_PORT < 0 && UART0_TX_PIN >= 0
#error Both UART0_TX_PORT and UART0_TX_PIN must be valid or invalid
#endif
#define UART_CTS_PORT_BASE GPIO_PORT_TO_BASE(UART_CTS_PORT)
#define UART_CTS_PIN_MASK GPIO_PIN_MASK(UART_CTS_PIN)
#if UART0_RX_PORT >= 0 && UART0_TX_PORT < 0 || \
UART0_RX_PORT < 0 && UART0_TX_PORT >= 0
#error Both UART0_RX and UART0_TX pads must be valid or invalid
#endif
#define UART_RTS_PORT_BASE GPIO_PORT_TO_BASE(UART_RTS_PORT)
#define UART_RTS_PIN_MASK GPIO_PIN_MASK(UART_RTS_PIN)
/*---------------------------------------------------------------------------*/
/*
* Once we know what UART we're on, configure correct values to be written to
* the correct registers
*/
#if UART_BASE==UART_1_BASE
/* Running, in sleep, in deep sleep, enable the clock for the correct UART */
#define SYS_CTRL_RCGCUART_UART SYS_CTRL_RCGCUART_UART1
#define SYS_CTRL_SCGCUART_UART SYS_CTRL_SCGCUART_UART1
#define SYS_CTRL_DCGCUART_UART SYS_CTRL_DCGCUART_UART1
#if UART_IN_USE(0) && UART0_RX_PORT < 0
#error Contiki is configured to use UART0, but its pads are not valid
#endif
#define NVIC_INT_UART NVIC_INT_UART1
#define IOC_PXX_SEL_UART_TXD IOC_PXX_SEL_UART1_TXD
#define IOC_UARTRXD_UART IOC_UARTRXD_UART1
#else /* Defaults for UART0 */
#define SYS_CTRL_RCGCUART_UART SYS_CTRL_RCGCUART_UART0
#define SYS_CTRL_SCGCUART_UART SYS_CTRL_SCGCUART_UART0
#define SYS_CTRL_DCGCUART_UART SYS_CTRL_DCGCUART_UART0
#ifndef UART1_RX_PORT
#define UART1_RX_PORT (-1)
#endif
#ifndef UART1_RX_PIN
#define UART1_RX_PIN (-1)
#endif
#if UART1_RX_PORT >= 0 && UART1_RX_PIN < 0 || \
UART1_RX_PORT < 0 && UART1_RX_PIN >= 0
#error Both UART1_RX_PORT and UART1_RX_PIN must be valid or invalid
#endif
#define NVIC_INT_UART NVIC_INT_UART0
#ifndef UART1_TX_PORT
#define UART1_TX_PORT (-1)
#endif
#ifndef UART1_TX_PIN
#define UART1_TX_PIN (-1)
#endif
#if UART1_TX_PORT >= 0 && UART1_TX_PIN < 0 || \
UART1_TX_PORT < 0 && UART1_TX_PIN >= 0
#error Both UART1_TX_PORT and UART1_TX_PIN must be valid or invalid
#endif
#define IOC_PXX_SEL_UART_TXD IOC_PXX_SEL_UART0_TXD
#define IOC_UARTRXD_UART IOC_UARTRXD_UART0
#if UART1_RX_PORT >= 0 && UART1_TX_PORT < 0 || \
UART1_RX_PORT < 0 && UART1_TX_PORT >= 0
#error Both UART1_RX and UART1_TX pads must be valid or invalid
#endif
#if UART_IN_USE(1) && UART1_RX_PORT < 0
#error Contiki is configured to use UART1, but its pads are not valid
#endif
#ifndef UART1_CTS_PORT
#define UART1_CTS_PORT (-1)
#endif
#ifndef UART1_CTS_PIN
#define UART1_CTS_PIN (-1)
#endif
#if UART1_CTS_PORT >= 0 && UART1_CTS_PIN < 0 || \
UART1_CTS_PORT < 0 && UART1_CTS_PIN >= 0
#error Both UART1_CTS_PORT and UART1_CTS_PIN must be valid or invalid
#endif
#ifndef UART1_RTS_PORT
#define UART1_RTS_PORT (-1)
#endif
#ifndef UART1_RTS_PIN
#define UART1_RTS_PIN (-1)
#endif
#if UART1_RTS_PORT >= 0 && UART1_RTS_PIN < 0 || \
UART1_RTS_PORT < 0 && UART1_RTS_PIN >= 0
#error Both UART1_RTS_PORT and UART1_RTS_PIN must be valid or invalid
#endif
/*---------------------------------------------------------------------------*/
/*
* Baud rate defines used in uart_init() to set the values of UART_IBRD and
* UART_FBRD in order to achieve the configured baud rates.
*/
#define UART_CLOCK_RATE 16000000 /* 16 MHz */
#define UART_CTL_HSE_VALUE 0
#define UART_CTL_VALUE (UART_CTL_RXE | UART_CTL_TXE | (UART_CTL_HSE_VALUE << 5))
/* DIV_ROUND() divides integers while avoiding a rounding error: */
#define DIV_ROUND(num, denom) (((num) + (denom) / 2) / (denom))
#define BAUD2BRD(baud) DIV_ROUND(UART_CLOCK_RATE << (UART_CTL_HSE_VALUE + 2), (baud))
#define BAUD2IBRD(baud) (BAUD2BRD(baud) >> 6)
#define BAUD2FBRD(baud) (BAUD2BRD(baud) & 0x3f)
/*---------------------------------------------------------------------------*/
typedef struct {
int8_t port;
int8_t pin;
} uart_pad_t;
typedef struct {
uint32_t sys_ctrl_rcgcuart_uart;
uint32_t sys_ctrl_scgcuart_uart;
uint32_t sys_ctrl_dcgcuart_uart;
uint32_t base;
uint32_t ioc_uartrxd_uart;
uint32_t ioc_pxx_sel_uart_txd;
uint32_t ibrd;
uint32_t fbrd;
uart_pad_t rx;
uart_pad_t tx;
uart_pad_t cts;
uart_pad_t rts;
uint8_t nvic_int;
} uart_regs_t;
/*---------------------------------------------------------------------------*/
static const uart_regs_t uart_regs[UART_INSTANCE_COUNT] = {
{
.sys_ctrl_rcgcuart_uart = SYS_CTRL_RCGCUART_UART0,
.sys_ctrl_scgcuart_uart = SYS_CTRL_SCGCUART_UART0,
.sys_ctrl_dcgcuart_uart = SYS_CTRL_DCGCUART_UART0,
.base = UART_0_BASE,
.ioc_uartrxd_uart = IOC_UARTRXD_UART0,
.ioc_pxx_sel_uart_txd = IOC_PXX_SEL_UART0_TXD,
.ibrd = BAUD2IBRD(UART0_CONF_BAUD_RATE),
.fbrd = BAUD2FBRD(UART0_CONF_BAUD_RATE),
.rx = {UART0_RX_PORT, UART0_RX_PIN},
.tx = {UART0_TX_PORT, UART0_TX_PIN},
.cts = {-1, -1},
.rts = {-1, -1},
.nvic_int = NVIC_INT_UART0
}, {
.sys_ctrl_rcgcuart_uart = SYS_CTRL_RCGCUART_UART1,
.sys_ctrl_scgcuart_uart = SYS_CTRL_SCGCUART_UART1,
.sys_ctrl_dcgcuart_uart = SYS_CTRL_DCGCUART_UART1,
.base = UART_1_BASE,
.ioc_uartrxd_uart = IOC_UARTRXD_UART1,
.ioc_pxx_sel_uart_txd = IOC_PXX_SEL_UART1_TXD,
.ibrd = BAUD2IBRD(UART1_CONF_BAUD_RATE),
.fbrd = BAUD2FBRD(UART1_CONF_BAUD_RATE),
.rx = {UART1_RX_PORT, UART1_RX_PIN},
.tx = {UART1_TX_PORT, UART1_TX_PIN},
.cts = {UART1_CTS_PORT, UART1_CTS_PIN},
.rts = {UART1_RTS_PORT, UART1_RTS_PIN},
.nvic_int = NVIC_INT_UART1
}
};
static int (* input_handler[UART_INSTANCE_COUNT])(unsigned char c);
/*---------------------------------------------------------------------------*/
static void
reset(void)
reset(uint32_t uart_base)
{
uint32_t lchr;
/* Make sure the UART is disabled before trying to configure it */
REG(UART_BASE | UART_CTL) = UART_CTL_VALUE;
REG(uart_base | UART_CTL) = UART_CTL_VALUE;
/* Clear error status */
REG(UART_BASE | UART_ECR) = 0xFF;
REG(uart_base | UART_ECR) = 0xFF;
/* Store LCHR configuration */
lchr = REG(UART_BASE | UART_LCRH);
lchr = REG(uart_base | UART_LCRH);
/* Flush FIFOs by clearing LCHR.FEN */
REG(UART_BASE | UART_LCRH) = 0;
REG(uart_base | UART_LCRH) = 0;
/* Restore LCHR configuration */
REG(UART_BASE | UART_LCRH) = lchr;
REG(uart_base | UART_LCRH) = lchr;
/* UART Enable */
REG(UART_BASE | UART_CTL) |= UART_CTL_UARTEN;
REG(uart_base | UART_CTL) |= UART_CTL_UARTEN;
}
/*---------------------------------------------------------------------------*/
static bool
permit_pm1(void)
{
/* Note: UART_FR.TXFE reads 0 if the UART clock is gated. */
return (REG(SYS_CTRL_RCGCUART) & SYS_CTRL_RCGCUART_UART) == 0 ||
(REG(UART_BASE | UART_FR) & UART_FR_TXFE) != 0;
const uart_regs_t *regs;
for(regs = &uart_regs[0]; regs < &uart_regs[UART_INSTANCE_COUNT]; regs++) {
/* Note: UART_FR.TXFE reads 0 if the UART clock is gated. */
if((REG(SYS_CTRL_RCGCUART) & regs->sys_ctrl_rcgcuart_uart) != 0 &&
(REG(regs->base | UART_FR) & UART_FR_TXFE) == 0) {
return false;
}
}
return true;
}
/*---------------------------------------------------------------------------*/
void
uart_init(void)
uart_init(uint8_t uart)
{
const uart_regs_t *regs;
if(uart >= UART_INSTANCE_COUNT) {
return;
}
regs = &uart_regs[uart];
if(regs->rx.port < 0 || regs->tx.port < 0) {
return;
}
lpm_register_peripheral(permit_pm1);
/* Enable clock for the UART while Running, in Sleep and Deep Sleep */
REG(SYS_CTRL_RCGCUART) |= SYS_CTRL_RCGCUART_UART;
REG(SYS_CTRL_SCGCUART) |= SYS_CTRL_SCGCUART_UART;
REG(SYS_CTRL_DCGCUART) |= SYS_CTRL_DCGCUART_UART;
REG(SYS_CTRL_RCGCUART) |= regs->sys_ctrl_rcgcuart_uart;
REG(SYS_CTRL_SCGCUART) |= regs->sys_ctrl_scgcuart_uart;
REG(SYS_CTRL_DCGCUART) |= regs->sys_ctrl_dcgcuart_uart;
/* Run on SYS_DIV */
REG(UART_BASE | UART_CC) = 0;
REG(regs->base | UART_CC) = 0;
/*
* Select the UARTx RX pin by writing to the IOC_UARTRXD_UARTn register
@ -139,91 +271,116 @@ uart_init(void)
*
* (port << 3) + pin
*/
REG(IOC_UARTRXD_UART) = (UART_RX_PORT << 3) + UART_RX_PIN;
REG(regs->ioc_uartrxd_uart) = (regs->rx.port << 3) + regs->rx.pin;
/*
* Pad Control for the TX pin:
* - Set function to UART0 TX
* - Set function to UARTn TX
* - Output Enable
*/
ioc_set_sel(UART_TX_PORT, UART_TX_PIN, IOC_PXX_SEL_UART_TXD);
ioc_set_over(UART_TX_PORT, UART_TX_PIN, IOC_OVERRIDE_OE);
ioc_set_sel(regs->tx.port, regs->tx.pin, regs->ioc_pxx_sel_uart_txd);
ioc_set_over(regs->tx.port, regs->tx.pin, IOC_OVERRIDE_OE);
/* Set RX and TX pins to peripheral mode */
GPIO_PERIPHERAL_CONTROL(UART_TX_PORT_BASE, UART_TX_PIN_MASK);
GPIO_PERIPHERAL_CONTROL(UART_RX_PORT_BASE, UART_RX_PIN_MASK);
GPIO_PERIPHERAL_CONTROL(GPIO_PORT_TO_BASE(regs->tx.port),
GPIO_PIN_MASK(regs->tx.pin));
GPIO_PERIPHERAL_CONTROL(GPIO_PORT_TO_BASE(regs->rx.port),
GPIO_PIN_MASK(regs->rx.pin));
if(regs->cts.port >= 0 || regs->rts.port >= 0) {
/* TODO Hardware flow control */
}
/*
* UART Interrupt Masks:
* Acknowledge RX and RX Timeout
* Acknowledge Framing, Overrun and Break Errors
*/
REG(UART_BASE | UART_IM) = UART_IM_RXIM | UART_IM_RTIM;
REG(UART_BASE | UART_IM) |= UART_IM_OEIM | UART_IM_BEIM | UART_IM_FEIM;
REG(regs->base | UART_IM) = UART_IM_RXIM | UART_IM_RTIM;
REG(regs->base | UART_IM) |= UART_IM_OEIM | UART_IM_BEIM | UART_IM_FEIM;
REG(UART_BASE | UART_IFLS) =
REG(regs->base | UART_IFLS) =
UART_IFLS_RXIFLSEL_1_8 | UART_IFLS_TXIFLSEL_1_2;
/* Make sure the UART is disabled before trying to configure it */
REG(UART_BASE | UART_CTL) = UART_CTL_VALUE;
REG(regs->base | UART_CTL) = UART_CTL_VALUE;
/* Baud Rate Generation */
uart_set_baudrate(UART_CONF_BAUD_RATE);
REG(regs->base | UART_IBRD) = regs->ibrd;
REG(regs->base | UART_FBRD) = regs->fbrd;
/* UART Control: 8N1 with FIFOs */
REG(UART_BASE | UART_LCRH) = UART_LCRH_WLEN_8 | UART_LCRH_FEN;
REG(regs->base | UART_LCRH) = UART_LCRH_WLEN_8 | UART_LCRH_FEN;
/* UART Enable */
REG(UART_BASE | UART_CTL) |= UART_CTL_UARTEN;
REG(regs->base | UART_CTL) |= UART_CTL_UARTEN;
/* Enable UART0 Interrupts */
nvic_interrupt_enable(NVIC_INT_UART);
nvic_interrupt_enable(regs->nvic_int);
}
/*---------------------------------------------------------------------------*/
void
uart_set_input(int (* input)(unsigned char c))
uart_set_input(uint8_t uart, int (* input)(unsigned char c))
{
input_handler = input;
}
/*---------------------------------------------------------------------------*/
void
uart_write_byte(uint8_t b)
{
/* Block if the TX FIFO is full */
while(REG(UART_BASE | UART_FR) & UART_FR_TXFF);
if(uart >= UART_INSTANCE_COUNT) {
return;
}
REG(UART_BASE | UART_DR) = b;
input_handler[uart] = input;
}
/*---------------------------------------------------------------------------*/
void
uart_isr(void)
uart_write_byte(uint8_t uart, uint8_t b)
{
uint32_t uart_base;
if(uart >= UART_INSTANCE_COUNT) {
return;
}
uart_base = uart_regs[uart].base;
/* Block if the TX FIFO is full */
while(REG(uart_base | UART_FR) & UART_FR_TXFF);
REG(uart_base | UART_DR) = b;
}
/*---------------------------------------------------------------------------*/
void
uart_isr(uint8_t uart)
{
uint32_t uart_base;
uint16_t mis;
ENERGEST_ON(ENERGEST_TYPE_IRQ);
uart_base = uart_regs[uart].base;
/* Store the current MIS and clear all flags early, except the RTM flag.
* This will clear itself when we read out the entire FIFO contents */
mis = REG(UART_BASE | UART_MIS) & 0x0000FFFF;
mis = REG(uart_base | UART_MIS) & 0x0000FFFF;
REG(UART_BASE | UART_ICR) = 0x0000FFBF;
REG(uart_base | UART_ICR) = 0x0000FFBF;
if(mis & (UART_MIS_RXMIS | UART_MIS_RTMIS)) {
while(!(REG(UART_BASE | UART_FR) & UART_FR_RXFE)) {
if(input_handler != NULL) {
input_handler((unsigned char)(REG(UART_BASE | UART_DR) & 0xFF));
while(!(REG(uart_base | UART_FR) & UART_FR_RXFE)) {
if(input_handler[uart] != NULL) {
input_handler[uart]((unsigned char)(REG(uart_base | UART_DR) & 0xFF));
} else {
/* To prevent an Overrun Error, we need to flush the FIFO even if we
* don't have an input_handler. Use mis as a data trash can */
mis = REG(UART_BASE | UART_DR);
mis = REG(uart_base | UART_DR);
}
}
} else if(mis & (UART_MIS_OEMIS | UART_MIS_BEMIS | UART_MIS_FEMIS)) {
/* ISR triggered due to some error condition */
reset();
reset(uart_base);
}
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
}
/*---------------------------------------------------------------------------*/
#define UART_ISR(u) void uart##u##_isr(void) { uart_isr(u); }
UART_ISR(0)
UART_ISR(1)
/** @} */