Author Topic: Decoding PWM duty  (Read 5576 times)

0 Members and 4 Guests are viewing this topic.

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Decoding PWM duty
« on: October 29, 2016, 07:02:49 am »
I need to read a 250 Hz PWM signal and determine what its duty is. I was thinking of using interrupts to trigger on the rising and falling edges of the signal. However it occurs to me that I must make sure I have enough time to carry out what I need to do in the interrupt. Using an 8 MHz clock I would have 320 clock cycles to carry out anything I want to do when the PWM is at 1%. I suppose at a worst-case scenario this is 80 instruction cycles. As all I would need to do is store the timestamp that the rising edge occurs at I would expect 80 instructions would be sufficient would it not?
 

Online JPortici

  • Super Contributor
  • ***
  • Posts: 3489
  • Country: it
Re: Decoding PWM duty
« Reply #1 on: October 29, 2016, 07:22:26 am »
what accuracy you need?
i am doing this same thing* with a PIC18F, 32 MHz (8 MIPS):
Free running timer
Rising edge measure the period by copying the value of the timer, resets the timer.
Falling edge measure the duty by copying the value of the timer.
Calculation done in the main loop. i have at least one decimal digit precision
if i wanted i could take care of the fixed error by adding/subtracting constants and increase the resolution a bit before i get to the limit imposed by the mcu frequency but i didn't even need to have that decimal digit precision

*well in my application i have to take account for random, variable frequency... or even worse "random pulse trains". if you are sure that once the system is running the signal is always present and the frequency will be fixed (or varying below a certain amount) you may want to spend a few cycles acquiring the period lenght and then optimize the hell out of it before the main cycle starts, transforming all divisions in multiplications and such...


Of course there are better methods, depending on your MCU of choice you should have hardware peripherals that take care of most of the work without upsetting the program flow with interrupts and you only have to make the calculations. in my case i had to work with existing hardware. i did upgrade the mcu with a better pin to pin compatible one but i still didn't have access to input capture or other peripherals.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #2 on: October 29, 2016, 07:44:53 am »
I'm using an AVR attiny24 which I can run at 8 MHz with internal clock. The 16 bit counter will be running anyway for the PWM output so I could use that to read off the current time when the interrupt fires or I could use one of the other eight bit counters. The other way of doing it would be to take the signal buffet and lowpass filter it and read it in analogue.
 

Offline bktemp

  • Super Contributor
  • ***
  • Posts: 1616
  • Country: de
Re: Decoding PWM duty
« Reply #3 on: October 29, 2016, 07:52:34 am »
It should be doable. If you want to measure really low duty cycles, you should do it in assembler, because a compiler will add unnecessary push/pops of status register.
In the ISR you need to read the timer and reconfigure the interrupt for the next edge. This takes probably 20-60 cyles. In assembler it is probably twice as fast.
If you also need to measure 0% and 100% you need to add a timeout and check if the signal is permanently low or high.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #4 on: October 29, 2016, 07:59:47 am »
I would expect the minimum duty to be 1%. I will declare to the customer the anything less than 1% will produce errors. Unfortunately I don't know assembler and have no intention of starting by now. Why do I need to reconfigure the interrupts? Don't the individual settings have their own vector so the low to high go to 1 set of instructions and the heightened low would go to a separate set of instructions or do I need to change the interrupts setting every time it fires to get ready for the reverse edge change?
 

Offline bktemp

  • Super Contributor
  • ***
  • Posts: 1616
  • Country: de
Re: Decoding PWM duty
« Reply #5 on: October 29, 2016, 08:13:35 am »
There is only one interrupt vector for each external interrupt pin.
One possible solution is configuring the interrupt to trigger on both edges (you could also use the pin change interrupt on any pin for this) and then read the pin value in software when reading the timer. It takes some additional time and may produce a wrong result if the pulse is too short or there was a glitch and has changed again before the software could read the state.
You could also reconfigure the edge in each interrupt, this avoids reading the pin state, because it only triggered at the selected edge. But you need to reconfigure the edge, so probably not much better than the first solution.
The other solution is connecting the signal to both interrupt pins: One configured for rising and the other one for falling edge. Then you have two seperate interrupt vectors and save some time, but you need an additional pin.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #6 on: October 29, 2016, 08:19:24 am »
Yes i thought of using both pins, sounds like that is the best solution. I certainly have pins to spare as it is PWM in and PWM out, nothing more.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2806
  • Country: nz
