mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/kinetis: Refactor LPTMR driver implementation
Uses timer freerunning counter mode (TFC) and updating an internal reference point instead of relying on RTT for reference time. The target accuracy has been greatly improved for all test cases in bench_periph_timer
This commit is contained in:
parent
3d2d3be267
commit
9e10cd4401
@ -1,8 +1,3 @@
|
|||||||
ifneq (,$(filter periph_rtc,$(USEMODULE)))
|
ifneq (,$(filter periph_rtc,$(USEMODULE)))
|
||||||
USEMODULE += periph_rtt
|
USEMODULE += periph_rtt
|
||||||
endif
|
endif
|
||||||
ifneq (,$(filter periph_timer,$(USEMODULE)))
|
|
||||||
# RTT is used as the time base for the LPTMR driver.
|
|
||||||
# TODO: Remove when LPTMR is refactored.
|
|
||||||
FEATURES_OPTIONAL += periph_rtt
|
|
||||||
endif
|
|
||||||
|
@ -293,6 +293,10 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
/** LPTMR device base pointer */
|
/** LPTMR device base pointer */
|
||||||
LPTMR_Type *dev;
|
LPTMR_Type *dev;
|
||||||
|
/** Input clock frequency */
|
||||||
|
uint32_t base_freq;
|
||||||
|
/** Clock source setting */
|
||||||
|
uint8_t src;
|
||||||
/** IRQn interrupt number */
|
/** IRQn interrupt number */
|
||||||
uint8_t irqn;
|
uint8_t irqn;
|
||||||
} lptmr_conf_t;
|
} lptmr_conf_t;
|
||||||
|
@ -52,28 +52,12 @@
|
|||||||
#error TIMER_NUMOF should be the total of PIT and LPTMR timers in the system
|
#error TIMER_NUMOF should be the total of PIT and LPTMR timers in the system
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* The RTC prescaler will normally count to 32767 every second unless configured
|
|
||||||
* otherwise through the time compensation register.
|
|
||||||
*/
|
|
||||||
#define TIMER_RTC_SUBTICK_MAX (0x7fff)
|
|
||||||
/*
|
|
||||||
* Number of bits in the ideal RTC prescaler counter
|
|
||||||
*/
|
|
||||||
#define TIMER_RTC_SUBTICK_BITS (15)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The number of ticks that will be lost when setting a new target in the LPTMR
|
* @brief The number of ticks that will be lost when setting a new target in the LPTMR
|
||||||
*
|
*
|
||||||
* The counter will otherwise drop ticks when setting new timeouts.
|
* The counter will otherwise drop ticks when setting new timeouts.
|
||||||
*/
|
*/
|
||||||
#define LPTMR_RELOAD_OVERHEAD 2
|
#define LPTMR_RELOAD_OVERHEAD 1
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Base clock frequency of the LPTMR module
|
|
||||||
*/
|
|
||||||
/* The LPTMR implementation is hard-coded to use ER32KCLK */
|
|
||||||
#define LPTMR_BASE_FREQ (32768ul)
|
|
||||||
|
|
||||||
/* PIT channel state */
|
/* PIT channel state */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -86,11 +70,9 @@ typedef struct {
|
|||||||
/* LPTMR state */
|
/* LPTMR state */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
timer_isr_ctx_t isr_ctx;
|
timer_isr_ctx_t isr_ctx;
|
||||||
uint32_t csr;
|
uint32_t cnr;
|
||||||
uint32_t cmr;
|
uint32_t cmr;
|
||||||
uint32_t running;
|
uint32_t running;
|
||||||
uint16_t reference;
|
|
||||||
uint16_t rtt_offset;
|
|
||||||
} lptmr_t;
|
} lptmr_t;
|
||||||
|
|
||||||
static const pit_conf_t pit_config[PIT_NUMOF] = PIT_CONFIG;
|
static const pit_conf_t pit_config[PIT_NUMOF] = PIT_CONFIG;
|
||||||
@ -350,35 +332,6 @@ static inline void lptmr_start(uint8_t dev);
|
|||||||
static inline void lptmr_stop(uint8_t dev);
|
static inline void lptmr_stop(uint8_t dev);
|
||||||
static inline void lptmr_irq_handler(tim_t tim);
|
static inline void lptmr_irq_handler(tim_t tim);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Read the prescaler register from the RTC as a reliable 47 bit time counter
|
|
||||||
*/
|
|
||||||
static inline uint32_t _rtt_get_subtick(void)
|
|
||||||
{
|
|
||||||
uint32_t tpr;
|
|
||||||
uint32_t tsr;
|
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
/* Read twice to make sure we get a stable reading */
|
|
||||||
tpr = RTC->TPR & RTC_TPR_TPR_MASK;
|
|
||||||
tsr = RTC->TSR & RTC_TSR_TSR_MASK;
|
|
||||||
|
|
||||||
if ((tsr == (RTC->TSR & RTC_TSR_TSR_MASK)) &&
|
|
||||||
(tpr == (RTC->TPR & RTC_TPR_TPR_MASK))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tpr > TIMER_RTC_SUBTICK_MAX) {
|
|
||||||
/* This only happens if the RTC time compensation value has been
|
|
||||||
* modified to compensate for RTC drift. See Kinetis ref.manual,
|
|
||||||
* RTC Time Compensation Register (RTC_TCR).
|
|
||||||
*/
|
|
||||||
tpr = TIMER_RTC_SUBTICK_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (tsr << TIMER_RTC_SUBTICK_BITS) | tpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _lptmr_set_cb_config(uint8_t dev, timer_cb_t cb, void *arg)
|
static inline void _lptmr_set_cb_config(uint8_t dev, timer_cb_t cb, void *arg)
|
||||||
{
|
{
|
||||||
/* set callback function */
|
/* set callback function */
|
||||||
@ -389,60 +342,32 @@ static inline void _lptmr_set_cb_config(uint8_t dev, timer_cb_t cb, void *arg)
|
|||||||
/**
|
/**
|
||||||
* @brief Compute the LPTMR prescaler setting, see reference manual for details
|
* @brief Compute the LPTMR prescaler setting, see reference manual for details
|
||||||
*/
|
*/
|
||||||
static inline int32_t _lptmr_compute_prescaler(uint32_t freq) {
|
static inline int32_t _lptmr_compute_prescaler(uint8_t dev, uint32_t freq) {
|
||||||
uint32_t prescale = 0;
|
uint32_t prescale = 0;
|
||||||
if ((freq > LPTMR_BASE_FREQ) || (freq == 0)) {
|
if ((freq > lptmr_config[dev].base_freq) || (freq == 0)) {
|
||||||
/* Frequency out of range */
|
/* Frequency out of range */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (freq < LPTMR_BASE_FREQ){
|
while (freq < lptmr_config[dev].base_freq){
|
||||||
++prescale;
|
++prescale;
|
||||||
freq <<= 1;
|
freq <<= 1;
|
||||||
}
|
}
|
||||||
if (freq != LPTMR_BASE_FREQ) {
|
if (freq != lptmr_config[dev].base_freq) {
|
||||||
/* freq was not a power of two division of LPTMR_BASE_FREQ */
|
/* freq was not a power of two division of base_freq */
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
if (prescale > 0) {
|
if (prescale == 0) {
|
||||||
/* LPTMR_PSR_PRESCALE == 0 yields LPTMR_BASE_FREQ/2,
|
|
||||||
* LPTMR_PSR_PRESCALE == 1 yields LPTMR_BASE_FREQ/4 etc.. */
|
|
||||||
return LPTMR_PSR_PRESCALE(prescale - 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Prescaler bypass enabled */
|
/* Prescaler bypass enabled */
|
||||||
return LPTMR_PSR_PBYP_MASK;
|
return LPTMR_PSR_PBYP_MASK;
|
||||||
}
|
}
|
||||||
}
|
/* LPTMR_PSR_PRESCALE == 0 yields base_freq / 2,
|
||||||
|
* LPTMR_PSR_PRESCALE == 1 yields base_freq / 4 etc.. */
|
||||||
/**
|
return LPTMR_PSR_PRESCALE(prescale - 1);
|
||||||
* @brief Update the offset between RTT and LPTMR
|
|
||||||
*/
|
|
||||||
static inline void _lptmr_update_rtt_offset(uint8_t dev)
|
|
||||||
{
|
|
||||||
lptmr[dev].rtt_offset = _rtt_get_subtick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update the reference time point (CNR=0)
|
|
||||||
*/
|
|
||||||
static inline void _lptmr_update_reference(uint8_t dev)
|
|
||||||
{
|
|
||||||
lptmr[dev].reference = _rtt_get_subtick() + LPTMR_RELOAD_OVERHEAD - lptmr[dev].rtt_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void _lptmr_set_counter(uint8_t dev)
|
|
||||||
{
|
|
||||||
_lptmr_update_reference(dev);
|
|
||||||
LPTMR_Type *hw = lptmr_config[dev].dev;
|
|
||||||
hw->CSR = 0;
|
|
||||||
hw->CMR = lptmr[dev].cmr;
|
|
||||||
/* restore saved state */
|
|
||||||
hw->CSR = lptmr[dev].csr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int lptmr_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *arg)
|
static inline int lptmr_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *arg)
|
||||||
{
|
{
|
||||||
int32_t prescale = _lptmr_compute_prescaler(freq);
|
int32_t prescale = _lptmr_compute_prescaler(dev, freq);
|
||||||
if (prescale < 0) {
|
if (prescale < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -452,13 +377,13 @@ static inline int lptmr_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *ar
|
|||||||
|
|
||||||
/* Turn on module clock */
|
/* Turn on module clock */
|
||||||
LPTMR_CLKEN();
|
LPTMR_CLKEN();
|
||||||
|
|
||||||
/* Completely disable the module before messing with the settings */
|
/* Completely disable the module before messing with the settings */
|
||||||
hw->CSR = 0;
|
hw->CSR = 0;
|
||||||
/* select ERCLK32K as clock source for LPTMR */
|
|
||||||
hw->PSR = LPTMR_PSR_PCS(2) | ((uint32_t)prescale);
|
|
||||||
|
|
||||||
/* Clear IRQ flag in case it was already set */
|
/* select clock source and configure prescaler */
|
||||||
hw->CSR = LPTMR_CSR_TCF_MASK;
|
hw->PSR = LPTMR_PSR_PCS(lptmr_config[dev].src) | ((uint32_t)prescale);
|
||||||
|
|
||||||
/* Enable IRQs on the counting channel */
|
/* Enable IRQs on the counting channel */
|
||||||
NVIC_ClearPendingIRQ(lptmr_config[dev].irqn);
|
NVIC_ClearPendingIRQ(lptmr_config[dev].irqn);
|
||||||
NVIC_EnableIRQ(lptmr_config[dev].irqn);
|
NVIC_EnableIRQ(lptmr_config[dev].irqn);
|
||||||
@ -466,9 +391,11 @@ static inline int lptmr_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *ar
|
|||||||
_lptmr_set_cb_config(dev, cb, arg);
|
_lptmr_set_cb_config(dev, cb, arg);
|
||||||
|
|
||||||
/* Reset state */
|
/* Reset state */
|
||||||
_lptmr_update_rtt_offset(dev);
|
|
||||||
lptmr[dev].running = 1;
|
lptmr[dev].running = 1;
|
||||||
lptmr_clear(dev);
|
lptmr[dev].cnr = 0;
|
||||||
|
lptmr[dev].cmr = 0;
|
||||||
|
hw->CMR = 0;
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK;
|
||||||
|
|
||||||
irq_restore(mask);
|
irq_restore(mask);
|
||||||
|
|
||||||
@ -480,86 +407,188 @@ static inline uint16_t lptmr_read(uint8_t dev)
|
|||||||
LPTMR_Type *hw = lptmr_config[dev].dev;
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
/* latch the current timer value into CNR */
|
/* latch the current timer value into CNR */
|
||||||
hw->CNR = 0;
|
hw->CNR = 0;
|
||||||
return lptmr[dev].reference + hw->CNR;
|
return lptmr[dev].cnr + hw->CNR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reload the timer with the given timeout, or spin if timeout is too small
|
||||||
|
*
|
||||||
|
* @pre IRQs masked, timer running
|
||||||
|
*/
|
||||||
|
static inline void lptmr_reload_or_spin(uint8_t dev, uint16_t timeout)
|
||||||
|
{
|
||||||
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
|
/* Disable timer and set target, 1 to 2 ticks will be dropped by the
|
||||||
|
* hardware during the disable-enable cycle */
|
||||||
|
/* Disable the timer interrupt first */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK;
|
||||||
|
if (timeout <= LPTMR_RELOAD_OVERHEAD) {
|
||||||
|
/* we spin if the timeout is too short to reload the timer */
|
||||||
|
hw->CNR = 0;
|
||||||
|
uint16_t cnr_begin = hw->CNR;
|
||||||
|
while ((hw->CNR - cnr_begin) <= timeout) {
|
||||||
|
hw->CNR = 0;
|
||||||
|
}
|
||||||
|
/* Emulate IRQ handler behaviour */
|
||||||
|
lptmr[dev].running = 0;
|
||||||
|
if (lptmr[dev].isr_ctx.cb != NULL) {
|
||||||
|
lptmr[dev].isr_ctx.cb(lptmr[dev].isr_ctx.arg, 0);
|
||||||
|
}
|
||||||
|
thread_yield_higher();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Update reference */
|
||||||
|
hw->CNR = 0;
|
||||||
|
lptmr[dev].cnr += hw->CNR + LPTMR_RELOAD_OVERHEAD;
|
||||||
|
/* Disable timer */
|
||||||
|
hw->CSR = 0;
|
||||||
|
hw->CMR = timeout - LPTMR_RELOAD_OVERHEAD;
|
||||||
|
/* Enable timer and IRQ */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK | LPTMR_CSR_TIE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int lptmr_set(uint8_t dev, uint16_t timeout)
|
static inline int lptmr_set(uint8_t dev, uint16_t timeout)
|
||||||
{
|
{
|
||||||
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
/* Disable IRQs to minimize jitter */
|
/* Disable IRQs to minimize jitter */
|
||||||
unsigned int mask = irq_disable();
|
unsigned int mask = irq_disable();
|
||||||
lptmr[dev].cmr = timeout;
|
lptmr[dev].running = 1;
|
||||||
/* Enable interrupt, enable timer */
|
if (!(hw->CSR & LPTMR_CSR_TEN_MASK)) {
|
||||||
lptmr[dev].csr = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TIE_MASK;
|
/* Timer is stopped, only update target */
|
||||||
if (lptmr[dev].running != 0) {
|
if (timeout > LPTMR_RELOAD_OVERHEAD) {
|
||||||
/* Timer is currently running */
|
/* Compensate for the reload delay */
|
||||||
/* Set new target */
|
lptmr[dev].cmr = timeout - LPTMR_RELOAD_OVERHEAD;
|
||||||
_lptmr_set_counter(dev);
|
}
|
||||||
|
else {
|
||||||
|
lptmr[dev].cmr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (hw->CSR & LPTMR_CSR_TCF_MASK) {
|
||||||
|
/* TCF is set, safe to update CMR live */
|
||||||
|
hw->CNR = 0;
|
||||||
|
hw->CMR = timeout + hw->CNR;
|
||||||
|
/* cppcheck-suppress selfAssignment
|
||||||
|
* Clear IRQ flags */
|
||||||
|
hw->CSR = hw->CSR;
|
||||||
|
/* Enable timer and IRQ */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK | LPTMR_CSR_TIE_MASK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lptmr_reload_or_spin(dev, timeout);
|
||||||
}
|
}
|
||||||
irq_restore(mask);
|
irq_restore(mask);
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int lptmr_set_absolute(uint8_t dev, uint16_t target)
|
static inline int lptmr_set_absolute(uint8_t dev, uint16_t target)
|
||||||
{
|
{
|
||||||
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
/* Disable IRQs to minimize jitter */
|
/* Disable IRQs to minimize jitter */
|
||||||
unsigned int mask = irq_disable();
|
unsigned int mask = irq_disable();
|
||||||
uint16_t offset = target - lptmr[dev].reference;
|
lptmr[dev].running = 1;
|
||||||
lptmr[dev].cmr = offset;
|
if (!(hw->CSR & LPTMR_CSR_TEN_MASK)) {
|
||||||
/* Enable interrupt, enable timer */
|
/* Timer is stopped, only update target */
|
||||||
lptmr[dev].csr = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TIE_MASK;
|
uint16_t timeout = target - lptmr[dev].cnr;
|
||||||
if (lptmr[dev].running != 0) {
|
if (timeout > LPTMR_RELOAD_OVERHEAD) {
|
||||||
/* Timer is currently running */
|
/* Compensate for the reload delay */
|
||||||
/* Set new target */
|
lptmr[dev].cmr = timeout - LPTMR_RELOAD_OVERHEAD;
|
||||||
_lptmr_set_counter(dev);
|
}
|
||||||
|
else {
|
||||||
|
lptmr[dev].cmr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (hw->CSR & LPTMR_CSR_TCF_MASK) {
|
||||||
|
/* TCF is set, safe to update CMR live */
|
||||||
|
hw->CMR = target - lptmr[dev].cnr;
|
||||||
|
/* cppcheck-suppress selfAssignment
|
||||||
|
* Clear IRQ flags */
|
||||||
|
hw->CSR = hw->CSR;
|
||||||
|
/* Enable timer and IRQ */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK | LPTMR_CSR_TIE_MASK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint16_t timeout = target - lptmr_read(dev);
|
||||||
|
lptmr_reload_or_spin(dev, timeout);
|
||||||
}
|
}
|
||||||
irq_restore(mask);
|
irq_restore(mask);
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int lptmr_clear(uint8_t dev)
|
static inline int lptmr_clear(uint8_t dev)
|
||||||
{
|
{
|
||||||
/* Disable IRQs to minimize jitter */
|
/* Disable IRQs to minimize jitter */
|
||||||
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
unsigned int mask = irq_disable();
|
unsigned int mask = irq_disable();
|
||||||
lptmr[dev].cmr = LPTMR_MAX_VALUE;
|
if (!lptmr[dev].running) {
|
||||||
/* Disable interrupt, enable timer */
|
/* Already clear */
|
||||||
lptmr[dev].csr = LPTMR_CSR_TEN_MASK;
|
irq_restore(mask);
|
||||||
if (lptmr[dev].running != 0) {
|
return 1;
|
||||||
/* Timer is currently running */
|
|
||||||
/* Set new target */
|
|
||||||
_lptmr_set_counter(dev);
|
|
||||||
}
|
}
|
||||||
|
lptmr[dev].running = 0;
|
||||||
|
if (!(hw->CSR & LPTMR_CSR_TEN_MASK)) {
|
||||||
|
/* Timer is stopped */
|
||||||
|
irq_restore(mask);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* Disable interrupt, enable timer */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK;
|
||||||
|
/* Clear IRQ if it occurred during this function */
|
||||||
|
NVIC_ClearPendingIRQ(lptmr_config[dev].irqn);
|
||||||
irq_restore(mask);
|
irq_restore(mask);
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void lptmr_start(uint8_t dev)
|
static inline void lptmr_start(uint8_t dev)
|
||||||
{
|
{
|
||||||
if (lptmr[dev].running != 0) {
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
/* Timer already running */
|
if (hw->CSR & LPTMR_CSR_TEN_MASK) {
|
||||||
return;
|
/* Timer is running */
|
||||||
}
|
|
||||||
lptmr[dev].running = 1;
|
|
||||||
_lptmr_set_counter(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void lptmr_stop(uint8_t dev)
|
|
||||||
{
|
|
||||||
if (lptmr[dev].running == 0) {
|
|
||||||
/* Timer already stopped */
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Disable IRQs to avoid race with ISR */
|
/* Disable IRQs to avoid race with ISR */
|
||||||
unsigned int mask = irq_disable();
|
unsigned int mask = irq_disable();
|
||||||
lptmr[dev].running = 0;
|
/* ensure hardware is reset */
|
||||||
|
hw->CSR = 0;
|
||||||
|
if (lptmr[dev].running) {
|
||||||
|
/* set target */
|
||||||
|
hw->CMR = lptmr[dev].cmr;
|
||||||
|
/* enable interrupt and start timer */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK | LPTMR_CSR_TIE_MASK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* no target */
|
||||||
|
hw->CMR = 0;
|
||||||
|
/* Disable interrupt, enable timer */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK;
|
||||||
|
}
|
||||||
|
/* compensate for the reload delay when starting the timer */
|
||||||
|
lptmr[dev].cnr += LPTMR_RELOAD_OVERHEAD;
|
||||||
|
irq_restore(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lptmr_stop(uint8_t dev)
|
||||||
|
{
|
||||||
|
/* Disable IRQs to avoid race with ISR */
|
||||||
|
unsigned int mask = irq_disable();
|
||||||
LPTMR_Type *hw = lptmr_config[dev].dev;
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
/* latch the current timer value into CNR */
|
if (!(hw->CSR & LPTMR_CSR_TEN_MASK)) {
|
||||||
hw->CNR = 12345;
|
/* Timer is already stopped */
|
||||||
uint16_t cnr = hw->CNR;
|
return;
|
||||||
lptmr[dev].cmr = hw->CMR - cnr;
|
}
|
||||||
lptmr[dev].csr = hw->CSR;
|
/* Update state */
|
||||||
_lptmr_update_reference(dev);
|
/* Latch counter value */
|
||||||
/* Disable counter and clear interrupt flag */
|
hw->CNR = 0;
|
||||||
hw->CSR = LPTMR_CSR_TCF_MASK;
|
lptmr[dev].cnr += hw->CNR;
|
||||||
|
uint16_t timeout = hw->CMR - hw->CNR;
|
||||||
|
/* Disable timer */
|
||||||
|
hw->CSR = 0;
|
||||||
|
if (timeout > LPTMR_RELOAD_OVERHEAD) {
|
||||||
|
/* Compensate for the delay in reloading */
|
||||||
|
lptmr[dev].cmr = timeout - LPTMR_RELOAD_OVERHEAD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lptmr[dev].cmr = timeout;
|
||||||
|
}
|
||||||
/* Clear any pending IRQ */
|
/* Clear any pending IRQ */
|
||||||
NVIC_ClearPendingIRQ(lptmr_config[dev].irqn);
|
NVIC_ClearPendingIRQ(lptmr_config[dev].irqn);
|
||||||
irq_restore(mask);
|
irq_restore(mask);
|
||||||
@ -569,17 +598,17 @@ static inline void lptmr_irq_handler(tim_t tim)
|
|||||||
{
|
{
|
||||||
uint8_t dev = _lptmr_index(tim);
|
uint8_t dev = _lptmr_index(tim);
|
||||||
LPTMR_Type *hw = lptmr_config[dev].dev;
|
LPTMR_Type *hw = lptmr_config[dev].dev;
|
||||||
lptmr_t *lptmr_ctx = &lptmr[dev];
|
|
||||||
lptmr_ctx->cmr = LPTMR_MAX_VALUE;
|
|
||||||
_lptmr_set_counter(dev);
|
|
||||||
|
|
||||||
if (lptmr_ctx->isr_ctx.cb != NULL) {
|
lptmr[dev].running = 0;
|
||||||
lptmr_ctx->isr_ctx.cb(lptmr_ctx->isr_ctx.arg, 0);
|
/* Disable interrupt generation, keep timer running */
|
||||||
|
/* Do not clear TCF flag here, it is required for writing CMR without
|
||||||
|
* disabling timer first */
|
||||||
|
hw->CSR = LPTMR_CSR_TEN_MASK | LPTMR_CSR_TFC_MASK;
|
||||||
|
|
||||||
|
if (lptmr[dev].isr_ctx.cb != NULL) {
|
||||||
|
lptmr[dev].isr_ctx.cb(lptmr[dev].isr_ctx.arg, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear interrupt flag */
|
|
||||||
bit_set32(&hw->CSR, LPTMR_CSR_TCF_SHIFT);
|
|
||||||
|
|
||||||
cortexm_isr_end();
|
cortexm_isr_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user