├── .gitignore ├── keywords.txt ├── library.properties ├── examples ├── SimpleTimer │ └── SimpleTimer.pde ├── MultipleTimers │ └── MultipleTimers.pde └── AvaliableTimer │ └── AvaliableTimer.pde ├── library.json ├── LICENSE.txt ├── DueTimer.h ├── README.md ├── TimerCounter.md └── DueTimer.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | DueTimer KEYWORD2 2 | start KEYWORD2 3 | stop KEYWORD2 4 | attachInterrupt KEYWORD2 5 | detachInterrupt KEYWORD2 6 | setPeriod KEYWORD2 7 | setFrequency KEYWORD2 8 | getFrequency KEYWORD2 9 | getPeriod KEYWORD2 10 | 11 | Timer KEYWORD1 12 | Timer0 KEYWORD1 13 | Timer1 KEYWORD1 14 | Timer2 KEYWORD1 15 | Timer3 KEYWORD1 16 | Timer4 KEYWORD1 17 | Timer5 KEYWORD1 18 | Timer6 KEYWORD1 19 | Timer7 KEYWORD1 20 | 21 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=DueTimer 2 | version=1.4.8 3 | author=Ivan Seidel 4 | maintainer=Ivan Seidel 5 | sentence=Timer Library fully implemented for Arduino DUE 6 | paragraph=There are 6 or 9 Timer objects already instantiated for you: Timer0, Timer1, Timer2, Timer3, Timer4, Timer5 and Timer6, Timer7, Timer8 where supported by the hardware. 7 | category=Timing 8 | url=https://github.com/ivanseidel/DueTimer 9 | architectures=sam 10 | -------------------------------------------------------------------------------- /examples/SimpleTimer/SimpleTimer.pde: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int myLed = 13; 4 | 5 | bool ledOn = false; 6 | void myHandler(){ 7 | ledOn = !ledOn; 8 | 9 | digitalWrite(myLed, ledOn); // Led on, off, on, off... 10 | } 11 | 12 | void setup(){ 13 | pinMode(myLed, OUTPUT); 14 | 15 | Timer3.attachInterrupt(myHandler); 16 | Timer3.start(50000); // Calls every 50ms 17 | } 18 | 19 | void loop(){ 20 | 21 | while(1){ 22 | // I'm stuck in here! help me... 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /examples/MultipleTimers/MultipleTimers.pde: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void firstHandler(){ 4 | Serial.println("[- ] First Handler!"); 5 | } 6 | 7 | void secondHandler(){ 8 | Serial.println("[ - ] Second Handler!"); 9 | } 10 | 11 | void thirdHandler(){ 12 | Serial.println("[ -] Third Handler!"); 13 | } 14 | 15 | void setup(){ 16 | Serial.begin(9600); 17 | 18 | Timer3.attachInterrupt(firstHandler).start(500000); // Every 500ms 19 | Timer4.attachInterrupt(secondHandler).setFrequency(1).start(); 20 | Timer5.attachInterrupt(thirdHandler).setFrequency(10); 21 | } 22 | 23 | void loop(){ 24 | delay(2000); 25 | Timer5.start(); 26 | 27 | delay(2000); 28 | Timer5.stop(); 29 | } -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DueTimer", 3 | "keywords": "timer", 4 | "description": "Timer Library fully implemented for Arduino DUE", 5 | "authors": 6 | [ 7 | { 8 | "name": "Ivan Seidel", 9 | "email": "ivanseidel@gmail.com", 10 | "url": "https://github.com/ivanseidel", 11 | "maintainer": true 12 | } 13 | ], 14 | "repository": 15 | { 16 | "type": "git", 17 | "url": "https://github.com/ivanseidel/DueTimer.git" 18 | }, 19 | "version": "1.4.8", 20 | "license": "MIT", 21 | "frameworks": "arduino", 22 | "platforms": "atmelsam", 23 | "examples": 24 | [ 25 | "examples/*/*.pde" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ivan Seidel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/AvaliableTimer/AvaliableTimer.pde: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void playVideogame(){ 4 | Serial.println("[- ] I'm playing Videogame!"); 5 | } 6 | 7 | void drinkWater(){ 8 | Serial.println("[ - ] I'm driking water!"); 9 | } 10 | 11 | void makeSushi(){ 12 | Serial.println("[ - ] Slicing Salmon..."); 13 | } 14 | 15 | void singOnShower(){ 16 | Serial.println("[ - ] Hello World! Hello world!"); 17 | } 18 | 19 | void studyMath(){ 20 | int x = random(1, 40), y = random(1,40); 21 | Serial.print("[ - ] "); 22 | Serial.print(x); Serial.print(" x "); Serial.print(y); Serial.print(" = "); 23 | Serial.println(x*y); 24 | } 25 | 26 | void watchStarTrek(){ 27 | Serial.println("[ - ] Long live and prosper \\\\//_"); 28 | } 29 | 30 | void eatSushi(){ 31 | Serial.println("[ - ] ..."); 32 | } 33 | 34 | void readTextMessage(){ 35 | Serial.println("[ - ] [Unlock ---->>]"); 36 | } 37 | 38 | void goToSleep(){ 39 | Serial.println("[ -] zzzzzz"); 40 | } 41 | 42 | void setup(){ 43 | Serial.begin(9600); 44 | 45 | Timer.getAvailable().attachInterrupt(playVideogame).start(); delay(50); 46 | Timer.getAvailable().attachInterrupt(drinkWater).start(); delay(50); 47 | Timer.getAvailable().attachInterrupt(makeSushi).start(); delay(50); 48 | Timer.getAvailable().attachInterrupt(singOnShower).start(); delay(50); 49 | Timer.getAvailable().attachInterrupt(studyMath).start(); delay(50); 50 | Timer.getAvailable().attachInterrupt(watchStarTrek).start(); delay(50); 51 | Timer.getAvailable().attachInterrupt(eatSushi).start(); delay(50); 52 | Timer.getAvailable().attachInterrupt(readTextMessage).start(); delay(50); 53 | Timer.getAvailable().attachInterrupt(goToSleep).start(); delay(50); 54 | } 55 | 56 | void loop(){ 57 | while(1){ 58 | // ... 59 | } 60 | } -------------------------------------------------------------------------------- /DueTimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | DueTimer.h - DueTimer header file, definition of methods and attributes... 3 | For instructions, go to https://github.com/ivanseidel/DueTimer 4 | 5 | Created by Ivan Seidel Gomes, March, 2013. 6 | Modified by Philipp Klaus, June 2013. 7 | Released into the public domain. 8 | */ 9 | 10 | #include "Arduino.h" 11 | 12 | #if defined(_SAM3XA_) 13 | 14 | #ifndef DueTimer_h 15 | #define DueTimer_h 16 | 17 | #include 18 | 19 | /* 20 | This fixes compatibility for Arduono Servo Library. 21 | Uncomment to make it compatible. 22 | 23 | Note that: 24 | + Timers: 0,2,3,4,5 WILL NOT WORK, and will 25 | neither be accessible by Timer0,... 26 | */ 27 | // #define USING_SERVO_LIB true 28 | 29 | #ifdef USING_SERVO_LIB 30 | #warning "HEY! You have set flag USING_SERVO_LIB. Timer0, 2,3,4 and 5 are not available" 31 | #endif 32 | 33 | 34 | #if defined TC2 35 | #define NUM_TIMERS 9 36 | #else 37 | #define NUM_TIMERS 6 38 | #endif 39 | 40 | class DueTimer 41 | { 42 | protected: 43 | 44 | // Represents the timer id (index for the array of Timer structs) 45 | const unsigned short timer; 46 | 47 | // Stores the object timer frequency 48 | // (allows to access current timer period and frequency): 49 | static double _frequency[NUM_TIMERS]; 50 | 51 | // Picks the best clock to lower the error 52 | static uint8_t bestClock(double frequency, uint32_t& retRC); 53 | 54 | // Make Interrupt handlers friends, so they can use callbacks 55 | friend void TC0_Handler(void); 56 | friend void TC1_Handler(void); 57 | friend void TC2_Handler(void); 58 | friend void TC3_Handler(void); 59 | friend void TC4_Handler(void); 60 | friend void TC5_Handler(void); 61 | #if NUM_TIMERS > 6 62 | friend void TC6_Handler(void); 63 | friend void TC7_Handler(void); 64 | friend void TC8_Handler(void); 65 | #endif 66 | 67 | static void (*callbacks[NUM_TIMERS])(); 68 | 69 | struct Timer 70 | { 71 | Tc *tc; 72 | uint32_t channel; 73 | IRQn_Type irq; 74 | }; 75 | 76 | // Store timer configuration (static, as it's fixed for every object) 77 | static const Timer Timers[NUM_TIMERS]; 78 | 79 | public: 80 | 81 | static DueTimer getAvailable(void); 82 | 83 | DueTimer(unsigned short _timer); 84 | DueTimer& attachInterrupt(void (*isr)()); 85 | DueTimer& detachInterrupt(void); 86 | DueTimer& start(double microseconds = -1); 87 | DueTimer& stop(void); 88 | DueTimer& setFrequency(double frequency); 89 | DueTimer& setPeriod(double microseconds); 90 | 91 | double getFrequency(void) const; 92 | double getPeriod(void) const; 93 | 94 | inline __attribute__((always_inline)) bool operator== (const DueTimer& rhs) const 95 | {return timer == rhs.timer; }; 96 | inline __attribute__((always_inline)) bool operator!= (const DueTimer& rhs) const 97 | {return timer != rhs.timer; }; 98 | }; 99 | 100 | // Just to call Timer.getAvailable instead of Timer::getAvailable() : 101 | extern DueTimer Timer; 102 | 103 | extern DueTimer Timer1; 104 | // Fix for compatibility with Servo library 105 | #ifndef USING_SERVO_LIB 106 | extern DueTimer Timer0; 107 | extern DueTimer Timer2; 108 | extern DueTimer Timer3; 109 | extern DueTimer Timer4; 110 | extern DueTimer Timer5; 111 | #endif 112 | #if NUM_TIMERS > 6 113 | extern DueTimer Timer6; 114 | extern DueTimer Timer7; 115 | extern DueTimer Timer8; 116 | #endif 117 | 118 | #endif 119 | 120 | #else 121 | #error Oops! Trying to include DueTimer on another device? 122 | #endif 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DueTimer 2 | 3 | Timer Library to work with Arduino DUE 4 | 5 | ## Installation 6 | 7 | 1. [Download](https://github.com/ivanseidel/DueTimer/releases) the Latest release from GitHub. 8 | 2. Unzip and modify the Folder name to "DueTimer" (Remove the '-version') 9 | 3. Paste the modified folder on your Library folder (On your `Libraries` folder inside Sketchbooks or Arduino software). 10 | 4. Re-open Arduino Software 11 | 12 | ## Getting Started 13 | 14 | To call a function `handler` every `1000` microseconds: 15 | 16 | ```c++ 17 | Timer3.attachInterrupt(handler).start(1000); 18 | // or: 19 | Timer3.attachInterrupt(handler).setPeriod(1000).start(); 20 | // or, to select whichever available timer: 21 | Timer.getAvailable().attachInterrupt(handler).start(1000); 22 | ``` 23 | 24 | To call a function `handler` `10` times a second: 25 | 26 | ```c++ 27 | Timer3.attachInterrupt(handler).setFrequency(10).start(); 28 | ``` 29 | 30 | In case you need to stop a timer, just do like this: 31 | 32 | ```c++ 33 | Timer3.stop(); 34 | ``` 35 | 36 | And to continue running: 37 | 38 | ```c++ 39 | Timer3.start(); 40 | ``` 41 | 42 | There are `9` Timer objects already instantiated for you: 43 | `Timer0`, `Timer1`, `Timer2`, `Timer3`, `Timer4`, `Timer5`, `Timer6`, `Timer7` and `Timer8`. 44 | 45 | ### TIPs and Warnings 46 | 47 | ```c++ 48 | Timer4.attachInterrupt(handler).setFrequency(10).start(); 49 | // Is the same as: 50 | Timer4.attachInterrupt(handler); 51 | Timer4.setFrequency(10); 52 | Timer4.start(); 53 | 54 | // To create a custom timer, refer to: 55 | DueTimer myTimer = DueTimer(0); // Creates a Timer 0 object. 56 | DueTimer myTimer = DueTimer(3); // Creates a Timer 3 object. 57 | DueTimer myTimer = DueTimer(t); // Creates a Timer t object. 58 | // Note: Maximum t allowed is 8, as there is only 9 timers [0..8]; 59 | 60 | Timer1.attachInterrupt(handler1).start(10); 61 | Timer1.attachInterrupt(handler2).start(10); 62 | DueTimer myTimer = DueTimer(1); 63 | myTimer.attachInterrupt(handler3).start(20); 64 | // Will run only handle3, on Timer 1 (You are just overriding the callback) 65 | 66 | Timer.getAvailable().attachInterrupt(callback1).start(10); 67 | // Start timer on first available timer 68 | DueTimer::getAvailable().attachInterrupt(callback2).start(10); 69 | // Start timer on second available timer 70 | // And so on... 71 | 72 | DueTimer myTimer = Timer.getAvailable(); 73 | if (myTimer != DueTimer(0)) 74 | // Now we know that the timer returned is actually available 75 | // Can compare timers using == or != 76 | 77 | ``` 78 | 79 | ### Compatibility with Servo.h 80 | 81 | Because Servo Library uses the same callbacks of DueTimer, we provides a custom solution for working with both of them. However, Timers 0,2,3,4 and 5 will not Work anymore. 82 | 83 | You will need uncommend the line in `DueTimer.h` in `DueTimer` folder inside the `Libraries` folder. Uncomment the following line in `DueTimer.h`: 84 | 85 | ``` 86 | #define USING_SERVO_LIB true 87 | ``` 88 | 89 | ## Library Reference 90 | 91 | ### You should know: 92 | 93 | - `getAvailable()` - Get the first available Timer. 94 | 95 | - `attachInterrupt(void (*isr)())` - Attach a interrupt (callback function) for the timer of the object. 96 | 97 | - `detachInterrupt()` - Detach current callback of timer. 98 | 99 | - `start(long microseconds = -1)` - Start the timer with an optional period parameter. 100 | 101 | - `stop()` - Stop the timer 102 | 103 | - `setFrequency(long frequency)` - Set the timer frequency 104 | 105 | - `long getFrequency()` - Get the timer frequency 106 | 107 | - `setPeriod(long microseconds)` - Set the timer period (in microseconds) 108 | 109 | - `long getPeriod()` - Get the timer period (in microseconds) 110 | 111 | ### You don't need to know: 112 | 113 | <<<<<<< HEAD 114 | - `int timer` - Stores the object timer id (to access Timers struct array). 115 | 116 | - `DueTimer(unsigned short _timer)` - Instantiate a new DueTimer object for Timer _timer (NOTE: All objects are already instantiated!). 117 | 118 | - `static const Timer Timers[]` - Stores all timers information 119 | 120 | - `static void (*callbacks[])()` - Stores all callbacks for all timers 121 | 122 | 123 | ### Hardware Information 124 | 125 | More information on the Timer Counter module of the µC on the Arduino Due 126 | can be found in the documentation file [TimerCounter](TimerCounter.md). -------------------------------------------------------------------------------- /TimerCounter.md: -------------------------------------------------------------------------------- 1 | # The Timer Counter blocks of Arduino Due's AT91SAM3X8E 2 | 3 | The AT91SAM3X8E embedds nine general-purpose 32-bit timers/counters. They are organized in three blocks (`TC0`, `TC1`, `TC2`) each containing three channels (`0`, `1`, `2`). 4 | Each block and channel has input lines for their clock and input/output lines which can be used in different ways, such as PWM pins etc. 5 | 6 | 7 | ### Resources 8 | 9 | Information on the Timer Counter and its I/O signals pins can be found here: 10 | 11 | * Section **37 Timer Counter (TC)** in the [Atmel SAM3X Datasheet][] 12 | You find this chapter on the pages 869 through 920. It explains in detail how to operate the counter in different modes and has nice diagrams that show you how the clock input works and how to set up interrupts based on counter values etc. 13 | * [Arduino Due's SAM3X Pin Mapping][] and the 14 | * [Arduino Due pinout diagram][]. 15 | 16 | ### The Timer Counter Signals / Pins 17 | 18 | Many of the I/O lines of the Timer Counter are directly accessible on pin headers of the Arduino Due board. This section helps you to find out which of them are and which are not. 19 | 20 | In order to actually connect those signals (`TCLKx`, `TIOAx` and `TIOBx`) to the pins on the Arduino Due, you must tell the Parallel I/O Controllers (`PIOA`, `PIOB`, `PIOC` or `PIOD`) to set up their mux (multiplexer) accordingly.
21 | A useful resource to find out how to set the PIOs is 22 | [Arduino's const array of PinDescriptions](https://github.com/arduino/Arduino/blob/ide-1.5.x/hardware/arduino/sam/variants/arduino_due_x/variant.cpp#L117) and the 23 | [definition of PinDescription](https://github.com/arduino/Arduino/blob/ide-1.5.x/hardware/arduino/sam/cores/arduino/Arduino.h#L166), the data type of its entries. 24 | 25 | #### Overview of the I/O lines of the Timer Counter 26 | 27 | Here is a table of the Timer Counter channels and their `TCLKx` (external clock input), `TIOAx` (I/O Line _A_) and `TIOBx` (I/O Line _B_) signals: 28 | 29 | Instance | TC | Channel| External Clock Input | I/O Line A | I/O Line B 30 | ---------|-----|--------|----------------------|------------|----------- 31 | T0 | TC0 | 0 | TCLK0 | TIOA0 | TIOB0 32 | T1 | TC0 | 1 | TCLK1 | TIOA1 | TIOB1 33 | T2 | TC0 | 2 | TCLK2 | TIOA2 | TIOB2 34 | T3 | TC1 | 0 | TCLK3 | TIOA3 | TIOB3 35 | T4 | TC1 | 1 | TCLK4 | TIOA4 | TIOB4 36 | T5 | TC1 | 2 | TCLK5 | TIOA5 | TIOB5 37 | T6 | TC2 | 0 | TCLK6 | TIOA6 | TIOB6 38 | T7 | TC2 | 1 | TCLK7 | TIOA7 | TIOB7 39 | T8 | TC2 | 2 | TCLK8 | TIOA8 | TIOB8 40 | 41 | ##### TCLKx 42 | 43 | Here is how the **external clock inputs** are routed to pins on the Arduino Due board: 44 | 45 | Clock Input | Port Pin of µC | Pin on Arduino Due Board 46 | ------------|------------|----------------------------- 47 | TCLK0 | PB 26 | Digital Pin 22 48 | TCLK1 | PA 4 | Analog In 5 49 | TCLK2 | PA 7 | Digital Pin 31 50 | TCLK3 | PA 22 | Analog In 3 51 | TCLK4 | PA 23 | Analog In 2 52 | TCLK5 | PB 16 | DAC1 53 | TCLK6 | PC 27 | / 54 | TCLK7 | PC 30 | LED "RX" 55 | TCLK8 | PD 9 | Digital Pin 30 56 | 57 | ##### TIOAx 58 | 59 | Here is how the **I/O Lines _A_** are routed to pins on the Arduino Due board: 60 | 61 | I/O Line A | Port Pin of µC | Pin on Arduino Due Board 62 | -----------|----------------|----------------------------- 63 | TIOA0 | PB 25 | Digital Pin 2 64 | TIOA1 | PA 2 | Analog In 7 65 | TIOA2 | PA 5 | / 66 | TIOA3 | PE 9 | / 67 | TIOA4 | PE 11 | / 68 | TIOA5 | PE 13 | / 69 | TIOA6 | PC 25 | Digital Pin 5 70 | TIOA7 | PC 28 | Digital Pin 3 71 | TIOA8 | PD 7 | Digital Pin 11 72 | 73 | ##### TIOBx 74 | 75 | Here is how the **I/O Lines _B_** are routed to pins on the Arduino Due board: 76 | 77 | I/O Line B|Port Pin of µC| Pin on Arduino Due Board 78 | ----------|--------------|------------------------------------------ 79 | TIOB0 | PB 27 | Digital Pin 13 / Amber LED "L" 80 | TIOB1 | PA 3 | Analog In 6 81 | TIOB2 | PA 6 | Analog In 4 82 | TIOB3 | PE 10 | / 83 | TIOB4 | PE 12 | / 84 | TIOB5 | PE 14 | / 85 | TIOB6 | PC 26 | Digital Pin 4 (also connected to PA29) 86 | TIOB7 | PC 29 | Digital Pin 10 (also connected to PA28) 87 | TIOB8 | PD 8 | Digital Pin 12 88 | 89 | #### TC Clock Source Selection 90 | 91 | The *clock of the counters* can be set very flexibly to internal or external signals. To *use the channel as a timer*, it makes sense to connect its clock input to an internal timer clock. Here are the options: 92 | 93 | Name | Definition 94 | -------------|----------- 95 | TIMER_CLOCK1 | `MCK / 2` 96 | TIMER_CLOCK2 | `MCK / 8` 97 | TIMER_CLOCK3 | `MCK / 32` 98 | TIMER_CLOCK4 | `MCK / 128` 99 | TIMER_CLOCK5 | `SLCK` 100 | 101 | 102 | Where `MCK` is the master clock (84 MHz for the Arduino Due) and SLCK is the slow clock (which can be clocked at 32.768 kHz by Y2 on the Arduino Due). 103 | 104 | [Atmel SAM3X Datasheet]: http://www.atmel.com/Images/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf 105 | [Arduino Due's SAM3X Pin Mapping]: http://arduino.cc/en/Hacking/PinMappingSAM3X 106 | [Arduino Due pinout diagram]: http://www.robgray.com/temp/Due-pinout.pdf 107 | -------------------------------------------------------------------------------- /DueTimer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | DueTimer.cpp - Implementation of Timers defined on DueTimer.h 3 | For instructions, go to https://github.com/ivanseidel/DueTimer 4 | 5 | Created by Ivan Seidel Gomes, March, 2013. 6 | Modified by Philipp Klaus, June 2013. 7 | Thanks to stimmer (from Arduino forum), for coding the "timer soul" (Register stuff) 8 | Released into the public domain. 9 | */ 10 | 11 | #include "DueTimer.h" 12 | 13 | const DueTimer::Timer DueTimer::Timers[NUM_TIMERS] = { 14 | {TC0,0,TC0_IRQn}, 15 | {TC0,1,TC1_IRQn}, 16 | {TC0,2,TC2_IRQn}, 17 | {TC1,0,TC3_IRQn}, 18 | {TC1,1,TC4_IRQn}, 19 | {TC1,2,TC5_IRQn}, 20 | #if NUM_TIMERS > 6 21 | {TC2,0,TC6_IRQn}, 22 | {TC2,1,TC7_IRQn}, 23 | {TC2,2,TC8_IRQn}, 24 | #endif 25 | }; 26 | 27 | // Fix for compatibility with Servo library 28 | #ifdef USING_SERVO_LIB 29 | // Set callbacks as used, allowing DueTimer::getAvailable() to work 30 | void (*DueTimer::callbacks[NUM_TIMERS])() = { 31 | (void (*)()) 1, // Timer 0 - Occupied 32 | (void (*)()) 0, // Timer 1 33 | (void (*)()) 1, // Timer 2 - Occupied 34 | (void (*)()) 1, // Timer 3 - Occupied 35 | (void (*)()) 1, // Timer 4 - Occupied 36 | (void (*)()) 1, // Timer 5 - Occupied 37 | #if NUM_TIMERS > 6 38 | (void (*)()) 0, // Timer 6 39 | (void (*)()) 0, // Timer 7 40 | (void (*)()) 0 // Timer 8 41 | #endif 42 | }; 43 | #else 44 | void (*DueTimer::callbacks[NUM_TIMERS])() = {}; 45 | #endif 46 | 47 | #if NUM_TIMERS > 6 48 | double DueTimer::_frequency[NUM_TIMERS] = {-1,-1,-1,-1,-1,-1,-1,-1,-1}; 49 | #else 50 | double DueTimer::_frequency[NUM_TIMERS] = {-1,-1,-1,-1,-1,-1}; 51 | #endif 52 | 53 | /* 54 | Initializing all timers, so you can use them like this: Timer0.start(); 55 | */ 56 | DueTimer Timer(0); 57 | 58 | DueTimer Timer1(1); 59 | // Fix for compatibility with Servo library 60 | #ifndef USING_SERVO_LIB 61 | DueTimer Timer0(0); 62 | DueTimer Timer2(2); 63 | DueTimer Timer3(3); 64 | DueTimer Timer4(4); 65 | DueTimer Timer5(5); 66 | #endif 67 | #if NUM_TIMERS > 6 68 | DueTimer Timer6(6); 69 | DueTimer Timer7(7); 70 | DueTimer Timer8(8); 71 | #endif 72 | 73 | DueTimer::DueTimer(unsigned short _timer) : timer(_timer){ 74 | /* 75 | The constructor of the class DueTimer 76 | */ 77 | } 78 | 79 | DueTimer DueTimer::getAvailable(void){ 80 | /* 81 | Return the first timer with no callback set 82 | */ 83 | 84 | for(int i = 0; i < NUM_TIMERS; i++){ 85 | if(!callbacks[i]) 86 | return DueTimer(i); 87 | } 88 | // Default, return Timer0; 89 | return DueTimer(0); 90 | } 91 | 92 | DueTimer& DueTimer::attachInterrupt(void (*isr)()){ 93 | /* 94 | Links the function passed as argument to the timer of the object 95 | */ 96 | 97 | callbacks[timer] = isr; 98 | 99 | return *this; 100 | } 101 | 102 | DueTimer& DueTimer::detachInterrupt(void){ 103 | /* 104 | Links the function passed as argument to the timer of the object 105 | */ 106 | 107 | stop(); // Stop the currently running timer 108 | 109 | callbacks[timer] = NULL; 110 | 111 | return *this; 112 | } 113 | 114 | DueTimer& DueTimer::start(double microseconds){ 115 | /* 116 | Start the timer 117 | If a period is set, then sets the period and start the timer 118 | */ 119 | 120 | if(microseconds > 0) 121 | setPeriod(microseconds); 122 | 123 | if(_frequency[timer] <= 0) 124 | setFrequency(1); 125 | 126 | NVIC_ClearPendingIRQ(Timers[timer].irq); 127 | NVIC_EnableIRQ(Timers[timer].irq); 128 | 129 | TC_Start(Timers[timer].tc, Timers[timer].channel); 130 | 131 | return *this; 132 | } 133 | 134 | DueTimer& DueTimer::stop(void){ 135 | /* 136 | Stop the timer 137 | */ 138 | 139 | NVIC_DisableIRQ(Timers[timer].irq); 140 | 141 | TC_Stop(Timers[timer].tc, Timers[timer].channel); 142 | 143 | return *this; 144 | } 145 | 146 | uint8_t DueTimer::bestClock(double frequency, uint32_t& retRC){ 147 | /* 148 | Pick the best Clock, thanks to Ogle Basil Hall! 149 | 150 | Timer Definition 151 | TIMER_CLOCK1 MCK / 2 152 | TIMER_CLOCK2 MCK / 8 153 | TIMER_CLOCK3 MCK / 32 154 | TIMER_CLOCK4 MCK /128 155 | */ 156 | const struct { 157 | uint8_t flag; 158 | uint8_t divisor; 159 | } clockConfig[] = { 160 | { TC_CMR_TCCLKS_TIMER_CLOCK1, 2 }, 161 | { TC_CMR_TCCLKS_TIMER_CLOCK2, 8 }, 162 | { TC_CMR_TCCLKS_TIMER_CLOCK3, 32 }, 163 | { TC_CMR_TCCLKS_TIMER_CLOCK4, 128 } 164 | }; 165 | float ticks; 166 | float error; 167 | int clkId = 3; 168 | int bestClock = 3; 169 | float bestError = 9.999e99; 170 | do 171 | { 172 | ticks = (float) SystemCoreClock / frequency / (float) clockConfig[clkId].divisor; 173 | // error = abs(ticks - round(ticks)); 174 | error = clockConfig[clkId].divisor * abs(ticks - round(ticks)); // Error comparison needs scaling 175 | if (error < bestError) 176 | { 177 | bestClock = clkId; 178 | bestError = error; 179 | } 180 | } while (clkId-- > 0); 181 | ticks = (float) SystemCoreClock / frequency / (float) clockConfig[bestClock].divisor; 182 | retRC = (uint32_t) round(ticks); 183 | return clockConfig[bestClock].flag; 184 | } 185 | 186 | 187 | DueTimer& DueTimer::setFrequency(double frequency){ 188 | /* 189 | Set the timer frequency (in Hz) 190 | */ 191 | 192 | // Prevent negative frequencies 193 | if(frequency <= 0) { frequency = 1; } 194 | 195 | // Remember the frequency — see below how the exact frequency is reported instead 196 | //_frequency[timer] = frequency; 197 | 198 | // Get current timer configuration 199 | Timer t = Timers[timer]; 200 | 201 | uint32_t rc = 0; 202 | uint8_t clock; 203 | 204 | // Tell the Power Management Controller to disable 205 | // the write protection of the (Timer/Counter) registers: 206 | pmc_set_writeprotect(false); 207 | 208 | // Enable clock for the timer 209 | pmc_enable_periph_clk((uint32_t)t.irq); 210 | 211 | // Find the best clock for the wanted frequency 212 | clock = bestClock(frequency, rc); 213 | 214 | switch (clock) { 215 | case TC_CMR_TCCLKS_TIMER_CLOCK1: 216 | _frequency[timer] = (double)SystemCoreClock / 2.0 / (double)rc; 217 | break; 218 | case TC_CMR_TCCLKS_TIMER_CLOCK2: 219 | _frequency[timer] = (double)SystemCoreClock / 8.0 / (double)rc; 220 | break; 221 | case TC_CMR_TCCLKS_TIMER_CLOCK3: 222 | _frequency[timer] = (double)SystemCoreClock / 32.0 / (double)rc; 223 | break; 224 | default: // TC_CMR_TCCLKS_TIMER_CLOCK4 225 | _frequency[timer] = (double)SystemCoreClock / 128.0 / (double)rc; 226 | break; 227 | } 228 | 229 | // Set up the Timer in waveform mode which creates a PWM 230 | // in UP mode with automatic trigger on RC Compare 231 | // and sets it up with the determined internal clock as clock input. 232 | TC_Configure(t.tc, t.channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | clock); 233 | // Reset counter and fire interrupt when RC value is matched: 234 | TC_SetRC(t.tc, t.channel, rc); 235 | // Enable the RC Compare Interrupt... 236 | t.tc->TC_CHANNEL[t.channel].TC_IER=TC_IER_CPCS; 237 | // ... and disable all others. 238 | t.tc->TC_CHANNEL[t.channel].TC_IDR=~TC_IER_CPCS; 239 | 240 | return *this; 241 | } 242 | 243 | DueTimer& DueTimer::setPeriod(double microseconds){ 244 | /* 245 | Set the period of the timer (in microseconds) 246 | */ 247 | 248 | // Convert period in microseconds to frequency in Hz 249 | double frequency = 1000000.0 / microseconds; 250 | setFrequency(frequency); 251 | return *this; 252 | } 253 | 254 | double DueTimer::getFrequency(void) const { 255 | /* 256 | Get current time frequency 257 | */ 258 | 259 | return _frequency[timer]; 260 | } 261 | 262 | double DueTimer::getPeriod(void) const { 263 | /* 264 | Get current time period 265 | */ 266 | 267 | return 1.0/getFrequency()*1000000; 268 | } 269 | 270 | 271 | /* 272 | Implementation of the timer callbacks defined in 273 | arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/sam3x8e.h 274 | */ 275 | // Fix for compatibility with Servo library 276 | #ifndef USING_SERVO_LIB 277 | void TC0_Handler(void){ 278 | TC_GetStatus(TC0, 0); 279 | DueTimer::callbacks[0](); 280 | } 281 | #endif 282 | void TC1_Handler(void){ 283 | TC_GetStatus(TC0, 1); 284 | DueTimer::callbacks[1](); 285 | } 286 | // Fix for compatibility with Servo library 287 | #ifndef USING_SERVO_LIB 288 | void TC2_Handler(void){ 289 | TC_GetStatus(TC0, 2); 290 | DueTimer::callbacks[2](); 291 | } 292 | void TC3_Handler(void){ 293 | TC_GetStatus(TC1, 0); 294 | DueTimer::callbacks[3](); 295 | } 296 | void TC4_Handler(void){ 297 | TC_GetStatus(TC1, 1); 298 | DueTimer::callbacks[4](); 299 | } 300 | void TC5_Handler(void){ 301 | TC_GetStatus(TC1, 2); 302 | DueTimer::callbacks[5](); 303 | } 304 | #endif 305 | #if NUM_TIMERS > 6 306 | void TC6_Handler(void){ 307 | TC_GetStatus(TC2, 0); 308 | DueTimer::callbacks[6](); 309 | } 310 | void TC7_Handler(void){ 311 | TC_GetStatus(TC2, 1); 312 | DueTimer::callbacks[7](); 313 | } 314 | void TC8_Handler(void){ 315 | TC_GetStatus(TC2, 2); 316 | DueTimer::callbacks[8](); 317 | } 318 | #endif 319 | --------------------------------------------------------------------------------