Merge pull request #7362 from gebart/pr/kinetis-lpuart

kinetis: Add support for LPUART module in parallel with UART module
This commit is contained in:
Martine Lenders 2017-11-08 01:06:58 +01:00 committed by GitHub
commit 5ce13b3562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 271 additions and 60 deletions

View File

@ -106,6 +106,7 @@ static const uart_conf_t uart_config[] = {
.scgc_addr = &SIM->SCGC4, .scgc_addr = &SIM->SCGC4,
.scgc_bit = SIM_SCGC4_UART1_SHIFT, .scgc_bit = SIM_SCGC4_UART1_SHIFT,
.mode = UART_MODE_8N1, .mode = UART_MODE_8N1,
.type = KINETIS_UART,
}, },
}; };

View File

@ -102,7 +102,8 @@ static const uart_conf_t uart_config[] = {
.irqn = UART0_RX_TX_IRQn, .irqn = UART0_RX_TX_IRQn,
.scgc_addr = &SIM->SCGC4, .scgc_addr = &SIM->SCGC4,
.scgc_bit = SIM_SCGC4_UART0_SHIFT, .scgc_bit = SIM_SCGC4_UART0_SHIFT,
.mode = UART_MODE_8N1 .mode = UART_MODE_8N1,
.type = KINETIS_UART,
}, },
}; };

View File

@ -117,7 +117,8 @@ static const uart_conf_t uart_config[] = {
.irqn = UART0_RX_TX_IRQn, .irqn = UART0_RX_TX_IRQn,
.scgc_addr = &SIM->SCGC4, .scgc_addr = &SIM->SCGC4,
.scgc_bit = SIM_SCGC4_UART0_SHIFT, .scgc_bit = SIM_SCGC4_UART0_SHIFT,
.mode = UART_MODE_8N1 .mode = UART_MODE_8N1,
.type = KINETIS_UART,
}, },
{ {
.dev = UART1, .dev = UART1,
@ -129,7 +130,8 @@ static const uart_conf_t uart_config[] = {
.irqn = UART1_RX_TX_IRQn, .irqn = UART1_RX_TX_IRQn,
.scgc_addr = &SIM->SCGC4, .scgc_addr = &SIM->SCGC4,
.scgc_bit = SIM_SCGC4_UART1_SHIFT, .scgc_bit = SIM_SCGC4_UART1_SHIFT,
.mode = UART_MODE_8N1 .mode = UART_MODE_8N1,
.type = KINETIS_UART,
}, },
}; };

View File

@ -103,7 +103,8 @@ static const uart_conf_t uart_config[] = {
.irqn = UART2_RX_TX_IRQn, .irqn = UART2_RX_TX_IRQn,
.scgc_addr = &SIM->SCGC4, .scgc_addr = &SIM->SCGC4,
.scgc_bit = SIM_SCGC4_UART2_SHIFT, .scgc_bit = SIM_SCGC4_UART2_SHIFT,
.mode = UART_MODE_8N1 .mode = UART_MODE_8N1,
.type = KINETIS_UART,
}, },
{ {
.dev = UART0, .dev = UART0,
@ -115,7 +116,8 @@ static const uart_conf_t uart_config[] = {
.irqn = UART0_RX_TX_IRQn, .irqn = UART0_RX_TX_IRQn,
.scgc_addr = &SIM->SCGC4, .scgc_addr = &SIM->SCGC4,
.scgc_bit = SIM_SCGC4_UART0_SHIFT, .scgc_bit = SIM_SCGC4_UART0_SHIFT,
.mode = UART_MODE_8N1 .mode = UART_MODE_8N1,
.type = KINETIS_UART,
} }
}; };

View File

@ -199,16 +199,28 @@ typedef enum {
#endif /* ndef DOXYGEN */ #endif /* ndef DOXYGEN */
/** /**
* @name CPU specific UART modes values * @brief UART transmission modes
* @{
*/ */
/** @brief 8 data bits, no parity, 1 stop bit */ typedef enum {
#define UART_MODE_8N1 (0) /** @brief 8 data bits, no parity, 1 stop bit */
/** @brief 8 data bits, even parity, 1 stop bit */ UART_MODE_8N1 = 0,
#define UART_MODE_8E1 (UART_C1_PE_MASK | UART_C1_M_MASK) /** @brief 8 data bits, even parity, 1 stop bit */
/** @brief 8 data bits, odd parity, 1 stop bit */ #if defined(UART_C1_M_MASK)
#define UART_MODE_8O1 (UART_C1_PE_MASK | UART_C1_M_MASK | UART_C1_PT_MASK) /* LPUART and UART mode bits coincide, so the same setting for UART works on
/** @} */ * the LPUART as well */
UART_MODE_8E1 = (UART_C1_M_MASK | UART_C1_PE_MASK),
#elif defined(LPUART_CTRL_M_MASK)
/* For CPUs which only have the LPUART */
UART_MODE_8E1 = (LPUART_CTRL_M_MASK | LPUART_CTRL_PE_MASK),
#endif
/** @brief 8 data bits, odd parity, 1 stop bit */
#if defined(UART_C1_M_MASK)
UART_MODE_8O1 = (UART_C1_M_MASK | UART_C1_PE_MASK | UART_C1_PT_MASK),
#elif defined(LPUART_CTRL_M_MASK)
/* For CPUs which only have the LPUART */
UART_MODE_8O1 = (LPUART_CTRL_M_MASK | LPUART_CTRL_PE_MASK | LPUART_CTRL_PT_MASK),
#endif
} uart_mode_t;
#ifndef DOXYGEN #ifndef DOXYGEN
/** /**
@ -309,11 +321,19 @@ enum {
#define TIMER_LPTMR_DEV(x) (TIMER_DEV(PIT_NUMOF + (x))) #define TIMER_LPTMR_DEV(x) (TIMER_DEV(PIT_NUMOF + (x)))
/** @} */ /** @} */
/**
* @brief UART hardware module types
*/
typedef enum {
KINETIS_UART, /**< Kinetis UART module type */
KINETIS_LPUART, /**< Kinetis Low-power UART (LPUART) module type */
} uart_type_t;
/** /**
* @brief UART module configuration options * @brief UART module configuration options
*/ */
typedef struct { 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 */ 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_rx; /**< RX pin, GPIO_UNDEF disables RX */
gpio_t pin_tx; /**< TX pin */ gpio_t pin_tx; /**< TX pin */
@ -322,7 +342,8 @@ typedef struct {
IRQn_Type irqn; /**< IRQ number for this module */ IRQn_Type irqn; /**< IRQ number for this module */
volatile uint32_t *scgc_addr; /**< Clock enable register, in SIM module */ volatile uint32_t *scgc_addr; /**< Clock enable register, in SIM module */
uint8_t scgc_bit; /**< Clock enable bit, within the register */ uint8_t scgc_bit; /**< Clock enable bit, within the register */
uint8_t mode; /**< UART mode: data bits, parity, stop bits */ uart_mode_t mode; /**< UART mode: data bits, parity, stop bits */
uart_type_t type; /**< Hardware module type (KINETIS_UART or KINETIS_LPUART)*/
} uart_conf_t; } uart_conf_t;
#if !defined(KINETIS_HAVE_PLL) #if !defined(KINETIS_HAVE_PLL)

View File

@ -24,13 +24,30 @@
* @} * @}
*/ */
#include <math.h>
#include "cpu.h" #include "cpu.h"
#include "bit.h" #include "bit.h"
#include "periph_conf.h" #include "periph_conf.h"
#include "periph/uart.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 #ifndef KINETIS_UART_ADVANCED
/** /**
* Attempts to determine the type of the UART, * Attempts to determine the type of the UART,
@ -41,51 +58,90 @@
#endif #endif
#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 uart_isr_ctx_t config[UART_NUMOF];
static inline void kinetis_set_brfa(UART_Type *dev, uint32_t baudrate, uint32_t clk) static inline void uart_init_pins(uart_t uart);
{
#if KINETIS_UART_ADVANCED
/* set baudrate fine adjust (brfa) */
uint8_t brfa = ((((4 * clk) / baudrate) + 1) / 2) % 32;
dev->C4 = UART_C4_BRFA(brfa);
#endif
}
static int init_base(uart_t uart, uint32_t baudrate); #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) int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
{ {
assert(uart < UART_NUMOF); 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 */ /* remember callback addresses */
config[uart].rx_cb = rx_cb; config[uart].rx_cb = rx_cb;
config[uart].arg = arg; config[uart].arg = arg;
/* enable receive interrupt */ uart_init_pins(uart);
NVIC_EnableIRQ(uart_config[uart].irqn);
dev->C2 |= (1 << UART_C2_RIE_SHIFT);
/* 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; 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; switch (uart_config[uart].type) {
uint32_t clk; case KINETIS_UART:
uint16_t ubd; uart_write_uart(uart, data, len);
break;
clk = uart_config[uart].freq; case KINETIS_LPUART:
uart_write_lpuart(uart, data, len);
break;
default:
return;
}
}
#endif
static inline void uart_init_pins(uart_t uart)
{
/* initialize pins */ /* initialize pins */
if (uart_config[uart].pin_rx != GPIO_UNDEF) { if (uart_config[uart].pin_rx != GPIO_UNDEF) {
gpio_init_port(uart_config[uart].pin_rx, uart_config[uart].pcr_rx); gpio_init_port(uart_config[uart].pin_rx, uart_config[uart].pcr_rx);
@ -93,13 +149,23 @@ static int init_base(uart_t uart, uint32_t baudrate)
if (uart_config[uart].pin_tx != GPIO_UNDEF) { if (uart_config[uart].pin_tx != GPIO_UNDEF) {
gpio_init_port(uart_config[uart].pin_tx, uart_config[uart].pcr_tx); gpio_init_port(uart_config[uart].pin_tx, uart_config[uart].pcr_tx);
} }
}
/* Turn on module clock gate */ #if KINETIS_HAVE_UART
bit_set32(uart_config[uart].scgc_addr, uart_config[uart].scgc_bit); 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 */ /* disable transmitter and receiver */
dev->C2 &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK); dev->C2 &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK);
/* set defaults, 8-bit mode, no parity */
/* Select mode */
dev->C1 = uart_config[uart].mode; dev->C1 = uart_config[uart].mode;
/* calculate baudrate */ /* calculate baudrate */
@ -108,9 +174,12 @@ static int init_base(uart_t uart, uint32_t baudrate)
/* set baudrate */ /* set baudrate */
dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8); dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8);
dev->BDL = (uint8_t)UART_BDL_SBR(ubd); dev->BDL = (uint8_t)UART_BDL_SBR(ubd);
kinetis_set_brfa(dev, baudrate, clk);
#if KINETIS_UART_ADVANCED #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 */ /* Enable FIFO buffers */
dev->PFIFO |= UART_PFIFO_RXFE_MASK | UART_PFIFO_TXFE_MASK; dev->PFIFO |= UART_PFIFO_RXFE_MASK | UART_PFIFO_TXFE_MASK;
/* Set level to trigger TDRE flag whenever there is space in the TXFIFO */ /* Set level to trigger TDRE flag whenever there is space in the TXFIFO */
@ -131,14 +200,18 @@ static int init_base(uart_t uart, uint32_t baudrate)
/* Clear all hardware buffers now, this must be done whenever the FIFO /* Clear all hardware buffers now, this must be done whenever the FIFO
* enable flags are modified. */ * enable flags are modified. */
dev->CFIFO = UART_CFIFO_RXFLUSH_MASK | UART_CFIFO_TXFLUSH_MASK; dev->CFIFO = UART_CFIFO_RXFLUSH_MASK | UART_CFIFO_TXFLUSH_MASK;
#endif #endif /* KINETIS_UART_ADVANCED */
/* enable transmitter and receiver */ /* enable transmitter and receiver + RX interrupt */
dev->C2 |= UART_C2_TE_MASK | UART_C2_RE_MASK; dev->C2 |= UART_C2_TE_MASK | UART_C2_RE_MASK | UART_C2_RIE_MASK;
return UART_OK;
/* 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; UART_Type *dev = uart_config[uart].dev;
@ -148,7 +221,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; UART_Type *dev = uart_config[uart].dev;
@ -183,34 +256,145 @@ static inline void irq_handler(uart_t uart)
#ifdef UART_0_ISR #ifdef UART_0_ISR
void UART_0_ISR(void) void UART_0_ISR(void)
{ {
irq_handler(UART_DEV(0)); irq_handler_uart(UART_DEV(0));
} }
#endif #endif
#ifdef UART_1_ISR #ifdef UART_1_ISR
void UART_1_ISR(void) void UART_1_ISR(void)
{ {
irq_handler(UART_DEV(1)); irq_handler_uart(UART_DEV(1));
} }
#endif #endif
#ifdef UART_2_ISR #ifdef UART_2_ISR
void UART_2_ISR(void) void UART_2_ISR(void)
{ {
irq_handler(UART_DEV(2)); irq_handler_uart(UART_DEV(2));
} }
#endif #endif
#ifdef UART_3_ISR #ifdef UART_3_ISR
void UART_3_ISR(void) void UART_3_ISR(void)
{ {
irq_handler(UART_DEV(3)); irq_handler_uart(UART_DEV(3));
} }
#endif #endif
#ifdef UART_4_ISR #ifdef UART_4_ISR
void UART_4_ISR(void) void UART_4_ISR(void)
{ {
irq_handler(UART_DEV(4)); irq_handler_uart(UART_DEV(4));
} }
#endif #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 */