From: arighi Date: Tue, 27 Apr 2010 13:16:25 +0000 (+0000) Subject: LPC2: UART driver. X-Git-Tag: 2.5.0~371 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=313b87e4d52ab09ba7b80c43d22c674133a7bd90;p=bertos.git LPC2: UART driver. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@3537 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/cfg/cfg_ser.h b/bertos/cfg/cfg_ser.h index dba27153..91a10e0b 100644 --- a/bertos/cfg/cfg_ser.h +++ b/bertos/cfg/cfg_ser.h @@ -32,8 +32,6 @@ * * \brief Configuration file for serial module. * - * \version $Id$ - * * \author Daniele Basile */ @@ -64,7 +62,7 @@ * Size of the outbound FIFO buffer for port 1 [bytes]. * $WIZ$ type = "int" * $WIZ$ min = 2 - * $WIZ$ supports = "at91 and not atmega8 and not atmega168 and not atmega32" + * $WIZ$ supports = "lm3s or lpc2 or (at91 and not atmega8 and not atmega168 and not atmega32)" */ #define CONFIG_UART1_TXBUFSIZE 32 @@ -72,7 +70,7 @@ * Size of the inbound FIFO buffer for port 1 [bytes]. * $WIZ$ type = "int" * $WIZ$ min = 2 - * $WIZ$ supports = "at91 and not atmega8 and not atmega168 and not atmega32" + * $WIZ$ supports = "lm3s or lpc2 or (at91 and not atmega8 and not atmega168 and not atmega32)" */ #define CONFIG_UART1_RXBUFSIZE 32 @@ -80,7 +78,7 @@ * Size of the outbound FIFO buffer for port 2 [bytes]. * $WIZ$ type = "int" * $WIZ$ min = 2 - * $WIZ$ supports = "lm3s" + * $WIZ$ supports = "lm3s or lpc2" */ #define CONFIG_UART2_TXBUFSIZE 32 @@ -88,10 +86,26 @@ * Size of the inbound FIFO buffer for port 2 [bytes]. * $WIZ$ type = "int" * $WIZ$ min = 2 - * $WIZ$ supports = "lm3s" + * $WIZ$ supports = "lm3s or lpc2" */ #define CONFIG_UART2_RXBUFSIZE 32 +/** + * Size of the outbound FIFO buffer for port 3 [bytes]. + * $WIZ$ type = "int" + * $WIZ$ min = 2 + * $WIZ$ supports = "lpc2" + */ +#define CONFIG_UART3_TXBUFSIZE 32 + +/** + * Size of the inbound FIFO buffer for port 3 [bytes]. + * $WIZ$ type = "int" + * $WIZ$ min = 2 + * $WIZ$ supports = "lpc2" + */ +#define CONFIG_UART3_RXBUFSIZE 32 + /** * Size of the outbound FIFO buffer for SPI port [bytes]. diff --git a/bertos/cpu/arm/drv/ser_arm.h b/bertos/cpu/arm/drv/ser_arm.h index cc2e48ac..8c39fcf7 100644 --- a/bertos/cpu/arm/drv/ser_arm.h +++ b/bertos/cpu/arm/drv/ser_arm.h @@ -42,6 +42,8 @@ #if CPU_ARM_AT91 #include "ser_at91.h" +#elif CPU_ARM_LPC2378 + #include "ser_lpc2.h" /*#elif Add other ARM families here */ #else #error Unknown CPU diff --git a/bertos/cpu/arm/drv/ser_lpc2.c b/bertos/cpu/arm/drv/ser_lpc2.c new file mode 100644 index 00000000..ec10bfb4 --- /dev/null +++ b/bertos/cpu/arm/drv/ser_lpc2.c @@ -0,0 +1,467 @@ +/** + * \file + * + * + * \brief LPC23xx UART driver. + * + * \author Andrea Righi + */ + +#include /* for BV() */ +#include /* cpu_relax() */ +#include +#include +#include /* vic_handler_t */ +#include +#include "cfg/cfg_ser.h" +#include "ser_lpc2.h" + +#define INT_UART0 6 +#define INT_UART1 7 +#define INT_UART2 28 +#define INT_UART3 29 + +/* From the high-level serial driver */ +extern struct Serial *ser_handles[SER_CNT]; + +struct LPC2Serial +{ + struct SerialHardware hw; + bool sending; + int irq; +}; + +/* Forward declaration */ +static struct LPC2Serial UARTDesc[SER_CNT]; + +struct uart_config +{ + uint32_t base; + uint32_t pconp; + uint32_t pclksel0; + uint32_t pclksel0_mask; + uint32_t pclksel1; + uint32_t pclksel1_mask; + uint32_t pinsel0; + uint32_t pinsel0_mask; + uint32_t pinsel4; + uint32_t pinsel4_mask; +}; + +/* UART registers configuration */ +static const struct uart_config uart_param[] = +{ + /* UART0 */ + { + .base = UART0_BASE_ADDR, + .pconp = BV(3), + + .pclksel0 = BV(6), + .pclksel0_mask = BV(7) | BV(6), + + .pclksel1 = 0, + .pclksel1_mask = 0, + + .pinsel0 = BV(6) | BV(4), + .pinsel0_mask = BV(7) | BV(6) | BV(5) | BV(4), + + .pinsel4 = 0, + .pinsel4_mask = 0, + }, + /* UART1 */ + { + .base = UART1_BASE_ADDR, + .pconp = BV(4), + + .pclksel0 = BV(8), + .pclksel0_mask = BV(9) | BV(8), + + .pclksel1 = 0, + .pclksel1_mask = 0, + + .pinsel0 = 0, + .pinsel0_mask = 0, + + .pinsel4 = BV(3) | BV(1), + .pinsel4_mask = BV(3) | BV(2) | BV(1) | BV(0), + }, + /* UART2 */ + { + .base = UART2_BASE_ADDR, + .pconp = BV(24), + + .pclksel0 = 0, + .pclksel0_mask = 0, + + .pclksel1 = BV(16), + .pclksel1_mask = BV(17) | BV(16), + + .pinsel0 = 0, + .pinsel0_mask = 0, + + .pinsel4 = BV(19) | BV(17), + .pinsel4_mask = BV(19) | BV(18) | BV(17) | BV(16), + }, + /* UART3 */ + { + .base = UART3_BASE_ADDR, + .pconp = BV(25), + + .pclksel0 = 0, + .pclksel0_mask = 0, + + .pclksel1 = BV(18), + .pclksel1_mask = BV(19) | BV(18), + + .pinsel0 = BV(3) | BV(1), + .pinsel0_mask = BV(3) | BV(2) | BV(1) | BV(0), + + .pinsel4 = 0, + .pinsel4_mask = 0, + }, +}; + +static void lpc2_uartSetBaudRate(int port, unsigned long baud) +{ + cpu_flags_t flags; + + IRQ_SAVE_DISABLE(flags); + + /* LCR: DLAB = 1 (enable divisor modify) */ + *(reg8_t *)(uart_param[port].base + 0x0c) |= 0x80; + /* DLL */ + *(reg8_t *)(uart_param[port].base + 0x00) = + DIV_ROUND(CPU_FREQ, 16 * baud) & 0xFF; + /* DLM */ + *(reg8_t *)(uart_param[port].base + 0x04) = + (DIV_ROUND(CPU_FREQ, 16 * baud) >> 8) & 0xFF; + *(reg32_t *)(uart_param[port].base + 0x0c) &= ~0x80; + /* LCR: DLAB = 0 (disable divisor modify) */ + *(reg8_t *)(uart_param[port].base + 0x0c) &= ~0x80; + + IRQ_RESTORE(flags); +} + +static void lpc2_uartSetParity(int port, int parity) +{ + /* Set 8-bit word, one stop bit by default */ + uint32_t config = BV(1) | BV(0); + + cpu_flags_t flags; + + IRQ_SAVE_DISABLE(flags); + + switch(parity) + { + case SER_PARITY_NONE: + break; + case SER_PARITY_ODD: + config |= BV(3); + break; + case SER_PARITY_EVEN: + config |= BV(4) | BV(3); + break; + default: + ASSERT(0); + IRQ_RESTORE(flags); + return; + } + /* LCR */ + *(reg8_t *)(uart_param[port].base + 0x0c) = config; + + IRQ_RESTORE(flags); +} + +static void lpc2_uartPutChar(uint32_t base, uint8_t c) +{ + reg8_t *lsr = (reg8_t *)base + 0x14; + reg8_t *thr = (reg8_t *)base + 0x00; + + while (!(*lsr & BV(6))) + cpu_relax(); + *thr = c; +} + +void lpc2_uartInit(int port) +{ + cpu_flags_t flags; + + IRQ_SAVE_DISABLE(flags); + + /* Power-on the device */ + PCONP |= uart_param[port].pconp; + /* Set UART clk to CPU_FREQ */ + PCLKSEL0 &= ~uart_param[port].pclksel0_mask; + PCLKSEL0 |= uart_param[port].pclksel0; + PCLKSEL1 &= ~uart_param[port].pclksel1_mask; + PCLKSEL1 |= uart_param[port].pclksel1; + + /* LCR: 8bit, 1 stop bit, no parity, DLAB = 1 (enable divisor modify) */ + *(reg8_t *)(uart_param[port].base + 0x0c) = 0x83; + /* DLL */ + *(reg8_t *)(uart_param[port].base + 0x00) = + DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) & 0xFF; + /* DLM */ + *(reg8_t *)(uart_param[port].base + 0x04) = + (DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) >> 8) & 0xFF; + /* FDR */ + *(reg32_t *)(uart_param[port].base + 0x28) = 0x10; + + /* Assign TX pin to UART0*/ + PINSEL0 &= ~uart_param[port].pinsel0_mask; + PINSEL0 |= uart_param[port].pinsel0; + PINSEL4 &= ~uart_param[port].pinsel4_mask; + PINSEL4 |= uart_param[port].pinsel4; + /* LCR: set 8bit, 1 stop bit, no parity, DLAB = 0 (disable divisor modify) */ + *(reg8_t *)(uart_param[port].base + 0x0c) = 0x03; + + /* TER: Enable transmitter */ + *(reg8_t *)(uart_param[port].base + 0x30) = BV(7); + /* IER: Enable RBR interrupt */ + *(reg8_t *)(uart_param[port].base + 0x04) = BV(0); + + IRQ_RESTORE(flags); +} + +static bool tx_sending(struct SerialHardware *_hw) +{ + struct LPC2Serial *hw = (struct LPC2Serial *)_hw; + return hw->sending; +} + +INLINE bool lpc2_uartRxReady(int port) +{ + /* LSR: check Receiver Data Ready (RDR) bit */ + return *(reg8_t *)(uart_param[port].base + 0x14) & BV(0) ? true : false; +} + +static void uart_irq_rx(int port) +{ + struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo; + char c; + + while (lpc2_uartRxReady(port)) + { + c = *(reg8_t *)(uart_param[port].base + 0x00); + if (fifo_isfull(rxfifo)) + ser_handles[port]->status |= SERRF_RXFIFOOVERRUN; + else + fifo_push(rxfifo, c); + } +} + +INLINE bool lpc2_uartTxReady(int port) +{ + /* LSR: check Transmitter Holding Register Empty (THRE) bit */ + return *(reg8_t *)(uart_param[port].base + 0x14) & BV(5) ? true : false; +} + +static void uart_irq_tx(int port) +{ + struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo; + + while (lpc2_uartTxReady(port)) + { + /* + * Disable TX empty interrupts if there're no more + * characters to transmit. + */ + if (fifo_isempty(txfifo)) + { + /* IER: Disable THRE interrupt */ + *(reg8_t *)(uart_param[port].base + 0x04) &= ~BV(1); + UARTDesc[port].sending = false; + break; + } + /* THR: put a character to the Transmit Holding Register */ + *(reg8_t *)(uart_param[port].base + 0x00) = fifo_pop(txfifo); + } +} + +static void uart_common_irq_handler(int port) +{ + /* IIR: identify the interrupt source */ + uint32_t status = *(reg32_t *)(uart_param[port].base + 0x08) >> 1 & 0x7; + + /* Receiver Data Ready (RDR) */ + if (status == 0x02) + uart_irq_rx(port); + /* Transmit Holding Register Empty (THRE) */ + else if (status == 0x01) + uart_irq_tx(port); + /* Signal the VIC we have completed the ISR */ + VICVectAddr = 0; +} + +static void lpc2_uartIRQEnable(int port, vic_handler_t handler) +{ + vic_setVector(UARTDesc[port].irq, handler); + vic_enable(UARTDesc[port].irq); +} + +static void lpc2_uartIRQDisable(int port) +{ + vic_disable(UARTDesc[port].irq); +} + +/* UART class definition */ +#define UART_PORT(port) \ + /* UART TX and RX buffers */ \ + static unsigned char \ + uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \ + static unsigned char \ + uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \ + \ + /* UART interrupt handler */ \ + static DECLARE_ISR(uart ## port ## _irq_handler) \ + { \ + uart_common_irq_handler(port); \ + } \ + \ + /* UART public methods */ \ + static void \ + uart ## port ## _txStart(struct SerialHardware *_hw) \ + { \ + struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo; \ + struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \ + \ + if (hw->sending) \ + return; \ + lpc2_uartPutChar(UART ## port ## _BASE_ADDR, fifo_pop(txfifo)); \ + if (!fifo_isempty(txfifo)) \ + { \ + hw->sending = true; \ + /* IER: Enable THRE interrupt */ \ + *(reg8_t *)(uart_param[port].base + 0x04) |= BV(1); \ + } \ + } \ + \ + static void \ + uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \ + unsigned long baud) \ + { \ + lpc2_uartSetBaudRate(port, baud); \ + } \ + \ + static void \ + uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \ + int parity) \ + { \ + lpc2_uartSetParity(port, parity); \ + } \ + \ + static void \ + uart ## port ## _cleanup(struct SerialHardware *_hw) \ + { \ + struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \ + \ + hw->sending = false; \ + lpc2_uartIRQDisable(port); \ + } \ + \ + static void \ + uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \ + UNUSED_ARG(struct Serial *, ser)) \ + { \ + lpc2_uartInit(port); \ + lpc2_uartIRQEnable(port, uart ## port ## _irq_handler); \ + } \ + \ + /* UART operations */ \ + static const struct SerialHardwareVT UART ## port ## _VT = \ + { \ + .init = uart ## port ## _init, \ + .cleanup = uart ## port ## _cleanup, \ + .setBaudrate = uart ## port ## _setbaudrate, \ + .setParity = uart ## port ## _setparity, \ + .txStart = uart ## port ## _txStart, \ + .txSending = tx_sending, \ + }; + +/* UART port instances */ +UART_PORT(0) +UART_PORT(1) +UART_PORT(2) +UART_PORT(3) + +static struct LPC2Serial UARTDesc[SER_CNT] = +{ + { + .hw = { + .table = &UART0_VT, + .txbuffer = uart0_txbuffer, + .rxbuffer = uart0_rxbuffer, + .txbuffer_size = sizeof(uart0_txbuffer), + .rxbuffer_size = sizeof(uart0_rxbuffer), + }, + .sending = false, + .irq = INT_UART0, + }, + { + .hw = { + .table = &UART1_VT, + .txbuffer = uart1_txbuffer, + .rxbuffer = uart1_rxbuffer, + .txbuffer_size = sizeof(uart1_txbuffer), + .rxbuffer_size = sizeof(uart1_rxbuffer), + }, + .sending = false, + .irq = INT_UART1, + }, + { + .hw = { + .table = &UART2_VT, + .txbuffer = uart2_txbuffer, + .rxbuffer = uart2_rxbuffer, + .txbuffer_size = sizeof(uart2_txbuffer), + .rxbuffer_size = sizeof(uart2_rxbuffer), + }, + .sending = false, + .irq = INT_UART2, + }, + { + .hw = { + .table = &UART3_VT, + .txbuffer = uart3_txbuffer, + .rxbuffer = uart3_rxbuffer, + .txbuffer_size = sizeof(uart3_txbuffer), + .rxbuffer_size = sizeof(uart3_rxbuffer), + }, + .sending = false, + .irq = INT_UART3, + }, +}; + +struct SerialHardware *ser_hw_getdesc(int port) +{ + ASSERT(port >= 0 && port < SER_CNT); + return &UARTDesc[port].hw; +} diff --git a/bertos/cpu/arm/drv/ser_lpc2.h b/bertos/cpu/arm/drv/ser_lpc2.h new file mode 100644 index 00000000..cd0669d0 --- /dev/null +++ b/bertos/cpu/arm/drv/ser_lpc2.h @@ -0,0 +1,73 @@ +/** + * \file + * + * + * \brief LPC23xx UART driver. + * + * \author Andrea Righi + */ + +#ifndef SER_LPC2_H +#define SER_LPC2_H + +#include +#include + +/* Serial hardware numbers */ +enum +{ + SER_UART0, + SER_UART1, + SER_UART2, + SER_UART3, + + SER_CNT //< Number of serial ports +}; + +/* Software errors */ +#define SERRF_RXFIFOOVERRUN BV(0) //< Rx FIFO buffer overrun +#define SERRF_RXTIMEOUT BV(1) //< Receive timeout +#define SERRF_TXTIMEOUT BV(2) //< Transmit timeout + +/* + * Hardware errors. + */ +#define SERRF_RXSROVERRUN 0 //< Input overrun +#define SERRF_FRAMEERROR 0 //< Stop bit missing +#define SERRF_PARITYERROR 0 //< Parity error +#define SERRF_NOISEERROR 0 //< Noise error + +/* Serial error/status flags */ +typedef uint32_t serstatus_t; + +void lpc2_uartInit(int port); + +#endif /* SER_LPC2_H */ diff --git a/examples/lpc2378/cfg/cfg_ser.h b/examples/lpc2378/cfg/cfg_ser.h index 7dae7c23..91a10e0b 100644 --- a/examples/lpc2378/cfg/cfg_ser.h +++ b/examples/lpc2378/cfg/cfg_ser.h @@ -32,8 +32,6 @@ * * \brief Configuration file for serial module. * - * \version $Id: cfg_ser.h 2845 2009-09-01 14:41:47Z lottaviano $ - * * \author Daniele Basile */ @@ -64,7 +62,7 @@ * Size of the outbound FIFO buffer for port 1 [bytes]. * $WIZ$ type = "int" * $WIZ$ min = 2 - * $WIZ$ supports = "at91 and not atmega8 and not atmega168 and not atmega32" + * $WIZ$ supports = "lm3s or lpc2 or (at91 and not atmega8 and not atmega168 and not atmega32)" */ #define CONFIG_UART1_TXBUFSIZE 32 @@ -72,10 +70,42 @@ * Size of the inbound FIFO buffer for port 1 [bytes]. * $WIZ$ type = "int" * $WIZ$ min = 2 - * $WIZ$ supports = "at91 and not atmega8 and not atmega168 and not atmega32" + * $WIZ$ supports = "lm3s or lpc2 or (at91 and not atmega8 and not atmega168 and not atmega32)" */ #define CONFIG_UART1_RXBUFSIZE 32 +/** + * Size of the outbound FIFO buffer for port 2 [bytes]. + * $WIZ$ type = "int" + * $WIZ$ min = 2 + * $WIZ$ supports = "lm3s or lpc2" + */ +#define CONFIG_UART2_TXBUFSIZE 32 + +/** + * Size of the inbound FIFO buffer for port 2 [bytes]. + * $WIZ$ type = "int" + * $WIZ$ min = 2 + * $WIZ$ supports = "lm3s or lpc2" + */ +#define CONFIG_UART2_RXBUFSIZE 32 + +/** + * Size of the outbound FIFO buffer for port 3 [bytes]. + * $WIZ$ type = "int" + * $WIZ$ min = 2 + * $WIZ$ supports = "lpc2" + */ +#define CONFIG_UART3_TXBUFSIZE 32 + +/** + * Size of the inbound FIFO buffer for port 3 [bytes]. + * $WIZ$ type = "int" + * $WIZ$ min = 2 + * $WIZ$ supports = "lpc2" + */ +#define CONFIG_UART3_RXBUFSIZE 32 + /** * Size of the outbound FIFO buffer for SPI port [bytes].