mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/qn908x: Add support for UART.
The QN908x has four FLEXCOMM interfaces that support a subset of UART, SPI or I2C each one. This patch adds generic support for dealing with the FLEXCOMM initialization and interrupts and adds a driver for RX/TX support in UART. With this patch is now possible to use a shell on the device over UART.
This commit is contained in:
parent
0427e28f17
commit
bd929a3746
@ -17,5 +17,7 @@ config BOARD_QN9080DK
|
||||
# Put defined MCU peripherals here (in alphabetical order)
|
||||
select BOARD_HAS_XTAL32K
|
||||
select BOARD_HAS_XTAL_32M
|
||||
select HAS_PERIPH_UART
|
||||
select HAS_PERIPH_UART_MODECFG
|
||||
|
||||
source "$(RIOTBOARD)/common/qn908x/Kconfig"
|
||||
|
@ -3,6 +3,7 @@ CPU_MODEL = qn9080xhn
|
||||
|
||||
# Put defined MCU peripherals here (in alphabetical order)
|
||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||
FEATURES_PROVIDED += periph_uart periph_uart_modecfg
|
||||
|
||||
# Include the common qn908x board features.
|
||||
include $(RIOTBOARD)/common/qn908x/Makefile.features
|
||||
|
@ -28,10 +28,24 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name UART configuration
|
||||
* @{
|
||||
*/
|
||||
static const uart_conf_t uart_config[] = {
|
||||
{
|
||||
.dev = USART0,
|
||||
.rx_pin = GPIO_PIN(PORT_A, 17),
|
||||
.tx_pin = GPIO_PIN(PORT_A, 16),
|
||||
},
|
||||
};
|
||||
|
||||
#define UART_NUMOF ARRAY_SIZE(uart_config)
|
||||
/** @} */
|
||||
|
||||
/* put here the board peripherals definitions:
|
||||
- Available clocks
|
||||
- Timers
|
||||
- UARTs
|
||||
- PWMs
|
||||
- SPIs
|
||||
- I2C
|
||||
|
@ -10,9 +10,8 @@ USEMODULE += vendor_fsl_clock
|
||||
# All peripherals use gpio_mux.h
|
||||
USEMODULE += periph_gpio_mux
|
||||
|
||||
# This cpu modules doesn't support UART peripherals yet, so we need to include
|
||||
# stdio_null.
|
||||
# TODO: Remove stdio_null once periph_uart is implemented in this module.
|
||||
USEMODULE += stdio_null
|
||||
ifneq (,$(filter periph_uart,$(USEMODULE)))
|
||||
USEMODULE += periph_flexcomm
|
||||
endif
|
||||
|
||||
include $(RIOTCPU)/cortexm_common/Makefile.dep
|
||||
|
@ -31,6 +31,32 @@ The GPIO driver uses the @ref GPIO_PIN(port, pin) macro to declare pins.
|
||||
No configuration is necessary.
|
||||
|
||||
|
||||
@defgroup cpu_qn908x_uart NXP QN908x UART
|
||||
@ingroup cpu_qn908x
|
||||
@brief NXP QN908x UART driver
|
||||
|
||||
There are several FLEXCOMM interfaces in this chip, but only two of these
|
||||
support UART (FLEXCOMM0 and FLEXCOMM1). The default UART mode is 8n1 and can
|
||||
be changed with the uart_mode() function. If only RX or only TX is desired, the
|
||||
other pin can be set to GPIO_UNDEF.
|
||||
|
||||
### UART configuration example (for periph_conf.h) ###
|
||||
|
||||
static const uart_conf_t uart_config[] = {
|
||||
{
|
||||
.dev = USART0,
|
||||
.rx_pin = GPIO_PIN(PORT_A, 17), /* or 5 */
|
||||
.tx_pin = GPIO_PIN(PORT_A, 16), /* or 4 */
|
||||
},
|
||||
{
|
||||
.dev = USART1,
|
||||
.rx_pin = GPIO_PIN(PORT_A, 9), /* or 13 */
|
||||
.tx_pin = GPIO_PIN(PORT_A, 8), /* or 12 */
|
||||
},
|
||||
};
|
||||
#define UART_NUMOF ARRAY_SIZE(uart_config)
|
||||
|
||||
|
||||
@defgroup cpu_qn908x_wdt NXP QN908x Watchdog timer (WDT)
|
||||
@ingroup cpu_qn908x
|
||||
@brief NXP QN908x Watchdog timer (WDT)
|
||||
|
67
cpu/qn908x/include/flexcomm.h
Normal file
67
cpu/qn908x/include/flexcomm.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2020 iosabi
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_qn908x
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Flexcomm interface functions.
|
||||
*
|
||||
* The FLEXCOMM blocks can operate in different modes such as UART, SPI and I2C,
|
||||
* but not all modules support all modes. These functions allow to initialize
|
||||
* and configure the FLEXCOMM, as well as route back the ISRs to the
|
||||
* corresponding module.
|
||||
*
|
||||
* @author iosabi <iosabi@protonmail.com>
|
||||
*/
|
||||
|
||||
#ifndef FLEXCOMM_H
|
||||
#define FLEXCOMM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Flexcomm PSELID values
|
||||
*
|
||||
* This value identifies the current function of a FLEXCOMM module.
|
||||
*/
|
||||
typedef enum {
|
||||
FLEXCOMM_ID_UART = 1, /**< UART mode. */
|
||||
FLEXCOMM_ID_SPI = 2, /**< SPI mode. */
|
||||
FLEXCOMM_ID_I2C = 3, /**< I2C mode. */
|
||||
} flexcom_pselid_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a flexcomm module to operate as the selected mode.
|
||||
*
|
||||
* @returns -1 in case of error, otherwise returns the number of flexcomm
|
||||
* instance initialized, such as 2 for FLEXCOMM2.
|
||||
*/
|
||||
int flexcomm_init(FLEXCOMM_Type *dev, flexcom_pselid_t mode);
|
||||
|
||||
/**
|
||||
* @brief Obtain the flexcomm block number (0-based) from the address.
|
||||
*
|
||||
* For example, the flexcomm block number of FLEXCOMM2, the pointer to the
|
||||
* FLEXCOMM_Type block is 2. If an invalid address is passed returns -1.
|
||||
*/
|
||||
int flexcomm_instance_from_addr(FLEXCOMM_Type *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FLEXCOMM_H */
|
||||
/** @} */
|
@ -140,6 +140,61 @@ enum {
|
||||
GPIO_PORTS_NUMOF /**< overall number of available ports */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief UART module configuration options
|
||||
*
|
||||
* QN908x doesn't have any UART standalone blocks, but it has two FLEXCOMM
|
||||
* blocks that can be put in UART mode. The USART_Type* address is one of the
|
||||
* FLEXCOMM_Type* addresses as well.
|
||||
*/
|
||||
typedef struct {
|
||||
USART_Type *dev; /**< Pointer to module hardware registers */
|
||||
gpio_t rx_pin; /**< RX pin, GPIO_UNDEF disables RX. */
|
||||
gpio_t tx_pin; /**< TX pin, GPIO_UNDEF disables TX. */
|
||||
} uart_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Definition of possible parity modes
|
||||
*
|
||||
* These are defined to match the values of the USART->CFG : PARITYSEL bit
|
||||
* field.
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
UART_PARITY_NONE = 0, /**< no parity */
|
||||
UART_PARITY_EVEN = 2, /**< even parity */
|
||||
UART_PARITY_ODD = 3, /**< odd parity */
|
||||
} uart_parity_t;
|
||||
#define HAVE_UART_PARITY_T
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Definition of possible data bits lengths in a UART frame
|
||||
*
|
||||
* These are defined to match the values of the USART->CFG : DATALEN bit field.
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
UART_DATA_BITS_7 = 0, /**< 7 data bits */
|
||||
UART_DATA_BITS_8 = 1, /**< 8 data bits */
|
||||
/* Note: There's a UART_DATA_BITS_9 possible in this hardware. */
|
||||
} uart_data_bits_t;
|
||||
#define HAVE_UART_DATA_BITS_T
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Definition of possible stop bits lengths
|
||||
*
|
||||
* These are defined to match the values of the USART->CFG : STOPLEN bit field.
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
UART_STOP_BITS_1 = 0, /**< 1 stop bit */
|
||||
UART_STOP_BITS_2 = 1, /**< 2 stop bits */
|
||||
} uart_stop_bits_t;
|
||||
#define HAVE_UART_STOP_BITS_T
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
142
cpu/qn908x/periph/flexcomm.c
Normal file
142
cpu/qn908x/periph/flexcomm.c
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2020 iosabi
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_qn908x
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Flexcomm interrupt dispatch.
|
||||
*
|
||||
* @author iosabi <iosabi@protonmail.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "cpu.h"
|
||||
#include "periph_conf.h"
|
||||
#include "vectors_qn908x.h"
|
||||
#include "flexcomm.h"
|
||||
|
||||
#include "vendor/drivers/fsl_clock.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
int flexcomm_init(FLEXCOMM_Type *dev, flexcom_pselid_t mode)
|
||||
{
|
||||
static const clock_ip_name_t flexcomm_clocks[] = FLEXCOMM_CLOCKS;
|
||||
int flexcomm_num = flexcomm_instance_from_addr(dev);
|
||||
|
||||
if (flexcomm_num < 0 || flexcomm_num >= (int)ARRAY_SIZE(flexcomm_clocks)) {
|
||||
DEBUG("Invalid flexcomm_num: %d\n", flexcomm_num);
|
||||
return -1;
|
||||
}
|
||||
CLOCK_EnableClock(flexcomm_clocks[flexcomm_num]);
|
||||
/* Reset the flexcomm. */
|
||||
SYSCON->RST_SW_SET = 1u << flexcomm_num;
|
||||
SYSCON->RST_SW_CLR = 1u << flexcomm_num;
|
||||
|
||||
/* Check that the mode is present in the FLEXCOMM.
|
||||
* Bits 4, 5 and 6 tell whether the UART, SPI and I2C respectively are
|
||||
* present. */
|
||||
if ((dev->PSELID & (1u << (mode + 3))) == 0) {
|
||||
DEBUG("Mode %d not present in FLEXCOMM%d\n", (int)mode, flexcomm_num);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This also locks the peripheral so it can't be changed until device or
|
||||
* peripheral is reset. */
|
||||
dev->PSELID = (dev->PSELID & ~FLEXCOMM_PSELID_PERSEL_MASK) |
|
||||
FLEXCOMM_PSELID_LOCK_MASK |
|
||||
FLEXCOMM_PSELID_PERSEL(mode);
|
||||
return flexcomm_num;
|
||||
}
|
||||
|
||||
int flexcomm_instance_from_addr(FLEXCOMM_Type *dev)
|
||||
{
|
||||
static const FLEXCOMM_Type *flexcomm_addrs[] = FLEXCOMM_BASE_PTRS;
|
||||
|
||||
for (uint8_t i = 0; i < ARRAY_SIZE(flexcomm_addrs); i++) {
|
||||
if (flexcomm_addrs[i] == dev) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
DEBUG("Invalid FLEXCOMM address.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef MODULE_PERIPH_UART
|
||||
extern void isr_flexcomm_uart(USART_Type *dev, uint32_t flexcomm_num);
|
||||
#endif /* MODULE_PERIPH_UART */
|
||||
|
||||
#ifdef MODULE_PERIPH_SPI
|
||||
extern void isr_flexcomm_spi(SPI_Type *dev, uint32_t flexcomm_num);
|
||||
#endif /* MODULE_PERIPH_SPI */
|
||||
|
||||
#ifdef MODULE_PERIPH_I2C
|
||||
extern void isr_flexcomm_i2c(I2C_Type *dev, uint32_t flexcomm_num);
|
||||
#endif /* MODULE_PERIPH_I2C */
|
||||
|
||||
/**
|
||||
* @brief General Flexcomm interrupt handler dispatch.
|
||||
*
|
||||
* The driver that should get an interrupt from the flexcomm depends on the
|
||||
* currently configured one, which can be obtained from the PSELID.
|
||||
*/
|
||||
static void isr_flexcomm(void *flexcomm, uint32_t flexcomm_num)
|
||||
{
|
||||
switch (((FLEXCOMM_Type *)flexcomm)->PSELID & FLEXCOMM_PSELID_PERSEL_MASK) {
|
||||
#ifdef MODULE_PERIPH_UART
|
||||
case FLEXCOMM_ID_UART:
|
||||
isr_flexcomm_uart((USART_Type *)(flexcomm), flexcomm_num);
|
||||
return;
|
||||
#endif /* MODULE_PERIPH_UART */
|
||||
#ifdef MODULE_PERIPH_SPI
|
||||
case FLEXCOMM_ID_SPI:
|
||||
isr_flexcomm_spi((SPI_Type *)(flexcomm), flexcomm_num);
|
||||
return;
|
||||
#endif /* MODULE_PERIPH_SPI */
|
||||
#ifdef MODULE_PERIPH_I2C
|
||||
case FLEXCOMM_ID_I2C:
|
||||
isr_flexcomm_i2c((I2C_Type *)(flexcomm), flexcomm_num);
|
||||
return;
|
||||
#endif /* MODULE_PERIPH_I2C */
|
||||
default:
|
||||
cortexm_isr_end();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FLEXCOMM0
|
||||
void isr_flexcomm0(void)
|
||||
{
|
||||
isr_flexcomm(FLEXCOMM0, 0);
|
||||
}
|
||||
#endif /* FLEXCOMM0 */
|
||||
|
||||
#ifdef FLEXCOMM1
|
||||
void isr_flexcomm1(void)
|
||||
{
|
||||
isr_flexcomm(FLEXCOMM1, 1);
|
||||
}
|
||||
#endif /* FLEXCOMM1 */
|
||||
|
||||
#ifdef FLEXCOMM2
|
||||
void isr_flexcomm2(void)
|
||||
{
|
||||
isr_flexcomm(FLEXCOMM2, 2);
|
||||
}
|
||||
#endif /* FLEXCOMM2 */
|
||||
|
||||
#ifdef FLEXCOMM3
|
||||
void isr_flexcomm3(void)
|
||||
{
|
||||
isr_flexcomm(FLEXCOMM3, 3);
|
||||
}
|
||||
#endif /* FLEXCOMM3 */
|
292
cpu/qn908x/periph/uart.c
Normal file
292
cpu/qn908x/periph/uart.c
Normal file
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (C) 2020 iosabi
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_qn908x
|
||||
* @ingroup drivers_periph_uart
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level UART driver implementation
|
||||
*
|
||||
* This implementation only supports blocking writing using busy-wait.
|
||||
*
|
||||
* @author iosabi <iosabi@protonmail.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "periph/uart.h"
|
||||
#include "gpio_mux.h"
|
||||
#include "flexcomm.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "vendor/drivers/fsl_clock.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief Runtime UART configuration with user callback function for RX.
|
||||
*/
|
||||
static uart_isr_ctx_t config[UART_NUMOF];
|
||||
|
||||
/**
|
||||
* @brief The device number (in the UART_NUMOF range) registered for each
|
||||
* flexcomm port.
|
||||
*/
|
||||
static uart_t uart_dev_from_flexcomm[FSL_FEATURE_SOC_FLEXCOMM_COUNT] = {
|
||||
[0 ... FSL_FEATURE_SOC_FLEXCOMM_COUNT - 1] = UART_NUMOF
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Limit values for the USART OSRVAL, BRGVAL and MULTx values.
|
||||
* @{
|
||||
*/
|
||||
#define UART_OSRVAL_MAX 15u
|
||||
#define UART_BRGVAL_MAX 0xffffu
|
||||
#define UART_MULTX_MAX 255u
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief The maximum UART frequency divisor possible.
|
||||
*
|
||||
* This will limit the lower end of the baudrate allowed.
|
||||
*/
|
||||
#define UART_MAX_DIVISOR ((UART_OSRVAL_MAX + 1) * (UART_BRGVAL_MAX + 1) * \
|
||||
(UART_MULTX_MAX + 256))
|
||||
|
||||
static int _uart_set_baudrate(USART_Type *dev, uint8_t flexcomm_num,
|
||||
uint32_t baudrate)
|
||||
{
|
||||
assert(flexcomm_num < 2);
|
||||
/* The FLEXCOMM clock for the FLEXCOMM0 and FLEXCOMM1 is based on the
|
||||
* kCLOCK_BusClk clock frequency with an optional divisor, using the
|
||||
* following formula:
|
||||
* flexcomm freq := bus freq / (1 + (MULTx / 256))
|
||||
* where MULTx is a value between 0 and 255 and is set with
|
||||
* CLOCK_SetClkDiv(). Only the FLEXCOMM0 and FLEXCOMM1 support USART
|
||||
* function, so the MULTx is always available.
|
||||
* The UART baudrate is then:
|
||||
* uart baudrate := flexcomm freq / ((OSRVAL + 1) * (BRGVAL + 1))
|
||||
* where OSRVAL (oversample selection value) is a number between 4 and 15
|
||||
* (the larger the better) and BRGVAL is between 0 and 0xffff.
|
||||
* Combining and expanding the previous expression:
|
||||
* bus freq * 256 / baudrate = (256 + MULTx) * (OSRVAL + 1) * (BRGVAL + 1)
|
||||
* so we need to find those the values that minimize the error and maximize
|
||||
* OSRVAL.
|
||||
*/
|
||||
if (baudrate == 0) {
|
||||
return UART_NOBAUD;
|
||||
}
|
||||
uint32_t bus_freq = CLOCK_GetFreq(kCLOCK_BusClk);
|
||||
uint32_t target;
|
||||
{
|
||||
/* The remainder of this division is unavoidable frequency error at the
|
||||
* current clock frequency so we can discard it now. We add 127 to round
|
||||
* up or down to the nearest target value. */
|
||||
const uint64_t target64 = (((uint64_t)(bus_freq) << 8ull) + 127u) /
|
||||
baudrate;
|
||||
if (target64 > UART_MAX_DIVISOR) {
|
||||
return UART_NOBAUD;
|
||||
}
|
||||
/* At this point we know the target value fits in 32-bit since
|
||||
* UART_MAX_DIVISOR fits in 32-bit. */
|
||||
target = target64;
|
||||
}
|
||||
|
||||
uint32_t best_osrval = 0;
|
||||
uint32_t best_multx = 0;
|
||||
uint32_t best_brgval = 0;
|
||||
uint32_t best_error = UINT_MAX;
|
||||
|
||||
/* To simplify the math, let's assume we need to pick 3 values A, B and C
|
||||
* such that A * B * C is as close as a possible to a given target T. In
|
||||
* other words, we need to minimize the error |T - A * B * C|.
|
||||
* To do that, we scan over all possible values of A and B (about 2000
|
||||
* possibilities) and compute the error value taking C as the following:
|
||||
* C := floor((T + A * B / 2) / (A * B))
|
||||
* To compute the error we can avoid some multiplications if we consider
|
||||
* that we can decompose a number N as "floor(N / M) * M + remainder(N, M)"
|
||||
* taking N = T + A * B / 2 and M = A * B we get the error:
|
||||
* |T - A * B * C| = |T + A * B / 2 - A * B * C - A * B / 2|
|
||||
* = |remainder(T + A * B / 2, A * B) - A * B / 2|
|
||||
*/
|
||||
for (uint8_t osrval_p1 = UART_OSRVAL_MAX + 1; osrval_p1 > 8; osrval_p1--) {
|
||||
/* Initial value of (OSRVAL + 1) * (256 + MULTx) */
|
||||
uint32_t m = osrval_p1 * 256;
|
||||
for (uint32_t multx_p256 = 256;
|
||||
multx_p256 < 256 + UART_MULTX_MAX && best_error != 0 &&
|
||||
m / 2 <= target;
|
||||
multx_p256++, m += osrval_p1) {
|
||||
uint32_t error = (target + m / 2) % m;
|
||||
error = abs((int32_t)error - (int32_t)(m / 2));
|
||||
if (error < best_error) {
|
||||
/* Only in this case we need to do the division as well. */
|
||||
uint32_t brgval_p1 = (target + m / 2) / m;
|
||||
if (brgval_p1 > (UART_BRGVAL_MAX + 1)) {
|
||||
continue;
|
||||
}
|
||||
best_osrval = osrval_p1 - 1;
|
||||
best_multx = multx_p256 - 256;
|
||||
best_brgval = brgval_p1 - 1;
|
||||
best_error = error;
|
||||
}
|
||||
}
|
||||
if (best_error == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_osrval == 0) {
|
||||
return UART_NOBAUD;
|
||||
}
|
||||
|
||||
CLOCK_SetClkDiv(flexcomm_num ? kCLOCK_DivFrg1 : kCLOCK_DivFrg0, best_multx);
|
||||
dev->BRG = best_brgval;
|
||||
dev->OSR = best_osrval;
|
||||
return UART_OK;
|
||||
}
|
||||
|
||||
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
|
||||
{
|
||||
assert(uart < UART_NUMOF);
|
||||
|
||||
const uart_conf_t *uart_conf = uart_config + uart;
|
||||
USART_Type *const dev = uart_conf->dev;
|
||||
|
||||
int flexcomm_num = flexcomm_init((FLEXCOMM_Type *)dev, FLEXCOMM_ID_UART);
|
||||
if (flexcomm_num < 0) {
|
||||
return UART_INTERR;
|
||||
}
|
||||
uart_dev_from_flexcomm[flexcomm_num] = uart;
|
||||
|
||||
int ret = _uart_set_baudrate(dev, flexcomm_num, baudrate);
|
||||
if (ret != UART_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* remember callback addresses */
|
||||
config[uart].rx_cb = rx_cb;
|
||||
config[uart].arg = arg;
|
||||
|
||||
/* Interrupt trigger and level for RX and TX disabled by default. */
|
||||
dev->FIFOTRIG = 0;
|
||||
|
||||
/* Enable RX side. */
|
||||
if (rx_cb != NULL && gpio_is_valid(uart_conf->rx_pin)) {
|
||||
/* Trigger RX interrupt when there is at least 1 byte (RXLVL = 0). */
|
||||
dev->FIFOTRIG |= USART_FIFOTRIG_RXLVLENA_MASK | USART_FIFOTRIG_RXLVL(0);
|
||||
|
||||
/* Enable RX interrupt. */
|
||||
dev->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK |
|
||||
USART_FIFOCFG_ENABLERX_MASK;
|
||||
dev->FIFOINTENSET = USART_FIFOINTENSET_RXLVL_MASK;
|
||||
/* flexcomm_num is the same as the USART instance number in the
|
||||
* USART_IRQS array. */
|
||||
const uint8_t usart_irqn[] = USART_IRQS;
|
||||
NVIC_EnableIRQ(usart_irqn[flexcomm_num]);
|
||||
}
|
||||
else {
|
||||
dev->FIFOCFG &= ~USART_FIFOCFG_ENABLERX_MASK;
|
||||
uart_conf->dev->FIFOINTENCLR = USART_FIFOINTENSET_RXLVL_MASK;
|
||||
}
|
||||
|
||||
/* Enable TX side. */
|
||||
if (gpio_is_valid(uart_conf->tx_pin)) {
|
||||
dev->FIFOCFG |= USART_FIFOCFG_EMPTYTX_MASK |
|
||||
USART_FIFOCFG_ENABLETX_MASK;
|
||||
}
|
||||
else {
|
||||
dev->FIFOCFG &= ~USART_FIFOCFG_ENABLETX_MASK;
|
||||
}
|
||||
|
||||
/* Configure RX and TX pins. RX/TX pins are always in function 4.
|
||||
* GPIO_UNDEF are ignored. */
|
||||
gpio_init_mux(uart_conf->rx_pin, 4);
|
||||
gpio_init_mux(uart_conf->tx_pin, 4);
|
||||
/* This call also enables the UART. */
|
||||
return uart_mode(uart, UART_DATA_BITS_8, UART_PARITY_NONE,
|
||||
UART_STOP_BITS_1);
|
||||
}
|
||||
|
||||
int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity,
|
||||
uart_stop_bits_t stop_bits)
|
||||
{
|
||||
/* Setup mode and enable USART. The values of the uart_data_bits_t,
|
||||
* uart_parity_t and uart_stop_bits_t enums were selected to match the
|
||||
* fields in this registers so there's no need to do any conversion. */
|
||||
uart_config[uart].dev->CFG = USART_CFG_PARITYSEL(parity)
|
||||
| USART_CFG_STOPLEN(stop_bits)
|
||||
| USART_CFG_DATALEN(data_bits)
|
||||
| USART_CFG_LOOP(0) | USART_CFG_ENABLE_MASK;
|
||||
return UART_OK;
|
||||
}
|
||||
|
||||
void uart_write(uart_t uart, const uint8_t *data, size_t len)
|
||||
{
|
||||
USART_Type *dev = uart_config[uart].dev;
|
||||
|
||||
/* If the TX side or the whole uart mode was not enabled during init or at
|
||||
* all yet we can only ignore this transmission. This allows DEBUG messages
|
||||
* to be ignored without hanging here before the uart is initialized. */
|
||||
if (!(dev->FIFOCFG & USART_FIFOCFG_ENABLETX_MASK) ||
|
||||
!(dev->CFG & USART_CFG_ENABLE_MASK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (; len; len--) {
|
||||
while (!(dev->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK)) {}
|
||||
dev->FIFOWR = *(data++);
|
||||
}
|
||||
/* Wait until we flush out all the bytes. */
|
||||
while (!(dev->STAT & USART_STAT_TXIDLE_MASK)) {}
|
||||
}
|
||||
|
||||
void uart_poweron(uart_t uart)
|
||||
{
|
||||
USART_Type *dev = uart_config[uart].dev;
|
||||
|
||||
dev->CFG |= USART_CFG_ENABLE_MASK;
|
||||
}
|
||||
|
||||
void uart_poweroff(uart_t uart)
|
||||
{
|
||||
USART_Type *dev = uart_config[uart].dev;
|
||||
|
||||
while (!(dev->STAT & USART_STAT_TXIDLE_MASK)) {}
|
||||
dev->CFG &= ~USART_CFG_ENABLE_MASK;
|
||||
}
|
||||
|
||||
void isr_flexcomm_uart(USART_Type *dev, uint32_t flexcomm_num)
|
||||
{
|
||||
uart_t uart = uart_dev_from_flexcomm[flexcomm_num];
|
||||
|
||||
while (dev->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK) {
|
||||
/* Reading from FIFORD may clear the FIFOSTAT RXNOTEMPTY if we read all
|
||||
* the bytes. */
|
||||
uint8_t data = dev->FIFORD;
|
||||
if (uart < UART_NUMOF && config[uart].rx_cb != NULL) {
|
||||
config[uart].rx_cb(config[uart].arg, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->FIFOSTAT & USART_FIFOSTAT_RXERR_MASK) {
|
||||
/* This is a USART FIFO RX overrun.
|
||||
* Note: writing a 1 to the FIFOSTAT flag clears it. */
|
||||
dev->FIFOSTAT = USART_FIFOSTAT_RXERR_MASK;
|
||||
/* TODO: Signal an error to the application. */
|
||||
}
|
||||
|
||||
cortexm_isr_end();
|
||||
}
|
Loading…
Reference in New Issue
Block a user