diff --git a/boards/frdm-k22f/include/periph_conf.h b/boards/frdm-k22f/include/periph_conf.h index 28c53ae9b6cf2..d3a70cc57f91f 100644 --- a/boards/frdm-k22f/include/periph_conf.h +++ b/boards/frdm-k22f/include/periph_conf.h @@ -93,6 +93,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 1405ccf9257f8..727c451f8f639 100644 --- a/boards/frdm-k64f/include/periph_conf.h +++ b/boards/frdm-k64f/include/periph_conf.h @@ -89,7 +89,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 fecdf2930e31c..abba3f2bbd322 100644 --- a/boards/mulle/include/periph_conf.h +++ b/boards/mulle/include/periph_conf.h @@ -105,7 +105,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, @@ -117,7 +118,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 5dfd2d1a793e2..bc11b5c34adb6 100644 --- a/boards/pba-d-01-kw2x/include/periph_conf.h +++ b/boards/pba-d-01-kw2x/include/periph_conf.h @@ -91,7 +91,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, @@ -103,7 +104,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 428cfc8c0ef79..1c5ac9b7399e6 100644 --- a/cpu/kinetis_common/include/periph_cpu.h +++ b/cpu/kinetis_common/include/periph_cpu.h @@ -307,11 +307,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 */ @@ -321,6 +329,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; /** 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 */