PWM _ Duty Cycle in Tenths

# Thread: PWM _ Duty Cycle in Tenths

1. ## PWM _ Duty Cycle in Tenths

I need to be able to get duty cycle of a PWM signal in Tenths. I currently am getting the duty cycle but only the whole number.

I get 37% instead of 37.2 %

Here is my code, any help is appreciated.
This program gets the duty cycle from the input, displays it, then converts it to a 0-5 vdc output using RC.

Here is the Clip.....

DEFINE OSC 20

'Frequency Input Channels

'Voltage output channels
VOut1 VAR PORTA.0

'Variables and Constants

pHigh VAR WORD ' W3 high pulse width
pLow VAR WORD ' W2 low pulse width
period VAR WORD ' cycle time (high + low)
T2 VAR WORD
T3 VAR WORD
Channel VAR BYTE
PulseLen CON 30 'Pulsein Time Less than 30 causes no readings
'Setup Max Channels to Scan
' -------------------------------------------------------------------------
DEFINE DEBUG_REG PORTB
DEFINE DEBUG_BIT 7
DEFINE DEBUG_BAUD 38400
DEFINE DEBUG_MODE 1
DEFINE PULSIN_MAX 750

'Set initial pin states
Output VOut1

'MAIN

Loop:
T2 = 0
T3 = 0

PulsIn F_pin1, 1, pHigh ' get high portion of input
PulsIn F_pin1, 0, pLow ' get low portion of input
Period = pHigh + pLow ' calculate cycle width

IF Period = 0 Then
Period = 0
T2 = 0
T3 = 0
GoTo DoIt
EndIF

t2 = plow
t3 = phigh
t3 = t2 + t3
t3 = t3 / 10
t2 = t2 * 10
t2 = t2/t3

DoIt:

Debug "Ch#:",#channel," DC:",#t2,13,10

t2 = t2 * 255 ' Converts % Duty cycle to a 255 step resolution
t2 = t2 / 100

PWM VOut1, t2,PulseLen
GoTo Loop

2. Did you find this post helpful? |
PWM is based on zero to 100%... you're aware that you're not going to get too many tenths if you're using 256 bit resolution?

The best is 100/256 or steps of around 0.39%... falls well short of tenths.

I would suggest using a PIC with hardware PWM as it's 10-bit and you'll get tenths of a percent there (100/1024=0.0976% steps).

Melanie

3. Did you find this post helpful? |
I measuring a 02 sensor which outputs 30% duty cycle at 0% oxygen and outputs 80% duty cycle at 21% oxygen. I then measure the duty cycle and convert it to 0-5 volts.

We read the % oxygen in tenths ie. 8.5% oxygen.

I can order a pic with hardware PWM I'll just have to look and see what best fits my needs. I have 4 channels to monitor.

Is there a better way to accomplish what I'm doing?

Thanks

4. Did you find this post helpful? |
Melanie,

Although I don't know much I thought hardware PWM was for outputting a PWM signal in the background. I'll have to look more because even the datasheets at Microchip talk about outputting a pwm but nothing about measuring pwm in.

Any specific chip you'd advise me to look at? BTW, this has absolutely nothing to do with Life support. It's measuring Exhaust o2 levels of engines.

Richard

5. Did you find this post helpful? |
My oversight, assumed it was PWM Output that you needed the tenths accuracy.

Is your problem with Pulsin or with the subsequent math?

If it's with the math, then you simply need to scale it up by a factor of ten, so that your division doesn't give you 37.2 (which because of integer math will truncate to 37), but result in 372. Therefore instead of 0-100, internally in your code you will work 0-1000 knowing that the least significant digit is TENTH's, and when outputting you can then place your decimal point appropriately.

6. Did you find this post helpful? |
Yes, it's the math. I can read the pulsin okay by scaling one of the values up by a factor of ten to give me tenths. That worked fine.

Follow me here a second and see if this solution holds water. Haven't tried it yet, but it appears it will work.

Here is the deal.
The valid pulsin will always be from 30% to 80% duty cycle - so that gives me a total difference of 50% (remember that 30% dc is 0% o2 and 80% dc is 21% o2)

Scaling the Value on the out put.
0 -21% is the value I'm trying to represent on the output, so 21% O2 is at 80% duty cycle. 21% / 256 steps gives me my 1/10 of a percent resolution.

Output = 0% o2 = 0 vdc
21% o2 = 5vdc

So to get around the 8 bit resolution, instead of me outputting a voltage representing the input duty cycle of 0-100% I'd be better off reading the duty cycle coming in, converting it to %o2, then outputting the voltage based on a scale of 0-21%

That should give me slightly better than 0.1% resolution.

Knowing all of that, now the math....
Example:
Sensor o2 = 10.5%
Sensor Duty Cycle in should be = 55%

Get Ratios and scale it....
Input Duty Cycle - StartDutyCycle = FinalDutyCycleVar
55 - 30 = 25

Display to lcd.....
25 x 42 = 1050 or 10.50% o2

Setup for pwm output..... 0=0 21% = 5vdc
25 x 256 = 6400
6400 / 50 = 128

128 steps of PWM Out = 2.5 volts which represents 10.5%

WhaLa, looks okay.
@ 21% there is not a 16 bit overflow so that should be okay

See any problems?
Richard

7. Did you find this post helpful? |
Well yes, so 0-100% PWM Output which is 0-5v which is 0-21% of O2. Sensible to concentrate operation to just the important bits you need.

First problem I see is right at the beginning where your math is insufficient for 0.1% resolution...

example1:

InputDutyCycle=55

55-30=25. Multiplied by 42 gives 1050 (or 10.50%)

example 2:

InputDutyCycle=56

50-30=26. Multiplied by 42 gives 1092 (or 10.92%).

You can see this isn't good enough. The Math getting the resultant into InputDutyCycle should give you a result which is at least a factor of five (at a minimum) bigger to get anywhere close to your resolution. Basically your scale of 30-80 (0-50) is too small... your math getting to here needs a far bigger scale (at least 0-210 for 0.1% resolution).

What is your PWM frequency or give me some typical values for PulseHigh and PulseLow and I'll show you how to achieve this.

8. Did you find this post helpful? |
Normally it's at 550hz although some modules output at 850hz. The manufacturer doesn't understand why.

I knew the math I put up was still based on whole numbers.
Sorry it was a typo, but I'm very curious what you have up your sleeve, maybe I'll learn something new "again" and I'm sure it will look much neater as well <grin>

PulsIn F_pin1, 1, pHigh ' get high portion of input
PulsIn F_pin1, 0, pLow ' get low portion of input

' calculate cycle width
Period = pHigh + pLow

'Display Zero - Avoid rest of Math
IF Period = 0 Then
Steps = 0
Period = 0
DutyCycle = 0
Oxygen = 0
GoTo DoIt
EndIF

'*****************************************
'Calculate Duty Cycle in format 37.7% = 377
t2 = plow
t3 = phigh
t3 = t2 + t3
t3 = t3 / 10
t2 = t2 * 100
DutyCycle = t2/t3 'Duty Cycle Value

'Actual Duty Cycle is Zero then Display Zero'
IF DutyCycle < 301 Then
Steps = 0
Period = 0
DutyCycle = 0
Oxygen = 0
GoTo DoIt
EndIF

'*************************************
'Calculate % Oxygen

TmpVar = DutyCycle - 300 'TmpVar = 377 - 300 = 77
Oxygen = TmpVar * 42 '77 * 42 = 3234 or 3.234% o2
OxygenTens = Oxygen // 1000
Oxygen = Oxygen / 1000

'Scale PWM Output Steps for % of oxygen

TmpVar = DutyCycle - 300 '377 - 300 = 77
TmpVar1 = TmpVar // 10 '77 // 10 = Remainder of 7
TmpVar = TmpVar / 10 '77 / 10 = 7 We loose too much here so get remainder and add back
TmpVar = TmpVar * 256 '7 * 256 = 1792
TmpVar1 = TmpVar1 * 256 '7 * 256 = 1792
TmpVar1 = TmpVar / 10 '1792 / 10 = 179
TmpVar = TmpVar + TmpVar1 'Add remainder 179 back
TmpVar = TmpVar / 50 '1971 / 50 = 39 Steps
Steps = TmpVar

DoIt:

PWM VOut1, Steps, PulseLen

Debug " 02: ", #Oxygen,".",#OxygenTens," Steps: ",#Steps, " Duty: ",#DutyCycle, 13,10
Toggle LED

GoTo Loop

9. Did you find this post helpful? |
First some hard facts that need to be understood with a worst-case scenario - that 850Hz PWM...

The period of 850Hz is 1.17ms (1170uS). The resolution of Pulsin at 4MHz is 10uS. That gives us 117 counts (1170/10) for a 4MHz PIC for 100% input period. But we're only interested in 50% of that (30-80% PWM giving 0-21%O2). That means we've only got a maximum range of 58 steps to play with across the scale. 21%O2 divided by 58 steps gives us a resolution of 0.362% which falls short of your required 0.1%. You can't use a 4MHz PIC if you're playing with Pulsin.

At 20MHz, Pulsin returns steps of 2uS. That gives us 585 steps for 100% input period, but again we're only interested in 50% of that, so we have 292 steps to play with. 21%O2 divided by 292 steps gives us increments of 0.07% and we're in business to achieve your 0.1% accuracy requirement. So you've got to use a 20MHz PIC if you want to play with Pulsin or use another method of measuring your pulse period.

Working the figures backwards you can calculate that we need a minimum of 210 counts at 50% Input PWM to maintain 0.1% resolution of 21%O2. 420 counts is 100% multiplied by 2uS (Pulsin resolution at 20MHz) gives you a maximum PWM ceiling of 1190Hz above which we lose 0.1% accuracy in our results. So that is our upper-ceiling for PWM frequency...

The lower-ceiling is determined by our math... in so far that we must never spill out of a word variable (unless it is a prelude to a DIV32 statement). So let's now plan for a worst-case lower-end scenario say 400Hz PWM...

400Hz has a period of 2.5mS or a Pulsin period count of 1250 (remember our 2uS count of a 20MHz PIC). We can multiply our Period count by up to 52 times before we spill out of a word, let's say 50 to make the figures nice and easy to play with. We'll call this Melanie's maximum multiplyer (MMM).

Now having calculated our operational limits we set them aside and start to do some code... remember, we have four possible error conditions that we have to trap before we are confident that everything is going to run smoothly...

1. Input PWM frequency is under 400Hz
2. Input PWM frequency is over 1190Hz
3. Input PWM Percentage exceeds 80% (greater than 21%O2)
4. Input PWM Percentage is below 30% (less than 0%O2)

Fist let's get some data... and your code here is fine...

PulsIn F_pin1, 1, pHigh ' get high portion of input
PulsIn F_pin1, 0, pLow ' get low portion of input
Period = pHigh + pLow
If Period < 420 then goto ErrorPWMtooHigh
If Period > 1250 then goto ErrorPWMtooLow

Those two extra lines trap any Period frequency exceptions and make sure the data we're playing with is within our pre-calculated, pre-determined play range. Error conditions 1 & 2 are now accounted for.

Normally, to get a percentage the formula would be pHigh*100/Period. But we MUST have greater accuracy, preferably to two decimal places if we can get away with it and still NOT spill out of a Word Variable. First we'll deal with Error condition 3 (>80% Input PWM). All Temp (Temporary) variables are WORDS... and notice I avoid integer DIVISION wherever possible to prevent the introduction of math errors...

TempA = Period*40

Why 40? If MMM is 50, mutiplying by 40 is the equivallent of a scaled up 80%. Now to continue...

TempB=pHigh*50
If TempB > TempA then goto ErrorO2tooHigh

What the two lines above have done is to trap the error if the input PWM exceeds 80%. Error condition 3 is now accounted for. Finally...

TempA = Period*15

Why 15? If MMM is 50, multiplying by 15 is the equivallent of a scaled up 30%. To continue...

If TempA > TempB then ErrorO2TooLow

With those lines above we've trapped any possibility of the inputPWM falling below the illegal 30% value... Error condition 4 is now trapped. We can now continue safe in the knowledge that nothing from this point onwards (short of you not paying your electricity bill) can wreck your day... so...

TempB=TempB-TempA

Now we've extracted out the bottom 30% leaving a pure input value referenced to zero. Remember TempB at this point has a range from zero (minimum 0%O2) to Period*25 (maximum 21%O2) - that's because 80%-30% equals 50% (our operational span) therefore 25 is 50% of our MMM.

Now we need to produce a 0-100% PWM Output value for your 0-5v Output...

TempA=Period*25

Notice that TempA will always be a 15 bit unsigned integer satisfying the requirement of the DIV32 divisor...

TempC=TempB*100
TempD=DIV32 TempA

TempD at this point is a WORD value ranging from 0-100 which can be used for your PWM Output (use Hardware PWM in preference rather than software PWM because the software PWM requires constant polling otherwise it will decay).

If you need a whole BYTE (0-255) value for TempD representing 0-100% PWM then simply do this instead...

TempC=TempB*255
TempD=DIV32 TempA

Finally say we need to produce a 0-21.00% LCD Output as well (to two decimal places)...

TempC=TempB*2100
TempE=DIV32 TempA
LCDOut #TempE/100,".",#TempE DIG 1,#TempE DIG 0

And note the above LCDOut line is the only place we've used an integer divide. Errors at a minimum, accuracy at a maximum.

Clear as the driven slush...

Melanie

10. Did you find this post helpful? |
Thanks Melanie, It seems to work fine after a minor modification. It was reading backwards so I changed:

TempB = pHigh * 50 to

TempB = pLow * 50

and it's working. I have to study the math. You gave good explanations which each and I "Really" do appreciate the help.
I just want to make sure I understand each step because I haven't ever used the Div32 before.

How bad is it going to screw things up when I put a calibrate routine in there.

Say for example, when clean air calibrating, 21% o2 actually is 83% duty cycle. I can subtract 50 from the reading and save that as a base subtract value right. In other words, simply subtract 33 instead of 30 from the duty cycle, hum, that doesn't look right.....

11. Did you find this post helpful? |
How does your Calibration affect the operation... does it...

Say clean Air is 83%...

(a) Change the SIZE of the span (eg 0%O2 is still 30% but 21%O2 is now 83%, so the span is now 53%), or...

(b) Change the REFERENCE of the Span (eg 0%O2 is 33% and 21%O2 is 83%, so you still have 50% total span, but the Reference has moved).

and Finally...

What is the maximum Calibration deviation you are likely to encounter?

12. Did you find this post helpful? |
I suspect the range moves to 33 to 83% but Honestly I'm not sure so I'll have to run more test with the sensor and remote computer.

The manufacturer only states, valid data is represented with a duty cycle of 30 to 80% which porportonal to the percent of oxygen.

We are tapping into the signal of a 02 sensor tied into an onboard engine control computer and transmitting the information via satellite to our terminal. We are trying to have our display read the same as the onboard computers display.

How the onboard computer calibrates..... Actual details totally unknown. We know that when it "does" calibrate, it's done by pressing a button while in clean air (21%) and it sits there for 2 minutes, then exits calibration mode. At that point, their display then shows 21%.

Our plans were to tie into their button, wait the two minutes, then also record the high range so we are calibrated the same.

We know that it can't simulate 0% Oxygen and it's only looking at Clean Air at 21%, waits 2 minutes for the sensor to settle.

Guessing, it's storing the max value and adjusting it to 21% however I will need to run several more test to see how it's output matchs a graph to be sure.

I justed tested a sensor that was actually installed and noticed 40.1% dc = 6.95% o2, which does not follow our chart, however I know for a fact that the sensor was not in "pure" clean air when it was calibrated so it is off.

Even though that is the case, we need to be off the same amount so we see the same reading as the onboard computer.

At this point, it would be best for me to take even more readings with my frequency meter during a calibration process to see how much the signal actually varies and then go from there.