1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/lpc2387: uart driver overhaul

This converts the hard-coded UART driver to the new ways.

 - allow the board to configure the RX & TX pins
 - allow for more than one UART
 - allow setting the baudrate
 - implement poweron()/poweroff() functions
This commit is contained in:
Benjamin Valentin 2019-10-30 00:53:39 +01:00
parent 5ec9f62a0b
commit d6a94d4e18
2 changed files with 221 additions and 44 deletions

View File

@ -19,9 +19,6 @@
#ifndef PERIPH_CPU_H #ifndef PERIPH_CPU_H
#define PERIPH_CPU_H #define PERIPH_CPU_H
#include "cpu.h"
#include "periph/dev_enums.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -75,6 +72,18 @@ typedef enum {
} gpio_flank_t; } gpio_flank_t;
#endif /* ndef DOXYGEN */ #endif /* ndef DOXYGEN */
/**
* @brief UART device configuration
*/
typedef struct {
lpc23xx_uart_t *dev; /**< pointer to the UART device */
uint8_t irq_prio_rx; /**< priority of the RX IRQ */
uint8_t pinsel_rx; /**< PINSEL# of the RX pin */
uint8_t pinsel_tx; /**< PINSEL# of the TX pin */
uint32_t pinsel_msk_rx; /**< RX PINSEL Mask */
uint32_t pinsel_msk_tx; /**< TX PINSEL Mask */
} uart_conf_t;
/** /**
* @brief Number of available timer channels * @brief Number of available timer channels
*/ */

View File

