mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
aef5b65244
This cleans up the USCI based UART and SPI implementations and allows multiple instances of either interface to be configured by the boards. In addition, it allows sharing the USCI peripherals to provide multiple serial interfaces with the same hardware (round-robin).
200 lines
4.7 KiB
C
200 lines
4.7 KiB
C
/*
|
|
* Copyright (C) 2015 Freie Universität 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_msp430_f2xx_g2xx
|
|
* @ingroup drivers_periph_uart
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Low-level UART driver implementation
|
|
*
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include "compiler_hints.h"
|
|
#include "cpu.h"
|
|
#include "irq.h"
|
|
#include "periph/gpio.h"
|
|
#include "periph/uart.h"
|
|
#include "periph_conf.h"
|
|
#include "periph_cpu.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
static uart_isr_ctx_t _isr_ctx[UART_NUMOF];
|
|
static msp430_usci_conf_t _confs[UART_NUMOF];
|
|
|
|
static void uart_rx_isr(uintptr_t uart);
|
|
|
|
void uart_init_pins(uart_t uart)
|
|
{
|
|
assume((unsigned)uart < UART_NUMOF);
|
|
|
|
const msp430_usci_uart_params_t *params = uart_config[uart].uart;
|
|
|
|
gpio_set(params->txd);
|
|
gpio_init(params->txd, GPIO_OUT);
|
|
gpio_periph_mode(params->txd, true);
|
|
|
|
gpio_init(params->rxd, GPIO_IN);
|
|
gpio_periph_mode(params->rxd, true);
|
|
}
|
|
|
|
void uart_deinit_pins(uart_t uart)
|
|
{
|
|
assume((unsigned)uart < UART_NUMOF);
|
|
|
|
const msp430_usci_uart_params_t *params = uart_config[uart].uart;
|
|
|
|
gpio_init(params->txd, GPIO_IN);
|
|
gpio_periph_mode(params->txd, false);
|
|
|
|
gpio_init(params->rxd, GPIO_IN);
|
|
gpio_periph_mode(params->rxd, false);
|
|
}
|
|
|
|
static void _init(uart_t uart)
|
|
{
|
|
const msp430_usci_uart_params_t *params = uart_config[uart].uart;
|
|
const msp430_usci_conf_t *conf = &_confs[uart];
|
|
uint8_t enable_rx_irq = 0;
|
|
/* enable RX IRQ, if callback function is set */
|
|
if (_isr_ctx[uart].rx_cb) {
|
|
enable_rx_irq = params->usci_params.rx_irq_mask;
|
|
}
|
|
/* acquire and configure USCI */
|
|
msp430_usci_acquire(¶ms->usci_params, conf);
|
|
/* reset error stats */
|
|
params->usci_params.dev->STAT = 0;
|
|
/* release USCI from reset and enable RX IRQ */
|
|
params->usci_params.dev->CTL1 &= ~(UCSWRST);
|
|
/* interrupt enable register is shared between two USCI peripherals, hence
|
|
* the other may be concurrently be configured */
|
|
unsigned irq_state = irq_disable();
|
|
*params->usci_params.interrupt_enable |= enable_rx_irq;
|
|
irq_restore(irq_state);
|
|
}
|
|
|
|
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
|
|
{
|
|
assume((unsigned)uart < UART_NUMOF);
|
|
|
|
/* prepare configuration */
|
|
_confs[uart] = (msp430_usci_conf_t){
|
|
.prescaler = msp430_usci_prescale(baudrate),
|
|
.ctl0 = 0,
|
|
};
|
|
|
|
/* save interrupt context */
|
|
_isr_ctx[uart] = (uart_isr_ctx_t){
|
|
.rx_cb = rx_cb,
|
|
.arg = arg,
|
|
};
|
|
|
|
/* prepare pins */
|
|
uart_init_pins(uart);
|
|
|
|
/* only enable USCI if RX is used, otherwise we enable it on-demand to
|
|
* allow sharing the USCI and conserve power */
|
|
|
|
if (rx_cb) {
|
|
_init(uart);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void uart_write(uart_t uart, const uint8_t *data, size_t len)
|
|
{
|
|
assume((unsigned)uart < UART_NUMOF);
|
|
|
|
const msp430_usci_params_t *usci = &uart_config[uart].uart->usci_params;
|
|
bool tx_only = !_isr_ctx[uart].rx_cb;
|
|
|
|
/* in TX-only mode, we enable the USCI on-demand */
|
|
if (tx_only) {
|
|
_init(uart);
|
|
}
|
|
|
|
while (len--) {
|
|
while (!(*usci->interrupt_flag & usci->tx_irq_mask)) { }
|
|
usci->dev->TXBUF = *data++;
|
|
}
|
|
|
|
if (tx_only) {
|
|
msp430_usci_release(usci);
|
|
}
|
|
}
|
|
|
|
void uart_poweron(uart_t uart)
|
|
{
|
|
assume((unsigned)uart < UART_NUMOF);
|
|
|
|
if (!_isr_ctx[uart].rx_cb) {
|
|
/* in TX only mode, the USCI will only be turned on on-demand anyway */
|
|
return;
|
|
}
|
|
|
|
_init(uart);
|
|
}
|
|
|
|
void uart_poweroff(uart_t uart)
|
|
{
|
|
assume((unsigned)uart < UART_NUMOF);
|
|
|
|
if (!_isr_ctx[uart].rx_cb) {
|
|
/* in TX only mode, the USCI will only be turned on on-demand anyway */
|
|
return;
|
|
}
|
|
|
|
const msp430_usci_params_t *usci = &uart_config[uart].uart->usci_params;
|
|
|
|
msp430_usci_release(usci);
|
|
}
|
|
|
|
static void uart_rx_isr(uintptr_t uart)
|
|
{
|
|
assume((unsigned)uart < UART_NUMOF);
|
|
const msp430_usci_params_t *usci = &uart_config[uart].uart->usci_params;
|
|
uint8_t stat = usci->dev->STAT;
|
|
uint8_t data = (uint8_t)usci->dev->RXBUF;
|
|
|
|
if (stat & (UCFE | UCOE | UCPE | UCBRK)) {
|
|
/* TODO: Add proper error handling */
|
|
usci->dev->STAT = 0;
|
|
DEBUG("[uart@%u] Error: %04x\n", (unsigned)uart, (unsigned)stat);
|
|
}
|
|
else {
|
|
_isr_ctx[uart].rx_cb(_isr_ctx[uart].arg, data);
|
|
}
|
|
|
|
}
|
|
|
|
/* only USCI A0 and USCI A1 can be used for UARTs, so at most two ISRS needed */
|
|
#ifdef UART0_RX_ISR
|
|
ISR(UART0_RX_ISR, isr_uart0)
|
|
{
|
|
__enter_isr();
|
|
uart_rx_isr(0);
|
|
__exit_isr();
|
|
}
|
|
#endif
|
|
|
|
#ifdef UART1_RX_ISR
|
|
ISR(UART1_RX_ISR, isr_uart1)
|
|
{
|
|
__enter_isr();
|
|
uart_rx_isr(1);
|
|
__exit_isr();
|
|
}
|
|
#endif
|