Author Topic: Measuring duty cycle of unknown frequency in .asm  (Read 1275 times)

0 Members and 1 Guest are viewing this topic.

Offline CirclotronTopic starter

  • Super Contributor
  • ***
  • Posts: 3180
  • Country: au
Measuring duty cycle of unknown frequency in .asm
« on: March 30, 2023, 12:14:35 am »
I have a situation where I want to measure the duty cycle of an external pulse and represent it as a number somewhere between 0 and 255. If the frequency of the pulse was known and exact it would simply be a matter of polling the port pin for the duration of the high period and scale the result.

But if the frequency is unknown, say somewhere between 80 and 120Hz I would have to sample both the high and the low time, add them, find high/total, then scale it 0-255.

I want to stick with polling a port pin, not use timer and interrupts because that gets a bit tricky and I'm not really a programming hot shot. Using TCNTH:TCNTL counter would be okay though.

Also would have to be in assembly because that's how I think. Not necessarily exact code, just psuedo code. Would be using NXP S08. Any ideas?

P.S. I had being achieving the above using a first order RC filter and feeding that to the A/D and it works very well, but only if the frequency is 1kHz or above. At lower frequencies any ripple in the LPF DC value causes a bit of jitter in the measured result. More filtering reduces this of course, but also makes the response time slower. Currently I am using 47K and 1uF so the TC is 47mS.  If I can go full digital that would be an improvement.

 

Offline jpanhalt

  • Super Contributor
  • ***
  • Posts: 3479
  • Country: us
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #1 on: March 30, 2023, 12:27:58 am »
I don't use NXP, but using a timer like Timer 1 is by far the easiest and most accurate way.  Let the timer run continuously, use capture to get the count at each appropriate edge, then use simple math (subtraction) to get your periods.  What frequency are you trying to measure?

If you are worried about complications, what about the complication of trying to do a calculation while polling a pin? (I am assuming the frequency and/or duty cycle changes.)
 

Offline PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 1546
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #2 on: March 30, 2023, 12:37:13 am »
... somewhere between 80 and 120Hz I would have to sample both the high and the low time, add them, find high/total, then scale it 0-255.
I want to stick with polling a port pin, not use timer and interrupts because that gets a bit tricky and I'm not really a programming hot shot. Using TCNTH:TCNTL counter would be okay though.
Also would have to be in assembly because that's how I think. Not necessarily exact code, just psuedo code. Would be using NXP S08. Any ideas?

The S08 I'm not familiar with, but most small MCUs have a capture ability, if the prescaler / clock scaling allows the slow period to 'fit' you usually do 3 captures
First at =\_ gets the zero point, second at _/= gets LOW time and 3rd at =\_ gets full scale.  Software then derives %

Polling might work, if you are ok with one-shot readings and doing nothing else during the capture time.
At 120Hz, 256 counts is  32us which is in the right ballpark for count-while-polling.
Of course, the precision / granularity is worse than edge-capture.

P.S. I had being achieving the above using a first order RC filter and feeding that to the A/D and it works very well, but only if the frequency is 1kHz or above. At lower frequencies any ripple in the LPF DC value causes a bit of jitter in the measured result. More filtering reduces this of course, but also makes the response time slower. Currently I am using 47K and 1uF so the TC is 47mS.  If I can go full digital that would be an improvement.
You can lower the jitter, if you feed an edge into an interrupt, and trigger the ADC with that, so it always samples at the same part of the ripple.
 

Offline CirclotronTopic starter

  • Super Contributor
  • ***
  • Posts: 3180
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #3 on: March 30, 2023, 01:11:38 am »
You can lower the jitter, if you feed an edge into an interrupt, and trigger the ADC with that, so it always samples at the same part of the ripple.
That's a good idea. This is on an existing board and for once I actually thought ahead to include the raw pulse input as well as the low pass filtered version. I'll have to see if I can somehow sample at the halfway point of the ripple upward slope, but that would need a bit of thinking because at 50% duty cycle the ripple is triangular but as you go away from 50% it gets more and more sawtooth so the sampling point would have to change because the halfway point is closer or further from the rising edge of the pulse.

