struct UART {
LPC_USART_T *hw;
unsigned char inst;
unsigned char open;
/* Rx FIFO */
unsigned char *rx_buff;
size_t rx_size;
volatile size_t rx_in; /* Changed in interrupt */
size_t rx_out;
/* Tx FIFO */
unsigned char *tx_buff;
size_t tx_size;
size_t tx_in;
volatile size_t tx_out; /* Changed in interrupt */
/* Tx callbacks */
void (*rx_callback)(UART *uart, unsigned char c);
void (*txstart_callback)(UART *uart);
void (*txend_callback)(UART *uart);
int (*tx_callback)(UART *uart);
/* Tx state-machine for dummy bytes */
volatile enum {
TX_IDLE,
TX_DUMMY,
TX_TXING
} txstate;
volatile unsigned char tx_dummy_cnt;
volatile unsigned char tx_dummy_value;
volatile unsigned char tx_dummy_num;
volatile unsigned char error; /* Changed in interrupt */
};
#define ROLLOVER(x, max) ((x) + 1) >= (max) ? 0 : (x + 1)
static inline int
_next_byte_to_send(UART *uart)
{
int c;
if (uart->tx_callback) { /* Get next byte from callback */
c = uart->tx_callback(uart);
} else { /* Get next byte from FIFO */
if(uart->tx_in == uart->tx_out) { /* Tx FIFO is empty */
c = EOF; /* No other character to send */
} else {
c = uart->tx_buff[uart->tx_out];
uart->tx_out = ROLLOVER(uart->tx_out, uart->tx_size);
}
}
return c;
}
static inline void
UARTx_IRQ_Tx(UART *uart)
{
LPC_USART_T *hw = uart->hw;
int c = EOF;
switch (uart->txstate) {
case TX_IDLE:
break;
case TX_DUMMY:
if (uart->tx_dummy_cnt) { /* Other dummy bytes to send */
uart->tx_dummy_cnt--;
c = uart->tx_dummy_value; /* Next byte to send is a dummy byte */
} else { /* No other dummy bytes to send */
c = _next_byte_to_send(uart);
if (uart->txstart_callback)
uart->txstart_callback(uart);
uart->txstate = TX_TXING;
}
break;
case TX_TXING:
c = _next_byte_to_send(uart);
break;
}
if (c == EOF) { /* No more bytes to send */
Chip_UART_IntDisable(uart->hw, UART_IER_THREINT); /* Disable immediately DRE interrupt (the last character will be shifted out to its end) */
if (uart->txend_callback)
uart->txend_callback(uart);
uart->txstate = TX_IDLE;
} else { /* A character must be send */
Chip_UART_SendByte(hw, c);
}
}
static void
UARTx_IRQHandler(UART *uart)
{
LPC_USART_T *hw = uart->hw;
uint32_t iir = hw->IIR;
if ((iir & UART_IIR_INTID_MASK) == UART_IIR_INTID_RDA) {
UARTx_IRQ_Rx(uart);
} else if ((iir & UART_IIR_INTID_MASK) == UART_IIR_INTID_THRE) {
UARTx_IRQ_Tx(uart);
}
}
void
uart_open(UART *uart, unsigned int baudrate)
{
/* Reset FIFO buffers */
uart->rx_in = uart->rx_out = 0;
uart->tx_in = uart->tx_out = 0;
Chip_UART_Init(uart->hw);
Chip_UART_SetBaud(uart->hw, baudrate);
Chip_UART_ConfigData(uart->hw, UART_LCR_WLEN8 | UART_LCR_SBS_1BIT | UART_LCR_PARITY_DIS);
Chip_UART_SetRS485Flags(uart->hw, UART_RS485CTRL_DCTRL_EN | UART_RS485CTRL_OINV_1); /* Auto-control of direction pin */
Chip_UART_TXEnable(uart->hw); /* Enable UART Transmit */
Chip_UART_SetupFIFOS(uart->hw, (UART_FCR_FIFO_EN | UART_FCR_RX_RS |
UART_FCR_TX_RS | UART_FCR_TRG_LEV0));
Chip_UART_IntEnable(uart->hw, UART_IER_RBRINT);
uart->error = 0;
uart->txstate = TX_IDLE;
NVIC_SetPriority(IRQ_list[uart->inst], 1);
NVIC_EnableIRQ(IRQ_list[uart->inst]);
uart->open = 1;
}
void
uart_putchar(UART *uart, unsigned char c)
{
uint32_t ier = Chip_UART_GetIntsEnabled(uart->hw);
size_t i = ROLLOVER(uart->tx_in, uart->tx_size);
while(i == uart->tx_out)
;
if ((ier & UART_IER_THREINT) == 0) {
if (uart->tx_dummy_num) {
uart->tx_buff[uart->tx_in] = c;
uart->tx_in = i;
Chip_UART_SendByte(uart->hw, uart->tx_dummy_value);
uart->tx_dummy_cnt = uart->tx_dummy_num - 1;
uart->txstate = TX_DUMMY;
} else {
if (uart->txstart_callback)
uart->txstart_callback(uart);
Chip_UART_SendByte(uart->hw, c);
uart->txstate = TX_TXING;
}
Chip_UART_IntEnable(uart->hw, UART_IER_THREINT);
} else {
uart->tx_buff[uart->tx_in] = c; /* Aggiunge un byte nella FIFO */
uart->tx_in = i;
if ((Chip_UART_GetIntsEnabled(uart->hw) & UART_IER_THREINT) == 0) {
Chip_UART_SendByte(uart->hw, _next_byte_to_send(uart));
}
Chip_UART_IntEnable(uart->hw, UART_IER_THREINT);
}
}