mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
270 lines
6.6 KiB
C
270 lines
6.6 KiB
C
/*
|
|
* Copyright (C) 2020 Koen Zandberg <koen@bergzand.net>
|
|
*
|
|
* 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_gd32v
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief GD32V UART peripheral implementation
|
|
*
|
|
* @author Koen Zandberg <koen@bergzand.net>
|
|
*/
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
|
|
#include "periph/uart.h"
|
|
#include "periph/gpio.h"
|
|
#include "periph_cpu.h"
|
|
#include "cpu.h"
|
|
#include "clic.h"
|
|
#include "pm_layered.h"
|
|
|
|
#define RXENABLE (USART_CTL0_REN_Msk | USART_CTL0_RBNEIE_Msk)
|
|
|
|
/**
|
|
* @brief Allocate memory to store the callback functions
|
|
*
|
|
* Extend standard uart_isr_ctx_t with data_mask field. This is needed
|
|
* in order to mask parity bit.
|
|
*/
|
|
static struct {
|
|
uart_rx_cb_t rx_cb; /**< data received interrupt callback */
|
|
void *arg; /**< argument to both callback routines */
|
|
uint8_t data_mask; /**< mask applied to the data register */
|
|
} isr_ctx[UART_NUMOF];
|
|
|
|
static inline void _uart_isr(unsigned uart);
|
|
|
|
static inline USART_Type *dev(uart_t uart)
|
|
{
|
|
return uart_config[uart].dev;
|
|
}
|
|
|
|
static inline void uart_init_pins(uart_t uart, uart_rx_cb_t rx_cb)
|
|
{
|
|
/* configure TX pin */
|
|
gpio_init_af(uart_config[uart].tx_pin, GPIO_AF_OUT_PP);
|
|
/* configure RX pin */
|
|
if (rx_cb) {
|
|
gpio_init(uart_config[uart].rx_pin, GPIO_IN_PU);
|
|
}
|
|
}
|
|
|
|
static inline void uart_enable_clock(uart_t uart)
|
|
{
|
|
if (isr_ctx[uart].rx_cb) {
|
|
pm_block(GD32V_PM_DEEPSLEEP);
|
|
}
|
|
periph_clk_en(uart_config[uart].bus, uart_config[uart].rcu_mask);
|
|
}
|
|
|
|
static inline void uart_disable_clock(uart_t uart)
|
|
{
|
|
periph_clk_dis(uart_config[uart].bus, uart_config[uart].rcu_mask);
|
|
if (isr_ctx[uart].rx_cb) {
|
|
pm_unblock(GD32V_PM_DEEPSLEEP);
|
|
}
|
|
}
|
|
|
|
static inline void uart_init_usart(uart_t uart, uint32_t baudrate)
|
|
{
|
|
uint16_t mantissa;
|
|
uint8_t fraction;
|
|
uint32_t clk;
|
|
|
|
/* calculate and apply baudrate */
|
|
clk = periph_apb_clk(uart_config[uart].bus) / baudrate;
|
|
mantissa = (uint16_t)(clk / 16);
|
|
fraction = (uint8_t)(clk - (mantissa * 16));
|
|
dev(uart)->BAUD = ((mantissa & 0x0fff) << 4) | (fraction & 0x0f);
|
|
}
|
|
|
|
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
|
|
{
|
|
assert(uart < UART_NUMOF);
|
|
|
|
/* save ISR context */
|
|
isr_ctx[uart].rx_cb = rx_cb;
|
|
isr_ctx[uart].arg = arg;
|
|
isr_ctx[uart].data_mask = 0xFF;
|
|
|
|
uart_enable_clock(uart);
|
|
|
|
/* reset UART configuration -> defaults to 8N1 mode */
|
|
dev(uart)->CTL0 = 0;
|
|
dev(uart)->CTL1 = 0;
|
|
dev(uart)->CTL2 = 0;
|
|
|
|
uart_init_usart(uart, baudrate);
|
|
|
|
/* Attach pins to enabled UART periph. Note: It is important that the UART
|
|
* interface is configured prior to attaching the pins, as otherwise the
|
|
* signal level flickers during initialization resulting in garbage being
|
|
* sent. */
|
|
uart_init_pins(uart, rx_cb);
|
|
|
|
/* enable RX interrupt if applicable */
|
|
if (rx_cb) {
|
|
clic_set_handler(uart_config[uart].irqn, _uart_isr);
|
|
clic_enable_interrupt(uart_config[uart].irqn, CPU_DEFAULT_IRQ_PRIO);
|
|
dev(uart)->CTL0 = (USART_CTL0_UEN_Msk | USART_CTL0_TEN_Msk | RXENABLE);
|
|
}
|
|
else {
|
|
dev(uart)->CTL0 = (USART_CTL0_UEN_Msk | USART_CTL0_TEN_Msk);
|
|
}
|
|
|
|
return UART_OK;
|
|
}
|
|
|
|
#ifdef MODULE_PERIPH_UART_MODECFG
|
|
int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity,
|
|
uart_stop_bits_t stop_bits)
|
|
{
|
|
assert(uart < UART_NUMOF);
|
|
|
|
isr_ctx[uart].data_mask = 0xFF;
|
|
|
|
if (parity) {
|
|
switch (data_bits) {
|
|
case UART_DATA_BITS_6:
|
|
data_bits = UART_DATA_BITS_7;
|
|
isr_ctx[uart].data_mask = 0x3F;
|
|
break;
|
|
case UART_DATA_BITS_7:
|
|
data_bits = UART_DATA_BITS_8;
|
|
isr_ctx[uart].data_mask = 0x7F;
|
|
break;
|
|
case UART_DATA_BITS_8:
|
|
#ifdef USART_CTL0_M0
|
|
data_bits = USART_CTL0_M0;
|
|
#else
|
|
data_bits = USART_CTL0_M;
|
|
#endif
|
|
break;
|
|
default:
|
|
return UART_NOMODE;
|
|
}
|
|
}
|
|
if ((data_bits & UART_INVALID_MODE) || (parity & UART_INVALID_MODE)) {
|
|
return UART_NOMODE;
|
|
}
|
|
|
|
#ifdef USART_CTL0_M1
|
|
if (!(dev(uart)->ISR & USART_ISR_TC)) {
|
|
return UART_INTERR;
|
|
}
|
|
dev(uart)->CTL0 &= ~(USART_CTL0_UEN_Msk | USART_CTL0_TEN_Msk);
|
|
#endif
|
|
|
|
dev(uart)->CTL1 &= ~USART_CTL1_STOP;
|
|
dev(uart)->CTL0 &= ~(USART_CTL0_PS | USART_CTL0_PCE | USART_CTL0_M);
|
|
|
|
dev(uart)->CTL1 |= stop_bits;
|
|
dev(uart)->CTL0 |= (USART_CTL0_UE | USART_CTL0_TE | data_bits | parity);
|
|
|
|
return UART_OK;
|
|
}
|
|
#endif /* MODULE_PERIPH_UART_MODECFG */
|
|
|
|
static inline void send_byte(uart_t uart, uint8_t byte)
|
|
{
|
|
while (!(dev(uart)->STAT & USART_STAT_TBE_Msk)) {}
|
|
dev(uart)->DATA = byte;
|
|
}
|
|
|
|
static inline void wait_for_tx_complete(uart_t uart)
|
|
{
|
|
while (!(dev(uart)->STAT & USART_STAT_TC_Msk)) {}
|
|
}
|
|
|
|
void uart_write(uart_t uart, const uint8_t *data, size_t len)
|
|
{
|
|
assert(uart < UART_NUMOF);
|
|
#if DEVELHELP
|
|
/* If tx is not enabled don't try to send */
|
|
if (!(dev(uart)->CTL0 & USART_CTL0_TEN_Msk)) {
|
|
return;
|
|
}
|
|
#endif
|
|
for (size_t i = 0; i < len; i++) {
|
|
send_byte(uart, data[i]);
|
|
}
|
|
/* make sure the function is synchronous by waiting for the transfer to
|
|
* finish */
|
|
wait_for_tx_complete(uart);
|
|
}
|
|
|
|
void uart_poweron(uart_t uart)
|
|
{
|
|
assert(uart < UART_NUMOF);
|
|
|
|
uart_enable_clock(uart);
|
|
|
|
dev(uart)->CTL0 |= (USART_CTL0_UEN_Msk);
|
|
}
|
|
|
|
void uart_poweroff(uart_t uart)
|
|
{
|
|
assert(uart < UART_NUMOF);
|
|
|
|
dev(uart)->CTL0 &= ~(USART_CTL0_UEN_Msk);
|
|
|
|
uart_disable_clock(uart);
|
|
}
|
|
|
|
static inline void _irq_handler(uart_t uart)
|
|
{
|
|
uint32_t status = dev(uart)->STAT;
|
|
|
|
if (status & USART_STAT_RBNE_Msk) {
|
|
isr_ctx[uart].rx_cb(isr_ctx[uart].arg,
|
|
(uint8_t)dev(uart)->DATA & isr_ctx[uart].data_mask);
|
|
}
|
|
if (status & USART_STAT_ORERR_Msk) {
|
|
dev(uart)->DATA;
|
|
}
|
|
}
|
|
|
|
static void _uart_isr(unsigned irq)
|
|
{
|
|
switch (irq) {
|
|
#ifdef UART_0_IRQN
|
|
case UART_0_IRQN:
|
|
_irq_handler(UART_DEV(0));
|
|
break;
|
|
#endif
|
|
#ifdef UART_1_IRQN
|
|
case UART_1_IRQN:
|
|
_irq_handler(UART_DEV(1));
|
|
break;
|
|
#endif
|
|
#ifdef UART_2_IRQN
|
|
case UART_2_IRQN:
|
|
_irq_handler(UART_DEV(2));
|
|
break;
|
|
#endif
|
|
#ifdef UART_3_IRQN
|
|
case UART_3_IRQN:
|
|
_irq_handler(UART_DEV(3));
|
|
break;
|
|
#endif
|
|
#ifdef UART_4_IRQN
|
|
case UART_4_IRQN:
|
|
_irq_handler(UART_DEV(4));
|
|
break;
|
|
#endif
|
|
#ifdef UART_5_IRQN
|
|
case UART_5_IRQN:
|
|
_irq_handler(UART_DEV(5));
|
|
break;
|
|
#endif
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|