1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00
RIOT/cpu/kinetis_common/periph/uart.c
Joakim Nohlgård 00a0740fcc kinetis: Add support for LPUART module in parallel with UART module
A dispatcher function is implemented for directing writes to the correct
function. The dispatcher is bypassed completely if the CPU only contain
one kind of UART module.

There are at least two different UART hardware modules deployed in
different Kinetis CPU families (or possibly three or more when counting
variations of the UART module). The UART module is an older 8 bit module
with advanced functionality, while the LPUART is a 32 bit module with
focus on low power consumption.

 - The older families in the K series all have UART modules.
 - The K22F family have both UART and LPUART modules in the same CPU.
 - Older L series (e.g. KL25Z) have two variations of the UART module
 - Newer L series (e.g. KL43Z) have LPUART modules, and sometimes
   UART as well.
 - Newer W series (KW41Z) have only LPUART
2017-11-07 14:19:42 +01:00

401 lines
10 KiB
C

/*
* Copyright (C) 2017 Eistec AB
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
* Copyright (C) 2014 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_kinetis_common
* @ingroup drivers_periph_uart
*
* @{
*
* @file
* @brief Low-level UART driver implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Johann Fischer <j.fischer@phytec.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/
#include "cpu.h"
#include "bit.h"
#include "periph_conf.h"
#include "periph/uart.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#ifndef KINETIS_HAVE_LPUART
#ifdef LPUART0
#define KINETIS_HAVE_LPUART 1
#else
#define KINETIS_HAVE_LPUART 0
#endif
#endif /* KINETIS_HAVE_LPUART */
#ifndef KINETIS_HAVE_UART
#ifdef UART0
#define KINETIS_HAVE_UART 1
#else
#define KINETIS_HAVE_UART 0
#endif
#endif /* KINETIS_HAVE_LPUART */
#ifndef KINETIS_UART_ADVANCED
/**
* Attempts to determine the type of the UART,
* using the BRFA field in the UART C4 register.
*/
#ifdef UART_C4_BRFA
#define KINETIS_UART_ADVANCED 1
#endif
#endif
#ifndef LPUART_OVERSAMPLING_RATE
/* Use 16x oversampling by default (hardware defaults) */
#define LPUART_OVERSAMPLING_RATE (16)
#endif
/**
* @brief Runtime configuration space, holds pointers to callback functions for RX
*/
static uart_isr_ctx_t config[UART_NUMOF];
static inline void uart_init_pins(uart_t uart);
#if KINETIS_HAVE_UART
static inline void uart_init_uart(uart_t uart, uint32_t baudrate);
#endif
#if KINETIS_HAVE_LPUART
static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate);
#endif
/* Only use the dispatch function for uart_write if both UART and LPUART are
* available at the same time */
#if KINETIS_HAVE_UART && KINETIS_HAVE_LPUART
#define KINETIS_UART_WRITE_INLINE static inline
KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, size_t len);
KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *data, size_t len);
#else
#define KINETIS_UART_WRITE_INLINE
#if KINETIS_HAVE_UART
#define uart_write_uart uart_write
#elif KINETIS_HAVE_LPUART
#define uart_write_lpuart uart_write
#endif
#endif
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
{
assert(uart < UART_NUMOF);
/* remember callback addresses */
config[uart].rx_cb = rx_cb;
config[uart].arg = arg;
uart_init_pins(uart);
/* Turn on module clock gate */
bit_set32(uart_config[uart].scgc_addr, uart_config[uart].scgc_bit);
switch (uart_config[uart].type) {
#if KINETIS_HAVE_UART
case KINETIS_UART:
uart_init_uart(uart, baudrate);
break;
#endif
#if KINETIS_HAVE_LPUART
case KINETIS_LPUART:
uart_init_lpuart(uart, baudrate);
break;
#endif
default:
return UART_NODEV;
}
return UART_OK;
}
#if KINETIS_HAVE_UART && KINETIS_HAVE_LPUART
/* Dispatch function to pass to the proper write function depending on UART type
* This function is only used when the CPU supports both UART and LPUART. */
void uart_write(uart_t uart, const uint8_t *data, size_t len)
{
switch (uart_config[uart].type) {
case KINETIS_UART:
uart_write_uart(uart, data, len);
break;
case KINETIS_LPUART:
uart_write_lpuart(uart, data, len);
break;
default:
return;
}
}
#endif
static inline void uart_init_pins(uart_t uart)
{
/* initialize pins */
if (uart_config[uart].pin_rx != GPIO_UNDEF) {
gpio_init_port(uart_config[uart].pin_rx, uart_config[uart].pcr_rx);
}
if (uart_config[uart].pin_tx != GPIO_UNDEF) {
gpio_init_port(uart_config[uart].pin_tx, uart_config[uart].pcr_tx);
}
}
#if KINETIS_HAVE_UART
static inline void uart_init_uart(uart_t uart, uint32_t baudrate)
{
/* do basic initialization */
UART_Type *dev = uart_config[uart].dev;
uint32_t clk;
uint16_t ubd;
clk = uart_config[uart].freq;
/* disable transmitter and receiver */
dev->C2 &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK);
/* Select mode */
dev->C1 = uart_config[uart].mode;
/* calculate baudrate */
ubd = (uint16_t)(clk / (baudrate * 16));
/* set baudrate */
dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8);
dev->BDL = (uint8_t)UART_BDL_SBR(ubd);
#if KINETIS_UART_ADVANCED
/* set baudrate fine adjust (brfa) */
uint8_t brfa = ((((4 * clk) / baudrate) + 1) / 2) % 32;
dev->C4 = UART_C4_BRFA(brfa);
/* Enable FIFO buffers */
dev->PFIFO |= UART_PFIFO_RXFE_MASK | UART_PFIFO_TXFE_MASK;
/* Set level to trigger TDRE flag whenever there is space in the TXFIFO */
/* FIFO size is 2^(PFIFO_TXFIFOSIZE + 1) (4, 8, 16 ...) for values != 0.
* TXFIFOSIZE == 0 means size = 1 (i.e. only one byte, no hardware FIFO) */
if ((dev->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) != 0) {
uint8_t txfifo_size =
(2 << ((dev->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) >>
UART_PFIFO_TXFIFOSIZE_SHIFT));
dev->TWFIFO = UART_TWFIFO_TXWATER(txfifo_size - 1);
}
else {
/* Missing hardware support */
dev->TWFIFO = 0;
}
/* Trigger RX interrupt when there is 1 byte or more in the RXFIFO */
dev->RWFIFO = 1;
/* Clear all hardware buffers now, this must be done whenever the FIFO
* enable flags are modified. */
dev->CFIFO = UART_CFIFO_RXFLUSH_MASK | UART_CFIFO_TXFLUSH_MASK;
#endif /* KINETIS_UART_ADVANCED */
/* enable transmitter and receiver + RX interrupt */
dev->C2 |= UART_C2_TE_MASK | UART_C2_RE_MASK | UART_C2_RIE_MASK;
/* enable receive interrupt */
NVIC_EnableIRQ(uart_config[uart].irqn);
}
#endif /* KINETIS_HAVE_UART */
#if KINETIS_HAVE_UART
KINETIS_UART_WRITE_INLINE void uart_write_uart(uart_t uart, const uint8_t *data, size_t len)
{
UART_Type *dev = uart_config[uart].dev;
for (size_t i = 0; i < len; i++) {
while (!(dev->S1 & UART_S1_TDRE_MASK)) {}
dev->D = data[i];
}
}
static inline void irq_handler_uart(uart_t uart)
{
UART_Type *dev = uart_config[uart].dev;
/*
* On Cortex-M0, it happens that S1 is read with LDR
* instruction instead of LDRB. This will read the data register
* at the same time and arrived byte will be lost. Maybe it's a GCC bug.
*
* Observed with: arm-none-eabi-gcc (4.8.3-8+..)
* It does not happen with: arm-none-eabi-gcc (4.8.3-9+11)
*/
if (dev->S1 & UART_S1_RDRF_MASK) {
/* RDRF flag will be cleared when dev-D was read */
uint8_t data = dev->D;
if (config[uart].rx_cb != NULL) {
config[uart].rx_cb(config[uart].arg, data);
}
}
#if (KINETIS_UART_ADVANCED == 0)
/* clear overrun flag */
if (dev->S1 & UART_S1_OR_MASK) {
dev->S1 = UART_S1_OR_MASK;
}
#endif
cortexm_isr_end();
}
#ifdef UART_0_ISR
void UART_0_ISR(void)
{
irq_handler_uart(UART_DEV(0));
}
#endif
#ifdef UART_1_ISR
void UART_1_ISR(void)
{
irq_handler_uart(UART_DEV(1));
}
#endif
#ifdef UART_2_ISR
void UART_2_ISR(void)
{
irq_handler_uart(UART_DEV(2));
}
#endif
#ifdef UART_3_ISR
void UART_3_ISR(void)
{
irq_handler_uart(UART_DEV(3));
}
#endif
#ifdef UART_4_ISR
void UART_4_ISR(void)
{
irq_handler_uart(UART_DEV(4));
}
#endif
#endif /* KINETIS_HAVE_UART */
#if KINETIS_HAVE_LPUART
static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate)
{
LPUART_Type *dev = uart_config[uart].dev;
uint32_t clk = uart_config[uart].freq;
/* Remember to select a module clock in board_init! (SIM->SOPT2[LPUART0SRC]) */
/* Select mode */
/* transmitter and receiver disabled */
dev->CTRL = uart_config[uart].mode;
/* calculate baud rate divisor */
uint32_t div = clk / (baudrate * LPUART_OVERSAMPLING_RATE);
/* set baud rate */
dev->BAUD = LPUART_BAUD_OSR(LPUART_OVERSAMPLING_RATE - 1) | LPUART_BAUD_SBR(div);
/* enable transmitter and receiver + RX interrupt */
dev->CTRL |= LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK | LPUART_CTRL_RIE_MASK;
/* enable receive interrupt */
NVIC_EnableIRQ(uart_config[uart].irqn);
}
KINETIS_UART_WRITE_INLINE void uart_write_lpuart(uart_t uart, const uint8_t *data, size_t len)
{
LPUART_Type *dev = uart_config[uart].dev;
for (size_t i = 0; i < len; i++) {
while ((dev->STAT & LPUART_STAT_TDRE_MASK) == 0) {}
dev->DATA = data[i];
}
}
static inline void irq_handler_lpuart(uart_t uart)
{
LPUART_Type *dev = uart_config[uart].dev;
uint32_t stat = dev->STAT;
/* Clear all IRQ flags */
dev->STAT = stat;
if (stat & LPUART_STAT_RDRF_MASK) {
/* RDRF flag will be cleared when LPUART_DATA is read */
uint8_t data = dev->DATA;
if (stat & (LPUART_STAT_FE_MASK | LPUART_STAT_PF_MASK)) {
if (stat & LPUART_STAT_FE_MASK) {
DEBUG("LPUART framing error %08" PRIx32 "\n", stat);
}
if (stat & LPUART_STAT_PF_MASK) {
DEBUG("LPUART parity error %08" PRIx32 "\n", stat);
}
/* FE is set whenever the next character to be read from LPUART_DATA
* was received with logic 0 detected where a stop bit was expected. */
/* PF is set whenever the next character to be read from LPUART_DATA
* was received when parity is enabled (PE = 1) and the parity bit in
* the received character does not agree with the expected parity value. */
}
/* Only run callback if no error occurred */
else if (config[uart].rx_cb != NULL) {
config[uart].rx_cb(config[uart].arg, data);
}
}
if (stat & LPUART_STAT_OR_MASK) {
/* Input buffer overflow, means that the software was too slow to
* receive the data */
DEBUG("LPUART overrun %08" PRIx32 "\n", stat);
}
cortexm_isr_end();
}
#ifdef LPUART_0_ISR
void LPUART_0_ISR(void)
{
irq_handler_lpuart(UART_DEV(0));
}
#endif
#ifdef LPUART_1_ISR
void LPUART_1_ISR(void)
{
irq_handler_lpuart(UART_DEV(1));
}
#endif
#ifdef LPUART_2_ISR
void LPUART_2_ISR(void)
{
irq_handler_lpuart(UART_DEV(2));
}
#endif
#ifdef LPUART_3_ISR
void LPUART_3_ISR(void)
{
irq_handler_lpuart(UART_DEV(3));
}
#endif
#ifdef LPUART_4_ISR
void LPUART_4_ISR(void)
{
irq_handler_lpuart(UART_DEV(4));
}
#endif
#endif /* KINETIS_HAVE_LPUART */