Re: Decoding PWM duty
« Reply #7 on: October 29, 2016, 08:41:17 am »
If you have a free-running 16 bit timer, running at 8Mhz it rolls over every 1/122th of a second, which is enough for 250Hz PWM rate:

- enable interrupt-on-change on the pin being monitored

In the interrupt:

- on rising edge you store the timer count in "rising_edge_time", and set a flag "rising_edge_valid"

- on the falling edge, if rising_edge_valid is set, you store "timer count - rising_edge_time" to the current pulse_length, and clear the "rising_edge_valid" flag.

Code: [Select]
void irq_pin_change(void) {
  /* get the counter straight away */
  this_count = get_counter();

  /* work out what to do with it */
  if(get_input_pin_state() = '1') {
     rising_edge_time = this_count;
     rising_edge_valid = 1;
  } else if(rising_edge_valid != 0) {
     pulse_length = this_count - rising_edge_time; 
     rising_edge_valid = 0;
  }
}

In the main program
- if pulse_length is not 0xFFFF, then you have a new length value you can use, and can set pulse_length to an invalid value (e.g. -1) if you want to detect when a new value arrives.

Code: [Select]
   /* Atomic read and reset of pulse_length */
   disable_interrupts();
   this_pulse_length = pulse_length;
   pulse_length = 0xFFFF;
   enable_interrupts();
   if(this_pulse_length != 0xFFFF) {
     /* Do stuff with this_pulse_length - it is a new reading */
   }

To make it bomb-proof you should also add code to detect when you don't receive a falling edge within the expected 250Hz window. This could be done by making "rising_edge_valid" a counter, and setting it to 2. A IRQ is then needed when the counter rolls over, which decrements a non-zero rising_edge_valid. After the counter roles over twice this 'forgets' about seeing the rising edge, as rising_edge_valid becomes zero (false) again.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Online Psi

  • Super Contributor
  • ***
  • Posts: 10124
  • Country: nz
Re: Decoding PWM duty
« Reply #8 on: October 29, 2016, 11:34:14 am »
Might have missed a step or two, did this from memory, but...

Dedicate a timer just for this.
Feed the signal into one of the input capture pins
Setup the timer clock so that it wont overflow until a bit after 250hz
Set timer to input capture on rising edge.
Now you can enable interrupt on input capture event.
On interrupt save capture timer value (number of timer ticks of "high" waveform). Then clear timer value change the input capture to falling edge.
When the interrupt next occurs you have the number of timer ticks of "low" waveform. Now you can calculate the duty. Set the timer back to capture on rising edge again and the cycle repeats.

Ideally you want to do the duty cycle calculation in the main loop and just save the values in the interrupt.
Ya want to keep the slow float/divide function out of the interrupt handler

And, as always, dont forget to use volatile :)
« Last Edit: October 29, 2016, 11:38:02 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline bap2703

  • Regular Contributor
  • *
  • Posts: 200
  • Country: io
Re: Decoding PWM duty
« Reply #9 on: October 30, 2016, 01:41:50 pm »
 

Offline lujji

  • Contributor
  • Posts: 29
  • Country: 00
Re: Decoding PWM duty
« Reply #10 on: October 30, 2016, 05:07:02 pm »
compiler will add unnecessary push/pops of status register.

AVR GCC allows declaring naked ISRs. This way prologue and epilogue are not generated by the compiler.
Code: [Select]
ISR(XXX_vect, ISR_NAKED) {
    ...
    reti();
}
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #11 on: October 30, 2016, 05:21:44 pm »
The simplest way is to just low pass filter it as all i need to know is the duty. so comparator with rail to rail output followed by a low pass filter.
 

Offline lujji

  • Contributor
  • Posts: 29
  • Country: 00
Re: Decoding PWM duty
« Reply #12 on: October 30, 2016, 06:05:44 pm »
Using supply voltage of 3.3V as ADC reference gives you approximately 3mV resolution. That is just about good enough to measure duty cycle with 1% resolution.
Since you're dealing with a low frequency signal (250Hz), you'd need more than a simple RC-network to provide <3mV ripple. Averaging ADC samples could be an option as well.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #13 on: October 30, 2016, 06:10:10 pm »
I've not yet been told how accurate they want to be. Yes averaging ADC samples is no problem, all I'm doing is taking one PWM in and converting it to another PWM out with perhaps some custom scaling, so all the CPU time can be used to run smoothing algorithms.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #14 on: October 30, 2016, 06:11:22 pm »
why would I not use 5V as the ref (same as comparator supply) ?
 

Offline lujji

  • Contributor
  • Posts: 29
  • Country: 00
