mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-25 06:23:53 +01:00
Merge pull request #15557 from iosabi/qn908x_ctimer
cpu/qn908x: Add timer driver based on CTIMER.
This commit is contained in:
commit
f72e98d0a0
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
174
cpu/qn908x/periph/timer.c
Normal file
174
cpu/qn908x/periph/timer.c
Normal file
@ -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 <iosabi@protonmail.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 */
|
||||
@ -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. */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user