mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
281 lines
6.8 KiB
C
281 lines
6.8 KiB
C
/*
|
|
* Copyright (C) 2008-2015, Freie Universitaet Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_lpc23xx
|
|
* @ingroup drivers_periph_uart
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Peripheral UART driver implementation
|
|
*
|
|
* @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>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "cpu.h"
|
|
#include "VIC.h"
|
|
#include "periph/uart.h"
|
|
|
|
static uart_rx_cb_t _rx_cb[UART_NUMOF];
|
|
static void * _cb_arg[UART_NUMOF];
|
|
|
|
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(d, m) ((m << 4) | d)
|
|
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)
|
|
{
|
|
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[dev] = rx_cb;
|
|
_cb_arg[dev] = arg;
|
|
|
|
uart->LCR = 0x80; /* DLAB = 1 */
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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(_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 dev, const uint8_t *data, size_t len)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void irq_handler(uart_t dev)
|
|
{
|
|
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 {
|
|
_rx_cb[dev](_cb_arg[dev], uart->RBR);
|
|
} while (uart->LSR & ULSR_RDR);
|
|
break;
|
|
|
|
default:
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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]);
|
|
}
|