Input PWM pulse width timing error

Hello I'm using interrupts to time a PWM signal from a receiver, I do not want to use the Servo library but am consequently experiencing timer issues, the pulse should read as a signal from 1000-2000 microseconds however the pulse the arduino is receiving ranges from 500-1500 microseconds at times and at other times from 1500-2500 microseconds. This is confusing me as the range is correct at 1000 microseconds every-time yet the minimum and maximum time varies. I am using digital pin 4 and am certain the receiver is functional as I had no issues measuring the same signal using a Raspberry Pi. Any suggestions as to stop this timing error ? I have searched for hours with no result.

Please post your program so we can see what you can see.

...R

void measure() {
 if (digitalRead(4) == 1) {
   starttime = micros();
 }
 else if (starttime != 0) {
   //pulsetime = ((volatile int)micros() - starttime);
   pulsetime = micros() - starttime;
   Serial.println(pulsetime);
   n += 1;
   if (((micros() - lasttime) < 20000000) and (n > 3)) {
     Serial.print("pulse ");
     if (( (mid + 20) <  pulsetime) and (pulsetime <= (highest + 20))) {
       Serial.println("pulse positive");
       neg = 0;
       pos = (pulsetime - (mid + 20)) * mult1;
     }
     else if ((pulsetime < (mid - 20)) and (pulsetime >= (lowest - 20))) {
       pos = 0;
       neg = (((pulsetime - (mid - 20)) / mult2));
       Serial.println("pulse negative");
     }
   }
   else if((micros() - lasttime) > 20000000) {
     n = 0;
   }
   Serial.print("  ");
   Serial.print(pos);
   Serial.print("  ");
   Serial.println(neg);
   pulsetime = 0;
   starttime = 0;
   lasttime = micros();
 }
}

I think the issue is related to how I have setup the timers, are there any timer setup functions that will remedy this issue ?

Please post your program - not an isolated snippet.

If measure() is your ISR then there is a lot wrong with - starting with the inclusion of print statements.

...R

volatile int starttime = 0;
volatile int pulsetime = 0;
volatile int lasttime = 0;
volatile int n = 0;
int out1 = 9;
int out2 = 10;
int loopvar;
int highest = 2340;
int mid = 1845;
int lowest = 1348;
volatile int pos = 0;
volatile int neg = 0;
float mult1;
float mult2;

void measure() {
  if (digitalRead(4) == 1) {
    starttime = micros();
  }
  else if (starttime != 0) {
    //pulsetime = ((volatile int)micros() - starttime);
    pulsetime = micros() - starttime;
    Serial.println(pulsetime);
    n += 1;
    if (((micros() - lasttime) < 20000000) and (n > 3)) {
      Serial.print("pulse ");
      if (( (mid + 20) <  pulsetime) and (pulsetime <= (highest + 20))) {
        Serial.println("pulse positive");
        neg = 0;
        pos = (pulsetime - (mid + 20)) * mult1; //produces value from 0-255 for WM output
      }
      else if ((pulsetime < (mid - 20)) and (pulsetime >= (lowest - 20))) {
        pos = 0;
        neg = (((pulsetime - (mid - 20)) / mult2)); //produces value from 0-255 for PWM output
        Serial.println("pulse negative");
      }
    }
    else if ((micros() - lasttime) > 20000000) {
      n = 0;
    }
    Serial.print("  ");
    Serial.print(pos);
    Serial.print("  ");
    Serial.println(neg);
    pulsetime = 0;
    starttime = 0;
    lasttime = micros();
  }
}


void setup() {
  Serial.begin(9600);
  attachInterrupt(1, measure, CHANGE);
  pinMode(out1, OUTPUT);
  TCCR1B = 0x05;
  pinMode(out2, OUTPUT);//the two output pins will be used for relays
  mult1 = float(1000 - ((highest - 30) - (mid + 20))) / float(1000); //the multipliers are wrong havent bothered changing them
  mult2 = float((lowest + 30) - (mid - 20)) / float(100);
  Serial.print(mult1);
  Serial.println(mult2);
}


//1348,1848,2340
void loop() {
  loopvar = 1;
}

Thanks. But you do not seem to have taken notice of my comment about the content of your measure() function.

Most of what you have in measure() should not be there - it should be in loop() or (better) in another function called from loop().

...R

I assumed I did not need anything in the main loop as I only need to take action when a signal is received, when a signal is recieved (pin 4 changes value) the measure function is called. Taking the measurements within the the main loop using the pulseIn function was even less acurate and the problem still persisted.

bakerw71:
I assumed I did not need anything in the main loop as I only need to take action when a signal is received,

In an ISR interrupts are turned off. Lots of things on the Arduino (including the print functions) require interrupts to be on so you need to get out of the ISR as quickly as possible. Just use it to record the time and update a flag variable to let the rest of the code know that an interrupt has happened.

Have a look at the .ino file in this link. The ISR is named sensorInterrupt() and its output is used in the function readRpm(). Note also the use of volatile for global variables used in the ISR and how the values are read into working variables in readRpm()

...R

Serial calls cannot be used in ISRs, delay cannot be used. Both can freeze the system waiting for interrupts.

Anything that blocks and waits is a bad idea in an ISR, because no other interrupts can happen.

OK reduced the code to bare minimum to measure a pulse and still getting inaccurate results, is there anything that runs by default on the arduino that would change these timings