Actually, at lower frequencies I could sample the LPF voltage at both the rising edge where the ripple voltage is lowest, and the falling edge where it is highest, add them, shift right once and I'm done! Hmm... food for thought. 
« Last Edit: March 30, 2023, 01:13:32 am by Circlotron »
 

Offline CirclotronTopic starter

  • Super Contributor
  • ***
  • Posts: 3180
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #4 on: March 30, 2023, 01:36:53 am »
Actually, at lower frequencies I could sample the LPF voltage at both the rising edge where the ripple voltage is lowest, and the falling edge where it is highest, add them, shift right once and I'm done! Hmm... food for thought.
Or rather, ROR not LSR because if after adding, the sum of the two analog samples is > 255 then the carry bit would be set and this 9th bit needs to be sucked back in to bit 8 position,
 

Offline CirclotronTopic starter

  • Super Contributor
  • ***
  • Posts: 3180
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #5 on: March 30, 2023, 06:04:46 am »
Actually it's not quite as simple as that... If I take the average of two samples of an exponentially changing voltage it will not correspond to the voltage halfway in time between the two samples. It would if the voltage was changing in a linear fashion, but not if it is changing non-linearly, which of course it is. Seems like a chunk of calculus would be involved. I'll have to think of an approximation instead. Maybe count the time between the rising and falling edge and use that figure to index a lookup table to supply a fudge factor to the reading.
« Last Edit: March 30, 2023, 06:07:17 am by Circlotron »
 

Offline PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 1546
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #6 on: March 30, 2023, 07:56:21 am »
Actually it's not quite as simple as that... If I take the average of two samples of an exponentially changing voltage it will not correspond to the voltage halfway in time between the two samples. It would if the voltage was changing in a linear fashion, but not if it is changing non-linearly, which of course it is. Seems like a chunk of calculus would be involved. I'll have to think of an approximation instead. Maybe count the time between the rising and falling edge and use that figure to index a lookup table to supply a fudge factor to the reading.

True, but for your one part in 256, and a good enough filter, I'd expect the error will not be large.
If the triangle wave is 500mV ~ 1V, it will be 'close enough' to triangle, for the interpolate to work.

Addit: for fun, I checked that in Spice. The two-peaks average checks confirms it is well below 8 bits LSB
5V of PWM 100Hz 50% into 47k 1uF filter is 265mV p-p ripple
Error from 50% is 333uV or ~14 bits
Error from 20% is 4.4mV or ~10 bits
Error from 5% is 1.3mV or ~12 bits


An alternative approach, for fun, using more hardware and less software, is to use a good prescaler to lock the timer to ~ 100% of the Period, using a DLL.
Then, the capture difference is already in %/256.

eg if we take 8MHz and 120Hz a prescaler that divides by 261 then by 256, results in 119.4Hz.
Small MCUs tends to not have granular prescalers, so a second timer and pin-link may be needed.
This approach works if the frequency does not change too rapidly. as the tracking lock is not fast.
« Last Edit: March 30, 2023, 08:50:09 pm by PCB.Wiz »
 

Offline CirclotronTopic starter

  • Super Contributor
  • ***
  • Posts: 3180
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #7 on: April 01, 2023, 12:17:19 am »
So I sampled the LPF waveform at the lowest and highest point of the ripple, corresponding to the rising and falling edges of the pulse that is being LPFd and from a practical point of view it works really well. Averaging the two samples turns out to be quite good enough for the purpose at hand.

The next issue is dealing with both zero and 100% duty cycle. In those situations there is no recurring pulses. The result is if I go suddenly from 50% to zero the measured output stays at 50% because there are now no pulses to update the measurement.

What I’ve done to deal with this is while waiting for a nonexistent pulse, have the 16 bit counter overflow after 1 second and cause an interrupt that forces a zero reading to where i am storing the duty cycle measurement. Haven’t quite got it sorted, but it’s mostly working.

Now, here’s a big question…  where the timer overflow interrupt may occur is part of a subroutine that as a consequence will be exited. I realise you just can’t exit a subroutine without using a RTS because you may end up causing a stack overflow. Is it okay then, after “improperly” exiting the subroutine to immediately use RSP and everything will be okay, or is that a disaster waiting to happen? The details that were pushed onto the stack are not needed following the naughty exit caused by the timer overflow interrupt.
 

