Sunday, February 9, 2014

Arduino micros() function with 0.5us precision - using my Timer2_Counter Library

Advertisement

Subscribe by Email! (via FeedBurner)
...merging the world of Arduino and Radio Control, one tool at a time...
...CODE FOR A PRECISE MICROS() FUNCTION IS POSTED BELOW...

"I wrote a libary to get 0.5us precision on a "micros()" replacement function, so that I can get repeatable results reading a PWM or PPM signal, to within 1us.  I searched all around the internet and could not find something comparable (or that was easy to use, and maintained the Arduino's ability to write PWM signals via the Servo Libary), so I think this is my first real contribution to the world of Arduino and Radio Control."  

Download the code by clicking here --> then go to "Download ZIP" at the bottom of the right-hand pane.  Extract the files, then run the example by opening the "Timer2_Counter_Basic_Example.ino" file and uploading it to your Arduino.  Then, open the Serial Monitor to see the output.


(font above is type "standard," from here)



By Gabriel Staples
Written: 9 Feb. 2014
Last Updated: 8 May 2015
-slight re-arrangement of a few bits of info - 8 May 2015
-changed license from GPL to LGPL V3 or later; added a new example demonstrating reading in ANY pulsing signal (to measure high pulse width and frequency), INCLUDING RC PWM signals, using Pin Change Interrupts! The example may be fundamentally quite complicated, but I assure you it is very easy to use. Download the latest version of code to see this new example. - 21 March 2015
-added PPM/PWM links at very bottom of post - 26 Feb. 2014
-moved code from Google Drive to GitHub, & updated download links - 12 April 2014
-I have now turned this into a full-blown, true Arduino library!  Now you can actually install it like a normal Arduino library, and use the #include <eRCaGuy_Timer2_Counter.h> line and everything!  Download the latest code by clicking the download link - 17 May 2014
-26 July 2014 - minor changes to function names (removed the "T2" parts since I made that change in my library several months back)

========================================================================
Please support my work and contributions by using my code and sharing links to my website! Also, *subscribe* to receive immediate notification of new articles, by using the links at the top-right!
========================================================================

Related Articles:

Timer2_Counter Library:

In order to interface Arduino with the world of Radio Control, I need to be able to interpret and even duplicate the signals which Radio Control vehicles use.  These signals include Pulse Position Modulation (PPM) and Pulse Width Modulation (PWM).  I will not go into the details of how these signals work, but I will say that I have spent a great deal of time learning about them to a very detailed level.  This way, I can have the knowledge to read and manipulate them.

For those of you who don't know: the signal coming out of the trainer port, or going into the trainer port, on the back of an RC transmitter (Tx), is a PPM signal.  The signal coming out of a receiver (Rx) and to a servo or Electronic Speed Conroller (ESC) is a PWM signal.

If an Arduino can read the PPM signal coming out of the back of a Tx, this is EXTREMELY useful in debugging your radio and setup by allowing you to see what pulse widths your Tx is outputting for each channel.  Additionally, it allows you to use your RC Tx as an INPUT DEVICE to control an Arduino!

You now have access to all these analog and digital inputs (sticks, scroll wheels, switches, etc), to manipulate your Arduino!  And, since many RC hobbyists (like me) have many cheap radios from RTF (Ready to Fly) kits and things lying around, this is very useful so that we can finally put them to use doing something else.  For instance, I plan on using one of my old radios, with an Arduino nano and a Leonardo-compatible device, to act as a wireless mouse for controlling my wife's computer when connected to the TV.  Then, I can sit on the couch and control the screen with an RC Tx!