volatile long starttime = 0;
volatile long endtime = 0;
volatile int n;
int pulsetime = 0;

void pin4rising() {
  if ((digitalRead(4) == 1) and (n != 1)){
    starttime = micros();
    n = 1;
}
  else{
    endtime = micros();
    n = 0;
  }
}

void setup() {
  Serial.begin(9600);
  attachInterrupt(1, pin4rising, CHANGE);
  pinMode(4, INPUT);
}

void loop() {
  pulsetime = endtime - starttime;
  Serial.println(pulsetime);
  Serial.print(endtime);
  Serial.println(starttime);
  if ((micros()-starttime) > 20000) {
    starttime = 0;
    endtime = 0;
  }
}
[\code]

You can't just read starttime and endtime in loop, they are being written to by the ISR.

  noInterrupts () ;
  long pulsetime = endtime - starttime ;  // read while they cannot be updated
  interrupts () ;
  if (pulsetime > 0)   // check endtime > starttime
  {
    // have a valid reading
  }

Made the changes, issue still persists. :confused:

Your attachInterrupt syntax is confusing.

attachInterrupt(1, pin4rising, CHANGE);

You are putting the interrupt on pin4 and it's not clear which Arduino you have which supports pin4 and if it is indeed interrup 1. I would use the syntax

attachInterrupt(digitalPinToInterrupt(4), pin4rising, CHANGE);

I tested your code on a UNO with a simulated Servo pulse to pin 3 and I was consistently reading the correct pulse width. You have an interrupt triggered on CHANGE, it is silly to name the isr "pin4rising". You do not need to clear the start and end times.

volatile long starttime = 0;
volatile long endtime = 0;
volatile int n;
int pulsetime = 0;

void pulseMeasure() {
  if ((digitalRead(3) == 1) and (n != 1)){
 
    starttime = micros();
    n = 1;
}
  else{
    endtime = micros();
    n = 0;
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(3, INPUT);
  attachInterrupt(digitalPinToInterrupt(3), pulseMeasure, CHANGE);
  
}

void loop() {
  //pulsetime = endtime - starttime;
  noInterrupts () ;
  long pulsetime = endtime - starttime ;  // read while they cannot be updated
  interrupts () ;
  if (pulsetime > 0)   // check endtime > starttime
  {
  Serial.println(pulsetime);
  Serial.println(endtime);
  Serial.println(starttime);
  Serial.println();
  }
 // if ((micros()-starttime) > 20000) {
  // starttime = 0;
 //  endtime = 0;
 // }
}

Changed the syntax, however attaching interrupt pin is odd in order to attach it to pin 4 the argument must be either 1 or digitalPinToInterrupt(4) neither of which have 3 in them :S. However the problem still persists, could this be due to me using a Arduino Nano ?

could this be due to me using a Arduino Nano ?

The nano does not have an external interrupt on pin 4. Change everything to pin 3 like I did when using the UNO. The nano and the uno use the same processor.

switched to pin 3 no change in readings :confused: switched the arduino as well with another nano and still getting bad readings :confused:

I question your input signal.
Run this small sketch of a simulated servo frame on another Arduino and jumper the pin 3 output to the interrupt pin you are using on the measuring Arduino. Connect the grounds on the two arduinos.

void setup() {
  pinMode(3, OUTPUT);
 
}
void loop() {
  digitalWrite(3, HIGH);
  delayMicroseconds(1400);
  digitalWrite(3, LOW);
  delayMicroseconds(18,600);
}

That returns 1800 :confused:

My mistake, I made an error in posting my test code and added a comma when I converted from millis to micros

void setup() {
  pinMode(3, OUTPUT);
 
}
void loop() {
  digitalWrite(3, HIGH);
  delayMicroseconds(1400);
  digitalWrite(3, LOW);
  delayMicroseconds(18600);
}

Running with this

volatile long starttime = 0;
volatile long endtime = 0;
volatile int n;
int pulsetime = 0;

void pulseMeasure() {
  if ((digitalRead(3) == 1) and (n != 1)){
 
    starttime = micros();
    n = 1;
}
  else{
    endtime = micros();
    n = 0;
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(3, INPUT);
  attachInterrupt(digitalPinToInterrupt(3), pulseMeasure, CHANGE);
  
}

void loop() {
  //pulsetime = endtime - starttime;
  noInterrupts () ;
  long pulsetime = endtime - starttime ;  // read while they cannot be updated
  unsigned long copy_endtime = endtime;
  unsigned long copy_starttime = starttime;
  interrupts () ;
  if (pulsetime > 0)   // check endtime > starttime
  {
  Serial.println(pulsetime);
  Serial.println(copy_endtime);
  Serial.println(copy_starttime);
  Serial.println();
  }
 // if ((micros()-starttime) > 20000) {
  // starttime = 0;
 //  endtime = 0;
 // }
}
1424
1675940
1674516

1420
1701656
1700236

1420
1727372
1725952

put the printing of the copy variables outside of the if statement to check all signals but received

-1792
5799416
5801208

1840
5828568
5826728

-1800
5854088
5855888

-1796
5883260
5885056

1848
5912420
5910572

-1792
5937944
5939736

1848
5967104
5965256

-1792
5992624
5994416

appears that the timer is not working as intended as at times it counts down :confused: