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:
parent
5ec9f62a0b
commit
d6a94d4e18
@ -19,9 +19,6 @@
|
||||
#ifndef PERIPH_CPU_H
|
||||
#define PERIPH_CPU_H
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/dev_enums.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -75,6 +72,18 @@ typedef enum {
|
||||
} gpio_flank_t;
|
||||
#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
|
||||
*/
|
||||
|
@ -17,6 +17,7 @@
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
* @author Heiko Will <hwill@inf.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 "periph/uart.h"
|
||||
|
||||
/* for now, we only support one UART device... */
|
||||
static uart_rx_cb_t _rx_cb;
|
||||
static void * _cb_arg;
|
||||
static uart_rx_cb_t _rx_cb[UART_NUMOF];
|
||||
static void * _cb_arg[UART_NUMOF];
|
||||
|
||||
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 0…3: DIVADDVAL
|
||||
* bits 4…7: 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)
|
||||
{
|
||||
(void) baudrate;
|
||||
/* for now, we only support one UART device and only the RX interrupt */
|
||||
if (dev != 0) {
|
||||
if (dev >= UART_NUMOF) {
|
||||
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 */
|
||||
_rx_cb = rx_cb;
|
||||
_cb_arg = arg;
|
||||
_rx_cb[dev] = rx_cb;
|
||||
_cb_arg[dev] = arg;
|
||||
|
||||
/* power on the UART device */
|
||||
PCONP |= PCUART0;
|
||||
/* UART0 clock divider is CCLK/8 */
|
||||
PCLKSEL0 |= BIT6 + BIT7;
|
||||
/* configure to 8N1 */
|
||||
U0LCR = 0x83;
|
||||
uart->LCR = 0x80; /* DLAB = 1 */
|
||||
|
||||
/* Baudrate calculation:
|
||||
* BR = PCLK (9 MHz) / (16 x 256 x DLM + DLL) x (1/(DIVADDVAL/MULVAL)) */
|
||||
/* TODO: UART Baudrate calculation using the baudrate parameter */
|
||||
U0FDR = 0x92; /* DIVADDVAL = 0010 = 2, MULVAL = 1001 = 9 */
|
||||
U0DLM = 0x00;
|
||||
U0DLL = 0x04;
|
||||
/* set UART PCLK to CCLK/8 */
|
||||
switch (idx) {
|
||||
case 0:
|
||||
PCLKSEL0 |= BIT6 + BIT7;
|
||||
break;
|
||||
case 1:
|
||||
PCLKSEL0 |= BIT8 + BIT9;
|
||||
break;
|
||||
case 2:
|
||||
PCLKSEL1 |= BIT16 + BIT17;
|
||||
break;
|
||||
case 3:
|
||||
PCLKSEL1 |= BIT18 + BIT19;
|
||||
break;
|
||||
}
|
||||
|
||||
U0LCR = 0x03; /* DLAB = 0 */
|
||||
U0FCR = 0x07; /* Enable and reset TX and RX FIFO */
|
||||
/* PCLK = CCLK/8; DL = PCLK / (16 * F_Baud) */
|
||||
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_irq(UART0_INT, UART0_IRQHandler, 6);
|
||||
U0IER |= BIT0; /* enable only RX irq */
|
||||
install_irq(_uart_int[idx], _uart_isr[dev], cfg->irq_prio_rx);
|
||||
uart->IER |= BIT0; /* enable only RX irq */
|
||||
|
||||
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;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
while (!(U0LSR & BIT5)) {}
|
||||
U0THR = data[i];
|
||||
lpc23xx_uart_t *uart = get_dev(dev);
|
||||
|
||||
for (const uint8_t *end = data + len; data != end; ++data) {
|
||||
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_RDA_INT: /* Receive Data Available */
|
||||
do {
|
||||
uint8_t c = (uint8_t)U0RBR;
|
||||
_rx_cb(_cb_arg, c);
|
||||
} while (U0LSR & ULSR_RDR);
|
||||
_rx_cb[dev](_cb_arg[dev], uart->RBR);
|
||||
} while (uart->LSR & ULSR_RDR);
|
||||
break;
|
||||
|
||||
default:
|
||||
U0LSR;
|
||||
U0RBR;
|
||||
uart->LSR;
|
||||
uart->RBR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if UART_NUMOF > 0
|
||||
static void UART0_IRQHandler(void)
|
||||
{
|
||||
irq_handler(0);
|
||||
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;
|
||||
/* not implemented (yet) */
|
||||
const uart_conf_t *cfg = &uart_config[uart];
|
||||
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;
|
||||
/* not implemented (yet) */
|
||||
const uart_conf_t *cfg = &uart_config[uart];
|
||||
const uint8_t idx = _uart_num(cfg->dev);
|
||||
|
||||
/* power off the UART device */
|
||||
PCONP &= ~(1 << _uart_pconp[idx]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user