Measuring duty cycle of unknown frequency in .asm - Page 1
Forum section: electronics / general

author: Circlotron
author_rank: Super Contributor
timestamp: March 30, 2023, 12:14:35 am
content: 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.

author: jpanhalt
author_rank: Super Contributor
timestamp: March 30, 2023, 12:27:58 am
content: 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.)

author: PCB.Wiz
author_rank: Super Contributor
timestamp: March 30, 2023, 12:37:13 am
content: Quote from: Circlotron on March 30, 2023, 12:14:35 amThe 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 capturesFirst 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.Quote from: Circlotron on March 30, 2023, 12:14:35 amYou 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.

author: Circlotron
author_rank: Super Contributor
timestamp: March 30, 2023, 01:11:38 am
content: Quote from: PCB.Wiz on March 30, 2023, 12:37:13 amThat'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.

author: Circlotron
author_rank: Super Contributor
timestamp: March 30, 2023, 01:36:53 am
content: Quote from: Circlotron on March 30, 2023, 01:11:38 amOr 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,

author: Circlotron
author_rank: Super Contributor
timestamp: March 30, 2023, 06:04:46 am
content: 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.

author: PCB.Wiz
author_rank: Super Contributor
timestamp: March 30, 2023, 07:56:21 am
content: Quote from: Circlotron on March 30, 2023, 06:04:46 amTrue, 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 LSB5V of PWM 100Hz 50% into 47k 1uF filter is 265mV p-p rippleError from 50% is 333uV or ~14 bitsError from 20% is 4.4mV or ~10 bitsError from 5% is 1.3mV or ~12 bitsAn 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.

author: Circlotron
author_rank: Super Contributor
timestamp: April 01, 2023, 12:17:19 am
content: 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.

author: PCB.Wiz
author_rank: Super Contributor
timestamp: April 01, 2023, 02:58:16 am
content: Quote from: Circlotron on April 01, 2023, 12:17:19 amWhy not have the missing pulse case simply read the ADC, as that covers both 0% and 100% no-edges case.Quote from: Circlotron on April 01, 2023, 12:17:19 amUsually you simply go to the end of the interrupt, and thus exit properly.It is very rare toneedto whack the stack pointer in normal code, as a band-aid, and certainly best avoided.

author: Circlotron
author_rank: Super Contributor
timestamp: April 01, 2023, 09:55:00 am
content: 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:QuoteFor 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?

author: PCB.Wiz
author_rank: Super Contributor
timestamp: April 01, 2023, 11:21:07 pm
content: Quote from: Circlotron on April 01, 2023, 09:55:00 amThat 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.Quote from: Circlotron on April 01, 2023, 09:55:00 amI presume you meantimmediately repeat itself?You are correct, on most MCU you clear the interrupt flag and the interrupt then occurs thenexttime that edge flag is set.

author: Circlotron
author_rank: Super Contributor
timestamp: April 09, 2023, 11:35:59 am
content: Quote from: PCB.Wiz on April 01, 2023, 11:21:07 pmSo would IRQACK, bit 2 be the one to clear just before returning from an interrupt?