If an Arduino can create a PPM signal to go into the trainer port on the back of a Tx, this is EXTREMELY useful because it allows you to use the Arduino to drive your RC vehicle via Feed-Forward loops (ie: you can preprogram commands in the Arduino, like "go forward 20 feet, then turn left 45 deg," or "do continuous figure 8's."

Similarly, putting your Arduino in at the Rx end is also very useful, because you can create your own custom mixes to manipulate your servos or speed controllers, or you can create or modify an autopilot.  Also, knowing how to read the PPM and PWM signals from a receiver also enables you to use a standard RC Tx/Rx combination as a wireless communication device for your Arduino!  You can even send data from one Arduino to another, over open-air distances up to 1km, using this technique, again, if you understand the signals well enough.  This makes RC and Arduino a very powerful combination, and to the Arduino user: I should note that many older 72MHz RC Tx/Rx combos exist out there that are being sold for ~$20 used by Radio Control hobbyists who are transitioning to the newer 2.4GHz spread-spectrum systems.  If you see a 72MHz system for only a few bucks, grab it!  It's a great tool for your Arduino.

Now...on to the title of this post.  In learning to manipulate and read PWM and PPM signals, it quickly became apparent to me that timing is critical.  I originally used the built-in Arduino micros() function for my timing, but it only has a resolution of 4us.  This is very poor, as it means that I get errors up to +/- 8us, over time (determined experimentally).  PWM and PPM signals usually range from 900~2100us max, but are more commonly 1100~1900us (this is default on a Spektrum brand Tx).  In your standard "computerized" (ie: microcontroller-run) RC Tx, when you change your "%" range or endpoint settings, each percent corresponds to 4us.  (This is calculated as follows: 1900-1100 = 800us, which is +/-100% of the standard travel, so 1500 to 1900us is the +100% portion, which covers 400us, and 1100 to 1500 is the -100% portion, which covers 400us.  Therefore, 400us/100% = 4us per 1%.)  Therefore, assuming an error span of 12us, that's a fluctuation in simply reading or writing a fixed channel value, of up to 3% as seen in the radio settings.

I didn't like that, so I wrote a "libary" to get 0.5us precision on a "micros()" replacement function, so that I can get repeatable results reading a PWM or PPM signal, to within 1us.  I searched all around the internet and could not find something comparable (or that was easy to use, and maintained the Arduino's ability to write PWM signals via the Servo Libary), so I think this is my first major contribution to the world of Arduino and Radio Control.

First, I will post an example code which shows how to use my Timer2_Counter "Library."  I have thoroughly commented all of my code.  You will see that I use micros() as a comparison, to show that my code does in fact work.

-Due to line wrap when I pasted the code on my blog, the formatting of the code below is not perfect.  When you view it in the Arduino IDE (Integrated Development Environment), however, it looks good.
Advertisement

Download the code by clicking here --> then go to "Download ZIP" at the bottom of the right-hand pane.  Extract the files, then run the example by opening the "Timer2_Counter_Basic_Example.ino" file and uploading it to your Arduino.  Then, open the Serial Monitor to see the output.

Additional References to Get Started Learning about Radio Control PPM (Pulse Position Modulation) and PWM (Pulse Width Modulation) Signals:
  1. http://www.camelsoftware.com/firetail/blog/radio/reading-pwm-signals-from-a-remote-control-receiver-with-arduino/ - Reading PWM Signals from a Remote Control Receiver, with Arduino
  2. http://www.mftech.de/ppm_en.htm - PPM Encoding
  3. http://www.mftech.de/buchsen_en.htm - R/C buddy box plugs/ pin assignment

========================================================================
Please support my work and contributions by subscribing and sharing using the buttons at the top-right of my website!  Also be sure to share links to my articles with your friends.
========================================================================


The Code:

(Update: 27 March 2015: the below code is outdated. Download the latest version of the code using the links already mentioned above, and reference the included files, including the examples, to see what the code and functions look like from the latest version of this library!)

Function Definitions of Timer2_Counter:
-This is a list of the available functions, for a full description of each function, refer to the comments at the top of the "Timer2_Counter.ino" file.

  • setup();
  • get_count();
  • get_micros();
  • reset();
  • revert_to_normal();
  • unsetup();
  • overflow_interrupt_off();
  • overflow_interrupt_on();

1) Example Code


/*
Timer2 Counter Basic Example
-A timer function with 0.5us precision, rather than 4us precision like the built-in Arduino micros() function has.
By Gabriel Staples
Visit my blog at http://electricrcaircraftguy.blogspot.com/
-My contact info is available by clicking the "Contact Me" tab at the top of my blog.
-Please support my work & contributions by buying something here: https://sites.google.com/site/ercaguystore1/
My original post containing this code can be found here: http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html
Written: 8 Feb. 2014
Updated: 9 Feb. 2014
*/