@ -17,6 +17,7 @@
* @author Kaspar Schleiser <kaspar@schleiser.de> * @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Heiko Will <hwill@inf.fu-berlin.de> * @author Heiko Will <hwill@inf.fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de> * @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Benjamin Valentin <benpicco@beuth-hochschule.de>
* *
* @} * @}
*/ */
@ -29,84 +30,251 @@
#include "VIC.h" #include "VIC.h"
#include "periph/uart.h" #include "periph/uart.h"
/* for now, we only support one UART device... */ static uart_rx_cb_t _rx_cb[UART_NUMOF];
static uart_rx_cb_t _rx_cb; static void * _cb_arg[UART_NUMOF];
static void * _cb_arg;
void UART0_IRQHandler(void) __attribute__((interrupt("IRQ"))); typedef void (*irq_fun_t)(void);
#if UART_NUMOF > 0
static void UART0_IRQHandler(void) __attribute__((interrupt("IRQ")));
#endif
#if UART_NUMOF > 1
static void UART1_IRQHandler(void) __attribute__((interrupt("IRQ")));
#endif
#if UART_NUMOF > 2
static void UART2_IRQHandler(void) __attribute__((interrupt("IRQ")));
#endif
#if UART_NUMOF > 3
static void UART3_IRQHandler(void) __attribute__((interrupt("IRQ")));
#endif
/**
* @brief Get the pointer to the base register of the given UART device
*
* @param[in] dev UART device identifier
*
* @return base register address
*/
static inline lpc23xx_uart_t *get_dev(uart_t dev)
{
return uart_config[dev].dev;
}
/* get the UART number from the address */
static inline uint8_t _uart_num(lpc23xx_uart_t* uart)
{
switch ((uint32_t) uart) {
case UART0_BASE_ADDR:
return 0;
case UART1_BASE_ADDR:
return 1;
case UART2_BASE_ADDR:
return 2;
case UART3_BASE_ADDR:
return 3;
}
return 0;
}
static const uint8_t _uart_int[] = {
UART0_INT, UART1_INT, UART2_INT, UART3_INT
};
static irq_fun_t _uart_isr[UART_NUMOF] = {
#if UART_NUMOF > 0
UART0_IRQHandler,
#endif
#if UART_NUMOF > 1
UART1_IRQHandler,
#endif
#if UART_NUMOF > 2
UART2_IRQHandler,
#endif
#if UART_NUMOF > 3
UART3_IRQHandler,
#endif
};
/* Table for FDR register contents
* bits 03: DIVADDVAL
* bits 47: MULVAL
*/
#define DIV_MUL(m, d) ((d << 4) | m)
static const uint8_t div_table[] = {
DIV_MUL(0, 1), /* 1 */
DIV_MUL(0, 1), /* 1.03125 */
DIV_MUL(1, 15), /* 1.0625 */
DIV_MUL(1, 11), /* 1.09375 */
DIV_MUL(1, 8), /* 1.125 */
DIV_MUL(2, 13), /* 1.15625 */
DIV_MUL(2, 11), /* 1.1875 */
DIV_MUL(2, 9), /* 1.21875 */
DIV_MUL(1, 4), /* 1.25 */
DIV_MUL(2, 7), /* 1.21875 */
DIV_MUL(4, 13), /* 1.3125 */
DIV_MUL(1, 3), /* 1.34375 */
DIV_MUL(3, 8), /* 1.375 */
DIV_MUL(2, 5), /* 1.40625 */
DIV_MUL(3, 7), /* 1.4375 */
DIV_MUL(7, 15), /* 1.46875 */
DIV_MUL(1, 2), /* 1.5 */
DIV_MUL(8, 15), /* 1.53125 */
DIV_MUL(5, 9), /* 1.5625 */
DIV_MUL(3, 5), /* 1.59375 */
DIV_MUL(5, 8), /* 1.625 */
DIV_MUL(9, 14), /* 1.65625 */
DIV_MUL(2, 3), /* 1.6875 */
DIV_MUL(5, 7), /* 1.71875 */
DIV_MUL(3, 4), /* 1.75 */
DIV_MUL(7, 9), /* 1.78125 */
DIV_MUL(9, 11), /* 1.8125 */
DIV_MUL(11,13), /* 1.84375 */
DIV_MUL(7, 8), /* 1.875 */
DIV_MUL(9, 13), /* 1.90625 */
DIV_MUL(14,15), /* 1.9375 */
DIV_MUL(14,15), /* 1.96875 */
};
int uart_init(uart_t dev, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) int uart_init(uart_t dev, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
{ {
(void) baudrate; if (dev >= UART_NUMOF) {
/* for now, we only support one UART device and only the RX interrupt */
if (dev != 0) {
return UART_NODEV; return UART_NODEV;
} }
const uart_conf_t *cfg = &uart_config[dev];
lpc23xx_uart_t *uart = cfg->dev;
const uint8_t idx = _uart_num(uart);
/* configure RX & TX pins */
*(&PINSEL0 + cfg->pinsel_rx) |= cfg->pinsel_msk_rx;
*(&PINSEL0 + cfg->pinsel_tx) |= cfg->pinsel_msk_tx;
uart_poweron(dev);
/* save interrupt context */ /* save interrupt context */
_rx_cb = rx_cb; _rx_cb[dev] = rx_cb;
_cb_arg = arg; _cb_arg[dev] = arg;
/* power on the UART device */ uart->LCR = 0x80; /* DLAB = 1 */
PCONP |= PCUART0;
/* UART0 clock divider is CCLK/8 */
PCLKSEL0 |= BIT6 + BIT7;
/* configure to 8N1 */
U0LCR = 0x83;
/* Baudrate calculation: /* set UART PCLK to CCLK/8 */
* BR = PCLK (9 MHz) / (16 x 256 x DLM + DLL) x (1/(DIVADDVAL/MULVAL)) */ switch (idx) {
/* TODO: UART Baudrate calculation using the baudrate parameter */ case 0:
U0FDR = 0x92; /* DIVADDVAL = 0010 = 2, MULVAL = 1001 = 9 */ PCLKSEL0 |= BIT6 + BIT7;
U0DLM = 0x00; break;
U0DLL = 0x04; case 1:
PCLKSEL0 |= BIT8 + BIT9;
break;
case 2:
PCLKSEL1 |= BIT16 + BIT17;
break;
case 3:
PCLKSEL1 |= BIT18 + BIT19;
break;
}
U0LCR = 0x03; /* DLAB = 0 */ /* PCLK = CCLK/8; DL = PCLK / (16 * F_Baud) */
U0FCR = 0x07; /* Enable and reset TX and RX FIFO */ uint16_t dl = CLOCK_CORECLOCK / (8 * 16 * baudrate);
/* 16 * DIVADDVAL / MULVAL = PCLK / (DL * F_Baud) - 16 */
/* frac = 16 * DIVADDVAL / MULVAL */
/* multiply everything by 2 for increased accuracy */
uint8_t frac = 2 * CLOCK_CORECLOCK / (8 * dl) / baudrate - 32;
uart->FDR = div_table[frac];
uart->DLM = dl >> 8;
uart->DLL = dl & 0xFF;
uart->LCR = 0x03; /* configure to 8N1 */
uart->FCR = 0x07; /* Enable and reset TX and RX FIFO */
/* install and enable the IRQ handler */ /* install and enable the IRQ handler */
install_irq(UART0_INT, UART0_IRQHandler, 6); install_irq(_uart_int[idx], _uart_isr[dev], cfg->irq_prio_rx);
U0IER |= BIT0; /* enable only RX irq */ uart->IER |= BIT0; /* enable only RX irq */
return UART_OK; return UART_OK;
} }
void uart_write(uart_t uart, const uint8_t *data, size_t len) void uart_write(uart_t dev, const uint8_t *data, size_t len)
{ {
(void) uart; lpc23xx_uart_t *uart = get_dev(dev);
for (size_t i = 0; i < len; i++) {
while (!(U0LSR & BIT5)) {} for (const uint8_t *end = data + len; data != end; ++data) {
U0THR = data[i]; while (!(uart->LSR & ULSR_THRE)) {}
uart->THR = *data;
} }
} }
void UART0_IRQHandler(void) static void irq_handler(uart_t dev)
{ {
switch (U0IIR & UIIR_ID_MASK) { lpc23xx_uart_t *uart = get_dev(dev);
switch (uart->IIR & UIIR_ID_MASK) {
case UIIR_CTI_INT: /* Character Timeout Indicator */ case UIIR_CTI_INT: /* Character Timeout Indicator */
case UIIR_RDA_INT: /* Receive Data Available */ case UIIR_RDA_INT: /* Receive Data Available */
do { do {
uint8_t c = (uint8_t)U0RBR; _rx_cb[dev](_cb_arg[dev], uart->RBR);
_rx_cb(_cb_arg, c); } while (uart->LSR & ULSR_RDR);
} while (U0LSR & ULSR_RDR);
break; break;
default: default:
U0LSR; uart->LSR;
U0RBR; uart->RBR;
break; break;
} }
}
#if UART_NUMOF > 0
static void UART0_IRQHandler(void)
{
irq_handler(0);
VICVectAddr = 0; /* Acknowledge Interrupt */ VICVectAddr = 0; /* Acknowledge Interrupt */
} }
#endif
#if UART_NUMOF > 1
static void UART1_IRQHandler(void)
{
irq_handler(1);
VICVectAddr = 0; /* Acknowledge Interrupt */
}
#endif
#if UART_NUMOF > 2
static void UART2_IRQHandler(void)
{
irq_handler(2);
VICVectAddr = 0; /* Acknowledge Interrupt */
}
#endif
#if UART_NUMOF > 3
static void UART3_IRQHandler(void)
{
irq_handler(3);
VICVectAddr = 0; /* Acknowledge Interrupt */
}
#endif
/* Bit in the Power Control for Peripherals Register */
static const uint8_t _uart_pconp[] = {
3, 4, 24, 25
};
void uart_poweron(uart_t uart) void uart_poweron(uart_t uart)
{ {
(void)uart; const uart_conf_t *cfg = &uart_config[uart];
/* not implemented (yet) */ const uint8_t idx = _uart_num(cfg->dev);
/* power on the UART device */
PCONP |= 1 << _uart_pconp[idx];
} }
void uart_poweroff(uart_t uart) void uart_poweroff(uart_t uart)
{ {
(void)uart; const uart_conf_t *cfg = &uart_config[uart];
/* not implemented (yet) */ const uint8_t idx = _uart_num(cfg->dev);
/* power off the UART device */
PCONP &= ~(1 << _uart_pconp[idx]);
} }