1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/msp430/periph/uart_usci.c
Marian Buschsieweke f4baa0f2e1
cpu/msp430/periph_uart: Fix uart_write() for USCI peripheral
In TX-only mode the UART was previously release before all bits of the
last byte were shifted out. This adds a busy loop waiting while the
peripheral is still busy, fixing the issue.

Co-authored-by: benpicco <benpicco@googlemail.com>
2024-04-15 14:43:10 +02:00

207 lines
5.0 KiB
C

/*
* Copyright (C) 2015 Freie Universität Berlin
* 2024 Marian Buschsieweke
*
* 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>
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
*
* @}
*/
#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(&params->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++;
}
while (usci->dev->STAT & UCBUSY) {
/* busy wait for completion, e.g. to avoid losing chars/bits
* before releasing the USCI in TX only mode. */
}
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