/*
===================================================================================================
  LICENSE & DISCLAIMER
  Copyright (C) 2014 Gabriel Staples.  All right reserved.
  
  This code was written entirely at home, during my own personal time, and is neither a product of work nor my employer.
  Furthermore, unless otherwise stated, it is owned entirely by myself.
  
  ------------------------------------------------------------------------------------------------
  License: GNU General Public License Version 3 (GPLv3) - http://www.gnu.org/licenses/gpl.html
  ------------------------------------------------------------------------------------------------

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see http://www.gnu.org/licenses/
===================================================================================================
*/

//CODE DESCRIPTION:
//This code demonstrates the use of my Timer2, which provides a more precise timer than micros().  
//micros() has a precision of only 4us.  However, Timer2 keeps track of time to a precision of 0.5us.
//This is especially important in my code which reads an RC receiver PWM signal, which varies from 900~2100us. 
//Though this code demonstrates the use of the Timer_2 functions I have written, it does not adequately demonstrate the 
//real utility of the code, so I will state the following:
//By using my Timer2 timer to measure the PWM high time interval on an RC receiver, in place of using micros(), I can get repeatable 
//pulse width reads with a fluctuation of ~1us, rather than having a read-in range fluctuating by as much as +/- 4~8 us when I use micros().
//This is an increase in precision of ~8x.

  
void setup() {
  //configure Timer2
  setup_T2(); //this MUST be done before the other functions work; Note: since this messes up PWM outputs on pins 11 & 3, 
              //you can always revert Timer2 back to normal by calling unsetup_T2()
  
  //prepare serial
  Serial.begin(115200);  
  
  //Output a header of info:
  Serial.println("Notes:");
  Serial.println("micros() has a precision of 4us");
  Serial.println("get_T2_count() with unsigned long final data type has a final precision of 1us, and is fast");
  Serial.println("get_T2_count() with float final data type has a final precision of 0.5us, and is not quite as fast");
  Serial.println("get_T2_micros() has a precision of 0.5us, and is slower than the above 2 methods, so one of the above 2 methods is preferred");
  Serial.println("==============================================");
}


void loop() {
  //Grab Start Times
  unsigned long t_start1 = micros(); //us; get the current time using the built-in Arduino function micros(), to a precision of 4us
  unsigned long t_start2 = get_T2_count(); //count units of 0.5us each; get my Timer2 count, where each count represents 0.5us; PREFERRED METHOD
  float t_start3 = get_T2_micros(); //us; get the current time using my Timer2; Note: THE METHOD ONE LINE ABOVE IS PREFERRED OVER THIS METHOD
                                    //since using floats is slower than using unsigned longs
  
  //Wait a bit                       
  delayMicroseconds(1000);
  
  //Grab End Times
  unsigned long t_end1 = micros(); //us; using built-in Arduino function that has a precision of 4us
  unsigned long t_end2 = get_T2_count(); //count units of 0.5us each; using my Timer2 count, where each count represents 0.5us
  float t_end3 = get_T2_micros(); //us; using my Timer2 micros, which has a precision of 0.5us
  
  //Calculate elapsed times
  unsigned int t_elapsed1 = t_end1 - t_start1; //us; using micros()
  unsigned int t_elapsed2_ul = (t_end2 - t_start2)/2; //us; to a precision of 1us, due to using unsigned long data type truncation, using Timer2 count
  float t_elapsed2_fl = (t_end2 - t_start2)/2.0; //us; to a precision of 0.5us, due to using float data type for final time difference calc; note that I divide by 2.0, NOT 2
  float t_elapsed3 = t_end3 - t_start3; //us; to a precision of 0.5us
  
  //Display the results
  Serial.println(""); //insert a space
  Serial.print("elapsed time using micros() = ");
  Serial.print(t_elapsed1);
  Serial.println("us");
  Serial.print("elapsed time using get_T2_count() with unsigned long final data type = ");
  Serial.print(t_elapsed2_ul);
  Serial.println("us");
  Serial.print("elapsed time using get_T2_count() with float final data type = ");
  Serial.print(t_elapsed2_fl);
  Serial.println("us");
  Serial.print("elapsed time using get_T2_micros() = ");
  Serial.print(t_elapsed3);
  Serial.println("us");
  
  //Wait a second before repeating
  delay(1000); 
}


