Toon Stegen 3f74a8a7b4 cpu/stm32_common/uart: init rts at right time
once hardware flow control is enabled, rts should only be initialized
after the uart is enabled by setting the UE flag. This is stated in the
stm32f4 errata.
2019-08-27 19:15:47 +02:00

473 lines
12 KiB
C

/*
* Copyright (C) 2014-2017 Freie Universität Berlin
* Copyright (C) 2016 OTA keys
* Copyright (C) 2018 Inria
*
* 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_stm32_common
* @ingroup drivers_periph_uart
* @{
*
* @file
* @brief Low-level UART driver implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Hermann Lelong <hermann@otakeys.com>
* @author Toon Stegen <toon.stegen@altran.com>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#include "cpu.h"
#include "sched.h"
#include "thread.h"
#include "assert.h"
#include "periph/uart.h"
#include "periph/gpio.h"
#include "pm_layered.h"
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0) || \
defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L4) || \
defined(CPU_FAM_STM32F7)
#define ISR_REG ISR
#define ISR_TXE USART_ISR_TXE
#define ISR_TC USART_ISR_TC
#define TDR_REG TDR
#else
#define ISR_REG SR
#define ISR_TXE USART_SR_TXE
#define ISR_TC USART_SR_TC
#define TDR_REG DR
#endif
#define RXENABLE (USART_CR1_RE | USART_CR1_RXNEIE)
/**
* @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 USART_TypeDef *dev(uart_t uart)
{
return uart_config[uart].dev;
}
static inline void uart_init_usart(uart_t uart, uint32_t baudrate);
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L4)
#ifdef MODULE_PERIPH_LPUART
static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate);
#endif
#endif
#ifdef MODULE_STM32_PERIPH_UART_HW_FC
static inline void uart_init_rts_pin(uart_t uart)
{
if (uart_config[uart].cts_pin != GPIO_UNDEF) {
gpio_init(uart_config[uart].rts_pin, GPIO_OUT);
#ifdef CPU_FAM_STM32F1
gpio_init_af(uart_config[uart].rts_pin, GPIO_AF_OUT_PP);
#else
gpio_init_af(uart_config[uart].rts_pin, uart_config[uart].rts_af);
#endif
}
}
static inline void uart_init_cts_pin(uart_t uart)
{
if (uart_config[uart].cts_pin != GPIO_UNDEF) {
gpio_init(uart_config[uart].cts_pin, GPIO_IN);
#ifndef CPU_FAM_STM32F1
gpio_init_af(uart_config[uart].cts_pin, uart_config[uart].cts_af);
#endif
}
}
#endif
static inline void uart_init_pins(uart_t uart, uart_rx_cb_t rx_cb)
{
/* configure TX pin */
gpio_init(uart_config[uart].tx_pin, GPIO_OUT);
/* set TX pin high to avoid garbage during further initialization */
gpio_set(uart_config[uart].tx_pin);
#ifdef CPU_FAM_STM32F1
gpio_init_af(uart_config[uart].tx_pin, GPIO_AF_OUT_PP);
#else
gpio_init_af(uart_config[uart].tx_pin, uart_config[uart].tx_af);
#endif
/* configure RX pin */
if (rx_cb) {
gpio_init(uart_config[uart].rx_pin, GPIO_IN);
#ifndef CPU_FAM_STM32F1
gpio_init_af(uart_config[uart].rx_pin, uart_config[uart].rx_af);
#endif
}
#ifdef MODULE_STM32_PERIPH_UART_HW_FC
uart_init_cts_pin(uart);
uart_init_rts_pin(uart);
#endif
}
static inline void uart_enable_clock(uart_t uart)
{
#ifdef STM32_PM_STOP
if (isr_ctx[uart].rx_cb) {
pm_block(STM32_PM_STOP);
}
#endif
periph_clk_en(uart_config[uart].bus, uart_config[uart].rcc_mask);
}
static inline void uart_disable_clock(uart_t uart)
{
periph_clk_dis(uart_config[uart].bus, uart_config[uart].rcc_mask);
#ifdef STM32_PM_STOP
if (isr_ctx[uart].rx_cb) {
pm_unblock(STM32_PM_STOP);
}
#endif
}
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_init_pins(uart, rx_cb);
uart_enable_clock(uart);
/* reset UART configuration -> defaults to 8N1 mode */
dev(uart)->CR1 = 0;
dev(uart)->CR2 = 0;
dev(uart)->CR3 = 0;
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L4)
switch (uart_config[uart].type) {
case STM32_USART:
uart_init_usart(uart, baudrate);
break;
#ifdef MODULE_PERIPH_LPUART
case STM32_LPUART:
uart_init_lpuart(uart, baudrate);
break;
#endif
default:
return UART_NODEV;
}
#else
uart_init_usart(uart, baudrate);
#endif
/* enable RX interrupt if applicable */
if (rx_cb) {
NVIC_EnableIRQ(uart_config[uart].irqn);
dev(uart)->CR1 = (USART_CR1_UE | USART_CR1_TE | RXENABLE);
}
else {
dev(uart)->CR1 = (USART_CR1_UE | USART_CR1_TE);
}
#ifdef MODULE_STM32_PERIPH_UART_HW_FC
if (uart_config[uart].cts_pin != GPIO_UNDEF) {
/* configure hardware flow control */
dev(uart)->CR3 = (USART_CR3_RTSE | USART_CR3_CTSE);
}
#endif
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_CR1_M0
data_bits = USART_CR1_M0;
#else
data_bits = USART_CR1_M;
#endif
break;
default:
return UART_NOMODE;
}
}
if ((data_bits & UART_INVALID_MODE) || (parity & UART_INVALID_MODE)) {
return UART_NOMODE;
}
#ifdef USART_CR1_M1
if (!(dev(uart)->ISR & USART_ISR_TC)) {
return UART_INTERR;
}
dev(uart)->CR1 &= ~(USART_CR1_UE | USART_CR1_TE);
#endif
dev(uart)->CR2 &= ~USART_CR2_STOP;
dev(uart)->CR1 &= ~(USART_CR1_PS | USART_CR1_PCE | USART_CR1_M);
dev(uart)->CR2 |= stop_bits;
dev(uart)->CR1 |= (USART_CR1_UE | USART_CR1_TE | data_bits | parity);
return UART_OK;
}
#endif /* MODULE_PERIPH_UART_MODECFG */
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)->BRR = ((mantissa & 0x0fff) << 4) | (fraction & 0x0f);
}
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L4)
#ifdef MODULE_PERIPH_LPUART
static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate)
{
uint32_t clk;
switch (uart_config[uart].clk_src) {
case 0:
clk = periph_apb_clk(uart_config[uart].bus);
break;
case RCC_CCIPR_LPUART1SEL_0:
clk = CLOCK_CORECLOCK;
break;
case (RCC_CCIPR_LPUART1SEL_0 | RCC_CCIPR_LPUART1SEL_1):
clk = 32768;
break;
default: /* HSI is not supported */
return;
}
RCC->CCIPR |= uart_config[uart].clk_src;
/* LSE can only be used with baudrate <= 9600 */
if ( (clk < (3 * baudrate)) || (clk > (4096 * baudrate))) {
return;
}
/* LPUARTDIV = f_clk * 256 / baudrate */
uint32_t brr = (uint32_t)(((uint64_t)clk << 8) / baudrate);
dev(uart)->BRR = brr;
}
#endif /* MODULE_PERIPH_LPUART */
#endif /* STM32L0 || STM32L4 */
static inline void send_byte(uart_t uart, uint8_t byte)
{
while (!(dev(uart)->ISR_REG & ISR_TXE)) {}
dev(uart)->TDR_REG = byte;
}
static inline void wait_for_tx_complete(uart_t uart)
{
while (!(dev(uart)->ISR_REG & ISR_TC)) {}
}
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)->CR1 & USART_CR1_TE)) {
return;
}
#endif
#ifdef MODULE_PERIPH_DMA
if (!len) {
return;
}
if (uart_config[uart].dma != DMA_STREAM_UNDEF) {
if (irq_is_in()) {
uint16_t todo = 0;
if (dev(uart)->CR3 & USART_CR3_DMAT) {
/* DMA transfer for UART on-going */
todo = dma_suspend(uart_config[uart].dma);
}
if (todo) {
dma_stop(uart_config[uart].dma);
dev(uart)->CR3 &= ~USART_CR3_DMAT;
}
for (unsigned i = 0; i < len; i++) {
send_byte(uart, data[i]);
}
if (todo > 0) {
wait_for_tx_complete(uart);
dev(uart)->CR3 |= USART_CR3_DMAT;
dma_resume(uart_config[uart].dma, todo);
}
}
else {
dma_acquire(uart_config[uart].dma);
dev(uart)->CR3 |= USART_CR3_DMAT;
dma_transfer(uart_config[uart].dma, uart_config[uart].dma_chan, data,
(void *)&dev(uart)->TDR_REG, len, DMA_MEM_TO_PERIPH, DMA_INC_SRC_ADDR);
/* make sure the function is synchronous by waiting for the transfer to
* finish */
wait_for_tx_complete(uart);
dev(uart)->CR3 &= ~USART_CR3_DMAT;
dma_release(uart_config[uart].dma);
}
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)->CR1 |= (USART_CR1_UE);
#ifdef MODULE_STM32_PERIPH_UART_HW_FC
/* STM32F4 errata 2.10.9: nRTS is active while RE or UE = 0
* we should only configure nRTS pin after setting UE */
uart_init_rts_pin(uart);
#endif
}
void uart_poweroff(uart_t uart)
{
assert(uart < UART_NUMOF);
#ifdef MODULE_STM32_PERIPH_UART_HW_FC
/* the uart peripheral does not put RTS high from hardware when
* UE flag is cleared, so we need to do this manually */
if (uart_config[uart].cts_pin != GPIO_UNDEF) {
gpio_init(uart_config[uart].rts_pin, GPIO_OUT);
gpio_set(uart_config[uart].rts_pin);
}
#endif
dev(uart)->CR1 &= ~(USART_CR1_UE);
uart_disable_clock(uart);
}
static inline void irq_handler(uart_t uart)
{
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0) || \
defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L4) || \
defined(CPU_FAM_STM32F7)
uint32_t status = dev(uart)->ISR;
if (status & USART_ISR_RXNE) {
isr_ctx[uart].rx_cb(isr_ctx[uart].arg,
(uint8_t)dev(uart)->RDR & isr_ctx[uart].data_mask);
}
if (status & USART_ISR_ORE) {
dev(uart)->ICR |= USART_ICR_ORECF; /* simply clear flag on overrun */
}
#else
uint32_t status = dev(uart)->SR;
if (status & USART_SR_RXNE) {
isr_ctx[uart].rx_cb(isr_ctx[uart].arg,
(uint8_t)dev(uart)->DR & isr_ctx[uart].data_mask);
}
if (status & USART_SR_ORE) {
/* ORE is cleared by reading SR and DR sequentially */
dev(uart)->DR;
}
#endif
cortexm_isr_end();
}
#ifdef UART_0_ISR
void UART_0_ISR(void)
{
irq_handler(UART_DEV(0));
}
#endif
#ifdef UART_1_ISR
void UART_1_ISR(void)
{
irq_handler(UART_DEV(1));
}
#endif
#ifdef UART_2_ISR
void UART_2_ISR(void)
{
irq_handler(UART_DEV(2));
}
#endif
#ifdef UART_3_ISR
void UART_3_ISR(void)
{
irq_handler(UART_DEV(3));
}
#endif
#ifdef UART_4_ISR
void UART_4_ISR(void)
{
irq_handler(UART_DEV(4));
}
#endif
#ifdef UART_5_ISR
void UART_5_ISR(void)
{
irq_handler(UART_DEV(5));
}
#endif