2014-06-11 14:59:24 +02:00
|
|
|
/*
|
2016-03-01 13:57:29 +01:00
|
|
|
* Copyright (C) 2014-2016 Freie Universität Berlin
|
2014-06-11 14:59:24 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2017-10-01 21:55:59 +02:00
|
|
|
* @ingroup cpu_stm32_common
|
2017-06-22 15:43:17 +02:00
|
|
|
* @ingroup drivers_periph_timer
|
2014-06-11 14:59:24 +02:00
|
|
|
* @{
|
|
|
|
*
|
2015-05-22 07:34:41 +02:00
|
|
|
* @file
|
2014-06-11 14:59:24 +02:00
|
|
|
* @brief Low-level timer driver implementation
|
|
|
|
*
|
|
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
|
|
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
|
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cpu.h"
|
2016-03-01 13:57:29 +01:00
|
|
|
#include "periph/timer.h"
|
2014-06-11 14:59:24 +02:00
|
|
|
|
2017-01-27 09:31:07 +01:00
|
|
|
/**
|
|
|
|
* @brief Timer specific additional bus clock presacler
|
|
|
|
*
|
|
|
|
* This prescale factor is dependent on the actual APBx bus clock divider, if
|
|
|
|
* the APBx presacler is != 1, it is set to 2, if the APBx prescaler is == 1, it
|
|
|
|
* is set to 1.
|
|
|
|
*
|
|
|
|
* See reference manuals section 'reset and clock control'.
|
|
|
|
*/
|
|
|
|
static const uint8_t apbmul[] = {
|
|
|
|
#if (CLOCK_APB1 < CLOCK_CORECLOCK)
|
|
|
|
[APB1] = 2,
|
|
|
|
#else
|
|
|
|
[APB1] = 1,
|
|
|
|
#endif
|
|
|
|
#if (CLOCK_APB2 < CLOCK_CORECLOCK)
|
|
|
|
[APB2] = 2
|
|
|
|
#else
|
|
|
|
[APB2] = 1
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2014-06-11 14:59:24 +02:00
|
|
|
/**
|
2016-03-01 13:57:29 +01:00
|
|
|
* @brief Interrupt context for each configured timer
|
2014-06-11 14:59:24 +02:00
|
|
|
*/
|
2016-03-01 13:57:29 +01:00
|
|
|
static timer_isr_ctx_t isr_ctx[TIMER_NUMOF];
|
2014-06-11 14:59:24 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
/**
|
|
|
|
* @brief Get the timer device
|
|
|
|
*/
|
|
|
|
static inline TIM_TypeDef *dev(tim_t tim)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-03-01 13:57:29 +01:00
|
|
|
return timer_config[tim].dev;
|
|
|
|
}
|
2014-06-11 14:59:24 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
|
|
|
|
{
|
|
|
|
/* check if device is valid */
|
|
|
|
if (tim >= TIMER_NUMOF) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-06-11 14:59:24 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
/* remember the interrupt context */
|
|
|
|
isr_ctx[tim].cb = cb;
|
|
|
|
isr_ctx[tim].arg = arg;
|
2014-06-11 14:59:24 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
/* enable the peripheral clock */
|
2016-03-16 12:15:43 +01:00
|
|
|
periph_clk_en(timer_config[tim].bus, timer_config[tim].rcc_mask);
|
2014-09-29 10:16:31 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
/* configure the timer as upcounter in continuous mode */
|
|
|
|
dev(tim)->CR1 = 0;
|
|
|
|
dev(tim)->CR2 = 0;
|
2016-12-07 10:58:14 +01:00
|
|
|
dev(tim)->ARR = timer_config[tim].max;
|
2017-01-27 09:31:07 +01:00
|
|
|
|
|
|
|
/* set prescaler */
|
|
|
|
dev(tim)->PSC = (((periph_apb_clk(timer_config[tim].bus) *
|
|
|
|
apbmul[timer_config[tim].bus]) / freq) - 1);
|
2016-03-01 13:57:29 +01:00
|
|
|
/* generate an update event to apply our configuration */
|
|
|
|
dev(tim)->EGR = TIM_EGR_UG;
|
2014-06-11 14:59:24 +02:00
|
|
|
|
|
|
|
/* enable the timer's interrupt */
|
2017-01-14 15:34:53 +01:00
|
|
|
NVIC_EnableIRQ(timer_config[tim].irqn);
|
2016-03-01 13:57:29 +01:00
|
|
|
/* reset the counter and start the timer */
|
|
|
|
timer_start(tim);
|
2014-06-11 14:59:24 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
int timer_set_absolute(tim_t tim, int channel, unsigned int value)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-12-07 10:58:14 +01:00
|
|
|
if (channel >= TIMER_CHAN) {
|
2016-03-01 13:57:29 +01:00
|
|
|
return -1;
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
|
|
|
|
2016-12-07 10:58:14 +01:00
|
|
|
dev(tim)->CCR[channel] = (value & timer_config[tim].max);
|
2016-03-01 13:57:29 +01:00
|
|
|
dev(tim)->SR &= ~(TIM_SR_CC1IF << channel);
|
|
|
|
dev(tim)->DIER |= (TIM_DIER_CC1IE << channel);
|
2014-09-29 10:16:31 +02:00
|
|
|
|
2014-06-11 14:59:24 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
int timer_clear(tim_t tim, int channel)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-12-07 10:58:14 +01:00
|
|
|
if (channel >= TIMER_CHAN) {
|
2016-03-01 13:57:29 +01:00
|
|
|
return -1;
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
2016-03-01 13:57:29 +01:00
|
|
|
|
|
|
|
dev(tim)->DIER &= ~(TIM_DIER_CC1IE << channel);
|
2014-06-11 14:59:24 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
unsigned int timer_read(tim_t tim)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-03-01 13:57:29 +01:00
|
|
|
return (unsigned int)dev(tim)->CNT;
|
|
|
|
}
|
2015-09-05 01:18:36 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
void timer_start(tim_t tim)
|
|
|
|
{
|
|
|
|
dev(tim)->CR1 |= TIM_CR1_CEN;
|
|
|
|
}
|
2015-09-05 01:18:36 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
void timer_stop(tim_t tim)
|
|
|
|
{
|
|
|
|
dev(tim)->CR1 &= ~(TIM_CR1_CEN);
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
static inline void irq_handler(tim_t tim)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-03-01 13:57:29 +01:00
|
|
|
uint32_t status = (dev(tim)->SR & dev(tim)->DIER);
|
|
|
|
|
2017-01-06 14:33:04 +01:00
|
|
|
for (unsigned int i = 0; i < TIMER_CHAN; i++) {
|
2016-03-01 13:57:29 +01:00
|
|
|
if (status & (TIM_SR_CC1IF << i)) {
|
|
|
|
dev(tim)->DIER &= ~(TIM_DIER_CC1IE << i);
|
|
|
|
isr_ctx[tim].cb(isr_ctx[tim].arg, i);
|
|
|
|
}
|
|
|
|
}
|
2016-11-30 18:26:05 +01:00
|
|
|
cortexm_isr_end();
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
#ifdef TIMER_0_ISR
|
|
|
|
void TIMER_0_ISR(void)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-03-01 13:57:29 +01:00
|
|
|
irq_handler(0);
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
2016-03-01 13:57:29 +01:00
|
|
|
#endif
|
2014-06-11 14:59:24 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
#ifdef TIMER_1_ISR
|
|
|
|
void TIMER_1_ISR(void)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-03-01 13:57:29 +01:00
|
|
|
irq_handler(1);
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
#ifdef TIMER_2_ISR
|
|
|
|
void TIMER_2_ISR(void)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-03-01 13:57:29 +01:00
|
|
|
irq_handler(2);
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
#ifdef TIMER_3_ISR
|
|
|
|
void TIMER_3_ISR(void)
|
2014-06-11 14:59:24 +02:00
|
|
|
{
|
2016-03-01 13:57:29 +01:00
|
|
|
irq_handler(3);
|
|
|
|
}
|
|
|
|
#endif
|
2014-09-29 10:16:31 +02:00
|
|
|
|
2016-03-01 13:57:29 +01:00
|
|
|
#ifdef TIMER_4_ISR
|
|
|
|
void TIMER_4_ISR(void)
|
|
|
|
{
|
|
|
|
irq_handler(4);
|
2014-06-11 14:59:24 +02:00
|
|
|
}
|
2016-03-01 13:57:29 +01:00
|
|
|
#endif
|