2) Timer2_Counter main "Library" file:
/*
Timer2 Counter
-A timer function with 0.5us precision, rather than 4us precision like the built-in Arduino micros() function has.
By Gabriel Staples
Visit my blog at http://electricrcaircraftguy.blogspot.com/
-My contact info is available by clicking the "Contact Me" tab at the top of my blog.
-Please support my work & contributions by buying something here: https://sites.google.com/site/ercaguystore1/
My original post containing this code can be found here: http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html
Written: 7 Dec. 2013
Last Updated: 11 Dec. 2013
*/

/*
===================================================================================================
  LICENSE & DISCLAIMER
  Copyright (C) 2014 Gabriel Staples.  All right reserved.
  
  This code was written entirely at home, during my own personal time, and is neither a product of work nor my employer.
  Furthermore, unless otherwise stated, it is owned entirely by myself.
  
  ------------------------------------------------------------------------------------------------
  License: GNU General Public License Version 3 (GPLv3) - http://www.gnu.org/licenses/gpl.html
  ------------------------------------------------------------------------------------------------

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see http://www.gnu.org/licenses/
===================================================================================================
*/

/*
CODE DESCRIPTION:
BACKGROUND:
-This code uses Timer2 to create a more precise timer than micros().  Micros() updates only every 4us.  However, I want something that will update every 0.5us, and that's 
what this provides. 
-The downside is that it changes the behavior of PWM output (using analogWrite) on Pins 3 & 11.
-The upside is that I have managed to get a precise timer using the 8-bit Timer2, rather than the 16-bit Timer1, so that I can keep the Timer1 unmodified so I can continue
 to use the servo library as desired.  Note here that the servo library relies on the Atmega328 16-bit Timer1.  I have deciphered this by knowing that
 A) using the servo library at all disables PWM output on pins 9 & 10; see here: http://www.arduino.cc/en/Reference/Servo
 and B) PWM on pins 9 & 10 is controlled by Timer1; see here: http://playground.arduino.cc/Main/TimerPWMCheatsheet
-Note: every now and then, but not very often, you will see that the value returned by get_T2_count or get_T2_micros is late by ~4us.  This is because Timer2 is only
 an 8-bit timer, so every 128us the overflow interrupt is called to increment the overflow counter, and stepping into and out of the interrupt takes 4~5us
 according to Nick Gammon. Source: Nick Gammon; "Interrupts" article; "How long does it take to execute an ISR?" section, found here: http://www.gammon.com.au/forum/?id=11488
IMPLEMENTATION:
-To use this code, simply copy this file (I have it named "Timer2_Counter.ino") into your directory (folder) where you are already working on a specific Arduino file.  
Next time you open up your main file you are working on, this code will automatically open up as an additional tab in the Arduino IDE.  
-I have tested this code ONLY on Arduinos using the Atmega328 microcontroller; more specifically, the Arduino Nano.

I heavily reference the 660 pg. Atmega328 datasheet, which can be found here:  http://www.atmel.com/Images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet.pdf


Version History:
(most recent event last; format year-month-day, ex: 20131211 is Dec. 11, 2013):
20131211 - completed first iteration of code

============================================================================================================================================================================
Function Definitions
 ___  _   _  _  _   ___  _____  ___  ___   _  _      ___   ___  ___  ___  _  _  ___  _____  ___  ___   _  _  ___ 
| __|| | | || \| | / __||_   _||_ _|/ _ \ | \| |    |   \ | __|| __||_ _|| \| ||_ _||_   _||_ _|/ _ \ | \| |/ __|
| _| | |_| || .` || (__   | |   | || (_) || .` |    | |) || _| | _|  | | | .` | | |   | |   | || (_) || .` |\__ \
|_|   \___/ |_|\_| \___|  |_|  |___|\___/ |_|\_|    |___/ |___||_|  |___||_|\_||___|  |_|  |___|\___/ |_|\_||___/
                                                     
setup_T2();  //This function MUST be called before any of the other Timer2 functions will work.  This function will generally only be called one time in your setup() loop.
             //"setup_T2()" prepares Timer2 and speeds it up to provide greater precision than micros() can give.
get_T2_count();  //gets the counter from Timer 2. Returns the Timer2 counter value as an unsigned long.  Each count represents a time interval of 0.5us.
                 //note that the time returned WILL update even in Interrupt Service Routines (ISRs), so if you call this function in an ISR, and you want the time to be
                 //as close to possible as a certain event that occured which called the ISR you are in, make sure to call "get_T2_count()" first thing when you enter
                 //the ISR.
                 //Also, note that calling "get_T2_count()" is faster than calling "get_T2_micros()," and is therefore the preferable way to measure a time interval.
                 //For example: call "get_T2_count()" at the beginning of some event, then at the end.  Take the difference and divide it by 2 to get the time interval
                 //in microseconds.
get_T2_micros(); //returns the Timer2 microsecond time, with a precision of 0.5us, as a float.  This function is slower than calling "get_T2_count()," and therefore is not
                 //the preferred way of getting time.  It is better to get the time by calling "get_T2_count()" then dividing the value by 2.  By choosing whether or not
                 //you want to call "get_T2_count()" or "get_T2_micros()," you can decide if you need the extra precision of a float, or not, at the cost of having 
                 //slightly slower code.
reset_T2(); //resets the Timer2 counters back to 0.  Very useful if you want to count up from a specific moment in time, or obtain an "elapsed time."
revert_T2_to_normal(); //this function might also be called "unsetup_T2".  It simply returns Timer2 to its normal state that Arduino had it in prior to calling "setup_T2"
unsetup_T2(); //the exact same as "revert_T2_to_normal()"
T2_overflow_interrupt_off(); //turns off the Timer 2 overflow interrrupt so that you no longer interrupt your code every 128us in order to increment your overflow counter. 
                       //This may be desirable when you are no longer referencing the T2 counter or timer and want your main code to run a touch faster, but you don't want
                       //to call unsetup_T2() in order to change all of Timer2's settings back to default.
                       //Since an interrupt takes ~5us to execute, and my Timer2 will overflow every 128us, disabling the Timer2 overflow interrupt will prevent 
                       //you from losing that amount of time (~5us) every 128us.
                       //Source: Nick Gammon; "Interrupts" article; "How long does it take to execute an ISR?" section, found here: http://www.gammon.com.au/forum/?id=11488
                       //Note: If you diable the Timer 2 overflow interrupt but still call get_TC_count() or get_TC_micros() at least every 128us, you will notice no 
                       //difference in the counter, since calling get_TC_count() or get_TC_micros() also checks the interrupt flag and increments the overflow counter 
                       //automatically.  You have to wait > 128us before you see any missed overflow counts.
T2_overflow_interrupt_on(); //turns Timer 2's overflow interrupt back on, so that the overflow counter will start to increment again; see "T2_overflow_interrupt_off()"
                            //explanation for more details.
============================================================================================================================================================================
References:
-cool font, type "small", from: http://www.network-science.de/ascii/

Additional Resources:
-Nick Gammon's "Timers & Counters" article - http://www.gammon.com.au/forum/?id=11504
-Nick Gammon's "Interrupts" article - http://www.gammon.com.au/forum/?id=11488
*/


//Set up Global Variables
//volatile (used in ISRs)
volatile unsigned long T2_overflow_count = 0; //initialize Timer2 overflow counter
volatile unsigned long T2_total_count = 0; //initialize Timer2 total counter
//normal variables
byte tccr2a_save; //initialize; will be used to backup default settings
byte tccr2b_save; //initialize; will be used to backup default settings


//Interrupt Service Routine (ISR) for when Timer2's counter overflows; this will occur every 128us
ISR(TIMER2_OVF_vect) //Timer2's counter has overflowed 
{
  T2_overflow_count++; //increment the timer2 overflow counter
}


//Configure Timer2
void setup_T2()
{
  //backup variables
  tccr2a_save = TCCR2A; //first, backup some values
  tccr2b_save = TCCR2B; //backup some more values
  
  //increase the speed of timer2; see below link, as well as the datasheet pg 158-159.
  TCCR2B = TCCR2B & 0b11111000 | 0x02; //Timer2 is now faster than default; see here for more info: http://playground.arduino.cc/Main/TimerPWMCheatsheet
  //Note: don't forget that when you speed up Timer2 like this you are also affecting any PWM output (using analogWrite) on Pins 3 & 11.  
  //Refer to the link just above, as well as to this source here:  http://www.oreilly.de/catalog/arduinockbkger/Arduino_Kochbuch_englKap_18.pdf

  //Enable Timer2 overflow interrupt; see datasheet pg. 159-160
  TIMSK2 |= 0b00000001; //enable Timer2 overflow interrupt. (by making the right-most bit in TIMSK2 a 1)
  //TIMSK2 &= 0b11111110; //use this code to DISABLE the Timer2 overflow interrupt, if you ever wish to do so later. (see datasheet pg. 159-160)
  
  //set timer2 to "normal" operation mode.  See datasheet pg. 147, 155, & 157-158 (incl. Table 18-8).  
  //-This is important so that the timer2 counter, TCNT2, counts only UP and not also down.
  //-To do this we must make WGM22, WGM21, & WGM20, within TCCR2A & TCCR2B, all have values of 0.
  TCCR2A &= 0b11111100; //set WGM21 & WGM20 to 0 (see datasheet pg. 155).
  TCCR2B &= 0b11110111; //set WGM22 to 0 (see pg. 158).
}  
  

//get total count for Timer2
unsigned long get_T2_count()
{
  noInterrupts(); //prepare for critical section of code
  uint8_t tcnt2_save = TCNT2; //grab the counter value from Timer2
  boolean flag_save = bitRead(TIFR2,0); //grab the timer2 overflow flag value
  if (flag_save) { //if the overflow flag is set
    tcnt2_save = TCNT2; //update variable just saved since the overflow flag could have just tripped between previously saving the TCNT2 value and reading bit 0 of TIFR2.  
                        //If this is the case, TCNT2 might have just changed from 255 to 0, and so we need to grab the new value of TCNT2 to prevent an error of up 
                        //to 127.5us in any time obtained using the T2 counter (ex: T2_micros). (Note: 255 counts / 2 counts/us = 127.5us)
                        //Note: this line of code DID in fact fix the error just described, in which I periodically saw an error of ~127.5us in some values read in
                        //by some PWM read code I wrote.
    T2_overflow_count++; //force the overflow count to increment
    TIFR2 |= 0b00000001; //reset Timer2 overflow flag since we just manually incremented above; see datasheet pg. 160; this prevents execution of Timer2's overflow ISR
  }
  T2_total_count = T2_overflow_count*256 + tcnt2_save; //get total Timer2 count
  interrupts(); //allow interrupts again
  return T2_total_count;
}


//get the time in microseconds, as determined by Timer2; the precision will be 0.5 microseconds instead of the 4 microsecond precision of micros()
float get_T2_micros()
{
  float T2_micros = get_T2_count()/2.0; 
  return T2_micros;
}


//reset Timer2's counters
void reset_T2()
{
  T2_overflow_count = 0; //reset overflow counter
  T2_total_count = 0; //reset total counter
  TCNT2 = 0; //reset Timer2 counter
  TIFR2 |= 0b00000001; //reset Timer2 overflow flag; see datasheet pg. 160; this prevents an immediate execution of Timer2's overflow ISR
}


//undo configuration changes for Timer2
void revert_T2_to_normal()
{
  T2_overflow_interrupt_off(); //turn off Timer2 overflow interrupts
  TCCR2A = tccr2a_save; //restore default settings
  TCCR2B = tccr2b_save; //restore default settings
}


//same as revert_T2_to_normal()
void unsetup_T2()
{
  revert_T2_to_normal();
}


//Turn off the Timer2 Overflow Interrupt
void T2_overflow_interrupt_off()
{
//  TIMSK2 &= 0b11111110; //use this code to DISABLE the Timer2 overflow interrupt; see datasheet pg. 159-160
  TIMSK2 &= ~(_BV(TOIE2)); //alternate code to do the above; see here for use of _BV: http://194.81.104.27/~brian/microprocessor/BVMacro.pdf 
}


//Turn the Timer2 Overflow Interrupt Back On
void T2_overflow_interrupt_on()
{
//  TIMSK2 |= 0b00000001; //enable Timer2 overflow interrupt. (by making the right-most bit in TIMSK2 a 1); see datasheet pg. 159-160
  TIMSK2 |= _BV(TOIE2); //alternate code to do the above; see here for use of _BV: http://194.81.104.27/~brian/microprocessor/BVMacro.pdf 
}


========================================================================
Please support my work and contributions by subscribing and sharing using the buttons at the top-right of my website!  Also be sure to share links to my articles with your friends.
========================================================================

THE END



***Subscribe by Email! (via FeedBurner)***

19 comments:

  1. Gabriel, I'm potentially interested in using your library but the GPL3 license is probably going to be too restrictive for me. As such I haven't even looked through what you have to see if it will work for me. Can you contact me so we can discuss what the license actually means and limits me to and so I can let you know what I have in mind for your library. My work is "mostly" for charity so maybe we can come to an arrangement. thanks. --Dave (contact@racecoordinator.net)

    ReplyDelete
    Replies
    1. Dave, thanks for the note! I emailed you. We can discuss more there.

      Delete
  2. can i generate a pulse width of 0.5 microsecond ??

    ReplyDelete
    Replies
    1. Using my code? At the moment, no, but I can add that in I believe pretty easily. Would you like this feature?

      Delete
    2. Or, if you want to learn on your own, study this. This is a fantastic resource: http://www.righto.com/2009/07/secrets-of-arduino-pwm.html

      Delete
  3. Dude, your lib is really very useful and precise.
    I just have one question, one your example code read_PWM_pulses_on_ANY_pin_via_pin_change_interrupt, why you do this???
    pulseTime = pulseCountsCopy/2.0;

    I actually had to do this (pulseCountsCopy * 10) / 2 to get the value the motor is expecting, but I really have no idea why you have to divide by 2 the original pulse time.

    ReplyDelete
    Replies
    1. Hi thanks for the question. The pulse counts have units of 0.5 microseconds (us). To convert from units of 0.5us to us you must divide by two. Ex: a 1000us wide pulse will be read as 2000 counts on the microcontroller's Timer2. Divide by two to get the time value of 1000us.

      Delete
    2. Units, NOT actual time......Damn!!! It was on my face the whole time :P
      Thanks.

      Gabriel, would you mind if I annoy you with other stuff related to RC and Arduinos?
      I'm a computer engineer, so coding is kind of easy.....but the rest is almost everything new.

      Delete
    3. Sure, it doesn't hurt to ask questions. I'm a pretty busy guy, but the worst that will happen is I simply won't have time to respond.....or I won't know the answer. Never hurts to ask though. If you're new to Arduino, also be sure to check out the links at the bottom of this article here, to learn and reference as you go: http://www.electricrcaircraftguy.com/2014/01/the-power-of-arduino.html.

      Delete
    4. Thanks, I'm not skipping steps here, really trying to read everything I can.

      Just one question than.....the damn jitter on the input signal (from the receiver to the arduino) where is that coming from?????
      I already checked everything, even my soldering and wires. Once I connect to the arduino, the signal goes everywhere .
      Yes yes, I already checked the examples in your library and did some research on the low level stuff you talked about. The only difference to what I'm doing is that I use the high level interrupts on pins 2 and 3 (it's just a 3 channels receiver and I don't care for the third).

      Delete
    5. Can you copy and paste some data from the serial monitor showing this jitter? I'm not sure what you're talking about exactly.

      Delete
    6. Also, I wrote my example code to read in only a single channel at a time. Unless you wrote your code to read in more than one channel, it's probably simply your code.

      Delete
    7. Line 5 at the top of my example says, "-this code only reads in a single channel at a time, though it could be expanded to read in signals on every Arduino pin, digital and analog, simultaneously."

      Delete
    8. Yes, I'm aware of all that :D
      As soon as I stable this thing, I'll work on the code to read more than one at a time.

      Just confirm me one thing, the input MUST also be a PWM pin?????
      I understood that only output had to be exclusively PWM to work properly and this is probably the cause of the interference.

      I'll be out of town for some days, when I come back I test if this is the case.

      Delete
    9. Hi André, no, the input does not have to be a PWM pin. It can literally be any pin on the Arduino (digital or analog pins, except A6 and A7)...assuming you're using an Uno, Duemilanove, Nano, Pro Mini, etc, but I recommend not using pins 0 or 1 since those are the serial communication pins.

      To add another couple pins to read in, look into the Arduino function "attachInterrupt" (http://www.arduino.cc/en/Reference/attachInterrupt). This is how external interrupts are used, which are easier to use than pin change interrupts. External interrupts are ONLY available on pins 2 and 3. So....if you want to read in 3 channels on a receiver, use my code to read in one receiver channel on any pin that isn't 2 or 3, then attach a couple external interrupts to read in the other two channels. Copy my code in the ISR that is already reading in the one channel, and duplicate that functionality in the ISRs you create for the two external interrupts. That will be the easiest way to read in more channels, and using that method you can pretty easily read in 3 channels. Reading in more than that will require a skillful use of pin change interrupts.

      Delete
    10. This comment has been removed by the author.

      Delete
    11. Ok......
      Making progress.....all power sources must have the same ground!!! :D
      And there is such a thing as Servo.writeMicroseconds () (why reading the lib documentation first, right????)
      Now the step motor works as expected.

      But the interruption output is still a mess.
      I'm using the rc steering channel on pin 2 whit attachInterrupt (). This is the output I'm geting:

      steering = 1507
      ----------------------------
      steering = 1507
      ----------------------------
      steering = 1507
      ----------------------------
      steering = 1507
      ----------------------------
      steering = 171
      ----------------------------
      steering = 893
      ----------------------------
      steering = 1507
      ----------------------------
      steering = 4093
      ----------------------------
      steering = 1504
      ----------------------------
      steering = 9076
      ----------------------------
      steering = 1507
      ----------------------------
      steering = 1507
      ----------------------------
      steering = 243
      ----------------------------
      steering = 966

      Delete
    12. Hi, yes always remember a common ground among devices. Otherwise you get a "floating" voltage on any device without a common ground, and it can randomly jump from low to high, or anything in between, simply from static electricity, or nearby devices....including your own hand, which will store some charge.

      Let's back up a bit. To help you someone needs to know the following:
      1) What are you trying to accomplish?
      2) What does your circuit look like?-- (feel free to describe thoroughly, take a picture, or post a schematic). Be specific with devices and parts. Also, I think you are using a *servo* motor, not a stepper motor?
      3) What does your code look like?

      To get project trouble-shooting help like this I think you should post on the Arduino Forum here, so we can see your code easier. You are welcome to post all the info in my comments here too, but I think the forum is better for this purpose, as your problems I think are generically code-related, not specific to my library: Arduino Forum: Project Guidance. <--be sure when you start a new topic to answer the above 3 questions so people can help you out.

      Post a link on your Arduino post back to here, so people can see what library you are using and where you got it, and post a link in the comments here, on my website, back to your Arduino forum post you will make (if you decide to do so), so people who read my article here (including myself), can be aware of your Arduino forum post to help you. I'll see what I can do to help, but I'll need more info. I'm sure we can get this resolved.

      Delete
    13. Ok! I fixed all the problems and you may be interested in the things I found.

      When using the high level API for interrupts on pins 2 and 3, it does make a difference if it's a PWM pin or not even for input. Pin 2 is not PWM and the signal goes everywhere as I shown you, but pin 3 is PWM and the signal from the RC receiver gets just perfectly stable.

      Someone already did a more interesting API to handle interrupts in all pins at the same time using Pin Change and it's available from Arduino web site. Check it out: http://playground.arduino.cc/Main/PinChangeInt

      I don't know if its just as efficient as doing all the dirty work ourselves, but the documentation says it uses the same method as you do.
      Don't mind the content on the Arduino website, it seams to be quite outdated. Go straight to the Git Hub repository for the latest release, its very simple and smooth.

      Thanks for all your help, I hope to have contributed a little to your efforts.

      Delete

Thanks for your comment or question! If it is a question, I will try to get back to you quickly.

Note: some HTML tags are allowed in your comments. To learn how to add bold (<b>...</b>), italics (<i>...</i>), or hyperlinks (<a href="URL">NAME</a>) to your comments, read here.

~Sincerely, Gabriel Staples.