From e2affdccc368f596ef6b3c29d4060ea72808ca09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Mon, 1 May 2017 15:09:33 +0200 Subject: [PATCH] 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 --- boards/frdm-k22f/include/periph_conf.h | 1 + boards/frdm-k64f/include/periph_conf.h | 3 +- boards/mulle/include/periph_conf.h | 6 +- boards/pba-d-01-kw2x/include/periph_conf.h | 6 +- cpu/kinetis_common/include/periph_cpu.h | 11 +- cpu/kinetis_common/periph/uart.c | 243 +++++++++++++++++---- 6 files changed, 222 insertions(+), 48 deletions(-) diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index 35c91c351d080..512b9b0e9695b 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -106,6 +106,7 @@ static const uart_conf_t uart_config[] = { .scgc_addr = &SIM_SCGC4, .scgc_bit = SIM_SCGC4_UART1_SHIFT, .mode = UART_MODE_8N1, + .type = KINETIS_UART, }, }; diff --git a/boards/frdm-k64f/include/periph_conf.h b/boards/frdm-k64f/include/periph_conf.h index 34bbfbc244d4e..a30dd38f2519d 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -102,7 +102,8 @@ static const uart_conf_t uart_config[] = { .irqn = UART0_RX_TX_IRQn, .scgc_addr = &SIM->SCGC4, .scgc_bit = SIM_SCGC4_UART0_SHIFT, - .mode = UART_MODE_8N1 + .mode = UART_MODE_8N1, + .type = KINETIS_UART, }, }; diff --git a/boards/mulle/include/periph_conf.h b/boards/mulle/include/periph_conf.h index 5ddcb7fdb266a..2c89d1268c87b 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -117,7 +117,8 @@ static const uart_conf_t uart_config[] = { .irqn = UART0_RX_TX_IRQn, .scgc_addr = &SIM->SCGC4, .scgc_bit = SIM_SCGC4_UART0_SHIFT, - .mode = UART_MODE_8N1 + .mode = UART_MODE_8N1, + .type = KINETIS_UART, }, { .dev = UART1, @@ -129,7 +130,8 @@ static const uart_conf_t uart_config[] = { .irqn = UART1_RX_TX_IRQn, .scgc_addr = &SIM->SCGC4, .scgc_bit = SIM_SCGC4_UART1_SHIFT, - .mode = UART_MODE_8N1 + .mode = UART_MODE_8N1, + .type = KINETIS_UART, }, }; diff --git a/boards/pba-d-01-kw2x/include/periph_conf.h b/boards/pba-d-01-kw2x/include/periph_conf.h index e53671ee5f57e..c3441713cf040 100644 --- a/boards/pba-d-01-kw2x/include/periph_conf.h +++ b/boards/pba-d-01-kw2x/include/periph_conf.h @@ -103,7 +103,8 @@ static const uart_conf_t uart_config[] = { .irqn = UART2_RX_TX_IRQn, .scgc_addr = &SIM->SCGC4, .scgc_bit = SIM_SCGC4_UART2_SHIFT, - .mode = UART_MODE_8N1 + .mode = UART_MODE_8N1, + .type = KINETIS_UART, }, { .dev = UART0, @@ -115,7 +116,8 @@ static const uart_conf_t uart_config[] = { .irqn = UART0_RX_TX_IRQn, .scgc_addr = &SIM->SCGC4, .scgc_bit = SIM_SCGC4_UART0_SHIFT, - .mode = UART_MODE_8N1 + .mode = UART_MODE_8N1, + .type = KINETIS_UART, } }; diff --git a/cpu/kinetis_common/include/periph_cpu.h b/cpu/kinetis_common/include/periph_cpu.h index dceffbabeff64..69b7b65ce376d 100644 --- a/cpu/kinetis_common/include/periph_cpu.h +++ b/cpu/kinetis_common/include/periph_cpu.h @@ -313,11 +313,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 */ @@ -327,6 +335,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; #if !defined(KINETIS_HAVE_PLL) diff --git a/cpu/kinetis_common/periph/uart.c b/cpu/kinetis_common/periph/uart.c index 820d33f643af2..161e9de3bfa5c 100644 --- a/cpu/kinetis_common/periph/uart.c +++ b/cpu/kinetis_common/periph/uart.c @@ -24,13 +24,27 @@ * @} */ -#include - #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, @@ -41,51 +55,90 @@ #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); @@ -93,12 +146,22 @@ static int init_base(uart_t uart, uint32_t baudrate) 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; @@ -108,9 +171,12 @@ 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 */ @@ -118,8 +184,8 @@ static int init_base(uart_t uart, uint32_t baudrate) * 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 { @@ -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; @@ -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; @@ -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 */