From 18d9e257d2cb4b4f6c2b1925ef2af846162453a8 Mon Sep 17 00:00:00 2001 From: Youen Toupin Date: Mon, 1 May 2017 18:42:01 +0200 Subject: [PATCH] Timer1 library from https://github.com/PaulStoffregen/TimerOne aaeb4a36f5f23e99e6011bea77cf76abb912e13a --- TimerOne.cpp | 49 +++++ TimerOne.h | 355 ++++++++++++++++++++++++++++++++++++ config/known_16bit_timers.h | 153 ++++++++++++++++ 3 files changed, 557 insertions(+) create mode 100644 TimerOne.cpp create mode 100644 TimerOne.h create mode 100644 config/known_16bit_timers.h diff --git a/TimerOne.cpp b/TimerOne.cpp new file mode 100644 index 0000000..8aaca16 --- /dev/null +++ b/TimerOne.cpp @@ -0,0 +1,49 @@ +/* + * Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328 + * Original code by Jesse Tane for http://labs.ideo.com August 2008 + * Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support + * Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop + * Modified Oct 2009 by Dan Clemens to work with timer1 of the ATMega1280 or Arduino Mega + * Modified April 2012 by Paul Stoffregen + * Modified again, June 2014 by Paul Stoffregen + * This version has been downloaded from https://github.com/PaulStoffregen/TimerOne (commit aaeb4a36f5f23e99e6011bea77cf76abb912e13a) + * + * This is free software. You can redistribute it and/or modify it under + * the terms of Creative Commons Attribution 3.0 United States License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ + * or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. + * + */ + +#include "TimerOne.h" + +TimerOne Timer1; // preinstatiate + +unsigned short TimerOne::pwmPeriod = 0; +unsigned char TimerOne::clockSelectBits = 0; +void (*TimerOne::isrCallback)() = TimerOne::isrDefaultUnused; + +// interrupt service routine that wraps a user defined function supplied by attachInterrupt +#if defined(__AVR__) +ISR(TIMER1_OVF_vect) +{ + Timer1.isrCallback(); +} + +#elif defined(__arm__) && defined(CORE_TEENSY) +void ftm1_isr(void) +{ + uint32_t sc = FTM1_SC; + #ifdef KINETISL + if (sc & 0x80) FTM1_SC = sc; + #else + if (sc & 0x80) FTM1_SC = sc & 0x7F; + #endif + Timer1.isrCallback(); +} + +#endif + +void TimerOne::isrDefaultUnused() +{ +} diff --git a/TimerOne.h b/TimerOne.h new file mode 100644 index 0000000..c578d55 --- /dev/null +++ b/TimerOne.h @@ -0,0 +1,355 @@ +/* + * Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328 + * Original code by Jesse Tane for http://labs.ideo.com August 2008 + * Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support + * Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop + * Modified April 2012 by Paul Stoffregen - portable to other AVR chips, use inline functions + * Modified again, June 2014 by Paul Stoffregen - support Teensy 3.x & even more AVR chips + * This version has been downloaded from https://github.com/PaulStoffregen/TimerOne (commit aaeb4a36f5f23e99e6011bea77cf76abb912e13a) + * + * + * This is free software. You can redistribute it and/or modify it under + * the terms of Creative Commons Attribution 3.0 United States License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ + * or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. + * + */ + +#ifndef TimerOne_h_ +#define TimerOne_h_ + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#include "config/known_16bit_timers.h" + +#define TIMER1_RESOLUTION 65536UL // Timer1 is 16 bit + + +// Placing nearly all the code in this .h file allows the functions to be +// inlined by the compiler. In the very common case with constant values +// the compiler will perform all calculations and simply write constants +// to the hardware registers (for example, setPeriod). + + +class TimerOne +{ + + +#if defined(__AVR__) + public: + //**************************** + // Configuration + //**************************** + void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { + TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer + TCCR1A = 0; // clear control register A + setPeriod(microseconds); + } + void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { + const unsigned long cycles = (F_CPU / 2000000) * microseconds; + if (cycles < TIMER1_RESOLUTION) { + clockSelectBits = _BV(CS10); + pwmPeriod = cycles; + } else + if (cycles < TIMER1_RESOLUTION * 8) { + clockSelectBits = _BV(CS11); + pwmPeriod = cycles / 8; + } else + if (cycles < TIMER1_RESOLUTION * 64) { + clockSelectBits = _BV(CS11) | _BV(CS10); + pwmPeriod = cycles / 64; + } else + if (cycles < TIMER1_RESOLUTION * 256) { + clockSelectBits = _BV(CS12); + pwmPeriod = cycles / 256; + } else + if (cycles < TIMER1_RESOLUTION * 1024) { + clockSelectBits = _BV(CS12) | _BV(CS10); + pwmPeriod = cycles / 1024; + } else { + clockSelectBits = _BV(CS12) | _BV(CS10); + pwmPeriod = TIMER1_RESOLUTION - 1; + } + ICR1 = pwmPeriod; + TCCR1B = _BV(WGM13) | clockSelectBits; + } + + //**************************** + // Run Control + //**************************** + void start() __attribute__((always_inline)) { + TCCR1B = 0; + TCNT1 = 0; // TODO: does this cause an undesired interrupt? + resume(); + } + void stop() __attribute__((always_inline)) { + TCCR1B = _BV(WGM13); + } + void restart() __attribute__((always_inline)) { + start(); + } + void resume() __attribute__((always_inline)) { + TCCR1B = _BV(WGM13) | clockSelectBits; + } + + //**************************** + // PWM outputs + //**************************** + void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { + unsigned long dutyCycle = pwmPeriod; + dutyCycle *= duty; + dutyCycle >>= 10; + if (pin == TIMER1_A_PIN) OCR1A = dutyCycle; + #ifdef TIMER1_B_PIN + else if (pin == TIMER1_B_PIN) OCR1B = dutyCycle; + #endif + #ifdef TIMER1_C_PIN + else if (pin == TIMER1_C_PIN) OCR1C = dutyCycle; + #endif + } + void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { + if (pin == TIMER1_A_PIN) { pinMode(TIMER1_A_PIN, OUTPUT); TCCR1A |= _BV(COM1A1); } + #ifdef TIMER1_B_PIN + else if (pin == TIMER1_B_PIN) { pinMode(TIMER1_B_PIN, OUTPUT); TCCR1A |= _BV(COM1B1); } + #endif + #ifdef TIMER1_C_PIN + else if (pin == TIMER1_C_PIN) { pinMode(TIMER1_C_PIN, OUTPUT); TCCR1A |= _BV(COM1C1); } + #endif + setPwmDuty(pin, duty); + TCCR1B = _BV(WGM13) | clockSelectBits; + } + void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { + if (microseconds > 0) setPeriod(microseconds); + pwm(pin, duty); + } + void disablePwm(char pin) __attribute__((always_inline)) { + if (pin == TIMER1_A_PIN) TCCR1A &= ~_BV(COM1A1); + #ifdef TIMER1_B_PIN + else if (pin == TIMER1_B_PIN) TCCR1A &= ~_BV(COM1B1); + #endif + #ifdef TIMER1_C_PIN + else if (pin == TIMER1_C_PIN) TCCR1A &= ~_BV(COM1C1); + #endif + } + + //**************************** + // Interrupt Function + //**************************** + void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { + isrCallback = isr; + TIMSK1 = _BV(TOIE1); + } + void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { + if(microseconds > 0) setPeriod(microseconds); + attachInterrupt(isr); + } + void detachInterrupt() __attribute__((always_inline)) { + TIMSK1 = 0; + } + static void (*isrCallback)(); + static void isrDefaultUnused(); + + private: + // properties + static unsigned short pwmPeriod; + static unsigned char clockSelectBits; + + + + + + +#elif defined(__arm__) && defined(CORE_TEENSY) + +#if defined(KINETISK) +#define F_TIMER F_BUS +#elif defined(KINETISL) +#define F_TIMER (F_PLL/2) +#endif + + public: + //**************************** + // Configuration + //**************************** + void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) { + setPeriod(microseconds); + } + void setPeriod(unsigned long microseconds) __attribute__((always_inline)) { + const unsigned long cycles = (F_TIMER / 2000000) * microseconds; + // A much faster if-else + // This is like a binary serch tree and no more than 3 conditions are evaluated. + // I haven't checked if this becomes significantly longer ASM than the simple ladder. + // It looks very similar to the ladder tho: same # of if's and else's + + /* + // This code does not work properly in all cases :( + // https://github.com/PaulStoffregen/TimerOne/issues/17 + if (cycles < TIMER1_RESOLUTION * 16) { + if (cycles < TIMER1_RESOLUTION * 4) { + if (cycles < TIMER1_RESOLUTION) { + clockSelectBits = 0; + pwmPeriod = cycles; + }else{ + clockSelectBits = 1; + pwmPeriod = cycles >> 1; + } + }else{ + if (cycles < TIMER1_RESOLUTION * 8) { + clockSelectBits = 3; + pwmPeriod = cycles >> 3; + }else{ + clockSelectBits = 4; + pwmPeriod = cycles >> 4; + } + } + }else{ + if (cycles > TIMER1_RESOLUTION * 64) { + if (cycles > TIMER1_RESOLUTION * 128) { + clockSelectBits = 7; + pwmPeriod = TIMER1_RESOLUTION - 1; + }else{ + clockSelectBits = 7; + pwmPeriod = cycles >> 7; + } + } + else{ + if (cycles > TIMER1_RESOLUTION * 32) { + clockSelectBits = 6; + pwmPeriod = cycles >> 6; + }else{ + clockSelectBits = 5; + pwmPeriod = cycles >> 5; + } + } + } + */ + if (cycles < TIMER1_RESOLUTION) { + clockSelectBits = 0; + pwmPeriod = cycles; + } else + if (cycles < TIMER1_RESOLUTION * 2) { + clockSelectBits = 1; + pwmPeriod = cycles >> 1; + } else + if (cycles < TIMER1_RESOLUTION * 4) { + clockSelectBits = 2; + pwmPeriod = cycles >> 2; + } else + if (cycles < TIMER1_RESOLUTION * 8) { + clockSelectBits = 3; + pwmPeriod = cycles >> 3; + } else + if (cycles < TIMER1_RESOLUTION * 16) { + clockSelectBits = 4; + pwmPeriod = cycles >> 4; + } else + if (cycles < TIMER1_RESOLUTION * 32) { + clockSelectBits = 5; + pwmPeriod = cycles >> 5; + } else + if (cycles < TIMER1_RESOLUTION * 64) { + clockSelectBits = 6; + pwmPeriod = cycles >> 6; + } else + if (cycles < TIMER1_RESOLUTION * 128) { + clockSelectBits = 7; + pwmPeriod = cycles >> 7; + } else { + clockSelectBits = 7; + pwmPeriod = TIMER1_RESOLUTION - 1; + } + + uint32_t sc = FTM1_SC; + FTM1_SC = 0; + FTM1_MOD = pwmPeriod; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_CPWMS | clockSelectBits | (sc & FTM_SC_TOIE); + } + + //**************************** + // Run Control + //**************************** + void start() __attribute__((always_inline)) { + stop(); + FTM1_CNT = 0; + resume(); + } + void stop() __attribute__((always_inline)) { + FTM1_SC = FTM1_SC & (FTM_SC_TOIE | FTM_SC_CPWMS | FTM_SC_PS(7)); + } + void restart() __attribute__((always_inline)) { + start(); + } + void resume() __attribute__((always_inline)) { + FTM1_SC = (FTM1_SC & (FTM_SC_TOIE | FTM_SC_PS(7))) | FTM_SC_CPWMS | FTM_SC_CLKS(1); + } + + //**************************** + // PWM outputs + //**************************** + void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) { + unsigned long dutyCycle = pwmPeriod; + dutyCycle *= duty; + dutyCycle >>= 10; + if (pin == TIMER1_A_PIN) { + FTM1_C0V = dutyCycle; + } else if (pin == TIMER1_B_PIN) { + FTM1_C1V = dutyCycle; + } + } + void pwm(char pin, unsigned int duty) __attribute__((always_inline)) { + setPwmDuty(pin, duty); + if (pin == TIMER1_A_PIN) { + *portConfigRegister(TIMER1_A_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + } else if (pin == TIMER1_B_PIN) { + *portConfigRegister(TIMER1_B_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE; + } + } + void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) { + if (microseconds > 0) setPeriod(microseconds); + pwm(pin, duty); + } + void disablePwm(char pin) __attribute__((always_inline)) { + if (pin == TIMER1_A_PIN) { + *portConfigRegister(TIMER1_A_PIN) = 0; + } else if (pin == TIMER1_B_PIN) { + *portConfigRegister(TIMER1_B_PIN) = 0; + } + } + + //**************************** + // Interrupt Function + //**************************** + void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { + isrCallback = isr; + FTM1_SC |= FTM_SC_TOIE; + NVIC_ENABLE_IRQ(IRQ_FTM1); + } + void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { + if(microseconds > 0) setPeriod(microseconds); + attachInterrupt(isr); + } + void detachInterrupt() __attribute__((always_inline)) { + FTM1_SC &= ~FTM_SC_TOIE; + NVIC_DISABLE_IRQ(IRQ_FTM1); + } + static void (*isrCallback)(); + static void isrDefaultUnused(); + + private: + // properties + static unsigned short pwmPeriod; + static unsigned char clockSelectBits; + +#undef F_TIMER + +#endif +}; + +extern TimerOne Timer1; + +#endif + diff --git a/config/known_16bit_timers.h b/config/known_16bit_timers.h new file mode 100644 index 0000000..bdae330 --- /dev/null +++ b/config/known_16bit_timers.h @@ -0,0 +1,153 @@ +#ifndef known_16bit_timers_header_ +#define known_16bit_timers_header_ + +// Wiring-S +// +#if defined(__AVR_ATmega644P__) && defined(WIRING) + #define TIMER1_A_PIN 5 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 6 + +// Teensy 2.0 +// +#elif defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY) + #define TIMER1_A_PIN 14 + #define TIMER1_B_PIN 15 + #define TIMER1_C_PIN 4 + #define TIMER1_ICP_PIN 22 + #define TIMER1_CLK_PIN 11 + #define TIMER3_A_PIN 9 + #define TIMER3_ICP_PIN 10 + +// Teensy++ 2.0 +#elif defined(__AVR_AT90USB1286__) && defined(CORE_TEENSY) + #define TIMER1_A_PIN 25 + #define TIMER1_B_PIN 26 + #define TIMER1_C_PIN 27 + #define TIMER1_ICP_PIN 4 + #define TIMER1_CLK_PIN 6 + #define TIMER3_A_PIN 16 + #define TIMER3_B_PIN 15 + #define TIMER3_C_PIN 14 + #define TIMER3_ICP_PIN 17 + #define TIMER3_CLK_PIN 13 + +// Teensy 3.0 +// +#elif defined(__MK20DX128__) + #define TIMER1_A_PIN 3 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 4 + +// Teensy 3.1 / Teensy 3.2 +// +#elif defined(__MK20DX256__) + #define TIMER1_A_PIN 3 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 4 + #define TIMER3_A_PIN 32 + #define TIMER3_B_PIN 25 + #define TIMER3_ICP_PIN 32 + +// Teensy 3.5 / Teensy 3.6 +// +#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) + #define TIMER1_A_PIN 3 + #define TIMER1_B_PIN 4 + #define TIMER1_ICP_PIN 4 + #define TIMER3_A_PIN 29 + #define TIMER3_B_PIN 30 + #define TIMER3_ICP_PIN 29 + +// Teensy-LC +// +#elif defined(__MKL26Z64__) + #define TIMER1_A_PIN 16 + #define TIMER1_B_PIN 17 + #define TIMER1_ICP_PIN 17 + #define TIMER3_A_PIN 3 + #define TIMER3_B_PIN 4 + #define TIMER3_ICP_PIN 4 + +// Arduino Mega +// +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + #define TIMER1_A_PIN 11 + #define TIMER1_B_PIN 12 + #define TIMER1_C_PIN 13 + #define TIMER3_A_PIN 5 + #define TIMER3_B_PIN 2 + #define TIMER3_C_PIN 3 + #define TIMER4_A_PIN 6 + #define TIMER4_B_PIN 7 + #define TIMER4_C_PIN 8 + #define TIMER4_ICP_PIN 49 + #define TIMER5_A_PIN 46 + #define TIMER5_B_PIN 45 + #define TIMER5_C_PIN 44 + #define TIMER3_ICP_PIN 48 + #define TIMER3_CLK_PIN 47 + +// Arduino Leonardo, Yun, etc +// +#elif defined(__AVR_ATmega32U4__) + #define TIMER1_A_PIN 9 + #define TIMER1_B_PIN 10 + #define TIMER1_C_PIN 11 + #define TIMER1_ICP_PIN 4 + #define TIMER1_CLK_PIN 12 + #define TIMER3_A_PIN 5 + #define TIMER3_ICP_PIN 13 + +// Uno, Duemilanove, LilyPad, etc +// +#elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega328P__) + #define TIMER1_A_PIN 9 + #define TIMER1_B_PIN 10 + #define TIMER1_ICP_PIN 8 + #define TIMER1_CLK_PIN 5 + +// Sanguino +// +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) + #define TIMER1_A_PIN 13 + #define TIMER1_B_PIN 12 + #define TIMER1_ICP_PIN 14 + #define TIMER1_CLK_PIN 1 + +// Wildfire - Wicked Devices +// +#elif defined(__AVR_ATmega1284P__) && defined(WILDFIRE_VERSION) && WILDFIRE_VERSION >= 3 + #define TIMER1_A_PIN 5 // PD5 + #define TIMER1_B_PIN 8 // PD4 + #define TIMER1_ICP_PIN 6 // PD6 + #define TIMER1_CLK_PIN 23 // PB1 + #define TIMER3_A_PIN 12 // PB6 + #define TIMER3_B_PIN 13 // PB7 + #define TIMER3_ICP_PIN 9 // PB5 + #define TIMER3_CLK_PIN 0 // PD0 +#elif defined(__AVR_ATmega1284P__) && defined(WILDFIRE_VERSION) && WILDFIRE_VERSION < 3 + #define TIMER1_A_PIN 5 // PD5 + #define TIMER1_B_PIN 4 // PD4 + #define TIMER1_ICP_PIN 6 // PD6 + #define TIMER1_CLK_PIN 15 // PB1 + #define TIMER3_A_PIN 12 // PB6 + #define TIMER3_B_PIN 13 // PB7 + #define TIMER3_ICP_PIN 11 // PB5 + #define TIMER3_CLK_PIN 0 // PD0 + +// Mighty-1284 - Maniacbug +// +#elif defined(__AVR_ATmega1284P__) + #define TIMER1_A_PIN 12 // PD5 + #define TIMER1_B_PIN 13 // PD4 + #define TIMER1_ICP_PIN 14 // PD6 + #define TIMER1_CLK_PIN 1 // PB1 + #define TIMER3_A_PIN 6 // PB6 + #define TIMER3_B_PIN 7 // PB7 + #define TIMER3_ICP_PIN 5 // PB5 + #define TIMER3_CLK_PIN 8 // PD0 + +#endif + +#endif