Re: Decoding PWM duty
« Reply #15 on: October 30, 2016, 06:26:24 pm »
all I'm doing is taking one PWM in and converting it to another PWM out with perhaps some custom scaling
Perhaps it might be reasonable to opt for a purely analog solution in that case?

Quote
why would I not use 5V as the ref (same as comparator supply) ?
It doesn't matter really, as long as you're using supply voltage for reference.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #16 on: October 30, 2016, 06:26:38 pm »
and it would be a 0.1% accuracy reading but like you say smothing should be applied.
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #17 on: October 30, 2016, 06:28:14 pm »
all I'm doing is taking one PWM in and converting it to another PWM out with perhaps some custom scaling
Perhaps it might be reasonable to opt for a purely analog solution in that case?

Quote
why would I not use 5V as the ref (same as comparator supply) ?
It doesn't matter really, as long as you're using supply voltage for reference.

no because i may have to do some math on the input to produce the output or use a lookup table. trying to do that stuff in analogue is asking for trouble and as it is yet undefined I'd rather be able to design the circuit and leave the logic until later if needs be and be able to modify on customer request.
 

Offline lujji

  • Contributor
  • Posts: 29
  • Country: 00
Re: Decoding PWM duty
« Reply #18 on: October 30, 2016, 06:38:28 pm »
and it would be a 0.1% accuracy reading but like you say smothing should be applied.
Yeap, you're right, I was a bit out (i.e. by an order of magnitude :D). 10bit ADC result would give you 0.1% resolution, not 1%.
 

Offline aromring

  • Supporter
  • ****
  • Posts: 8
  • Country: us
Re: Decoding PWM duty
« Reply #19 on: February 20, 2019, 11:21:59 pm »
I had the same problem: too short pulse widths were taxing my MCU. They way I solved it involves converting the <0 - period> pulse width to <period - 2*period> pulse width much more manageable by the MCU. See details here:

https://www.instructables.com/id/How-to-Measure-High-Frequency-and-Duty-Cycle-Simul/
Great minds discuss ideas.
Average minds discuss events.
Small minds discuss people.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3212
  • Country: ca
Re: Decoding PWM duty
« Reply #20 on: February 21, 2019, 12:07:26 am »
A standard way of doing this is very simple:

- a free running timer with rollower frequency 1/2 of your signal or less.
- an Input Capture module (IC1) which latches the timer on the rising edge
- another Input Capture module (IC2) which latches the timer on the falling edge and produces an interrupt

In the ISR, you just calculate (IC2 - IC1) /(IC1 - IC1_previous). If this turns out to be negative, then the duty is almost 1 and you have captured the next IC1 already. In this case you simply add 1 to your result.

Does your chip have Input Capture modules?
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3212
  • Country: ca
Re: Decoding PWM duty
« Reply #21 on: February 21, 2019, 12:38:45 am »
Another method is this:

- free running timer (T1) of high frequency producing interrupts at rollover
- a gated timer (T2) of the same frequency - gated by the signal you measure

Then in the ISR you calculate (T2 - T2_previous)/timer_period

Does you chip have a gated timer?
 

Offline Fire Doger

  • Regular Contributor
  • *
  • Posts: 208
  • Country: 00
  • Stefanos
Re: Decoding PWM duty
« Reply #22 on: February 21, 2019, 02:32:00 am »
I had the same problem: too short pulse widths were taxing my MCU. They way I solved it involves converting the <0 - period> pulse width to <period - 2*period> pulse width much more manageable by the MCU. See details here:

https://www.instructables.com/id/How-to-Measure-High-Frequency-and-Duty-Cycle-Simul/

I guess in 2 years he found a solution  :-//
Its not very nice to resurrect dead questions (and by dead I mean with latest reply before 1 year+) to post your publishing from other sites...  :)
« Last Edit: February 21, 2019, 02:33:36 am by Fire Doger »
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17886
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Decoding PWM duty
« Reply #23 on: February 21, 2019, 07:24:59 am »
I had the same problem: too short pulse widths were taxing my MCU. They way I solved it involves converting the <0 - period> pulse width to <period - 2*period> pulse width much more manageable by the MCU. See details here:

https://www.instructables.com/id/How-to-Measure-High-Frequency-and-Duty-Cycle-Simul/

I guess in 2 years he found a solution  :-//
Its not very nice to resurrect dead questions (and by dead I mean with latest reply before 1 year+) to post your publishing from other sites...  :)

quite so and yes after 2 years I do have a solution and it's in the latest ATmega4809 series that have counters with specific modes measuring PWM
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf