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:
commit
5ce13b3562
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -199,16 +199,28 @@ typedef enum {
|
||||
#endif /* ndef DOXYGEN */
|
||||
|
||||
/**
|
||||
* @name CPU specific UART modes values
|
||||
* @{
|
||||
* @brief UART transmission modes
|
||||
*/
|
||||
/** @brief 8 data bits, no parity, 1 stop bit */
|
||||
#define UART_MODE_8N1 (0)
|
||||
/** @brief 8 data bits, even parity, 1 stop bit */
|
||||
#define UART_MODE_8E1 (UART_C1_PE_MASK | UART_C1_M_MASK)
|
||||
/** @brief 8 data bits, odd parity, 1 stop bit */
|
||||
#define UART_MODE_8O1 (UART_C1_PE_MASK | UART_C1_M_MASK | UART_C1_PT_MASK)
|
||||
/** @} */
|
||||
typedef enum {
|
||||
/** @brief 8 data bits, no parity, 1 stop bit */
|
||||
UART_MODE_8N1 = 0,
|
||||
/** @brief 8 data bits, even parity, 1 stop bit */
|
||||
#if defined(UART_C1_M_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
|
||||
/**
|
||||
@ -309,11 +321,19 @@ enum {
|
||||
#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
|
||||
*/
|
||||
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 */
|
||||
@ -322,7 +342,8 @@ typedef struct {
|
||||
IRQn_Type irqn; /**< IRQ number for this module */
|
||||
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 */
|
||||
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;
|
||||
|
||||
#if !defined(KINETIS_HAVE_PLL)
|
||||
|
||||
@ -24,13 +24,30 @@
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#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,
|
||||
@ -41,51 +58,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);
|
||||
#endif
|
||||
}
|
||||
static inline void uart_init_pins(uart_t uart);
|
||||
|
||||
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)
|
||||
{
|
||||
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,13 +149,23 @@ 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 */
|
||||
|
||||
/* Select mode */
|
||||
dev->C1 = uart_config[uart].mode;
|
||||
|
||||
/* calculate baudrate */
|
||||
@ -108,9 +174,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 */
|
||||
@ -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
|
||||
* 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 +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;
|
||||
|
||||
@ -183,34 +256,145 @@ 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]) */
|
||||
|
||||
/* 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 */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user