mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
93ed3cd9d6
The current xmega don't have a way to disable peripherals that are not in used. Add peripheral management to allow enable only the mcu blocks that will be used by application. This saves power on active and sleep modes. By default, at clock initialization, all peripherals are now disabled and each drive must activate at initialization phase. The periph_timer and periph_uart were updated with this new feature. Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
539 lines
10 KiB
C
539 lines
10 KiB
C
/*
|
|
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
|
|
*
|
|
* 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_atxmega
|
|
* @ingroup cpu_atxmega_periph
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Low-level TIMER driver implementation
|
|
*
|
|
* @author Gerson Fernando Budke <nandojve@gmail.com>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <avr/interrupt.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include "cpu.h"
|
|
#include "cpu_pm.h"
|
|
#include "thread.h"
|
|
|
|
#include "periph/timer.h"
|
|
|
|
#include "board.h"
|
|
#include "periph_conf.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* @brief We have 7 possible prescaler values
|
|
*/
|
|
#define PRESCALE_NUMOF (7U)
|
|
|
|
/**
|
|
* @brief Possible prescaler values, encoded as 2 ^ val
|
|
*/
|
|
static const uint8_t prescalers[] = { 0, 1, 2, 3, 6, 8, 10 };
|
|
|
|
/**
|
|
* @brief Timer state context
|
|
*/
|
|
typedef struct {
|
|
timer_cb_t cb; /**< interrupt callback */
|
|
void *arg; /**< interrupt callback argument */
|
|
uint8_t prescaler; /**< remember the prescaler value */
|
|
uint8_t channels; /**< number of channels */
|
|
} ctx_t;
|
|
|
|
/**
|
|
* @brief Allocate memory for saving the device states
|
|
* @{
|
|
*/
|
|
#ifdef TIMER_NUMOF
|
|
static ctx_t ctx[TIMER_NUMOF] = { { NULL } };
|
|
#else
|
|
/* fallback if no timer is configured */
|
|
static ctx_t *ctx[] = { { NULL } };
|
|
#endif
|
|
/** @} */
|
|
|
|
static uint32_t _oneshot;
|
|
|
|
static inline void set_oneshot(tim_t tim, int chan)
|
|
{
|
|
_oneshot |= (1 << chan) << (TIMER_CH_MAX_NUMOF * tim);
|
|
}
|
|
|
|
static inline void clear_oneshot(tim_t tim, int chan)
|
|
{
|
|
_oneshot &= ~((1 << chan) << (TIMER_CH_MAX_NUMOF * tim));
|
|
}
|
|
|
|
static inline bool is_oneshot(tim_t tim, int chan)
|
|
{
|
|
return _oneshot & ((1 << chan) << (TIMER_CH_MAX_NUMOF * tim));
|
|
}
|
|
|
|
/**
|
|
* @brief Setup the given timer
|
|
*/
|
|
int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
|
|
{
|
|
DEBUG("timer.c: freq = %ld, Core Clock = %ld\n", freq, CLOCK_CORECLOCK);
|
|
|
|
TC0_t *dev;
|
|
uint8_t pre;
|
|
uint8_t ch;
|
|
|
|
assert(TIMER_CH_MAX_NUMOF * TIMER_NUMOF < 32);
|
|
|
|
/* make sure given device is valid */
|
|
if (tim >= TIMER_NUMOF) {
|
|
return -1;
|
|
}
|
|
|
|
/* figure out if freq is applicable */
|
|
for (pre = 0; pre < PRESCALE_NUMOF; pre++) {
|
|
if ((CLOCK_CORECLOCK >> prescalers[pre]) == freq) {
|
|
break;
|
|
}
|
|
}
|
|
if (pre == PRESCALE_NUMOF) {
|
|
DEBUG("timer.c: prescaling failed!\n");
|
|
return -1;
|
|
}
|
|
|
|
pm_periph_enable(timer_config[tim].pwr);
|
|
|
|
dev = timer_config[tim].dev;
|
|
|
|
/* stop and reset timer */
|
|
dev->CTRLA = 0; /* Stop */
|
|
dev->CTRLFSET = TC_CMD_RESET_gc; /* Force Reset */
|
|
|
|
/* save interrupt context and timer Prescaler */
|
|
ctx[tim].cb = cb;
|
|
ctx[tim].arg = arg;
|
|
ctx[tim].prescaler = (0x07 & (pre + 1));
|
|
|
|
/* Check enabled channels */
|
|
ctx[tim].channels = 0;
|
|
for (ch = 0; ch < TIMER_CH_MAX_NUMOF; ch++) {
|
|
if (timer_config[tim].int_lvl[ch] != CPU_INT_LVL_OFF) {
|
|
ctx[tim].channels++;
|
|
}
|
|
}
|
|
|
|
if (timer_config[tim].type != TC_TYPE_0
|
|
&& timer_config[tim].type != TC_TYPE_4) {
|
|
if (ctx[tim].channels > 2) {
|
|
DEBUG("timer.c: wrong number of channels. max value is 2.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (timer_config[tim].type == TC_TYPE_2) {
|
|
DEBUG("timer.c: Timer version %d is current not supported.\n",
|
|
timer_config[tim].type);
|
|
return -1;
|
|
}
|
|
|
|
/* Normal Counter with rollover */
|
|
dev->CTRLB = TC_WGMODE_NORMAL_gc;
|
|
|
|
/* Compare or Capture disable all channels */
|
|
dev->INTCTRLB = 0;
|
|
|
|
/* Free running counter */
|
|
dev->PER = 0xFFFF;
|
|
|
|
DEBUG("timer.c: prescaler set to %d \n", ctx[tim].prescaler);
|
|
dev->CTRLA = ctx[tim].prescaler;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int timer_set_absolute(tim_t tim, int channel, unsigned int value)
|
|
{
|
|
if (tim >= TIMER_NUMOF) {
|
|
return -1;
|
|
}
|
|
|
|
if (channel >= ctx[tim].channels) {
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("Setting timer %i channel %i to %04x\n", tim, channel, value);
|
|
|
|
TC0_t *dev = timer_config[tim].dev;
|
|
|
|
dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
|
|
dev->INTFLAGS |= TC0_CCAIF_bm << channel;
|
|
|
|
uint8_t irq_state = irq_disable();
|
|
|
|
*(((uint16_t *)(&dev->CCA)) + channel) = (uint16_t)value;
|
|
|
|
irq_restore(irq_state);
|
|
set_oneshot(tim, channel);
|
|
|
|
dev->INTCTRLB |= (timer_config[tim].int_lvl[channel] << (channel * 2));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags)
|
|
{
|
|
(void)flags;
|
|
|
|
if (tim >= TIMER_NUMOF) {
|
|
return -1;
|
|
}
|
|
|
|
if (channel > 0 || ctx[tim].channels != 1) {
|
|
DEBUG("Only channel 0 can be set as periodic and channels must be 1\n");
|
|
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("Setting timer %i channel 0 to %i and flags %i (repeating)\n",
|
|
tim, value, flags);
|
|
|
|
TC0_t *dev = timer_config[tim].dev;
|
|
uint8_t irq_state = irq_disable();
|
|
|
|
dev->CTRLA = 0;
|
|
dev->CTRLFSET = TC_CMD_RESET_gc;
|
|
dev->CTRLB = TC_WGMODE_FRQ_gc;
|
|
dev->INTCTRLB = 0;
|
|
dev->INTFLAGS |= TC0_CCAIF_bm;
|
|
dev->CCA = (uint16_t)value;
|
|
dev->INTCTRLB = timer_config[tim].int_lvl[0];
|
|
dev->CTRLA = ctx[tim].prescaler;
|
|
|
|
clear_oneshot(tim, channel);
|
|
irq_restore(irq_state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int timer_clear(tim_t tim, int channel)
|
|
{
|
|
if (tim >= TIMER_NUMOF) {
|
|
return -1;
|
|
}
|
|
|
|
if (channel >= ctx[tim].channels) {
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("timer_clear channel %d\n", channel );
|
|
|
|
TC0_t *dev = timer_config[tim].dev;
|
|
|
|
/* Compare or Capture Disable */
|
|
dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
|
|
|
|
/* Clear Interrupt Flag
|
|
* The CCxIF is automatically cleared when the corresponding
|
|
* interrupt vector is executed.*/
|
|
dev->INTFLAGS |= TC0_CCAIF_bm << channel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int timer_set(tim_t tim, int channel, unsigned int timeout)
|
|
{
|
|
if (tim >= TIMER_NUMOF) {
|
|
return -1;
|
|
}
|
|
|
|
if (channel >= ctx[tim].channels) {
|
|
return -1;
|
|
}
|
|
|
|
TC0_t *dev = timer_config[tim].dev;
|
|
|
|
/* Compare or Capture Disable */
|
|
dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
|
|
|
|
/* Clear Interrupt Flag */
|
|
dev->INTFLAGS |= TC0_CCAIF_bm << channel;
|
|
|
|
uint8_t irq_state = irq_disable();
|
|
|
|
/* set value to compare with rollover */
|
|
uint16_t absolute = dev->CNT + timeout;
|
|
*(((uint16_t *)(&dev->CCA)) + channel) = absolute;
|
|
|
|
irq_restore(irq_state);
|
|
set_oneshot(tim, channel);
|
|
|
|
/* Compare or Capture Enable */
|
|
dev->INTCTRLB |= (timer_config[tim].int_lvl[channel] << (channel * 2));
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned int timer_read(tim_t tim)
|
|
{
|
|
if (tim >= TIMER_NUMOF) {
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("timer_read\n");
|
|
return (unsigned int)timer_config[tim].dev->CNT;
|
|
}
|
|
|
|
void timer_stop(tim_t tim)
|
|
{
|
|
if (tim >= TIMER_NUMOF) {
|
|
return;
|
|
}
|
|
|
|
DEBUG("timer_stop\n");
|
|
timer_config[tim].dev->CTRLA = 0;
|
|
timer_config[tim].dev->CTRLFSET = TC_CMD_RESTART_gc;
|
|
pm_periph_disable(timer_config[tim].pwr);
|
|
}
|
|
|
|
void timer_start(tim_t tim)
|
|
{
|
|
if (tim >= TIMER_NUMOF) {
|
|
return;
|
|
}
|
|
|
|
DEBUG("timer_start\n");
|
|
pm_periph_enable(timer_config[tim].pwr);
|
|
timer_config[tim].dev->CTRLA = ctx[tim].prescaler;
|
|
}
|
|
|
|
#ifdef TIMER_NUMOF
|
|
static inline void _isr(tim_t tim, int channel)
|
|
{
|
|
avr8_enter_isr();
|
|
|
|
DEBUG("timer %d _isr channel %d\n", tim, channel);
|
|
|
|
if (is_oneshot(tim, channel)) {
|
|
timer_config[tim].dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
|
|
}
|
|
|
|
if (ctx[tim].cb) {
|
|
ctx[tim].cb(ctx[tim].arg, channel);
|
|
}
|
|
|
|
avr8_exit_isr();
|
|
}
|
|
#endif
|
|
|
|
#ifdef TIMER_0_ISRA
|
|
ISR(TIMER_0_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(0, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_0_ISRB
|
|
ISR(TIMER_0_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(0, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_0_ISRC
|
|
ISR(TIMER_0_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(0, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_0_ISRD
|
|
ISR(TIMER_0_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(0, 3);
|
|
}
|
|
#endif /* TIMER_0 */
|
|
|
|
#ifdef TIMER_1_ISRA
|
|
ISR(TIMER_1_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(1, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_1_ISRB
|
|
ISR(TIMER_1_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(1, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_1_ISRC
|
|
ISR(TIMER_1_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(1, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_1_ISRD
|
|
ISR(TIMER_1_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(1, 3);
|
|
}
|
|
#endif /* TIMER_1 */
|
|
|
|
#ifdef TIMER_2_ISRA
|
|
ISR(TIMER_2_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(2, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_2_ISRB
|
|
ISR(TIMER_2_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(2, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_2_ISRC
|
|
ISR(TIMER_2_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(2, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_2_ISRD
|
|
ISR(TIMER_2_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(2, 3);
|
|
}
|
|
#endif /* TIMER_2 */
|
|
|
|
#ifdef TIMER_3_ISRA
|
|
ISR(TIMER_3_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(3, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_3_ISRB
|
|
ISR(TIMER_3_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(3, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_3_ISRC
|
|
ISR(TIMER_3_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(3, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_3_ISRD
|
|
ISR(TIMER_3_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(3, 3);
|
|
}
|
|
#endif /* TIMER_3 */
|
|
|
|
#ifdef TIMER_4_ISRA
|
|
ISR(TIMER_4_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(4, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_4_ISRB
|
|
ISR(TIMER_4_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(4, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_4_ISRC
|
|
ISR(TIMER_4_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(4, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_4_ISRD
|
|
ISR(TIMER_4_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(4, 3);
|
|
}
|
|
#endif /* TIMER_4 */
|
|
|
|
#ifdef TIMER_5_ISRA
|
|
ISR(TIMER_5_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(5, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_5_ISRB
|
|
ISR(TIMER_5_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(5, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_5_ISRC
|
|
ISR(TIMER_5_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(5, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_5_ISRD
|
|
ISR(TIMER_5_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(5, 3);
|
|
}
|
|
#endif /* TIMER_5 */
|
|
|
|
#ifdef TIMER_6_ISRA
|
|
ISR(TIMER_6_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(6, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_6_ISRB
|
|
ISR(TIMER_6_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(6, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_6_ISRC
|
|
ISR(TIMER_6_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(6, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_6_ISRD
|
|
ISR(TIMER_6_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(6, 3);
|
|
}
|
|
#endif /* TIMER_6 */
|
|
|
|
#ifdef TIMER_7_ISRA
|
|
ISR(TIMER_7_ISRA, ISR_BLOCK)
|
|
{
|
|
_isr(7, 0);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_7_ISRB
|
|
ISR(TIMER_7_ISRB, ISR_BLOCK)
|
|
{
|
|
_isr(7, 1);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_7_ISRC
|
|
ISR(TIMER_7_ISRC, ISR_BLOCK)
|
|
{
|
|
_isr(7, 2);
|
|
}
|
|
#endif
|
|
#ifdef TIMER_7_ISRB
|
|
ISR(TIMER_7_ISRD, ISR_BLOCK)
|
|
{
|
|
_isr(7, 3);
|
|
}
|
|
#endif /* TIMER_7 */
|