From 7af1283ce327bfe58eae21e167d20a47acf5bbd0 Mon Sep 17 00:00:00 2001 From: lottaviano Date: Sat, 9 Apr 2011 13:44:08 +0000 Subject: [PATCH] Implement AVR XMega timer driver. Signed-off-by: Onno git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4849 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/cpu/avr/drv/timer_avr.c | 57 +++++++++++++ bertos/cpu/avr/drv/timer_avr.h | 55 +++++++++++++ bertos/cpu/avr/drv/timer_mega.c | 4 +- bertos/cpu/avr/drv/timer_mega.h | 8 +- bertos/cpu/avr/drv/timer_xmega.c | 126 +++++++++++++++++++++++++++++ bertos/cpu/avr/drv/timer_xmega.h | 134 +++++++++++++++++++++++++++++++ 6 files changed, 378 insertions(+), 6 deletions(-) create mode 100644 bertos/cpu/avr/drv/timer_avr.c create mode 100644 bertos/cpu/avr/drv/timer_avr.h create mode 100644 bertos/cpu/avr/drv/timer_xmega.c create mode 100644 bertos/cpu/avr/drv/timer_xmega.h diff --git a/bertos/cpu/avr/drv/timer_avr.c b/bertos/cpu/avr/drv/timer_avr.c new file mode 100644 index 00000000..2141cc34 --- /dev/null +++ b/bertos/cpu/avr/drv/timer_avr.c @@ -0,0 +1,57 @@ +/** + * \file + * + * + * \author Onno + * + * \brief Low-level timer module for AVR (implementation). + * + * This module is automatically included so no need to include + * in test list. + * notest: avr + */ + + +#ifndef WIZ_AUTOGEN + #warning This file is deprecated, you should probably use timer_mega.c + + #include + + #if CPU_AVR_MEGA + #include "timer_mega.c" + #elif CPU_AVR_XMEGA + #include "timer_xmega.c" + /*#elif Add other AVR families here */ + #else + #error Unknown CPU + #endif +#endif /* WIZ_AUTOGEN */ + diff --git a/bertos/cpu/avr/drv/timer_avr.h b/bertos/cpu/avr/drv/timer_avr.h new file mode 100644 index 00000000..ecabb9db --- /dev/null +++ b/bertos/cpu/avr/drv/timer_avr.h @@ -0,0 +1,55 @@ +/** + * \file + * + * + * \brief Low-level timer module for AVR (interface). + * + * \author Onno + * + */ + +#ifndef TIMER_AVR_H_ +#define TIMER_AVR_H_ + +#include + +#if CPU_AVR_MEGA + #include "timer_mega.h" +#elif CPU_AVR_XMEGA + #include "timer_xmega.h" +/*#elif Add other AVR families here */ +#else + #error Unknown CPU +#endif + + +#endif /* TIMER_AVR_H_ */ diff --git a/bertos/cpu/avr/drv/timer_mega.c b/bertos/cpu/avr/drv/timer_mega.c index ee63e76f..d6e88d21 100644 --- a/bertos/cpu/avr/drv/timer_mega.c +++ b/bertos/cpu/avr/drv/timer_mega.c @@ -34,14 +34,14 @@ * \author Francesco Sacchi * \author Luca Ottaviano * - * \brief Low-level timer module for AVR (implementation). + * \brief Low-level timer module for AVR MEGA (implementation). * * This module is automatically included so no need to include * in test list. * notest: avr */ -#include +#include #include // BV() #include diff --git a/bertos/cpu/avr/drv/timer_mega.h b/bertos/cpu/avr/drv/timer_mega.h index 2146620c..d0d98c27 100644 --- a/bertos/cpu/avr/drv/timer_mega.h +++ b/bertos/cpu/avr/drv/timer_mega.h @@ -31,7 +31,7 @@ * * --> * - * \brief Low-level timer module for AVR (interface). + * \brief Low-level timer module for AVR MEGA (interface). * * \author Bernie Innocenti * \author Francesco Sacchi @@ -39,8 +39,8 @@ * */ -#ifndef DRV_TIMER_AVR_H -#define DRV_TIMER_AVR_H +#ifndef DRV_TIMER_MEGA_H +#define DRV_TIMER_MEGA_H #include /* CPU_FREQ */ @@ -170,4 +170,4 @@ void timer_hw_init(void); -#endif /* DRV_TIMER_AVR_H */ +#endif /* DRV_TIMER_MEGA_H */ diff --git a/bertos/cpu/avr/drv/timer_xmega.c b/bertos/cpu/avr/drv/timer_xmega.c new file mode 100644 index 00000000..5bebf1c4 --- /dev/null +++ b/bertos/cpu/avr/drv/timer_xmega.c @@ -0,0 +1,126 @@ +/** + * \file + * + * + * \author Onno + * + * \brief Low-level timer module for AVR XMEGA (implementation). + * + * This file is heavily inspired by the AVR implementation for BeRTOS, + * but uses a different approach for implementing the different debug + * ports, by using the timer structs. + * + * This module is automatically included so no need to include + * in test list. + * notest: xmega + */ + +#include +#include // BV() + +#include +#include + +#include + +/* Helper MACROS taken from the Atmel examples and altered + * + * The AVR XMEGA has different structures for TC0 and TC1, + * however these only differ in the amount of compare/capture + * channels, so for this purpose they can be used exactly the same + */ + +//check if the bitvalues of the TC0 and TC1 Timer/Counters are really the same +#if TC0_CLKSEL_gm != TC1_CLKSEL_gm || TC0_WGMODE_gm != TC1_WGMODE_gm || TC0_OVFINTLVL_gm != TC1_OVFINTLVL_gm + #error TC0 and TC1 Timer/Counters cannot be configured with the same bitvalues +#endif + +#define TIMER_CONFIG_CLOCK_SOURCE(_clkSel)\ + ((TIMERCOUNTER).CTRLA = ((TIMERCOUNTER).CTRLA & ~TC0_CLKSEL_gm) | _clkSel) + +#define TIMER_CLEAR_FLAGS() ((TIMERCOUNTER).INTFLAGS = 0xFF) + +#define TIMER_SET_PERIOD( _period ) ( (TIMERCOUNTER).PER = (_period) ) + +#define TIMER_SET_OVERFLOW_INTERRUPT_LEVEL( _interruptLevel )\ + ((TIMERCOUNTER).INTCTRLA = ( (TIMERCOUNTER).INTCTRLA & ~TC0_OVFINTLVL_gm ) | _interruptLevel) + +#define TIMER_CONFIG_WGM(_wgm)\ + ((TIMERCOUNTER).CTRLB = ( (TIMERCOUNTER).CTRLB & ~TC0_WGMODE_gm ) | _wgm) + +#define TIMER_RESET() ( (TIMERCOUNTER).CTRLFSET = TC_CMD_RESET_gc ) + +//Define TIMER_PRESCALE_REG_VALUE bases on the provided +//TIMER_PRESCALER value +#if TIMER_PRESCALER == 0 + #define TIMER_CLKSEL_gc TC_CLKSEL_OFF_gc +#elif TIMER_PRESCALER == 1 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV1_gc +#elif TIMER_PRESCALER == 2 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV2_gc +#elif TIMER_PRESCALER == 4 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV4_gc +#elif TIMER_PRESCALER == 16 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV16_gc +#elif TIMER_PRESCALER == 64 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV64_gc +#elif TIMER_PRESCALER == 256 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV256_gc +#elif TIMER_PRESCALER == 1024 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV1024_gc +#else + #error Invalid value for TIMER_PRESCALER has been defined! Using default of 1 + #define TIMER_CLKSEL_gc TC_CLKSEL_DIV1_gc +#endif + +void timer_hw_init(void) +{ + //Save and disable IRQ + cpu_flags_t flags; + IRQ_SAVE_DISABLE(flags); + //disable the timer + TIMER_CONFIG_CLOCK_SOURCE(TC_CLKSEL_OFF_gc); + //clear all flags + TIMER_CLEAR_FLAGS(); + //setup the Periode register value + //CNT register will be reset to 0 when CNT == PER + TIMER_SET_PERIOD(TIMER_PERIOD_VALUE); + //set the Waveform Generation Mode to Normal + TIMER_CONFIG_WGM(TC_WGMODE_NORMAL_gc); + //enable the overflow interrupt + //use the highest priority + TIMER_SET_OVERFLOW_INTERRUPT_LEVEL(TC_OVFINTLVL_HI_gc); + //enable timer by setting the correct prescaler/clock + TIMER_CONFIG_CLOCK_SOURCE(TIMER_CLKSEL_gc); + //Restore IRQ + IRQ_RESTORE(flags); +} diff --git a/bertos/cpu/avr/drv/timer_xmega.h b/bertos/cpu/avr/drv/timer_xmega.h new file mode 100644 index 00000000..092cb4c4 --- /dev/null +++ b/bertos/cpu/avr/drv/timer_xmega.h @@ -0,0 +1,134 @@ +/** + * \file + * + * + * \brief Low-level timer module for AVR XMEGA (interface). + * + * This file is heavily inspired by the AVR implementation for BeRTOS, + * but uses a different approach for implementing the different debug + * ports, by using the timer structs. + * + * \author Onno + * + */ + +#ifndef DRV_TIMER_XMEGA_H +#define DRV_TIMER_XMEGA_H + +#include /* CPU_FREQ */ + +#include "cfg/cfg_timer.h" /* CONFIG_TIMER */ +#include /* uint8_t */ +#include /* DIV_ROUND */ + +#include +#include + +/* + * \name Values for CONFIG_TIMER. + * + * Select which hardware timer interrupt to use for system clock and softtimers. + * $WIZ$ timer_select = "TIMER_USE_TCC0", "TIMER_USE_TCC1", "TIMER_USE_TCD0", "TIMER_USE_TCE0", "TIMER_USE_TCD1", "TIMER_DEFAULT" + */ +#define TIMER_USE_TCC0 1 +#define TIMER_USE_TCC1 2 +#define TIMER_USE_TCD0 3 +#define TIMER_USE_TCE0 4 +// The XMEGA A Family has one extra timer +#ifdef CPU_AVR_XMEGA_A + #define TIMER_USE_TCD1 5 +#endif + +#define TIMER_DEFAULT TIMER_USE_TCC1 ///< Default system timer + +/* + * Hardware dependent timer initialization. + */ +#if (CONFIG_TIMER == TIMER_USE_TCC0) + #define TIMER_OVF_VECT TCC0_OVF_vect + #define TIMERCOUNTER TCC0 +#elif (CONFIG_TIMER == TIMER_USE_TCC1) + #define TIMER_OVF_VECT TCC1_OVF_vect + #define TIMERCOUNTER TCC1 +#elif (CONFIG_TIMER == TIMER_USE_TCD0) + #define TIMER_OVF_VECT TCD0_OVF_vect + #define TIMERCOUNTER TCD0 +#elif (CONFIG_TIMER == TIMER_USE_TCE0) + #define TIMER_OVF_VECT TCE0_OVF_vect + #define TIMERCOUNTER TCE0 +#elif (CONFIG_TIMER == TIMER_USE_TCD1) + #define TIMER_OVF_VECT TCD1_OVF_vect + #define TIMERCOUNTER TCD1 +#else + #error Unimplemented value for CONFIG_TIMER +#endif /* CONFIG_TIMER */ + +//define the Interrupt Service Routine for this Timer Mode +#define DEFINE_TIMER_ISR DECLARE_ISR_CONTEXT_SWITCH(TIMER_OVF_VECT) +//define the Ticks per second we want +#define TIMER_TICKS_PER_SEC 1000 +//define the Prescaler to use, which is dependend on the amount +//of ticks per second, the maximum value for the TOP value of the +//timer (0xFFFF) and the clock frequency. +//The maximum clock frequency is 32Mhz, so as long as the TIMER_TICKS_PER_SEC +//is larger then (about) 500 no prescaler is required. +#define TIMER_PRESCALER 1 +//define the TOP/PERIOD value +#define TIMER_PERIOD_VALUE DIV_ROUND(DIV_ROUND(CPU_FREQ, TIMER_PRESCALER), TIMER_TICKS_PER_SEC) +//check if the TIMER_PRESCALER is large enough to accomate for the TIMER_TICKS_PER_SEC +#if TIMER_PERIOD_VALUE > 0xFFFF + #error Timer cannot generate the required Ticks per second, please adjust TIMER_PRESCALER +#endif +//define TIMER_HW_CNT it is used by the timer.c module to determine the 'edge' of the hardware counter +#define TIMER_HW_CNT TIMER_PERIOD_VALUE +/** Frequency of the hardware high precision timer. */ +#define TIMER_HW_HPTICKS_PER_SEC DIV_ROUND(CPU_FREQ, TIMER_PRESCALER) + +// Type of time expressed in ticks of the hardware high-precision timer +typedef uint16_t hptime_t; +#define SIZEOF_HPTIME_T 2 + +INLINE hptime_t timer_hw_hpread(void) +{ + return (TIMERCOUNTER).CNT; +} + +/* Not needed, IRQ timer flag cleared automatically */ +#define timer_hw_irq() do {} while (0) + +/* Not needed, timer IRQ handler called only for timer source */ +#define timer_hw_triggered() (true) + +void timer_hw_init(void); + +#endif /* DRV_TIMER_XMEGA_H */ -- 2.25.1