Offline PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 1546
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #8 on: April 01, 2023, 02:58:16 am »
What I’ve done to deal with this is while waiting for a nonexistent pulse, have the 16 bit counter overflow after 1 second and cause an interrupt that forces a zero reading to where i am storing the duty cycle measurement. Haven’t quite got it sorted, but it’s mostly working.
Why not have the missing pulse case simply read the ADC, as that covers both 0% and 100% no-edges case.

... I realise you just can’t exit a subroutine without using a RTS because you may end up causing a stack overflow. Is it okay then, after “improperly” exiting the subroutine to immediately use RSP and everything will be okay, or is that a disaster waiting to happen? The details that were pushed onto the stack are not needed following the naughty exit caused by the timer overflow interrupt.
Usually you simply go to the end of the interrupt, and thus exit properly.
It is very rare to need to whack the stack pointer in normal code, as a band-aid, and certainly best avoided.
 

Offline CirclotronTopic starter

  • Super Contributor
  • ***
  • Posts: 3180
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #9 on: April 01, 2023, 09:55:00 am »
Finally got the thing working properly.
My big mistake was thinking a RTI would return to the next instruction after the one that caused the interrupt. The manual says:
Quote
10.6.2 Interrupt Sequence
When an interrupt is requested, the CPU completes the current instruction before responding to the interrupt. At this point, the program counter is pointing at the start of the next instruction, which is where the CPU should return after servicing the interrupt.
For some unknown reason this was simply not the case. 9S08PA16 micro. It seemed to return to the instruction that caused the interrupt. Was watching the program flow with a scope on a spare pin and various BSETs and BCLRs at strategic points along the way.

So now I've gotten rid of all that interrupt nonsense and just polling the pulse input and the TOF bit for the both rising and falling edge pulse input. It's been a long day...


Edit -> Having said that, in the interrupt service routine I would reset the timer and clear the TOF bit. Is that enough to ensure the interrupt doesn't simply repeat itself?
« Last Edit: April 01, 2023, 09:47:19 pm by Circlotron »
 

Offline PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 1546
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #10 on: April 01, 2023, 11:21:07 pm »
Finally got the thing working properly.
My big mistake was thinking a RTI would return to the next instruction after the one that caused the interrupt. The manual says:
Quote
10.6.2 Interrupt Sequence
When an interrupt is requested, the CPU completes the current instruction before responding to the interrupt. At this point, the program counter is pointing at the start of the next instruction, which is where the CPU should return after servicing the interrupt.
For some unknown reason this was simply not the case. 9S08PA16 micro. It seemed to return to the instruction that caused the interrupt. Was watching the program flow with a scope on a spare pin and various BSETs and BCLRs at strategic points along the way.

That would be very unusual, but you may be confusing other delays that are present.
Saying 'return to the next instruction after the one that caused the interrupt' suggests you are changing a pin to trigger an interrupt ? - a common test approach.
There are usually delays in interrupts and pins.
If you have a pin-opcode, the actual pin changes at the end, or sometimes onto the next opcode.
Interrupt logic needs to fire, be recognized, and then the current opcode finished. Then the interrupt jump is forced onto the MCU.
Most MCU data should give you shortest and longest interrupt response times, tho those are usually from interrupt flag set.


So now I've gotten rid of all that interrupt nonsense and just polling the pulse input and the TOF bit for the both rising and falling edge pulse input. It's been a long day...
Edit -> Having said that, in the interrupt service routine I would reset the timer and clear the TOF bit. Is that enough to ensure the interrupt doesn't simply repeat itself?
I presume you meant immediately repeat itself ?
You are correct, on most MCU you clear the interrupt flag and the interrupt then occurs the next time that edge flag is set.
 

Offline CirclotronTopic starter

  • Super Contributor
  • ***
  • Posts: 3180
  • Country: au
Re: Measuring duty cycle of unknown frequency in .asm
« Reply #11 on: April 09, 2023, 11:35:59 am »
Having said that, in the interrupt service routine I would reset the timer and clear the TOF bit. Is that enough to ensure the interrupt doesn't simply repeat itself?
I presume you meant immediately repeat itself ?
You are correct, on most MCU you clear the interrupt flag and the interrupt then occurs the next time that edge flag is set.
So would IRQACK, bit 2 be the one to clear just before returning from an interrupt?
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf