2018-02-02 16:35:18 -04:00
|
|
|
/******************************************************************************
|
|
|
|
* The MIT License
|
|
|
|
|
|
|
|
(c) 2017 night_ghost@ykoctpa.ru
|
|
|
|
|
|
|
|
based on:
|
|
|
|
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011 LeafLabs, LLC.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person
|
|
|
|
* obtaining a copy of this software and associated documentation
|
|
|
|
* files (the "Software"), to deal in the Software without
|
|
|
|
* restriction, including without limitation the rights to use, copy,
|
|
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
|
|
* of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#pragma GCC optimize ("O2")
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file timer.c
|
|
|
|
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
|
|
|
* @brief New-style timer interface
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "timer.h"
|
|
|
|
#include "dma.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "nvic.h"
|
|
|
|
|
|
|
|
/* Just like the corresponding DIER bits:
|
|
|
|
* [0] = Update handler;
|
|
|
|
* [1,2,3,4] = capture/compare 1,2,3,4 handlers, respectively;
|
|
|
|
* [5] = COM;
|
|
|
|
* [6] = TRG;
|
|
|
|
* [7] = BRK. */
|
|
|
|
#define NR_ADV_HANDLERS 8
|
|
|
|
/* Update, capture/compare 1,2,3,4; <junk>; trigger. */
|
|
|
|
#define NR_GEN_HANDLERS 7
|
|
|
|
/* Update only. */
|
|
|
|
#define NR_BAS_HANDLERS 1
|
|
|
|
|
|
|
|
|
|
|
|
static Handler tim1_handlers[NR_ADV_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim2_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim3_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim4_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim5_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim6_handlers[NR_BAS_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim7_handlers[NR_BAS_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim8_handlers[NR_ADV_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim9_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim10_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim11_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim12_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim13_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
static Handler tim14_handlers[NR_GEN_HANDLERS] IN_CCM;
|
|
|
|
|
|
|
|
static timerState timer_state[14] IN_CCM;
|
|
|
|
|
|
|
|
const timer_dev timers[] = {
|
|
|
|
{ /** Timer 1 device (advanced) */
|
|
|
|
|
|
|
|
.regs = TIM1,
|
|
|
|
.clk = RCC_APB2Periph_TIM1,
|
|
|
|
.handlers = tim1_handlers,
|
|
|
|
.af = GPIO_AF_TIM1,
|
|
|
|
.type = TIMER_ADVANCED,
|
|
|
|
.n_handlers = NR_ADV_HANDLERS,
|
|
|
|
.bus = 1,
|
|
|
|
.id = 1,
|
|
|
|
.state = &timer_state[0],
|
|
|
|
.ch_dma = { // dma per channel: stream, channel
|
|
|
|
{ DMA2_STREAM1, 6 },
|
|
|
|
{ DMA2_STREAM2, 6 },
|
|
|
|
{ DMA2_STREAM6, 6 },
|
|
|
|
{ DMA2_STREAM4, 6 },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 2 device (general-purpose) */
|
|
|
|
.regs = TIM2,
|
|
|
|
.clk = RCC_APB1Periph_TIM2,
|
|
|
|
.handlers = tim2_handlers,
|
|
|
|
.af = GPIO_AF_TIM2,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 2,
|
|
|
|
.state = &timer_state[1],
|
|
|
|
.ch_dma = { // dma per channel: stream, channel
|
|
|
|
{ DMA1_STREAM5, 3 },
|
|
|
|
{ DMA1_STREAM6, 3 },
|
|
|
|
{ DMA1_STREAM1, 3 },
|
|
|
|
{ DMA1_STREAM7, 6 },
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 3 device (general-purpose) */
|
|
|
|
.regs = TIM3,
|
|
|
|
.clk = RCC_APB1Periph_TIM3,
|
|
|
|
.handlers = tim3_handlers,
|
|
|
|
.af = GPIO_AF_TIM3,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 3,
|
|
|
|
.state = &timer_state[2],
|
|
|
|
.ch_dma = { // dma per channel: stream, channel
|
|
|
|
{ DMA1_STREAM4, 5 },
|
|
|
|
{ DMA1_STREAM5, 5 },
|
|
|
|
{ DMA1_STREAM7, 5 },
|
|
|
|
{ DMA1_STREAM2, 5 },
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 4 device (general-purpose) */
|
|
|
|
.regs = TIM4,
|
|
|
|
.clk = RCC_APB1Periph_TIM4,
|
|
|
|
.handlers = tim4_handlers,
|
|
|
|
.af = GPIO_AF_TIM4,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 4,
|
|
|
|
.state = &timer_state[3],
|
|
|
|
.ch_dma = { // dma per channel: stream, channel
|
|
|
|
{ DMA1_STREAM0, 2 },
|
|
|
|
{ DMA1_STREAM3, 2 },
|
|
|
|
{ DMA1_STREAM7, 2 },
|
|
|
|
{ -1, -1 },
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 5 device (general-purpose) */
|
|
|
|
.regs = TIM5,
|
|
|
|
.clk = RCC_APB1Periph_TIM5,
|
|
|
|
.handlers = tim5_handlers,
|
|
|
|
.af = GPIO_AF_TIM5,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 5,
|
|
|
|
.state = &timer_state[4],
|
|
|
|
.ch_dma = { // dma per channel: stream, channel
|
|
|
|
{ DMA1_STREAM2, 6 },
|
|
|
|
{ DMA1_STREAM4, 6 },
|
|
|
|
{ DMA1_STREAM0, 6 },
|
|
|
|
{ DMA1_STREAM1, 6 },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 6 device (basic) */
|
|
|
|
.regs = TIM6,
|
|
|
|
.clk = RCC_APB1Periph_TIM6,
|
|
|
|
.handlers = tim6_handlers,
|
|
|
|
.af = 0,
|
|
|
|
.type = TIMER_BASIC,
|
|
|
|
.n_handlers = NR_BAS_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 6,
|
|
|
|
.state = &timer_state[5],
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 7 device (basic) */
|
|
|
|
.regs = TIM7,
|
|
|
|
.clk = RCC_APB1Periph_TIM7,
|
|
|
|
.handlers = tim7_handlers,
|
|
|
|
.af = 0,
|
|
|
|
.type = TIMER_BASIC,
|
|
|
|
.n_handlers = NR_BAS_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 7,
|
|
|
|
.state = &timer_state[6],
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 8 device (advanced) */
|
|
|
|
.regs = TIM8,
|
|
|
|
.clk = RCC_APB2Periph_TIM8,
|
|
|
|
.handlers = tim8_handlers,
|
|
|
|
.af = GPIO_AF_TIM8,
|
|
|
|
.type = TIMER_ADVANCED,
|
|
|
|
.n_handlers = NR_ADV_HANDLERS,
|
|
|
|
.bus = 1,
|
|
|
|
.id = 8,
|
|
|
|
.state = &timer_state[7],
|
|
|
|
.ch_dma = { // dma per channel: stream, channel
|
|
|
|
{ DMA2_STREAM2, 7 },
|
|
|
|
{ DMA2_STREAM3, 7 },
|
|
|
|
{ DMA2_STREAM4, 7 },
|
|
|
|
{ DMA2_STREAM7, 7 },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// another timers don't has DMA
|
|
|
|
|
|
|
|
{ /** Timer 9 device (general-purpose) */
|
|
|
|
.regs = TIM9,
|
|
|
|
.clk = RCC_APB2Periph_TIM9,
|
|
|
|
.handlers = tim9_handlers,
|
|
|
|
.af = GPIO_AF_TIM9,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 1,
|
|
|
|
.id = 9,
|
|
|
|
.state = &timer_state[8],
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 10 device (general-purpose) */
|
|
|
|
.regs = TIM10,
|
|
|
|
.clk = RCC_APB2Periph_TIM10,
|
|
|
|
.handlers = tim10_handlers,
|
|
|
|
.af = GPIO_AF_TIM10,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 1,
|
|
|
|
.id = 10,
|
|
|
|
.state = &timer_state[9],
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 11 device (general-purpose) */
|
|
|
|
.regs = TIM11,
|
|
|
|
.clk = RCC_APB2Periph_TIM11,
|
|
|
|
.handlers = tim11_handlers,
|
|
|
|
.af = GPIO_AF_TIM11,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 1,
|
|
|
|
.id = 11,
|
|
|
|
.state = &timer_state[10],
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 12 device (general-purpose) */
|
|
|
|
.regs = TIM12,
|
|
|
|
.clk = RCC_APB1Periph_TIM12,
|
|
|
|
.handlers = tim12_handlers,
|
|
|
|
.af = GPIO_AF_TIM12,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 12,
|
|
|
|
.state = &timer_state[11],
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 13 device (general-purpose) */
|
|
|
|
.regs = TIM13,
|
|
|
|
.clk = RCC_APB1Periph_TIM13,
|
|
|
|
.handlers = tim13_handlers,
|
|
|
|
.af = GPIO_AF_TIM13,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 13,
|
|
|
|
.state = &timer_state[12],
|
|
|
|
},
|
|
|
|
|
|
|
|
{ /** Timer 14 device (general-purpose) */
|
|
|
|
.regs = TIM14,
|
|
|
|
.clk = RCC_APB1Periph_TIM14,
|
|
|
|
.handlers = tim14_handlers,
|
|
|
|
.af = GPIO_AF_TIM14,
|
|
|
|
.type = TIMER_GENERAL,
|
|
|
|
.n_handlers = NR_GEN_HANDLERS,
|
|
|
|
.bus = 0,
|
|
|
|
.id = 14,
|
|
|
|
.state = &timer_state[13],
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convenience routines
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void disable_channel(const timer_dev *dev, uint8_t channel);
|
|
|
|
static void pwm_mode(const timer_dev *dev, uint8_t channel);
|
|
|
|
static void output_compare_mode(const timer_dev *dev, uint8_t channel);
|
|
|
|
|
|
|
|
static inline void enable_irq(const timer_dev *dev, uint8_t interrupt, uint8_t priority);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize a timer (enable timer clock)
|
|
|
|
* @param dev Timer to initialize
|
|
|
|
*/
|
|
|
|
void timer_init(const timer_dev *dev) {
|
|
|
|
|
|
|
|
if(dev->bus)
|
|
|
|
RCC_APB2PeriphClockCmd(dev->clk, ENABLE);
|
|
|
|
else
|
|
|
|
RCC_APB1PeriphClockCmd(dev->clk, ENABLE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize a timer, and reset its register map.
|
|
|
|
* @param dev Timer to initialize
|
|
|
|
*/
|
|
|
|
void timer_reset(const timer_dev *dev) {
|
|
|
|
memset(dev->handlers, 0, dev->n_handlers * sizeof(Handler));
|
|
|
|
memset(dev->state, 0, sizeof(*dev->state));
|
|
|
|
|
|
|
|
if(dev->bus) {
|
|
|
|
RCC_APB2PeriphClockCmd(dev->clk, ENABLE);
|
|
|
|
RCC_APB2PeriphResetCmd(dev->clk, ENABLE);
|
|
|
|
RCC_APB2PeriphResetCmd(dev->clk, DISABLE);
|
|
|
|
} else {
|
|
|
|
RCC_APB1PeriphClockCmd(dev->clk, ENABLE);
|
|
|
|
RCC_APB1PeriphResetCmd(dev->clk, ENABLE);
|
|
|
|
RCC_APB1PeriphResetCmd(dev->clk, DISABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Disable a timer.
|
|
|
|
*
|
|
|
|
* The timer will stop counting, all DMA requests and interrupts will
|
|
|
|
* be disabled, and no state changes will be output.
|
|
|
|
*
|
|
|
|
* @param dev Timer to disable.
|
|
|
|
*/
|
|
|
|
void timer_disable(const timer_dev *dev) {
|
|
|
|
dev->regs->CR1 = 0;
|
|
|
|
dev->regs->DIER = 0;
|
|
|
|
|
|
|
|
switch (dev->type) {
|
|
|
|
case TIMER_ADVANCED:
|
|
|
|
case TIMER_GENERAL:
|
|
|
|
(dev->regs)->CCER = 0;
|
|
|
|
break;
|
|
|
|
case TIMER_BASIC:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// initial configuration - set required frequency (in kHz) and period (in ticks)
|
|
|
|
// returns real timers freq
|
|
|
|
uint32_t configTimeBase(const timer_dev *dev, uint16_t period, uint16_t khz)
|
|
|
|
{
|
|
|
|
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
|
|
|
|
TIM_TypeDef *tim = dev->regs;
|
|
|
|
|
|
|
|
timer_init(dev); // turn it on
|
|
|
|
|
|
|
|
timer_pause(dev);
|
|
|
|
|
|
|
|
dev->regs->CR1 = TIMER_CR1_ARPE;
|
|
|
|
dev->regs->PSC = 1;
|
|
|
|
dev->regs->SR = 0;
|
|
|
|
dev->regs->DIER = 0;
|
|
|
|
dev->regs->EGR = TIMER_EGR_UG;
|
|
|
|
|
|
|
|
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
|
|
|
|
|
|
|
|
TIM_TimeBaseStructure.TIM_Period = (period - 1) & get_timer_mask(dev); // AKA TIMx_ARR
|
|
|
|
uint32_t freq = (uint32_t)khz * 1000;
|
|
|
|
uint16_t prescaler;
|
|
|
|
uint32_t tf; // timer's frequency
|
|
|
|
|
|
|
|
if (tim == TIM1 || tim == TIM8 || tim == TIM9 || tim == TIM10 || tim == TIM11) { // 168MHz
|
|
|
|
tf = SystemCoreClock;
|
|
|
|
} else { // 84 MHz
|
|
|
|
tf = SystemCoreClock / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
prescaler = ((tf + freq/4) / freq) - 1; // ==41 for 2MHz
|
|
|
|
|
|
|
|
TIM_TimeBaseStructure.TIM_Prescaler = prescaler;
|
|
|
|
freq = tf / prescaler; // real timer's frequency
|
|
|
|
|
|
|
|
dev->state->freq = freq; // store
|
|
|
|
|
|
|
|
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
|
|
|
|
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
|
|
|
|
TIM_TimeBaseInit(tim, &TIM_TimeBaseStructure);
|
|
|
|
|
|
|
|
switch (dev->type) {
|
|
|
|
case TIMER_ADVANCED:
|
|
|
|
dev->regs->BDTR = TIMER_BDTR_MOE | TIMER_BDTR_LOCK_OFF; // break and dead-time register, enable output
|
|
|
|
// fall-through
|
|
|
|
case TIMER_GENERAL:
|
|
|
|
case TIMER_BASIC:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
timer_set_count(dev,0);
|
|
|
|
|
|
|
|
dev->state->freq_scale = (float)freq / (khz * 1000); // remember ratio for correction
|
|
|
|
|
|
|
|
return freq;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void disable_channel(const timer_dev *dev, timer_Channel channel) {
|
|
|
|
timer_detach_interrupt(dev, channel);
|
|
|
|
timer_cc_disable(dev, channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pwm_mode(const timer_dev *dev, timer_Channel channel) {
|
|
|
|
timer_disable_irq(dev, channel);
|
|
|
|
timer_oc_set_mode(dev, channel, BOARD_PWM_MODE, TIMER_OC_PE);
|
|
|
|
timer_cc_enable(dev, channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void output_compare_mode(const timer_dev *dev, timer_Channel channel) {
|
|
|
|
timer_oc_set_mode(dev, channel, TIMER_OC_MODE_ACTIVE_ON_MATCH, 0);
|
|
|
|
timer_cc_enable(dev, channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the mode of an individual timer channel.
|
|
|
|
*
|
|
|
|
* Note that not all timers can be configured in every mode. For
|
|
|
|
* example, basic timers cannot be configured to output compare mode.
|
|
|
|
* Be sure to use a timer which is appropriate for the mode you want.
|
|
|
|
*
|
|
|
|
* @param dev Timer whose channel mode to set
|
|
|
|
* @param channel Relevant channel
|
|
|
|
* @param mode New timer mode for channel
|
|
|
|
*/
|
|
|
|
void timer_set_mode(const timer_dev *dev, timer_Channel channel, timer_mode mode) {
|
|
|
|
assert_param(channel > 0 && channel <= 4);
|
|
|
|
|
|
|
|
/* TODO decide about the basic timers */
|
2018-04-16 07:00:15 -03:00
|
|
|
if (!dev || dev->type == TIMER_BASIC)
|
2018-02-02 16:35:18 -04:00
|
|
|
return;
|
2018-06-20 12:28:21 -03:00
|
|
|
|
|
|
|
assert_param(dev->type != TIMER_BASIC);
|
2018-02-02 16:35:18 -04:00
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case TIMER_DISABLED:
|
|
|
|
disable_channel(dev, channel);
|
|
|
|
break;
|
|
|
|
case TIMER_PWM:
|
|
|
|
pwm_mode(dev, channel);
|
|
|
|
break;
|
|
|
|
case TIMER_OUTPUT_COMPARE:
|
|
|
|
output_compare_mode(dev, channel);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Call a function on timer devices.
|
|
|
|
* @param fn Function to call on each timer device.
|
|
|
|
*/
|
|
|
|
void timer_foreach(void (*fn)(const timer_dev*)) {
|
|
|
|
uint8_t i;
|
|
|
|
for(i=0; i<(sizeof(timers)/sizeof(timer_dev)); i++){
|
|
|
|
fn(&timers[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Attach a timer interrupt.
|
|
|
|
* @param dev Timer device
|
|
|
|
* @param interrupt Interrupt number to attach to; this may be any
|
|
|
|
* timer_interrupt_id or timer_channel value appropriate
|
|
|
|
* for the timer.
|
|
|
|
* @param handler Handler to attach to the given interrupt.
|
|
|
|
* @see timer_interrupt_id
|
|
|
|
* @see timer_channel
|
|
|
|
*/
|
|
|
|
void timer_attach_interrupt(const timer_dev *dev, timer_interrupt_id interrupt, Handler handler, uint8_t priority) {
|
|
|
|
if(interrupt>=dev->n_handlers) return;
|
|
|
|
|
|
|
|
dev->handlers[interrupt] = handler;
|
|
|
|
timer_enable_irq(dev, interrupt);
|
|
|
|
enable_irq(dev, interrupt, priority);
|
|
|
|
}
|
|
|
|
|
|
|
|
// attach all timer's interrupts to one handler - for PWM/PPM input
|
|
|
|
void timer_attach_all_interrupts(const timer_dev *dev, Handler handler) {
|
|
|
|
uint16_t i;
|
|
|
|
for(i=0; i < dev->n_handlers; i++) {
|
|
|
|
dev->handlers[i] = handler;
|
|
|
|
}
|
|
|
|
// enabling by caller
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Detach a timer interrupt.
|
|
|
|
* @param dev Timer device
|
|
|
|
* @param interrupt Interrupt number to detach; this may be any
|
|
|
|
* timer_interrupt_id or timer_channel value appropriate
|
|
|
|
* for the timer.
|
|
|
|
* @see timer_interrupt_id
|
|
|
|
* @see timer_channel
|
|
|
|
*/
|
|
|
|
void timer_detach_interrupt(const timer_dev *dev, timer_interrupt_id interrupt) {
|
|
|
|
timer_disable_irq(dev, interrupt);
|
|
|
|
if(interrupt>=dev->n_handlers) return;
|
|
|
|
dev->handlers[interrupt] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IRQ handlers
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline void dispatch_adv_brk(const timer_dev *dev);
|
|
|
|
static inline void dispatch_adv_up(const timer_dev *dev);
|
|
|
|
static inline void dispatch_adv_trg_com(const timer_dev *dev);
|
|
|
|
static inline void dispatch_adv_cc(const timer_dev *dev);
|
|
|
|
static inline void dispatch_general(const timer_dev *dev);
|
|
|
|
static inline void dispatch_general_h(const timer_dev *dev);
|
|
|
|
static inline void dispatch_basic(const timer_dev *dev);
|
|
|
|
|
|
|
|
void TIM1_BRK_TIM9_IRQHandler(void);
|
|
|
|
void TIM1_UP_TIM10_IRQHandler(void);
|
|
|
|
void TIM1_TRG_COM_TIM11_IRQHandler(void);
|
|
|
|
void TIM1_CC_IRQHandler(void);
|
|
|
|
void TIM2_IRQHandler(void);
|
|
|
|
void TIM3_IRQHandler(void);
|
|
|
|
void TIM4_IRQHandler(void);
|
|
|
|
void TIM5_IRQHandler(void);
|
|
|
|
void TIM6_DAC_IRQHandler(void);
|
|
|
|
void TIM7_IRQHandler(void);
|
|
|
|
void TIM8_BRK_TIM12_IRQHandler(void);
|
|
|
|
void TIM8_CC_IRQHandler(void);
|
|
|
|
void TIM8_UP_TIM13_IRQHandler(void);
|
|
|
|
void TIM8_TRG_COM_TIM14_IRQHandler(void);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void TIM1_BRK_TIM9_IRQHandler(void)
|
|
|
|
{
|
|
|
|
dispatch_adv_brk(TIMER1);
|
|
|
|
dispatch_general_h(TIMER9);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM1_UP_TIM10_IRQHandler(void) {
|
|
|
|
dispatch_adv_up(TIMER1);
|
|
|
|
dispatch_general_h(TIMER10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM1_TRG_COM_TIM11_IRQHandler(void) {
|
|
|
|
dispatch_adv_trg_com(TIMER1);
|
|
|
|
dispatch_general_h(TIMER11);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM1_CC_IRQHandler(void) {
|
|
|
|
dispatch_adv_cc(TIMER1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM2_IRQHandler(void) {
|
|
|
|
dispatch_general(TIMER2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM3_IRQHandler(void) {
|
|
|
|
dispatch_general(TIMER3);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM4_IRQHandler(void) {
|
|
|
|
dispatch_general(TIMER4);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM5_IRQHandler(void) {
|
|
|
|
dispatch_general(TIMER5);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM6_DAC_IRQHandler(void) {
|
|
|
|
dispatch_basic(TIMER6);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM7_IRQHandler(void) {
|
|
|
|
dispatch_basic(TIMER7);
|
|
|
|
}
|
|
|
|
|
|
|
|
// used in PWM
|
|
|
|
void TIM8_BRK_TIM12_IRQHandler(void) { // used in PWM tim12
|
|
|
|
dispatch_adv_brk(TIMER8);
|
|
|
|
dispatch_general_h(TIMER12);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM8_CC_IRQHandler(void) { // used in PWM tim8
|
|
|
|
dispatch_adv_cc(TIMER8);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TIM8_UP_TIM13_IRQHandler(void) { // not conflicts with PWM
|
|
|
|
dispatch_adv_up(TIMER8);
|
|
|
|
dispatch_general_h(TIMER13);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIM8_TRG_COM_TIM14_IRQHandler(void) {
|
|
|
|
dispatch_adv_trg_com(TIMER8);
|
|
|
|
dispatch_general_h(TIMER14);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Note: the following dispatch routines make use of the fact that
|
|
|
|
* DIER interrupt enable bits and SR interrupt flags have common bit
|
|
|
|
* positions. Thus, ANDing DIER and SR lets us check if an interrupt
|
|
|
|
* is enabled and if it has occurred simultaneously.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static INLINE void dispatch_single_irq(const timer_dev *dev,
|
|
|
|
timer_interrupt_id iid,
|
|
|
|
uint32_t irq_mask) {
|
|
|
|
|
|
|
|
uint32_t dsr = dev->regs->DIER & dev->regs->SR & irq_mask;
|
|
|
|
if (dsr) {
|
|
|
|
|
|
|
|
Handler handler = (dev->handlers)[iid];
|
|
|
|
if (handler) {
|
|
|
|
revo_call_handler(handler, (uint32_t)dev->regs);
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->regs->SR &= ~dsr; // reset IRQ inspite of installed handler! @NG
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For dispatch routines which service multiple interrupts. */
|
|
|
|
static INLINE void handle_irq(const timer_dev *dev, uint32_t dier_sr, uint32_t irq_mask, uint32_t iid) {
|
|
|
|
if (dier_sr & irq_mask) {
|
|
|
|
Handler handler = (dev->handlers)[iid];
|
|
|
|
if (handler) {
|
|
|
|
revo_call_handler(handler, (uint32_t)dev->regs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dispatch_adv_brk(const timer_dev *dev) {
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
uint32_t t=stopwatch_getticks();
|
|
|
|
#endif
|
|
|
|
dispatch_single_irq(dev, TIMER_BREAK_INTERRUPT, TIMER_SR_BIF);
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
t = stopwatch_getticks() - t;
|
|
|
|
isr_time += t;
|
|
|
|
if(t>max_isr_time) max_isr_time=t;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dispatch_adv_up(const timer_dev *dev) {
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
uint32_t t=stopwatch_getticks();
|
|
|
|
#endif
|
|
|
|
dispatch_single_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF);
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
t = stopwatch_getticks() - t;
|
|
|
|
isr_time += t;
|
|
|
|
if(t>max_isr_time) max_isr_time=t;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dispatch_adv_trg_com(const timer_dev *dev) {
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
uint32_t t=stopwatch_getticks();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32_t dsr = dev->regs->DIER & dev->regs->SR;
|
|
|
|
/* Logical OR of SR interrupt flags we end up
|
|
|
|
* handling. We clear these. User handlers
|
|
|
|
* must clear overcapture flags, to avoid
|
|
|
|
* wasting time in output mode. */
|
|
|
|
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_TIF, TIMER_TRG_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_COMIF, TIMER_COM_INTERRUPT);
|
|
|
|
|
|
|
|
dev->regs->SR &= ~dsr; // handled ALL enabled interrupts! AFTER ISR itself!
|
|
|
|
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
t = stopwatch_getticks() - t;
|
|
|
|
isr_time += t;
|
|
|
|
if(t>max_isr_time) max_isr_time=t;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dispatch_adv_cc(const timer_dev *dev) {
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
uint32_t t=stopwatch_getticks();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32_t dsr = dev->regs->DIER & dev->regs->SR;
|
|
|
|
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC4IF, TIMER_CC4_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC3IF, TIMER_CC3_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC2IF, TIMER_CC2_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC1IF, TIMER_CC1_INTERRUPT);
|
|
|
|
|
|
|
|
dev->regs->SR &= ~dsr; // handled ALL enabled interrupts! AFTER ISR itself!
|
|
|
|
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
t = stopwatch_getticks() - t;
|
|
|
|
isr_time += t;
|
|
|
|
if(t>max_isr_time) max_isr_time=t;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dispatch_general(const timer_dev *dev) {
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
uint32_t t=stopwatch_getticks();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32_t dsr = dev->regs->DIER & dev->regs->SR;
|
|
|
|
|
|
|
|
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_TIF, TIMER_TRG_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC4IF, TIMER_CC4_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC3IF, TIMER_CC3_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC2IF, TIMER_CC2_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC1IF, TIMER_CC1_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_UIF, TIMER_UPDATE_INTERRUPT);
|
|
|
|
|
|
|
|
dev->regs->SR &= ~dsr; // handled ALL enabled interrupts! AFTER ISR itself!
|
|
|
|
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
t = stopwatch_getticks() - t;
|
|
|
|
isr_time += t;
|
|
|
|
if(t>max_isr_time) max_isr_time=t;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dispatch_general_h(const timer_dev *dev) {
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
uint32_t t=stopwatch_getticks();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32_t dsr = dev->regs->DIER & dev->regs->SR;
|
|
|
|
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_TIF, TIMER_TRG_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC2IF, TIMER_CC2_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_CC1IF, TIMER_CC1_INTERRUPT);
|
|
|
|
handle_irq(dev, dsr, TIMER_SR_UIF, TIMER_UPDATE_INTERRUPT);
|
|
|
|
|
|
|
|
dev->regs->SR &= ~dsr; // handled ALL enabled interrupts! AFTER ISR itself!
|
|
|
|
|
|
|
|
#ifdef ISR_PERF
|
|
|
|
t = stopwatch_getticks() - t;
|
|
|
|
isr_time += t;
|
|
|
|
if(t>max_isr_time) max_isr_time=t;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// don't count time of basic timer because it used only as timer scheduler's interrupt
|
|
|
|
static inline void dispatch_basic(const timer_dev *dev) {
|
|
|
|
dispatch_single_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Utilities
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void enable_advanced_irq(const timer_dev *dev, timer_interrupt_id id, uint8_t priority);
|
|
|
|
|
|
|
|
#define PRIO_DISABLE_FLAG 0x80
|
|
|
|
|
|
|
|
static inline void enable_irq(const timer_dev *dev, timer_interrupt_id iid, uint8_t priority) {
|
|
|
|
|
|
|
|
IRQn_Type irq;
|
|
|
|
|
|
|
|
switch(dev->id){
|
|
|
|
case 1: /* 1 - advanced */ enable_advanced_irq(dev, iid, priority); return;
|
|
|
|
case 2: irq=TIM2_IRQn; break;
|
|
|
|
case 3: irq=TIM3_IRQn; break;
|
|
|
|
case 4: irq=TIM4_IRQn; break;
|
|
|
|
case 5: irq=TIM5_IRQn; break;
|
|
|
|
case 6: irq=TIM6_DAC_IRQn; break;
|
|
|
|
case 7: irq=TIM7_IRQn; break;
|
|
|
|
case 8: /* 8 - advanced */ enable_advanced_irq(dev, iid, priority); return;
|
|
|
|
case 9: irq=TIM1_BRK_TIM9_IRQn; break;
|
|
|
|
case 10: irq=TIM1_UP_TIM10_IRQn; break;
|
|
|
|
case 11: irq=TIM1_TRG_COM_TIM11_IRQn; break;
|
|
|
|
case 12: irq=TIM8_BRK_TIM12_IRQn; break;
|
|
|
|
case 13: irq=TIM8_UP_TIM13_IRQn; break;
|
|
|
|
case 14: irq=TIM8_TRG_COM_TIM14_IRQn; break;
|
|
|
|
|
|
|
|
default: return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(priority & PRIO_DISABLE_FLAG){
|
|
|
|
NVIC_DisableIRQ(irq);
|
|
|
|
}else {
|
|
|
|
enable_nvic_irq(irq, priority);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void enable_advanced_irq(const timer_dev *dev, timer_interrupt_id id, uint8_t priority) {
|
|
|
|
uint8_t is_timer1 = (dev->id == 1);
|
|
|
|
|
|
|
|
IRQn_Type irq=0;
|
|
|
|
|
|
|
|
switch (id) {
|
|
|
|
case TIMER_UPDATE_INTERRUPT:
|
|
|
|
irq = is_timer1 ? TIM1_UP_TIM10_IRQn : TIM8_UP_TIM13_IRQn;
|
|
|
|
break;
|
|
|
|
case TIMER_CC1_INTERRUPT:
|
|
|
|
case TIMER_CC2_INTERRUPT:
|
|
|
|
case TIMER_CC3_INTERRUPT:
|
|
|
|
case TIMER_CC4_INTERRUPT:
|
|
|
|
irq = is_timer1 ? TIM1_CC_IRQn : TIM8_CC_IRQn;
|
|
|
|
break;
|
|
|
|
case TIMER_COM_INTERRUPT:
|
|
|
|
case TIMER_TRG_INTERRUPT:
|
|
|
|
irq = is_timer1 ? TIM1_TRG_COM_TIM11_IRQn : TIM8_TRG_COM_TIM14_IRQn;
|
|
|
|
break;
|
|
|
|
case TIMER_BREAK_INTERRUPT:
|
|
|
|
irq = is_timer1 ? TIM1_BRK_TIM9_IRQn : TIM8_BRK_TIM12_IRQn;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(!irq) return;
|
|
|
|
if(priority & PRIO_DISABLE_FLAG){
|
|
|
|
NVIC_DisableIRQ(irq);
|
|
|
|
}else {
|
|
|
|
NVIC_EnableIRQ(irq);
|
|
|
|
NVIC_SetPriority(irq,priority);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void timer_enable_NVICirq(const timer_dev *dev, uint8_t interrupt, uint8_t priority) {
|
|
|
|
enable_irq(dev,interrupt, priority);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void timer_disable_NVICirq(const timer_dev *dev, uint8_t interrupt){
|
|
|
|
enable_irq(dev,interrupt, PRIO_DISABLE_FLAG);
|
|
|
|
}
|