Skip to content

Commit

Permalink
kinetis: Add support for LPUART module in parallel with UART module
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Joakim Nohlgård committed Jul 14, 2017
1 parent 7927a2e commit a5988bc
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 43 deletions.
11 changes: 10 additions & 1 deletion cpu/kinetis_common/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,19 @@ enum {
#define TIMER_LPTMR_DEV(x) (TIMER_DEV(PIT_NUMOF + (x)))
/** @} */

/**
* @brief UART hardware module types
*/
enum {
KINETIS_UART, /**< Kinetis UART module type */
KINETIS_LPUART, /**< Kinetis Low-power UART (LPUART) module type */
};

/**
* @brief UART module configuration options
*/
typedef struct {
UART_Type *dev; /**< Pointer to module hardware registers */
void *dev; /**< Pointer to module hardware registers */
uint32_t freq; /**< Module clock frequency, usually CLOCK_CORECLOCK or CLOCK_BUSCLOCK */
gpio_t pin_rx; /**< RX pin, GPIO_UNDEF disables RX */
gpio_t pin_tx; /**< TX pin */
Expand All @@ -322,6 +330,7 @@ typedef struct {
volatile uint32_t *scgc_addr; /**< Clock enable register, in SIM module */
uint8_t scgc_bit; /**< Clock enable bit, within the register */
uint8_t mode; /**< UART mode: data bits, parity, stop bits */
uint8_t type; /**< Hardware module type (KINETIS_UART or KINETIS_LPUART)*/
} uart_conf_t;

/**
Expand Down
243 changes: 201 additions & 42 deletions cpu/kinetis_common/periph/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,27 @@
* @}
*/

#include <math.h>

#include "cpu.h"
#include "bit.h"
#include "periph_conf.h"
#include "periph/uart.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,
Expand All @@ -41,64 +55,113 @@
#endif
#endif

#ifndef LPUART_OVERSAMPLING_RATE
/* Use 16x oversampling by default (hardware defaults) */
#define LPUART_OVERSAMPLING_RATE (16)
#endif

/**
* @brief Allocate memory to store the callback functions.
* @brief Runtime configuration space, holds pointers to callback functions for RX
*/
static uart_isr_ctx_t config[UART_NUMOF];

static inline void kinetis_set_brfa(UART_Type *dev, uint32_t baudrate, uint32_t clk)
{
#if KINETIS_UART_ADVANCED
/* set baudrate fine adjust (brfa) */
uint8_t brfa = ((((4 * clk) / baudrate) + 1) / 2) % 32;
dev->C4 = UART_C4_BRFA(brfa);
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
}

static int init_base(uart_t uart, uint32_t baudrate);
/* 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);
/* do basic initialization */
int res = init_base(uart, baudrate);
if (res != UART_OK) {
return res;
}
UART_Type *dev = uart_config[uart].dev;

/* remember callback addresses */
config[uart].rx_cb = rx_cb;
config[uart].arg = arg;

/* enable receive interrupt */
NVIC_EnableIRQ(uart_config[uart].irqn);
dev->C2 |= (1 << UART_C2_RIE_SHIFT);
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;
}

static int init_base(uart_t uart, uint32_t baudrate)
#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)
{
UART_Type *dev = uart_config[uart].dev;
uint32_t clk;
uint16_t ubd;

clk = uart_config[uart].freq;
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);
}
}

/* Turn on module clock gate */
bit_set32(uart_config[uart].scgc_addr, uart_config[uart].scgc_bit);
#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);

/* set defaults, 8-bit mode, no parity */
dev->C1 = uart_config[uart].mode;

Expand All @@ -108,18 +171,21 @@ static int init_base(uart_t uart, uint32_t baudrate)
/* set baudrate */
dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8);
dev->BDL = (uint8_t)UART_BDL_SBR(ubd);
kinetis_set_brfa(dev, baudrate, clk);

#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));
(2 << ((dev->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) >>
UART_PFIFO_TXFIFOSIZE_SHIFT));
dev->TWFIFO = UART_TWFIFO_TXWATER(txfifo_size - 1);
}
else {
Expand All @@ -131,14 +197,18 @@ static int init_base(uart_t uart, uint32_t baudrate)
/* 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
#endif /* KINETIS_UART_ADVANCED */

/* enable transmitter and receiver */
dev->C2 |= UART_C2_TE_MASK | UART_C2_RE_MASK;
return UART_OK;
/* 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 */

void uart_write(uart_t uart, const uint8_t *data, size_t len)
#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;

Expand All @@ -148,7 +218,7 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len)
}
}

static inline void irq_handler(uart_t uart)
static inline void irq_handler_uart(uart_t uart)
{
UART_Type *dev = uart_config[uart].dev;

Expand Down Expand Up @@ -183,34 +253,123 @@ static inline void irq_handler(uart_t uart)
#ifdef UART_0_ISR
void UART_0_ISR(void)
{
irq_handler(UART_DEV(0));
irq_handler_uart(UART_DEV(0));
}
#endif

#ifdef UART_1_ISR
void UART_1_ISR(void)
{
irq_handler(UART_DEV(1));
irq_handler_uart(UART_DEV(1));
}
#endif

#ifdef UART_2_ISR
void UART_2_ISR(void)
{
irq_handler(UART_DEV(2));
irq_handler_uart(UART_DEV(2));
}
#endif

#ifdef UART_3_ISR
void UART_3_ISR(void)
{
irq_handler(UART_DEV(3));
irq_handler_uart(UART_DEV(3));
}
#endif

#ifdef UART_4_ISR
void UART_4_ISR(void)
{
irq_handler(UART_DEV(4));
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]) */

/* set defaults, 8-bit mode, no parity */
/* transmitter and receiver disabled */
dev->CTRL = 0;

/* 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;

if (dev->STAT & LPUART_STAT_RDRF_MASK) {
/* RDRF flag will be cleared when LPUART_DATA is read */
uint8_t data = dev->DATA;

if (config[uart].rx_cb != NULL) {
config[uart].rx_cb(config[uart].arg, data);
}
}

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 */

0 comments on commit a5988bc

Please sign in to comment.