Sunday, February 9, 2014

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


Subscribe by Email!
...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."  

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

Download this library:
  • Click the above link, optionally donate (just set the price you want to pay to $0 if you like), & download the library. You may also click here, then choose the appropriate download. 
    • For the email address field, you may enter none@gmail.com if you like. Or, enter your real email if you want to receive an email with the download link, and if you don't mind if I send you an occasional email with something I'm working on (perhaps a few times a year or so).
  • Install the library into the Arduino IDE (using Sketch --> Include Library --> Add .ZIP Library), then run the examples to see how to use the library.
  • GitHub: https://github.com/ElectricRCAircraftGuy/eRCaGuy_TimerCounter

By Gabriel Staples
Written: 9 Feb. 2014
Last Updated: 15 June 2017

Update History (newest on top):
-15 June 2017 - added "A Few Links to Projects Using This Library" section & links at bottom
-7 Apr. 2017 - added link to custom Timer2 example w/1% duty cycle 10kHz PWM output (1us HIGH/99us LOW); moved code blocks below into syntax-highlighted boxes
-4 Aug 2015 - minor updates
-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
-26 July 2014 - minor changes to function names (removed the "T2" parts since I made that change in my library several months back)
-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
-moved code from Google Drive to GitHub, & updated download links - 12 April 2014
-added PPM/PWM links at very bottom of post - 26 Feb. 2014

========================================================================
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.

Download:
Download using the link at the top of this article.

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

A Few Links to Projects Using This Library:

========================================================================
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!***

61 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. 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.

    ReplyDelete
  4. 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.

    ReplyDelete
  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.

    ReplyDelete
  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.

    ReplyDelete
  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."

    ReplyDelete
  8. 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.

    ReplyDelete
  9. 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.

    ReplyDelete
  10. Thanks for posting this code, it saved me from a couple more hours of debugging - I see that you had the exact same problem as me (with TCNT2 overflowing while returning microseconds) ;)

    ReplyDelete
    Replies
    1. Yessir! It was tricky. Glad to have helped! If you share or write anything up, any links back to my site here are appreciated.

      Delete
  11. Hi all.

    Gabriel, I am working on a project involving the use of the ATMega328P-PU chip's limited number of timers. In researching a problem, I fortunately came across your .05 microsecond PWM measurement article. Bravo!

    I thoroughly read through your code, and I have a quick question for you. I noticed that, in the loop() section of your test code, you do not call reset_T2() at the top of the loop before t_start1, t_start_2, and t_start3 are set by the respective function calls. Is there a reason for this?

    If I understand your code correctly, without calling reset_T2() before these variables are assigned values, the unsigned long (uint32_t) T2_overflow_count variable will wrap around in value. If this happens, the end number would be smaller than the start number, and the elapsed time calculation (t_end(x) - tstart(x)) would be negative.


    Thoughts?

    ReplyDelete
    Replies
    1. Hi zx11, great question. I never use the reset_T2() function I wrote. It's not required. Resetting the timer means it cannot be used for timing more than one thing at a time, and I'm usually timing multiple things, or keeping a long-term timer for other purposes or something. Unlike languages like MATLAB, where every variable is automatically a double, the beauty of unsigned integer types (ex: byte, unsigned int, unsigned long) is that they cannot hold negative values. Therefore, when they hit the top, they roll over, or "overflow". Similarly, when they hit the bottom, they "underflow."

      Try it out in a test sketch on an Arduino, and you'll see the following. Let's assume a byte data type. 255+1 = 0. Similarly, 0-1 = 255. 10-20 = 246, etc. So, if your start time is 100, and your end time is 99, it means 255 counts have passed by. Sure enough, 99-100 = 255.

      Delete
    2. Let me add this too. You can have repeated (multiple) over/underflows. Ex: 100 + 513 = 101, since it overflows twice, coming right back to where it started, + 1. (100+513 is the same as 100+256+256+1, and each time you add or subtract 256, you end up where you started). <--again, assuming a byte (uint8_t/char) data type.

      Delete
    3. Run this sketch:

      /*
      overflow_and_underflow_test.ino
      -prove that unsigned data types cannot hold negative values, and have both overflow and "underflow" traits
      By Gabriel Staples
      26 Aug. 2015
      */

      void setup()
      {
      Serial.begin(115200);

      byte b1, b2, b3;

      b1 = 255;
      b2 = 1;
      b3 = b1 + b2; //0
      Serial.println(b3); //0

      b1 = 0;
      b2 = 1;
      b3 = b1 - b2; //255
      Serial.println(b3); //255

      b1 = 10;
      b2 = 20;
      b3 = b1 - b2; //246
      Serial.println(b3); //246

      b1 = 100;
      b2 = 99;
      b3 = b2 - b1; //255
      Serial.println(b3); //255

      b1 = 100;
      b2 = 513; //1
      b3 = b1 + b2; //101
      Serial.println(b2); //1
      Serial.println(b3); //101
      }

      void loop()
      {
      }

      Delete
    4. Hi all.

      Thanks Gabriel. I understand now. With unsigned numbers, the subtrahend's being bigger or smaller than the minuend will always result in a positive difference, and end minus start will always yield the distance (clock ticks) between them.

      Thanks again.


      Take care.

      -Z

      Delete
  12. Hi all.

    Gabriel, I have another question for you, and it deals with the checking of the flag_save variable in the get_T2_count() function.

    In the get_T2_count() function, you use the following statement to retrieve Timer 2's current overflow status:

    boolean flag_save = bitRead(TIFR2,0);

    Then, you check the value with the following:

    if (flag_save) ...


    It is possible that Timer 2 could overflow after the bitRead(TIFR2, 0) and before the if(flag_save) statements. Instead, I think it would be better to use direct register manipulation to minimize the chance of missing an overflow during the check for it. For example, you could probably do the following:

    uint8_t tcnt2_save = TCNT2; //grab the counter value from Timer2
    if(TIFR2 & 0x01)
    {
    tcnt2_save = TCNT2;
    T2_overflow_count++;
    TIFR2 |= 0b00000001;
    }


    In the above code, you directly derive Timer 2's overflow status in minimal time and without calling a slower function to do so.


    Thoughts?


    Take care.

    -Z

    ReplyDelete
  13. Z, you're just filled with curiosity! :) Another good question.

    bitRead(TIFR2,0) is actually identical to (TIFR2 & 0x01) in this case. bitRead() isn't a function, it's actually a macro, processed by the precompiler, not at runtime, so it has no overhead like a function would. It is defined here as:
    "#define bitRead(value, bit) (((value) >> (bit)) & 0x01)"

    You have a point about getting rid of my overflowFlag boolean however, to speed things up, though the compiler might optimize that out anyways since I don't use it elsewhere...so maybe it wouldnt change anything afterall. You'd have to check the assembly code that comes out of it.

    As for the possibility that Timer2 could overflow after the bitRead and before the if(flag_save), yes, it's possible, but it's irrelevant how I've written the code. No overflow are ever gone unaccounted for. Let's look at some cases.

    Case 1:
    Let's assume it's about to overflow, so we read TCNT2 at 255, and we read that the overflow flag is *not* set. Now, after reading the overflow flag and before checking the if statement, it overflows. Oh well, that overflow will be captured then and accounted for in the overflowISR. No problem there. The difference between someOverFlowCount*256 + 255 [the result of the count returned in the former case] and (someOverFlowCount+1)*256 + 0 [the result of the count returned in the latter case] is still only one time count.

    Case 2:
    Now, Let's assume it's about to overflow, so we read TCNT2 as 255. Next, we actually do read there was an overflow between storing the value from TCNT2 and checking the overflow flag. Oh well, notice that if there's an overflow we read TCNT2 again, so now we'd read it as 0 or 1 perhaps. The overflow count gets incremented, we read TCNT2 again....all is well, no significant error occurs. If I were to *not* read TCNT2 again, however, errors up to 255 counts or so would occur, which is a problem. Therefore, I have to read TCNT2 again in the event the overflow flag is true.

    Case 3:
    What happens if you were to reverse the order, and read the overflow bit and *then* read TCNT2? Well, that's where we'd have problems, so I was careful *not* to do it in that order. If I did it that way, I could read no overflow (while TCNT2 is actually at 255, for instance), but then the overflow occurs between reading the overflow flag and reading TCNT2, and by the time I read TCNT2 it's back at 0 or 1. In other words, this case would mean I missed an overflow event essentially by reading TCNT2 as 255 or 254 when in fact I should have read it as 0 or 1. This would introduce an error of up to 255 counts or so, so I was careful to do it the way I did it.

    The way I wrote it, no overflow are ever missed, no counts are ever missed. It works. I have worked on this for many hours, and I see no alternative way, nor order, to do this. Any other order of reading and doing things could mess it up.

    What I'm doing is essentially using software to turn an 8-bit counter into a 32-bit counter, by accounting for overflows. The only problem with this technique at all is that running the overflow ISR has overhead of a few microseconds. Therefore, every 128us, when an overflow interrupt occurs, a few microseconds is taken up, and timestamps cannot be grabbed during this time. Consequently, about 4/128 = 3% of the time you grab timestamps, you will see that even though their resolution is 0.5us, they will be off by 3~4 us.

    So, one out of 30 reads or so will have some error of a few us. Using Timer1 with my library would reduce this to only one out of every 8000 reads or so, since timer overflows would occur 65536/256 = 256 times less frequently.

    ReplyDelete
  14. MAJOR UPDATE TO LIBRARY COMING SOON!
    Since you seem quite interested in this library, I'll add this too:
    I'm in the middle of a major update to this library. My new library will be called eRCaGuy_TimerCounter, and includes the ability to use Timer0, Timer1, or Timer2, with*out* losing PWM outputs while using the timer! If you use Timer0 or 2, you can continue using 8-bit PWM (command 0 to 255) on the two respective pins, except at an increased frequency of 7.8kHz instead of Arduino's normal 977Hz for PWM on Timer0 and 490Hz for Timer2. As for Timer1, my new library will allow 16-bit PWM (command 0 to 65535) on the two respective pins, except at a freq. of only 30.5Hz instead of Arduino's normal 490Hz.

    I'm debating other additions, such as software interrupts like the Timer1 Library allows (http://playground.arduino.cc/Code/Timer1), but we'll see.

    ReplyDelete
  15. Hi all.

    Gabriel, again, thank you for your thorough explanation.

    I asked these questions because the project on which I'm working requires precise timing (just like the reason for which you wrote this library). I wanted to use Timer 1 (16-bit), but it was already in use by another library. Therefore, I had to write extra code to use Timer 2 (8-bit).

    I commend your efforts, as they have helped me to solve a logic issue into which I ran while coding my project.

    I look forward to the next major library update.


    Take care.

    -Z

    ReplyDelete
    Replies
    1. Hi Z, I'm curious, what are your timing requirements?
      -resolution?
      -max time span?

      What is the library taking up Timer1?
      -I ask because sometimes you can port libraries to other timers, combine libraries or features, or add features to an existing library
      -these are generally advanced skills, but with enough ingenuity and trickery, pretty much *any* library on a 16-bit timer can be made to work on an 8-bit timer, with some tradeoffs and understanding of performance.

      Delete
    2. Hi all.


      Gabriel, I am using the RGBmatrixPanel library from Adafruit (https://learn.adafruit.com/32x16-32x32-rgb-led-matrix?view=all#test-example-code). On an Arduino Uno, this library uses Timer 1, PORTB, and PORTD.

      Because I need to take full advantage of the ATMega328P-PU chip in the Uno, I have decided to use it stand-alone to make use of all of the chip's I/O pins. Using the chip in the Uno prevents this. The stock chip is set to use the internal 8 MHz oscillator with a pre-scale of CLK/8. I have set the chip's fuses to use a 16 MHz clock signal via external crystal with 22 pF capacitors and no pre-scale.

      In my project, I am using Timer 2 in concert with timer and pin-change interrupts. The output of the project is sent to the RGB matrix panel. The RGB panel uses Timer1. millis(), micros(), and delay() use Timer 0. Timer 2 is used by tone(). Since my application does not use tone(), I decided it was safe to use Timer 2. Microsecond resolution is sufficient. The time span needs to be as long as possible, so the variable that tracks timer overflows may need to be uint64_t.

      I thought about trying to change the RGB matrix panel library to use a timer other than the 16-bit Timer 1. However, I did not want to deal with issues in a future release/upgrade of the library due to my modifications of the present release.

      Please let me know if you have further questions.


      Take care.

      -Z

      Delete
    3. Z, makes sense, except for one thing: "Because I need to take full advantage of the ATMega328P-PU chip in the Uno, I have decided to use it stand-alone to make use of all of the chip's I/O pins. Using the chip in the Uno prevents this."

      What does using a bare chip allow you to do that you can't do on an Uno directly?
      -That's the part I don't see at the moment.

      Delete
    4. If you want to change the bootloader, manually set fuses, use the internal oscillator vs external, etc etc, that's all doable on an Uno......and I don't see what IO pins on an Uno you don't have access to (https://www.arduino.cc/en/Hacking/PinMapping168).

      Delete
    5. Hi all.

      Gabriel, for example, the Arduino Uno R3 does not expose the PCINT6 and PCINT7 pins on the ATmega328P-PU. Besides interrupts, those pins are used by the external crystal. The ATmega328P-PU can be driven to a 20 MHz maximum. However, in order to do so, you need a 20 MHz crystal attached across those pins along with the proper capacitors.

      More importantly, when I work on a project, I use my Uno only for the initial phase of the project's logic coding. Once that is done, I typically then move to a stand-alone ATmega328P-PU chip. I can't afford to dedicate an Uno to a single project, as that would be come expensive over time. An Uno R3 is $24.95. An ATmega328P-PU is less than $4.00. With the supporting elements (l.e.d.'s, resistors, etc.), the cost is typically no more than $15, if that.


      Take care.

      -Z

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

    ReplyDelete
  17. Hi Gabriel,
    thanks for your precision timer - great stuff.

    My project involves taking precise timings of external non-PWM signals. If I setup external interrupts, can my external interrupt code call timer2.get_count() ok, or will that cause some kind of interrupt clash ?

    Thanks very much.

    ReplyDelete
    Replies
    1. No clash. That will work perfectly. I have also taken care to prevent inadvertent nested interrupts from occurring when you do exactly that.

      Delete
  18. hi, could i ask you why didn't you use the smallest prescaler?
    TCCR2B = TCCR2B & 0b11111000 | 0x01;

    wouldn't it be the best?

    ReplyDelete
    Replies
    1. No, because it increases the percentage of time you are introducing bias error into your time stamps due to the several microseconds it takes to count overflows in the overflow interrupt. Every time we increase resolution we get less *range*. To maintain good range we must *count overflows*. Counting overflows takes several microseconds (3~4us or something like that--I'd have to check my notes), and occurs in the overflow interrupt. Since it takes time, it introduces *bias error*. By increasing resolution (using smaller prescaler) overflows occur more often, which means we introduce bias error more often. It's a tradeoff. I've determined that for an 8-bit timer/counter, resolution steps <0.5us or so just aren't worth it. Every 128us as it is, in 0.5us counts, I introduce 3~4us bias error for ~4us of the 128us, or for ~3% of the time. The percent of the time you introduce bias error increases each time you decrease prescaler.

      Delete
    2. Furthermore, if measuring a pulse width (or frequency, it's inverse), two time-stamps are required. Since each time stamp has a 3% probability or so of having a 3~4us bias error or whatever, the chance of having a bias error when measuring a pulse width (which takes two timestamps), is approximately *double* that, or 6% or so. I don't want to have bias error any more often than that, or else the benefits of increased resolution begin, in my opinion, to be undermined by the detriments of a high probability of having a several microsecond bias error.

      Delete
  19. I am currently using the micros() Function to time a 2000 microsecond delay. Unfortunately the +/-4 microseconds error in the micros() function is unacceptable. I've tried following one of your examples, but the timing is not working out. Here is what I'm doing.

    Loop() {
    while(zero_timer + 2000 > timer2.get_count());
    zero_timer = timer2.get_count();

    // main code here

    }

    How do I use get_count() to time exactly 2000 microsecond?

    ReplyDelete
    Replies
    1. Don't write blocking code if you don't have to. Also, get_count() returns counts that are 0.5us each, so you need to divide by 2 to get microseconds...or wait 4000 counts instead of 2000 counts to get 2000 us. Here's my recommended solution:

      loop()
      {
      //do something every 2000us (4000 counts of 0.5us ticks each)
      unsigned long tNow = timer2.get_count(); //0.5us counts
      static unsigned long tStart = tNow; //0.5us counts
      if (tNow - tStart >= 2000*2) //if 2000us has elapsed
      {
      tStart += 2000*2; //0.5us counts; update
      //do something
      }
      }

      Now, this will have a fair amount of jitter. If you're trying to drive a servo like this or something it will be pretty poor. To eliminate jitter you must use more sophisticated methods such as this. You can also look into using the Timer1 library.

      Delete
    2. Forgive the lack of indentation...not sure why it eliminates it in my code I pasted there.

      Delete
    3. Also, if you're trying to control a servo, just use the Arduino built-in Servo library instead. What's your application?

      Delete
    4. Gabriel,

      Yes. I'm trying to write a sketch with 5 RC Input Channels and 6 Servos. On the Arduino Forum, I've been told that the jitter problem is due to noisy servos and I needed to look up decoupling and bypass capacitors. I've done this and don't believe this is what is going on. I believe I have too many interrupts being triggered all at once. I wrote a simple test sketch to test my theory using the servo library that moves and pauses all 6 servos with virtually NO jitter. I then wrote a sketch to test my Receiver Input routine. Reading the PWM input from my RC Receiver and outputting the values in servo monitor to my computer, the signals vary +/- 3, with the occasional +/-4. This does not account for the jitter either. But, when I put both input and output together, things don't work too well.
      This is why I believe that I have colliding interrupts. So I'm trying to eliminate as many interrupts as I can. I found this: YMFC-AL - The Arduino auto-level quadcopter: http://www.brokking.net/ymfc-al_main.html and specifically this code: http://www.brokking.net/YMFC-3D/YMFC-3D_esc_output.zip which goes to the registers to implement servo output. It does not use interrupts. I have it working using micros() and not your library, but then my Receiver Inputs vary +/-8. I'm having trouble putting it all together. Your code suggestion above is helping. I really appreciate your help and your library.

      Delete
    5. Gabriel,

      I got it working and figured out that if I used the library without converting counts to micros, I would have much better precision. Thank you.

      I have a sensitive section of code that I'd like to turn interrupts off, but I need to also use a timer. Here is what I've done. Is this OK, and is there possibly a better way to do it?

      noInterrupts();
      while(PORTD >= 3){ // Exit Loop Until Digital Pins 8 to 11 Are LOW.
      interrupts();
      tTime = timer2.get_count(); // Store the Current Count.
      noInterrupts();
      if(ltAilTime <= tTime) PORTD &= B11111011;
      if(ltTVTime <= tTime) PORTD &= B11110111;
      if(ltESCTime <= tTime) PORTD &= B11101111;
      if(rtESCTime <= tTime) PORTD &= B11011111;
      if(rtTVTime <= tTime) PORTD &= B10111111;
      if(rtAilTime <= tTime) PORTD &= B01111111;
      }
      interrupts();

      Delete
    6. Converting counts to micros is a floating point operation, which is very slow, so that makes sense. Always better to avoid floating point operations if at all possible.

      No need to turn interrupts back on for get_count() to work. It works even with interrupts off, with NO time error introduced so long as it is called within 128us of when interrupts were disabled, and so long as it is called at least every 128us thereafter.

      I'm not sure what you're trying to do here exactly so I can't tell you if there's possibly a better way to do it. It appears to me you are writing your own servo outputs using this code???--and this is the loop to write the pulses LOW? I'm I'm correct here, why not use the Servo library? If I'm not, please explain what you're doing. Also, if writing signals, using output compare registers is almost always a better solution. That's how the servo library works---output compare in conjunction with output compare interrupts.

      Delete
  20. Hi Gabriel,

    I'm trying to measure pulse width of a 4 kHz signal with your library, using interrupts, but when I display the results in the serial monitor, I have values around 250 µs for exactly the first 1000 recordings, and then I have values from 0 to 128 µs ; expected values are about 140 µs. This is the case each time I launch the program.

    I assume this has to do with the "overflow interrupt" as you explain in the CPP file, but really I don't know how to handle this issue, even with your overflow_interrupt_on/off functions.

    I have two interrupts in my program : the first one calls timer2.get_count() when a rising signal is detected, the second one calls timer2.get_count() again when a falling signal is detected. Then I get the pulse width by doing (endTime - startTime) / 2.

    I can show the full code if you want to.

    Thank you in advance, and greetings from France.

    ReplyDelete
    Replies
    1. Pinhapple, Bonjour!

      S'il vous plaît envoyez-moi tout votre code. Vous pouvez trouver mon courriel en faisant clic sur l'onglet "Feedback? Corrections? Contact me!" ci-dessus.

      Aussi, je pense que vous pourriez avoir besoin d'un oscilloscope pour diagnostiquer le problème. Veuillez voir si vous pouvez obtenir un oscilloscope pour m'envoyer des captures d'écran du signal réel. Cela peut être essentiel pour mieux comprendre le problème.

      Merci, et salutations des États-Unis! Est-ce que vous êtes ingénieur aussi, peut-être?

      Delete
    2. Hi, I haven't received any code from you. Have you decided not to send it to me or do you not speak French, so you couldn't understand my response?

      Delete
    3. Hi Gabriel,

      May you excuse me for the delay between my first message and this one : I tried many solutions and finally chose not to use your library as I didn't understand quite well how it worked... :/

      However, I think it's still the only one that I could use to achieve what I'm trying to do. I'll send you a message through the feedback tab in a few minutes to tell you more !

      I'm a engineering student, graduating in October ! ;)

      Thank you in advance, and greetings from France again !

      Delete
  21. How can i get more accurate Timing using micros output pulse.
    here is my code.
    when testing the pulse is not accurate..
    would your code files work ??
    unsigned long currentMicros, Micros;
    int HILO = 0;
    //////////////////////// SEND BIT 0
    int DELAYHI0 = 262;
    int DELAYLO0 = 418;
    //////////////////////// SEND BIT 1
    int DELAYHI1 = 471;
    int DELAYLO1 = 209;
    ////////////////////////
    void setup()
    {
    DDRD |= 1 << 5; // SET D5 AS OUTPUT

    currentMicros = micros();
    Micros = currentMicros;
    }
    void loop()
    {
    PULSE();
    }

    void PULSE()
    {
    while(1)
    {
    currentMicros = micros();
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////// SEND BIT 0
    if(currentMicros - Micros > DELAYLO0 && HILO == 0) { PORTD |= 0B00100000; HILO = 1; Micros = currentMicros; } // SET D5 HIGH FOR 262us
    currentMicros = micros();
    if(currentMicros - Micros > DELAYHI0 && HILO == 1) { PORTD &= 0B00000000; HILO = 2; Micros = currentMicros; } // SET D5 LOW FOR 418us
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////// SEND BIT 1
    currentMicros = micros();
    if(currentMicros - Micros > DELAYLO1 && HILO == 2) { PORTD |= 0B00100000; HILO = 3; Micros = currentMicros; } // SET D5 HIGH FOR 262us
    currentMicros = micros();
    if(currentMicros - Micros > DELAYHI1 && HILO == 3) { PORTD &= 0B00000000; HILO = 0; Micros = currentMicros; } // SET D5 LOW FOR 418us
    }
    }

    ReplyDelete
    Replies
    1. Using micros() or my timer2_counter micros replacement functions are best used as timestamps for precise *input* signals, not output signals. For precisely *outputting* a signal you need to use more sophisticated methods via direct manipulation of the timers/counters and manipulation of output compare registers. See my example here for much more detail and a good example of this: http://www.electricrcaircraftguy.com/2017/03/arduino-atmega328-timer-PWM-output.html.

      (Note that for extra precise inputs you even need to use External Interrupts or Pin Change interrupts with my timer2_counter timestamps, or....for perfect input reading, Input Capture).

      If you need further assistance in generating a perfect output pulse as you are trying to do, let's arrange a video chat to discuss and I'd be happy to work with you as a paid consultant. I'm not cheap, but 1) I do excellent work, 2) I guarantee it or your money back, and 3) if the project is simple enough (ex: a basic pulse generator), it might be as cheap as a couple hundred bucks. Consultations are free. If you require a consultation refer to my link just above.

      Delete
  22. André Frazatto, someday I'll do a proper port of the code to the 32U4 microcontroller (used on the Leonardo, Micro, Pro Micro, etc) and to other microcontrollers, and to other timers on each microcontroller, including to Timer1 on the ATmega328, but for now I don't have time.

    You're on the right track, so good job on that.

    You are mistaken, however, about your claims. The 32U4 is a great little microcontroller and is very capable. It doesn't have only 1 timer, it has 4. Refer to the datasheet linked-to below. Timer0 is 8-bits, Timer1 and Timer3 are 16-bits, and Timer4 is 10-bits and high-speed capable. The ATmega328 has only 3 timers: Timer0 and Timer2 are 8-bits, and Timer1 is 16-bits.

    Also, you don't have Pin Change Interrupts on only 3 pins. Study this pinout and the 32U4 datasheet. On the Pro Micro you actually have 6 Pin Change Interrupts on pins 8, 9, 10, 14, 15, 16, and you have 5 External Interrupts (better than Pin Change: see the Arduino attachInterrupt documentation to use them) on pins 0, 1, 2, 3, 7, and you have an Input Capture unit (better than External Interrupts) on pin 4.

    As for getting my eRCaGuy_Timer2_Counter code to work, like I said, someday I'll just port it over nicely, but for now you need to do the following:
    Use an Arduino Uno, Nano, or Pro Mini (ATmega328-based microcontroller), OR try the following quick fix for the Leonardo/Micro/Pro Micro (ATmega32U4-based microcontrollers):

    Note: this quick fix has NOT been tested yet, but give it a shot. I expect it to BREAK millis() and micros(), but that's ok since you can use my timing functions here instead anyway. Place the following code near the top of "eRCaGuy_Timer2_Counter.cpp".

    #if defined(__AVR_ATmega32U4__)
    #define TCCR2A TCCR0A
    #define TCCR2B TCCR0B
    #define TIMSK2 TIMSK0
    #define TCNT2 TCNT0
    #define TIFR2 TIFR0
    #define TOIE2 TOIE0
    #define TIMER2_OVF_vect TIMER0_OVF_vect
    #endif

    PLEASE LET ME KNOW IF THIS WORKS. I don't have time to test it right now. Thanks!

    ReplyDelete
  23. Hi Gabriel! Great library! Must have worked very hard on it!

    I have one question pertaining to delay times. I'm trying to get my delay precision down to the delay precision others have achieved using your library, but find myself getting a more sporatic delay, often with at least +1.5 us higher than the intended delay. Am I making a common error and if so do you have any insight to what it would be?

    Thank you!

    Printscreen of screen with code and Serial Monitor: https://i.imgur.com/OOpzo2l.png

    ReplyDelete
  24. Hi Unknown (Ben), I got your email. Here's my response:

    Ben, unfortunately I still haven't been able to take the time to give you a thorough response. What I'd like to do is this below. Perhaps you can take the initiative instead:

    1. Use an oscilloscope to profile the actual wait time of `delayMicroseconds(1)`. Perhaps it is closer to 3 us in reality. To do this, use direct pin register manipulation on an output PORT to toggle a pin on then off before and after the delay, respectively. Subtract the difference, and the time it takes for 1 pin toggle, to get the actual delay that occurred. It is possible that for really short delays, such as 1us, the error is large and delayMicroseconds actually delays for more like 3 us or something.
    2. Get rid of floating point types to see how that affects the result. Floating point math is *slow* compared to integer math.
    3. Discuss overflow and how that affects the results of the timer by introducing jitter every 128us.
    4. Discuss the meaning of resolution, precision, and accuracy, all of which are different. (Do some Googling on this). I claim 0.5us *resolution*, NOT precision or accuracy. Precision is probably more like +/- 4us, 96% of the time, and +/-8us 4% of the time, and accuracy is based on the accuracy of the oscillator. I'm guessing on my numbers here. I'd have to review my notes, formulas, and testing data.
    5. Discuss how timestamps are used for measuring inputs precisely, *not* controlling outputs precisely. You are controlling outputs. That is best done with Output Compare registers (Google it), not timestamps.

    Those are some areas for you to further research. The honest truth is I may never find the time to fully answer your questions, but those are some areas you need to begin learning. The library is performing according to its specifications and design, and for proper and precise delays there are much better techniques, such as using output compare in the AVR timers/counters.

    ReplyDelete
  25. Hello Gabriel,
    Nice Lib, I am allready playing with it for hours. I attached a frequency generator on a interrupt pin and a scope.
    Interrputcode:
    void Measure() {
    if (LogBuffer != EndBuffer) {
    NewMicroInterval = get_T2_count();
    MicroInterval[LogBuffer++] = (NewMicroInterval - OldMicroInterval); //LogBuffer byte will start from 0 to EndBuffer(max)
    OldMicroInterval = NewMicroInterval;

    }
    When i save the micro_count value at about 20kHz, the output of the filled buffer shows that there is some strange delay in the arduino sometimes about 6-10 counts. It looks like this:
    97
    97
    98
    98
    97
    97
    98
    98
    97
    98
    97
    105
    90
    98
    97
    98
    97
    98
    97
    Very promising.
    I checked if it is the overflow or anything else, but could not find where it is coming from.
    In the end, the precision of the timer fault is 8 counts for me.
    do you have any idea where this can come from? or how to avoid.

    kind regards, Frits from the Netherlands. fritsy a.t. gmail

    ReplyDelete
    Replies
    1. Hi Unknown, this is, unfortunately, normal and expected behavior. In order to obtain a 32-bit count from an 8-bit counter, you have to count overflows. This means each time the count goes from 255 back to 0, I have to call an overflow interrupt, increment an overflow counter, and exit. This overflow interrupt takes several microseconds, introducing some jitter of a few microseconds (however long the interrupt takes to process and exit) if you try to obtain a timestamp at exactly this moment.

      This behavior for the ATmega328 microcontroller is unavoidable. The only thing you can do is make it 256x less frequent by moving from Timer2 to Timer1, since Timer1 is a 16-bit timer/counter instead of 8-bit like Timer0 and Timer2. 16-bits has 2^16 = 65536 unique counts, which is 256x greater than 2^8 = 256 unique counts, meaning that the overflow interrupt will fire 256x less frequently on the 16-bit timer for any given count speed.

      Delete
  26. Also tested it on the MEGA instead of the NANO, the same thing happens. could it be an interrupt from the bootloader? its reading is just shifted, so something is a kind of blocking the execution of the timer reading.

    ReplyDelete
    Replies
    1. Hello Gabriel, thanks for your answere,
      is the overflow every 256 counts? because then I should expect more of these time shifts. What I did in your code to check if it was the overflow interrupt, was adding (average of) 8 counts to the OldMicroInterval value. in the routine:

      ISR(TIMER2_OVF_vect) //Timer2's counter has overflowed
      {
      T2_overflow_count++; //increment the timer2 overflow counter
      //if xInsidePinInterrupt {
      OldMicroInterval=OldMicroInterval+4;// testing if this is delaying.
      //}
      }

      I Also made a bool for if the interrupt pin triggered, and if the ISR(TIMER2_OVF_vect) was called, when the bool was treu, that it could introduce a small correction. But it just introduced a lot of extra measure errors, and the existing 8 count timeshift kept the same.

      The way I introduce a correctionfactor if the ISR is called exactly when the Pininterrupt is active, should in my point of view work at least a bit to flatten out the 8 counts it takes to do this ISR.

      I will try some extra things, because this was also what I suspected in the first place.

      Kind regards, Frits.

      Delete
    2. Fritz, let's continue this discussion on GitHub. Please create an issue here and paste how my code used to look, followed by your code changes so we can continue to discuss.

      Delete
  27. Hi Gabriel. Im working on a project where task is to to time-stamp the sound propagating through the air using 2 MEMs Mics attached with interrupts om Timer1 and Timer 2. eg pin 3 and three on a Arduiono-Leonardo. Is it possible to use your Lib to get better accuracy down to 1 micros för this purpouse?// Best Nils


    int mic1 = 2;
    int mic2 = 3;

    volatile unsigned long tid1 = 0;
    volatile unsigned long tid2 = 0;


    volatile int stop1 = 0;
    volatile int stop2 = 0;
    volatile unsigned long diff = 0;
    char dir = ('x');
    void setup() {


    pinMode(mic1, INPUT);
    pinMode(mic2, INPUT);
    Serial.begin(9600);
    attachInterrupt(digitalPinToInterrupt(mic1), trig1, RISING);
    attachInterrupt(digitalPinToInterrupt(mic2), trig2, RISING);

    }


    void loop() {


    if (( stop1 == 1 && stop2 == 1 )) {
    stop1 = 3;
    stop2 = 3;

    if (tid1 > tid2) {
    diff =(tid1 - tid2);
    dir= ('H');



    }
    if (tid1 < tid2) {
    diff = (tid2 - tid1);
    dir=('V');



    }

    Serial.println();
    Serial.print(diff);
    Serial.print(" / ");
    Serial.print(dir);
    delay(1000);
    stop1=0;
    stop2=0;



    }

    }

    void trig1() {

    if (stop1 == 0);
    tid1 = micros();
    stop1 = 1;

    }

    void trig2() {

    if (stop2 == 0);
    tid2 = micros();
    stop2 = 1;
    }

    ReplyDelete
  28. Hi Gabriel. I'm working on a project where task ( if I have two pins in high , I want time between making one pin low then another is low) how can I do it with interrupt?

    ReplyDelete

Thanks for your comment or question! If it is a question, I will try to get back to you quickly. Notice to spammers: I personally remove all spam promptly and report spammers to Google, so please don't do it.

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.

P.S. Yo hablo español también. Je parle français aussi. (I speak Spanish & French too).