mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/rpx0xx: implement periph_uart
Co-authored-by: nickw96 <nick.weiler@st.ovgu.de> Co-authored-by: MaestroOnICe <justus.krebs@st.ovgu.de> Co-authored-by: Franz2000 <franz.freitag@st.ovgu.de>
This commit is contained in:
parent
ea56dfc3ff
commit
e3821480f1
@ -11,6 +11,8 @@ config CPU_FAM_RPX0XX
|
||||
select HAS_CPU_RPX0XX
|
||||
select HAS_PERIPH_GPIO
|
||||
select HAS_PERIPH_GPIO_IRQ
|
||||
select HAS_PERIPH_UART_MODECFG
|
||||
select HAS_PERIPH_UART_RECONFIGURE
|
||||
|
||||
config CPU_FAM
|
||||
default "RPX0XX" if CPU_FAM_RPX0XX
|
||||
|
@ -1 +0,0 @@
|
||||
DEFAULT_MODULE += stdio_null
|
@ -5,3 +5,5 @@ include $(RIOTCPU)/cortexm_common/Makefile.features
|
||||
|
||||
FEATURES_PROVIDED += periph_gpio
|
||||
FEATURES_PROVIDED += periph_gpio_irq
|
||||
FEATURES_PROVIDED += periph_uart_reconfigure
|
||||
FEATURES_PROVIDED += periph_uart_modecfg
|
||||
|
@ -390,6 +390,16 @@ typedef struct {
|
||||
} gpio_io_ctrl_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Configuration details for an UART interface needed by the RPX0XX peripheral
|
||||
*/
|
||||
typedef struct {
|
||||
UART0_Type *dev; /**< Base address of the I/O registers of the device */
|
||||
gpio_t rx_pin; /**< GPIO pin to use for RX */
|
||||
gpio_t tx_pin; /**< GPIO pin to use for TX */
|
||||
IRQn_Type irqn; /**< IRQ number of the UART interface */
|
||||
} uart_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Get the PAD control register for the given GPIO pin as word
|
||||
*
|
||||
|
280
cpu/rpx0xx/periph/uart.c
Normal file
280
cpu/rpx0xx/periph/uart.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Nick Weiler, Justus Krebs, Franz Freitag
|
||||
* Copyright (C) 2021 Otto-von-Guericke Universität Magdeburg
|
||||
*
|
||||
* 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_rpx0xx
|
||||
* @ingroup drivers_periph_uart
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief UART driver implementation for the RP2040
|
||||
*
|
||||
* @author Franz Freitag <franz.freitag@st.ovgu.de>
|
||||
* @author Justus Krebs <justus.krebs@st.ovgu.de>
|
||||
* @author Nick Weiler <nick.weiler@st.ovgu.de>
|
||||
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "assert.h"
|
||||
#include "bitarithm.h"
|
||||
#include "board.h"
|
||||
#include "io_reg.h"
|
||||
#include "irq.h"
|
||||
#include "mutex.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 ctx[UART_NUMOF];
|
||||
|
||||
/* back up values of registers used during uart_poweroff() / uart_poweron() */
|
||||
static uint32_t uartibrd;
|
||||
static uint32_t uartfbrd;
|
||||
static uint32_t uartlcr_h;
|
||||
static uint32_t uartcr;
|
||||
|
||||
void _irq_enable(uart_t uart)
|
||||
{
|
||||
UART0_Type *dev = uart_config[uart].dev;
|
||||
dev->UARTIMSC.reg = UART0_UARTIMSC_RXIM_Msk;
|
||||
NVIC_EnableIRQ(uart_config[uart].irqn);
|
||||
}
|
||||
|
||||
void _set_symbolrate(uart_t uart, uint32_t baud)
|
||||
{
|
||||
assert(baud != 0);
|
||||
UART0_Type *dev = uart_config[uart].dev;
|
||||
uint32_t baud_rate_div = (8 * CLOCK_PERIPH / baud);
|
||||
uint32_t baud_ibrd = baud_rate_div >> 7;
|
||||
uint32_t baud_fbrd;
|
||||
|
||||
if (baud_ibrd == 0) {
|
||||
baud_ibrd = 1;
|
||||
baud_fbrd = 0;
|
||||
}
|
||||
else if (baud_ibrd >= 65535) {
|
||||
baud_ibrd = 65535;
|
||||
baud_fbrd = 0;
|
||||
}
|
||||
else {
|
||||
baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2;
|
||||
}
|
||||
|
||||
dev->UARTIBRD.reg = baud_ibrd;
|
||||
dev->UARTFBRD.reg = baud_fbrd;
|
||||
}
|
||||
|
||||
int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t uart_parity,
|
||||
uart_stop_bits_t stop_bits)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
UART0_Type *dev = uart_config[uart].dev;
|
||||
|
||||
io_reg_atomic_clear(&dev->UARTCR.reg,
|
||||
UART0_UARTCR_UARTEN_Msk | UART0_UARTCR_TXE_Msk | UART0_UARTCR_RXE_Msk);
|
||||
|
||||
/* Beware of strange hardware bug: If the configuration bitmask is prepared in register and
|
||||
* transferred with a single 32 bit write (updating both parity and number of data bits at the
|
||||
* same time), the configuration change of the parity bits will not take place until after the
|
||||
* next char send out. If the configuration is updated in multiple bus accesses, it will apply
|
||||
* directly to the next char. So: Double check e.g. with tests/periph_uart_mode after touching
|
||||
* the initialization code here */
|
||||
dev->UARTLCR_H.reg = (uint32_t)data_bits << UART0_UARTLCR_H_WLEN_Pos;
|
||||
|
||||
if (stop_bits == UART_STOP_BITS_2) {
|
||||
io_reg_atomic_set(&dev->UARTLCR_H.reg, UART0_UARTLCR_H_STP2_Msk);
|
||||
}
|
||||
|
||||
switch (uart_parity) {
|
||||
case UART_PARITY_NONE:
|
||||
break;
|
||||
case UART_PARITY_EVEN:
|
||||
io_reg_atomic_set(&dev->UARTLCR_H.reg, UART0_UARTLCR_H_EPS_Msk | UART0_UARTLCR_H_PEN_Msk);
|
||||
break;
|
||||
case UART_PARITY_ODD:
|
||||
io_reg_atomic_set(&dev->UARTLCR_H.reg, UART0_UARTLCR_H_PEN_Msk);
|
||||
break;
|
||||
default:
|
||||
return UART_NOMODE;
|
||||
}
|
||||
|
||||
io_reg_atomic_set(&dev->UARTCR.reg,
|
||||
UART0_UARTCR_UARTEN_Msk | UART0_UARTCR_TXE_Msk | UART0_UARTCR_RXE_Msk);
|
||||
|
||||
return UART_OK;
|
||||
}
|
||||
|
||||
void uart_init_pins(uart_t uart)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
/* not reusing gpio_init() here to not send a char of garbage upon init. Also, we
|
||||
* can use a lower drive strength here, hopefully limiting the current a bit when TX is
|
||||
* connected to the recipients TX by accident. */
|
||||
const gpio_pad_ctrl_t tx_pad_config = {
|
||||
.drive_strength = DRIVE_STRENGTH_2MA,
|
||||
};
|
||||
const gpio_io_ctrl_t tx_io_config = {
|
||||
.function_select = FUNCTION_SELECT_UART,
|
||||
};
|
||||
const gpio_pad_ctrl_t rx_pad_config = {
|
||||
.pull_up_enable = 1,
|
||||
.input_enable = 1,
|
||||
.schmitt_trig_enable = 1,
|
||||
};
|
||||
const gpio_io_ctrl_t rx_io_config = {
|
||||
.function_select = FUNCTION_SELECT_UART,
|
||||
};
|
||||
gpio_set_pad_config(uart_config[uart].tx_pin, tx_pad_config);
|
||||
gpio_set_io_config(uart_config[uart].tx_pin, tx_io_config);
|
||||
if (ctx[uart].rx_cb) {
|
||||
gpio_set_pad_config(uart_config[uart].rx_pin, rx_pad_config);
|
||||
gpio_set_io_config(uart_config[uart].rx_pin, rx_io_config);
|
||||
}
|
||||
}
|
||||
|
||||
void uart_deinit_pins(uart_t uart)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
gpio_reset_all_config(uart_config[uart].tx_pin);
|
||||
SIO->GPIO_OE_CLR.reg = 1LU << uart_config[uart].tx_pin;
|
||||
if (ctx[uart].rx_cb) {
|
||||
gpio_reset_all_config(uart_config[uart].rx_pin);
|
||||
}
|
||||
}
|
||||
|
||||
static void _poweron(uart_t uart)
|
||||
{
|
||||
uint32_t reset_bit_mask = (uart) ? RESETS_RESET_uart1_Msk : RESETS_RESET_uart0_Msk;
|
||||
periph_reset(reset_bit_mask);
|
||||
periph_reset_done(reset_bit_mask);
|
||||
}
|
||||
|
||||
void uart_poweron(uart_t uart)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
_poweron(uart);
|
||||
UART0_Type *dev = uart_config[uart].dev;
|
||||
/* restore configuration registers */
|
||||
dev->UARTIBRD.reg = uartibrd;
|
||||
dev->UARTFBRD.reg = uartfbrd;
|
||||
dev->UARTLCR_H.reg = uartlcr_h;
|
||||
dev->UARTCR.reg = uartcr;
|
||||
/* restore IRQs, if needed */
|
||||
if (ctx[uart].rx_cb != NULL) {
|
||||
_irq_enable(uart);
|
||||
}
|
||||
uart_init_pins(uart);
|
||||
}
|
||||
|
||||
void uart_poweroff(uart_t uart)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
UART0_Type *dev = uart_config[uart].dev;
|
||||
/* backup configuration registers */
|
||||
uartibrd = dev->UARTIBRD.reg;
|
||||
uartfbrd = dev->UARTFBRD.reg;
|
||||
uartlcr_h = dev->UARTLCR_H.reg;
|
||||
uartcr = dev->UARTCR.reg;
|
||||
/* disconnect GPIOs and power off peripheral */
|
||||
uart_deinit_pins(uart);
|
||||
periph_reset((uart) ? RESETS_RESET_uart1_Msk : RESETS_RESET_uart0_Msk);
|
||||
}
|
||||
|
||||
int uart_init(uart_t uart, uint32_t baud, uart_rx_cb_t rx_cb, void *arg)
|
||||
{
|
||||
if (uart >= UART_NUMOF) {
|
||||
return UART_NODEV;
|
||||
}
|
||||
|
||||
UART0_Type *dev = uart_config[uart].dev;
|
||||
ctx[uart].rx_cb = rx_cb;
|
||||
ctx[uart].arg = arg;
|
||||
|
||||
_poweron(uart);
|
||||
uart_init_pins(uart);
|
||||
|
||||
/* Beware: Changes to the symbol rate only take affect after touching the UARTLCR_H register,
|
||||
* which is done in uart_mode(). */
|
||||
_set_symbolrate(uart, baud);
|
||||
|
||||
if (uart_mode(uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1) != UART_OK) {
|
||||
return UART_NOMODE;
|
||||
}
|
||||
|
||||
/* enable RX and IRQs, if needed */
|
||||
if (rx_cb != NULL) {
|
||||
_irq_enable(uart);
|
||||
/* clear any pending data and IRQ to avoid receiving a garbage char */
|
||||
uint32_t status = dev->UARTRIS.reg;
|
||||
dev->UARTICR.reg = status;
|
||||
(void)dev->UARTDR.reg;
|
||||
io_reg_atomic_set(&dev->UARTCR.reg, UART0_UARTCR_RXE_Msk);
|
||||
}
|
||||
|
||||
return UART_OK;
|
||||
}
|
||||
|
||||
void uart_write(uart_t uart, const uint8_t *data, size_t len)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
UART0_Type *dev = uart_config[uart].dev;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
dev->UARTDR.reg = data[i];
|
||||
while (!(dev->UARTRIS.reg & UART0_UARTRIS_TXRIS_Msk)) { }
|
||||
}
|
||||
}
|
||||
|
||||
gpio_t uart_pin_rx(uart_t uart)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
return uart_config[uart].rx_pin;
|
||||
}
|
||||
|
||||
gpio_t uart_pin_tx(uart_t uart)
|
||||
{
|
||||
assert((unsigned)uart < UART_NUMOF);
|
||||
return uart_config[uart].tx_pin;
|
||||
}
|
||||
|
||||
void isr_handler(uint8_t num)
|
||||
{
|
||||
UART0_Type *dev = uart_config[num].dev;
|
||||
|
||||
uint32_t status = dev->UARTMIS.reg;
|
||||
dev->UARTICR.reg = status;
|
||||
|
||||
if (status & UART0_UARTMIS_RXMIS_Msk) {
|
||||
uint32_t data = dev->UARTDR.reg;
|
||||
if (data & (UART0_UARTDR_BE_Msk | UART0_UARTDR_PE_Msk | UART0_UARTDR_FE_Msk)) {
|
||||
DEBUG_PUTS("[rpx0xx] uart RX error (parity, break, or framing error");
|
||||
}
|
||||
else {
|
||||
ctx[num].rx_cb(ctx[num].arg, (uint8_t)data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void isr_uart0(void)
|
||||
{
|
||||
isr_handler(0);
|
||||
cortexm_isr_end();
|
||||
}
|
||||
|
||||
void isr_uart1(void)
|
||||
{
|
||||
isr_handler(1);
|
||||
cortexm_isr_end();
|
||||
}
|
Loading…
Reference in New Issue
Block a user