Advice on reducing PWM "jitter"

Status
Not open for further replies.

Zaite12

Member
Hi Folks,

I am using a Teensy 3.6 to time PWM pulses from my RC receiver. I've noticed there is about 1 microsecond of "jitter". Is there any way to reduce this? Here is the ISR code I am using. Thank you very much for any advice!

Code:
void Receiver::UpdatePWM(uint8_t ch) {
  // Current time in microseconds
  event_t = micros();

  if (digitalReadFast(gc->RX_PIN[ch]) == HIGH)
  {
    // Rising edge, store the time
    ch_time_last[ch] = event_t;
  }
  else
  {
    // Falling edge
    if (((event_t - ch_time_last[ch]) >= gc->PWM_HARD_MIN_RC) && ((event_t - ch_time_last[ch]) <= gc->PWM_HARD_MAX_RC)) {
      ch_pwm[ch] = (event_t - ch_time_last[ch]);
    }
  }
}
 
I may be wrong, but my playing around earlier with receiving RC pulse widths this way you are unlikely to be be able to measure more accurate than 1us... Just do to the nature of using micros().

That is suppose when you asked for micros it was something like 400.99us and the other edge of the pulse it was 410.01, you get 10us difference versus
401.01 and 410.01 which give you 9us difference...

If you need accuracy I would look at using some form of timer input capture. There are some libraries that do this. Maybe something like freqMeasure
 
I don't see the code generating this - KurtE understands R/C specifics/context better ... Paul once posted this on improving Jitter - some of it may apply. I recently used this with IntervalTimer and saw good results:

I did even more experimenting. After observing for longer times, I did see the Systick interrupt cause the waveform to jitter by about 1 microsecond.

Writing to the SCB_SHPR3 register to lower the Systick priority, and of course IntervalTimer's priority function to make it the highest proirity, solves that.

... POST HERE
 
Thank you very much for the guidance, I have changed my code and it's now much more accurate (no jitter at the microsecond level). This is the new code I am using, posting here perhaps it can help others...

Code:
volatile uint32_t cyccnt_t;
volatile uint32_t last_cyccnt[_RX_NUM_CHANNELS];

Receiver::Receiver(GlobalConfig *gconfig) {
  gc = gconfig;

  // Access to CPU cycle counter
  ARM_DEMCR |= ARM_DEMCR_TRCENA;
  ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
}

// Private ISR processing routine:
void Receiver::UpdatePWM(uint8_t ch) {
  // Current CPU cycle
  cyccnt_t = ARM_DWT_CYCCNT;

  if (digitalReadFast(gc->RX_PIN[ch]) == HIGH)
  {
    // Rising edge, store
    last_cyccnt[ch] = cyccnt_t;
  }
  else
  {
    uint32_t pwm_t;

    // Falling edge
    if (cyccnt_t < last_cyccnt[ch]) {
      // Timer wrapped
      pwm_t = ((UINT32_MAX - last_cyccnt[ch]) + cyccnt_t)/(F_CPU/1000000UL);  // Microseconds
    }
    else {
      pwm_t = (cyccnt_t - last_cyccnt[ch])/(F_CPU/1000000UL);  // Microseconds
    }

    //  Only update PWM if valid (within hard limits)
    if (pwm_t >= gc->PWM_HARD_MIN_RC && pwm_t <= gc->PWM_HARD_MAX_RC) {
      ch_pwm[ch] = pwm_t;
    }
  }
}
 
Last edited:
Status
Not open for further replies.
Back
Top