Shop OBEX P1 Docs P2 Docs Learn Events
Measuring PWM ratios — Parallax Forums

Measuring PWM ratios

JSWrightOCJSWrightOC Posts: 49
edited 2009-01-26 21:10 in BASIC Stamp
I'm trying to write a routine which will determine the ratio of an incoming PWM signal, ideally expressed as a value 0-255 to indicate 0% - 100%. I know that 0% and 100% are going to be DC values, in which case PULSIN will return 0. I will account for this using other methods not described here.

What I have been doing thus far is to measure the high period using PULSIN, then measuring the low period using PULSIN, and adding the two together to get the total time period. The PWM frequency I am trying to measure is about 100Hz, so the total period time is approx. 10mS - well within the measurement range of all Stamp versions.

Here is where I begin having trouble. Because the Stamp is limited to integer values, I cannot simply perform tHIGH / tTOTAL and then multiply the result by 255, otherwise that would be the obvious solution. What I have tried thus far almost works, but again due to the integer math I am running into trouble. If I divide tTOTAL by 256, and then divide tHIGH by the result, I get a value that at first appears to be 0-255, but actually will exceed 255 due to rounding errors in the first equation. Incidentally this only works when tTOTAL is greater than 256. I know that the Divide Modulus (//) operator might be useful here, though all it does is return the remainder that would be left behind after integer division, and nothing creative is coming to mind.

Here's an example.

=============================

LoTime VAR Word
HiTime VAR Word
ToTime VAR Word
PWMVal VAR Word
Divisor VAR Byte

PULSIN 0, 0, LoTime
PULSIN 0, 1, HiTime

ToTime = HiTime + LoTime
Divisor = ToTime / 256
PWMVal = HiTime / Divisor

DEBUG DEC5 ?PWMVal

=============================

In this code example PWMVal (which should be 0-255) is a Word value because it isn't 0-255; otherwise it could be a Byte value. Divisor will always be less than 256, because the input period will never come close to 65,535 PULSIN ticks long in this application.

Some real-world numbers:

HiTime = 04062
LoTime = 01112

04062 + 01112 = 05174

05174 / 256 = 20.2109375 (NOTE that the Stamp ignores everything to the right of the decimal point; this is the cause of my math errors)

04062 / 20 = 203.1 (the .1 gets truncated, which in this case is not a problem because I just need 8 bits of resolution)

If we did not ignore the fractional part as shown above:

04062 / 20.2109375 = ~200.98 (I truncated the remaining fractional part for the sake of legibility)

As you can see, 203.1 is not equal to ~200.98. The extent of this error depends on the exact value of ToTime. As ToTime reaches something evenly divisible by 256, the error is minimized. When the remainder of ToTime / 256 is the greatest, the error will also be the greatest.

Unfortuniately I have to account for long-term variations and component tolerances which will affect the frequency of the PWM signal, so I cannot simply assume that tTOTAL will always be a certain value, and use constants that will give me the numbers I need. Tthe numbers shown above were derived from a BS2 with a measured input frequency of 97.09Hz using a frequency counter.

I might be missing something obvious, in which case I deserve a firm smack to the forehead, because it's got me stumped! Help would be much appreciated.

Comments

  • metron9metron9 Posts: 1,100
    edited 2009-01-24 16:08
    If your tTotal will be less than 65535 then you can multiply the values by 10 first to get one additional decimal place. In that case your trunicated value for the numbers you show above would be 201.



    40620+11120 = 51740

    51740 / 256 = 202· (202.109375)

    40620 / 202 = 201 (201.089108...)

    How much resolution do you need though is the real question.

    The initial pulsin has a 1/2%·error as well

    97.09 HZ should return a total of 5149.86095... using 2us pulsin. assuming the 97.09 from the frequency counter is correct. You still will have a 2us * 4 = 8us potential error as the two start times and two stop times may be rounded from the initial input. About 0.012% error between highest and lowest readings.



    There is also a 0.012% error rate using 2us as a counter as you start and stop 4 times. If the pulse came in on the boundry of all four start/stop times.














    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Think Inside the box first and if that doesn't work..
    Re-arrange what's inside the box then...
    Think outside the BOX!

    Post Edited (metron9) : 1/24/2009 4:19:44 PM GMT
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2009-01-24 18:03
    The most accurate method is binary long division:
    HiTime = 04062
    LoTime = 01112
    N = HiTime
    D = HiTime + LoTime
    '
    binary division loop
    for J=15 to 0 ' 16 bits
    N=N//D<<1 ' remainder*2
    F.bit0(J)=N/D ' next bit
    next
    '
    ' F = 51450
    DEBUG DEC F, "=", DEC F/256, " * 256 + ", DEC F//256, CR ' prints 51450 = 200*256+250
    DEBUG "rounded = ", DEC F/256 + (F//256/128), CR ' prints rounded=201
    F = F ** 10000 ' convert to 0.00 to 99.99 %
    DEBUG DEC F/100, ".", DEC2 F, "%", CR ' prints 78.50%

    For explanation see www.emesys.com/BS2math2.htm#binary_long

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • JSWrightOCJSWrightOC Posts: 49
    edited 2009-01-26 16:42
    Wow, thanks guys. I'll try those methods and see how they work.

    Really what I am hoping to do is to have a value that I can put through a lookdown table, so that I can translate a number of arbitrary PWM levels to specific values. It would be most logical to translate the PWM input to a value that varies from 0-255, but really any range will do for this application. Precision is not dramatically important, as the end result will be a number that is 0-7, but the end result does not scale linearly with the PWM ratio. What I do need though was something that would work with a given range of input frequencies without having bugs as a result of math errors.

    metron9,

    I know there is going to be a 1-2 count "bobble" in what the Stamp sees for the high and low times, as the input pulses are not synchronized with the timebase (this phenomenon is also present in many frequency counters). While my meter read 97.09Hz, I did the math on the values the Stamp was indicating and the two did not match - I attributed this mainly to operating frequency tolerance of the ceramic resonator (which the Stamp uses for its timebase), and possibly the calibration of my meter. What is the 1/2% error using PULSIN that you speak of?

    Tracy,

    I'm not suprised that you had a method of performing long division, considering the nature of your work. I read the article you linked me to, and that's pretty neat. I think it's too bad Parallax did not (or could not?) impliment something like this in machine language as a PBASIC instruction.

    So I can better understand what the algorithm does, I is the integer part of the result, and F is the fractional part, expressed in units of 65536... where ~0.0000152587890625 (1/65536) would be F=1, ~0.000030517578125 (2/65536) would be F=2, etc. correct?
  • metron9metron9 Posts: 1,100
    edited 2009-01-26 17:28
    The 1/2% error is I think an error I made in thinking. Tried to rethink why I put that in there but I am at a loss.

    However, the initial accuracy using the truncated numbers won't matter anyway since you are going to scale it down to a number 0-7.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Think Inside the box first and if that doesn't work..
    Re-arrange what's inside the box then...
    Think outside the BOX!
  • Tracy AllenTracy Allen Posts: 6,662
    edited 2009-01-26 18:15
    JS, You are correct about the meaning of the F=fractional part. Any proper fraction A/B can also be closely approximated as a ratio F/65536. The division algorithm finds the value of F, such that A/B lies between F/65536 and (F+1)/65536. It is a very useful function and I am just happy that Chip did include the */ and ** operators. I think the original Stamp was so crowded that there was not room for any more operators. The ** is also available on the Propeller, where there is a whopping 32 bits to work with instead of 16, but the binary division still is still up to the application programmer.

    I should have mentioned that the starting value of D (the divisor) has to be less than 32768, so that the shift left operation will work without overflow. That did not seem to be an issue with your example values.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Tracy Allen
    www.emesystems.com
  • JSWrightOCJSWrightOC Posts: 49
    edited 2009-01-26 21:10
    First, I would like to thank you both for the useful information you guys have provided. I have come up with a solution based on Tracy's example that does exactly what I need it to do. I modified the output to give me a 000-255 value, instead of 0-10000.

    metron9,

    The truncated numbers were not a significant issue with regard to the accuracy of the output, however I don't think making the rounding errors 10 times smaller will help quite enough.

    51730+00010 = 51740
    51740 / 255 = 202 (202.901960)
    51730 / 202 = 256 (256.089108)

    If I used a byte value for the output it would overflow. I also had an error in my original example; I should be dividing by 255 since I wanted 255 = 100%. Even still, it could have the potential to result in an overflow, and the error is still frequency dependent. This would definitely work if I were starting with smaller numbers, however.

    Tracy,

    I don't think the limitations of D will be a concern, as the lowest frequency I am planning on working with would be 60Hz (0.0167S) which would be a total time period of 8333 counts on a BS2 and 20833 on a BS2sx. Before I PULSIN and do the binary long division I'll be checking the input to see if it is toggling slower than 50Hz (COUNT <pin>, 20, <variable> if it were a BS2, 50 for an SX), and if so I check the pin to see if it's 0 or 1. I then bypass the long division algorithm, simply giving a 000 or 255 for the result; this should trap any bad input before it gets to the division. I know that a slowly changing signal will result in the output toggling between 000 and 255, but this is an acceptable failure mode for my application.

    Once again, thank you both for the info, and for an excellent group of people on this forum!
Sign In or Register to comment.