diff --git a/boards/qn9080dk/Kconfig b/boards/qn9080dk/Kconfig index 1f44df993d..9dde9a2d30 100644 --- a/boards/qn9080dk/Kconfig +++ b/boards/qn9080dk/Kconfig @@ -17,6 +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_TIMER select HAS_PERIPH_UART select HAS_PERIPH_UART_MODECFG diff --git a/boards/qn9080dk/Makefile.features b/boards/qn9080dk/Makefile.features index f4cfcb4ccc..81ce8c8757 100644 --- a/boards/qn9080dk/Makefile.features +++ b/boards/qn9080dk/Makefile.features @@ -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_timer FEATURES_PROVIDED += periph_uart periph_uart_modecfg # Include the common qn908x board features. diff --git a/boards/qn9080dk/include/periph_conf.h b/boards/qn9080dk/include/periph_conf.h index abfaee80fb..3687ba5a5b 100644 --- a/boards/qn9080dk/include/periph_conf.h +++ b/boards/qn9080dk/include/periph_conf.h @@ -43,9 +43,15 @@ static const uart_conf_t uart_config[] = { #define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ +/** + * @name Timer configuration + * @{ + */ +#define TIMER_NUMOF 4 +/** @} */ + /* put here the board peripherals definitions: - Available clocks - - Timers - PWMs - SPIs - I2C diff --git a/cpu/qn908x/doc.txt b/cpu/qn908x/doc.txt index e77ea1896c..e574b6736b 100644 --- a/cpu/qn908x/doc.txt +++ b/cpu/qn908x/doc.txt @@ -31,6 +31,25 @@ The GPIO driver uses the @ref GPIO_PIN(port, pin) macro to declare pins. No configuration is necessary. +@defgroup cpu_qn908x_timer NXP QN908x Standard counter/timers (CTIMER) +@ingroup cpu_qn908x +@brief NXP QN908x timer driver + +The QN908x have 4 standard counter/timers (CTIMER). These timers allow to count +clock cycles from the APB clock with a 32-bit prescaler, effectively dividing +the APB clock frequency by a configurable number up to 2^32, allowing a great +range of timer frequencies selected at runtime. Each timer has 4 independent +channels to match against which can generate an interrupt. + +TODO: These CTIMERs and the SCT timers can both be used as PWM as well, with +different set of capabilities. Boards should be able to split these CTIMER and +SCT blocks between pwm and timer functions. + +### Timer configuration example (for periph_conf.h) ### + + #define TIMER_NUMOF 4 + + @defgroup cpu_qn908x_uart NXP QN908x UART @ingroup cpu_qn908x @brief NXP QN908x UART driver diff --git a/cpu/qn908x/include/periph_cpu.h b/cpu/qn908x/include/periph_cpu.h index 6d87741dc2..49191ddc8f 100644 --- a/cpu/qn908x/include/periph_cpu.h +++ b/cpu/qn908x/include/periph_cpu.h @@ -81,9 +81,9 @@ typedef uint16_t gpio_t; * clocks installed on the board. Figure out a way to configure this limit based * on the clock used. */ -#define NWDT_TIME_LOWER_LIMIT (0) +#define NWDT_TIME_LOWER_LIMIT (1U) #define NWDT_TIME_UPPER_LIMIT (268435U) -#define WWDT_TIME_LOWER_LIMIT (0) +#define WWDT_TIME_LOWER_LIMIT (1U) #define WWDT_TIME_UPPER_LIMIT (268435U) /** @} */ @@ -127,6 +127,7 @@ typedef enum { GPIO_HIGH = 1, /**< emit interrupt when the value is high */ GPIO_RISING = 2, /**< emit interrupt on rising flank */ GPIO_FALLING = 3, /**< emit interrupt on falling flank */ + GPIO_BOTH = 4, /**< not supported -- rising and falling flanks */ } gpio_flank_t; /** @} */ #endif /* ndef DOXYGEN */ @@ -140,6 +141,14 @@ enum { GPIO_PORTS_NUMOF /**< overall number of available ports */ }; +/** + * @brief CPU specific timer Counter/Timers (CTIMER) configuration + * @{ + */ +#define TIMER_CHANNELS (4) +#define TIMER_MAX_VALUE (0xffffffff) +/** @} */ + /** * @brief UART module configuration options * @@ -153,6 +162,13 @@ typedef struct { gpio_t tx_pin; /**< TX pin, GPIO_UNDEF disables TX. */ } uart_conf_t; +/** + * @brief Invalid UART mode mask + * + * Signals that the mode is invalid or not supported by the CPU. + */ +#define UART_INVALID_MODE (0x80) + /** * @brief Definition of possible parity modes * @@ -161,9 +177,11 @@ typedef struct { * @{ */ typedef enum { - UART_PARITY_NONE = 0, /**< no parity */ - UART_PARITY_EVEN = 2, /**< even parity */ - UART_PARITY_ODD = 3, /**< odd parity */ + UART_PARITY_NONE = 0, /**< no parity */ + UART_PARITY_EVEN = 2, /**< even parity */ + UART_PARITY_ODD = 3, /**< odd parity */ + UART_PARITY_MARK = 0x10 | UART_INVALID_MODE, /**< mark parity */ + UART_PARITY_SPACE = 0x20 | UART_INVALID_MODE, /**< space parity */ } uart_parity_t; #define HAVE_UART_PARITY_T /** @} */ @@ -175,8 +193,10 @@ typedef enum { * @{ */ typedef enum { - UART_DATA_BITS_7 = 0, /**< 7 data bits */ - UART_DATA_BITS_8 = 1, /**< 8 data bits */ + UART_DATA_BITS_5 = 0x10 | UART_INVALID_MODE, /**< 5 data bits */ + UART_DATA_BITS_6 = 0x20 | UART_INVALID_MODE, /**< 6 data bits */ + 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 diff --git a/cpu/qn908x/periph/gpio.c b/cpu/qn908x/periph/gpio.c index ea0eb7744a..6d9880a4ba 100644 --- a/cpu/qn908x/periph/gpio.c +++ b/cpu/qn908x/periph/gpio.c @@ -122,6 +122,10 @@ static gpio_isr_cb_state_t gpio_isr_state[TOTAL_GPIO_PINS] = {}; int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, gpio_cb_t cb, void *arg) { + if (flank == GPIO_BOTH) { + /* GPIO_BOTH is not supported. */ + return -1; + } uint8_t gpio_num = GPIO_T_PORT(pin) * PINS_PER_PORT + GPIO_T_PIN(pin); if (gpio_num >= TOTAL_GPIO_PINS) { @@ -154,6 +158,9 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, base->INTTYPESET = mask; /* SET = edge */ base->INTPOLSET = mask; /* SET = rising */ break; + case GPIO_BOTH: + /* Handled above */ + break; } gpio_irq_enable(pin); return 0; diff --git a/cpu/qn908x/periph/timer.c b/cpu/qn908x/periph/timer.c new file mode 100644 index 0000000000..775abb5d3c --- /dev/null +++ b/cpu/qn908x/periph/timer.c @@ -0,0 +1,174 @@ +/* + * 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_timer + * + * @{ + * + * @file + * @brief Low-level timer driver implementation + * + * This driver leverages the Freescale/NXP implementation distributed with the + * SDK. + * + * @author iosabi + * + * @} + */ + +#include + +#include "cpu.h" +#include "board.h" +#include "periph_conf.h" +#include "periph/timer.h" + +#include "vendor/drivers/fsl_clock.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Interrupt context information for configured timers. + */ +static timer_isr_ctx_t isr_ctx[FSL_FEATURE_SOC_CTIMER_COUNT]; + +/** + * @brief CTIMER peripheral base pointers. + */ +static CTIMER_Type* const ctimers[FSL_FEATURE_SOC_CTIMER_COUNT] = + CTIMER_BASE_PTRS; + +/** + * @brief CTIMER IRQ numbers. + */ +static IRQn_Type const ctimers_irqn[FSL_FEATURE_SOC_CTIMER_COUNT] = + CTIMER_IRQS; + +/** + * @brief CTIMER Clocks. + */ +static const clock_ip_name_t ctimers_clocks[FSL_FEATURE_SOC_CTIMER_COUNT] = + CTIMER_CLOCKS; + + +/** + * @brief Check the board config to make sure we do not exceed max number of + * timers + */ +#if TIMER_NUMOF > FSL_FEATURE_SOC_CTIMER_COUNT +#error "ERROR in board timer configuration: too many timers defined" +#endif + +int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg) +{ + DEBUG("timer_init(%u, %lu)\n", tim, freq); + if (tim >= TIMER_NUMOF) { + return -1; + } + + isr_ctx[tim].cb = cb; + isr_ctx[tim].arg = arg; + + CLOCK_EnableClock(ctimers_clocks[tim]); + + CTIMER_Type *dev = ctimers[tim]; + + /* CTIMER blocks are driven from the APB clock. */ + uint32_t core_freq = CLOCK_GetFreq(kCLOCK_ApbClk); + uint32_t prescale = (core_freq + freq / 2) / freq - 1; + if (prescale == (uint32_t)(-1)) { + DEBUG("timer_init: Frequency %lu is too fast for core_freq=%lu", + freq, core_freq); + return -1; + } + + dev->CTCR = CTIMER_CTCR_CTMODE(0); /* timer mode */ + dev->PR = CTIMER_PR_PRVAL(prescale); + /* Enable timer interrupts in the NVIC. */ + NVIC_EnableIRQ(ctimers_irqn[tim]); + + /* Timer should be started after init. */ + dev->TCR |= CTIMER_TCR_CEN_MASK; + return 0; +} + +int timer_set_absolute(tim_t tim, int channel, unsigned int value) +{ + DEBUG("timer_set_absolute(%u, %u, %u)\n", tim, channel, value); + if ((tim >= TIMER_NUMOF) || (channel >= TIMER_CHANNELS)) { + return -1; + } + CTIMER_Type* const dev = ctimers[tim]; + dev->MR[channel] = value; + dev->MCR |= (CTIMER_MCR_MR0I_MASK << (channel * 3)); + return 0; +} + +int timer_clear(tim_t tim, int channel) +{ + DEBUG("timer_clear(%u, %d)\n", tim, channel); + if ((tim >= TIMER_NUMOF) || (channel >= TIMER_CHANNELS)) { + return -1; + } + CTIMER_Type* const dev = ctimers[tim]; + dev->MCR &= ~(CTIMER_MCR_MR0I_MASK << (channel * 3)); + return 0; +} + +unsigned int timer_read(tim_t tim) +{ + DEBUG("timer_read(%u) --> %" PRIu32 "\n", tim, ctimers[tim]->TC); + return ctimers[tim]->TC; +} + +void timer_start(tim_t tim) +{ + DEBUG("timer_start(%u)\n", tim); + ctimers[tim]->TCR |= CTIMER_TCR_CEN_MASK; +} + +void timer_stop(tim_t tim) +{ + DEBUG("timer_stop(%u)\n", tim); + ctimers[tim]->TCR &= ~CTIMER_TCR_CEN_MASK; +} + +static inline void isr_ctimer_n(CTIMER_Type *dev, uint32_t ctimer_num) +{ + DEBUG("isr_ctimer_%" PRIu32 " flags=0x%" PRIx32 "\n", + ctimer_num, dev->IR); + for (uint32_t i = 0; i < TIMER_CHANNELS; i++) { + if (dev->IR & (1u << i)) { + /* Note: setting the bit to 1 in the flag register will clear the + * bit. */ + dev->IR = 1u << i; + dev->MCR &= ~(CTIMER_MCR_MR0I_MASK << (i * 3)); + isr_ctx[ctimer_num].cb(isr_ctx[ctimer_num].arg, 0); + } + } + cortexm_isr_end(); +} + +#ifdef CTIMER0 +void isr_ctimer0(void) { isr_ctimer_n(CTIMER0, 0); } +#endif /* CTIMER0 */ + +#ifdef CTIMER1 +void isr_ctimer1(void) { isr_ctimer_n(CTIMER1, 1); } +#endif /* CTIMER1 */ + +#ifdef CTIMER2 +void isr_ctimer2(void) { isr_ctimer_n(CTIMER2, 2); } +#endif /* CTIMER2 */ + +#ifdef CTIMER3 +void isr_ctimer3(void) { isr_ctimer_n(CTIMER3, 3); } +#endif /* CTIMER3 */ diff --git a/cpu/qn908x/periph/uart.c b/cpu/qn908x/periph/uart.c index 9844fd03ad..527890b169 100644 --- a/cpu/qn908x/periph/uart.c +++ b/cpu/qn908x/periph/uart.c @@ -223,6 +223,9 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, uart_stop_bits_t stop_bits) { + if ((data_bits & UART_INVALID_MODE) || (parity & UART_INVALID_MODE)) { + return UART_NOMODE; + } /* 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. */