Update 9 Feb. 2014:
In order to enhance the accuracy and precision of my code below, here's my timer to replace "micros()." It has a precision of 0.5us, so your PWM reads will be much more precise & accurate now. Simply replace micros() with my "get_T2_count()" function, then after taking the time difference, divide by 2 to convert from "counts" (with units of 0.5us per count) to microseconds (us).
ElectricRCAircraftGuy.com--RC, Arduino, Programming, & Electronics: Arduino micros() function with 0.5us precision - using my Timer2_Counter Library
I am using an attachInterrupt method to read in a PWM (pulse width modulation) signal from a standard hobby RC receiver. My code below is functioning properly, but I don't understand why placing the "noInterrupts" command where I originally had it will completely crash the code and make it not work! I have commented it out for the code to work. You can see the commented out line denoted by the following statement: "//turn off interrupts during data processing ////////////FOR SOME WEIRD REASON, ENABLING THIS LINE BREAKS THE ENTIRE CODE!!!"
The new location of "noInterrupts" has been moved to within my "interrupt_triggered()" function. To duplicate my problem, simply uncomment the commented out one, and comment out the one in the "interrupt_triggered()" function. Why is it doing this???
Thanks for your help!
/*
PWM Reader2
-this program is meant to read a PWM signal from an RC receiver (Rx) in order to tell you what pulse width is being sent out by the Rx on any given channel.
-this version of the code uses attachInterrupt, and is more accurate (but less precise) than using pulseIn().
--this version has an accuracy AND precision of ~+/- 4us, whereas the version using pulseIn() instead of attachInterrupt has an accuracy of approximately +0 / -10 ~ -12 us,
and a precision of 1~2us.
--Ex: in this version, if the actual high pulse width is 2000us it will read either 1996, 2000, or 2004us. However, in the version using th pulseIn function instead,
it will read something more like 1988us.Written by Gabriel Staples
http://ElectricRCAircraftGuy.blogspot.com/
4 Nov. 2013SETUP:
-The only input required is to connect a jumper FROM one of the signal wires on an RC reciever (Rx) (ex: the throttle signal wire) TO digital pin 2 on your Arduino.
--Also connect the grounds together (Rx ground to Arduino ground)
-Then, open the Serial Monitor to see the pulse width coming from the Rx!*/
//Global Initializations
const int input_pin = 2;
const int LED = 13; //output LED, to blink//make all variables volatile if they must be modified in the interrupt function
volatile int input_state = LOW; //initialize input state
volatile boolean process_data = false; //is it time to process the data?
volatile unsigned long t_start = 0; //us; the start time of a HIGH pulse
volatile unsigned long t_end = 0; //us; the end time of a HIGH pulse
volatile unsigned long t_end_old = 0; //us; the previous t_end value (ie: from the previous iteration)
volatile unsigned long duration = 0; //us; the time of a high pulseint LED_state = LOW; //state of the indicator LED built-in to the board, and on pin 13
unsigned long dt = 0; //us; the period of the entire PWM cycle (ex: for a 50Hz PWM RC communication signal, the period, or dt = 20ms = 20000us)
unsigned long counter = 0; //loop counter
float freq = 1; //Hz; the PWM frequency//initial calcs
//calculate length of the data arrays
int array_length = sizeof(duration)/sizeof(unsigned long);void setup() {
pinMode(input_pin, INPUT);
pinMode(LED, OUTPUT);
attachInterrupt(0, interrupt_triggered, CHANGE); //attach an interrupt to interrupt 0 (on digital pin 2)
Serial.begin(115200);}
void loop() {
if (process_data)
{
// noInterrupts(); //turn off interrupts during data processing //////////FOR SOME WEIRD REASON, ENABLING THIS LINE BREAKS THE ENTIRE CODE!!!!!!!!!
LED_state = !LED_state; //update
digitalWrite(LED, LED_state); //blink LED 13//get PWM period
dt = t_end - t_end_old; //us; the PWM period in microseconds
//get PWM freq
freq = 1.0/(dt*(float)1e-6); //Hz; the PWM frequencycounter++; //update the loop counter
//output the data every ___ cycles (if you set this ___ to 'freq,' you get an output once per second)
if (counter%((unsigned int)freq/10)==0){ //if counter modulus ___ == 0 //for a 45Hz PWM signal being read, output data at ~4 Hz.
// if (counter%1==0){ //}if counter modulus ___ == 0 //output every iteration
Serial.print("High Time(us): ");
Serial.print(duration); //the high pulse time
Serial.print(", Low Time(us): ");
Serial.print(t_end - duration - t_end_old); //us, the low pulse time which occured just BEFORE the high pulse time
Serial.print(", Period(us): ");
Serial.print(t_end - t_end_old); //the total PWM period
Serial.print(", Freq(Hz): ");
freq = 1.0/((t_end - t_end_old)*(float)1e-6); //freq, Hz
Serial.println(freq);
}process_data = false; //reset
interrupts(); //turn interrupts back on
} //end of processing data}
void interrupt_triggered()
{
//begin interrupt code
volatile int val = digitalRead(input_pin);if (val!=input_state){ //if a change actually occured and it was NOT just noise on the pin
input_state = val; //update the pin input state
if (input_state==HIGH) //if the pin is now HIGH
{
t_start = micros(); //capture the time stamp of the rising edge
}
else //if the pin is now LOW
{
t_end_old = t_end; //us; update this value just before you replace t_end with a new value
t_end = micros(); //capture the time stamp of the falling edge (remember, we already ensured that an input_state change DID occur)
duration = t_end - t_start; //us; the duration of the HIGH pulse
process_data = true; //update variable in order to process & output data
noInterrupts(); //turn off interrupts during data processing
}
}}