3

Well im new to coding, but i do have the basic knowledge. I'm using an UNO to measure a duty cycle. The source of pulse is a Tektronic signal generator (Duty cycle set to 50.000 with frequency of 2khz). my code is as follows,

byte PWM_PIN = 3;
float pwm_value_H;
float pwm_value_L;
float D;

void setup() {
    pinMode(PWM_PIN, INPUT);
    Serial.begin(115200);
}

void loop() {
    pwm_value_H = pulseIn(PWM_PIN, HIGH);
    Serial.println(pwm_value_H);
    pwm_value_L = pulseIn(PWM_PIN, LOW);
    Serial.println(pwm_value_L);
    D = ((pwm_value_H) / (pwm_value_H + pwm_value_L)) * 100.0;
    Serial.print("D=");
    Serial.println(D);
    Serial.print("D=%");
}

but the problem is that my output value of "D" keeps on varying between 49.71, 49.90, 50.00 and 50.7 Is this because of PULSEIN() function ineffiency? or can anyone suggest a better way to measure the duty cycle with +/- 0.01 accuracy.

Regards, Cj

2 Answers 2

2

There's two basic things wrong with what you are doing there:

  1. pulseIn() isn't good for very accurate measurements
  2. The signal is still happening while you're doing other things, such as printing the high/low times.

You're not actually measuring the duty cycle of one single cycle, but the HIGH time of one cycle followed by the low cycle of a later one. If you want to do it properly you will have to ditch pulseIn() and instead get your head around interrupts and timestamps.

The basic method is to present the signal to one of the INT pins, and attach an interrupt to that pin with the trigger set to CHANGE. The interrupt routine you specify will then be triggered every time the signal changes from HIGH to LOW or from LOW to HIGH.

Then the interrupt routine does very little. It basically records the time the interrupt happened (that must be the very first thing you do), then looks to see if it's a HIGH or a LOW signal on the pin. You then use the previous timestamp from the last time the interrupt was triggered to work out how long the previous phase lasted and set either a HIGH or a LOW variable depending on the current level of the state (if it's low set a high variable since that was the previous phase level, and if it's high set a low variable) with the time that phase lasted.

Then in your main loop you interrogate those variables periodically to display the duty cycle.

If that still isn't accurate enough then you should look at the hardware Input Capture module built into the MCU. That uses a hardware timer in much the same way as the interrupt but is able to capture a much more accurate time stamp for you since the time stamp is grabbed the moment the signal changes, rather than after a short delay once the interrupt routine has been called. Details can be found in the datasheet for the MCU on your Arduino.

1
  • As i said, im new to coding. I understood what you said, but it is going to take me some time to implement it. And by the way, thanks a loads for the reply Majenko!!
    – CjZ
    Feb 25, 2016 at 3:26
2

I would start by removing Serial interrupts from the pulseIn period.

void loop() {
    pwm_value_H = pulseIn(PWM_PIN, HIGH);
    pwm_value_L = pulseIn(PWM_PIN, LOW);
    Serial.println(pwm_value_H);
    Serial.println(pwm_value_L);
    D = ((pwm_value_H) / (pwm_value_H + pwm_value_L)) * 100.0;
    Serial.print("D=");
    Serial.println(D);
    Serial.print("D=%");
    Serial.flush();
}

For higher accuracy I would then look to the Timer based Input Capture.

Cheers!

1
  • This seems logical. I'll give it a shot and check if the accuracy is meeting my requirements. And by the way, thanks a loads for the reply Mikael
    – CjZ
    Feb 25, 2016 at 3:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.