PDA

View Full Version : Power Control PWM, HELP needed



Kloney
- 30th June 2013, 09:36
I'm falling apart and this post might be my last hope.. I can't find anyway to understand this
I'm trying to generate a sinewave for a 3-phase inverter and I'm not asking for a written code
using 18f4431 , i read the datasheet part of Power control pwm
I also read Bruce's example,yet I can't get what are PTPER,PTMR used for and how are they related to duty cycle ?
how pdc0l and pdc0h work ?
for example how can I use a lookup table of these entries "0, 25, 50, 74, 98, 120, 142, 162, 180, 197, 212, 225, 236, 244, 250, 254, 255, 254, 250, 244, 236, 225, 212, 197, 180, 162, 142, 120, 98, 74, 50, 25"
to generate half sine wave ?
I apologize for my bad English but I am desperate and need help .
thanks in advance

HenrikOlsson
- 30th June 2013, 19:58
Hi,
PTMR is the actual timer providing the timebase for the 4 PWM modules. You shouldn't need to read or write those register at all. PTPER is the period register, it is what controls the period, or frequency, of the PWM signal. Think of it this way, the timer counts from 0 and up, when it hits the value you write to PTPER it start over. Each time it start over a new cycle begins. The dutycycle is set by writing to the PDCx register for the specific channel.

There is a relationship between the PTPER and the actual dutycycle. Think of it, if the timer starts over at a value 50 then the maximum dutycycle value will be 50. If the timer starts over at 500 then the maximum dutycycle value is 500 so if you change PTPER you'll change the PWM frequency AND the dutycycle.

Bruce's example really does cover it quite well. If you can't get it going post your code and we'll take a look.

/Henrik.

Kloney
- 1st July 2013, 00:19
Hello henrik
so if I put 0150 in PTPER and pdc0l= 00 , pd0h = 01 timer will count from 0 till it reaches 150 then what happens each time it reaches that value ? and what also happens during counting from 0-50 ?
does that mean it will send a pulse of 100 duty cycle (out of 255)(39%) to pwm0 each timer reaches 150 ?
I mean if i want to generate pulses of duty cycles of these entries in the this sequence to pwm0
0, 25, 50, 74, 98, 120, 142, 162, 180, 197, 212, 225, 236, 244, 250, 254, 255, 254, 250, 244, 236, 225, 212, 197, 180, 162, 142, 120, 98, 74, 50, 25"
what should I do ? and also what does it mean to make a pulse of 500 duty cycle ? i thought 255 = 100%
thanks a lot for your help :)

HenrikOlsson
- 1st July 2013, 06:22
Hi,
OK, lots of questions....Lets take a step back and look at how the PWM module works - basically.
There's a timer (PTMR), a period register (PTPER) - these are common for all "channels". Then there are a 4 dutycycle registers and comparators.

The timer starts at 0 and counts up until its value equals that of the period register, it then starts over at 0 and it all repeats. If you load the period register with the value 150 the timer will count from 0 to 150 and then start over at 0, count to 150 and so on. If the timer is driven with a frequency of 1MHz it will count to 150 in 151us so the PWM frequency 6622Hz.

When the timer starts at 0 the PWM output is set. As the timer counts upwards its value is compared to that of the dutycycle register. When the two are equal the PWM output is reset.

So, if you load the period register with 150 and the dutycycle register with 50 you'll get a dutycycle of ~33% because the PWM period is divided into 151 steps. If you now change the value of period register to 499 the PWM frequency will change because it now takes 500us (instead of 151) to reach its destination and the dutycycle will change to 10% because there's now 500 steps in the cycle compared to 151 before. (This again asumes a 1MHz frequency being fed to the timer and is only meant to describe how the PWM generation works).

So, as you probably can see, the absolute value of the dutycyle register at which you'll get 100% dutycycle is equal to that of that of the period register.

This is basically how it works.

/Henrik.

Kloney
- 2nd July 2013, 02:53
That explained A LOT !
now it all makes sense
thanks a lot for your help this couldn't be explained any better :)

Kloney
- 2nd July 2013, 07:17
but shouldn't it be "Fosc/4" if I'm using (1:1) prescale ? (1MHz/4 in our case ) which gives PWM frequency 1655Hz ?

HenrikOlsson
- 2nd July 2013, 07:40
Hi,
Note that I said - If the timer is driven with a frequency of 1MHz - which it would be if you run the PIC at 4MHz and select Fosc/4 as the PWM Timebase input.
Apart from that you are correct. If you'd run the PIC at 1MHz and set the PWM timebase to derive it's clock from Fosc/4 then the timebase will run at 250kHz and you'd get a PWM frequency of 1655.5Hz with the values in the previous example.

/Henrik.

Kloney
- 3rd July 2013, 02:30
My bad
Once again Thanks Henrik

Kloney
- 4th July 2013, 08:09
Last two questions (I hope so )
in Bruce's Example he's multiplying pdcx by (Lowbyte,Highbyte) Low and high byte of what and what is the purpose of doing that ?
also
anyway I'm going to post the Code since I can't find the problem yet .. what I'm Trying to do is to generate a sinewave from pwm0 with an offset to use that later in a three phase inverter.. so I made this test which gives unexpected results
what I'm trying to do is using entries from a lookup table to generate pulses with duty cycles starting from 1000 to 2000 then back to 1000 (Positive half Cycle) after that going from 1000 to 0 and going back again to 1000 (Negative half Cycle) which isn't happening ..
I'm using 10MHz osc 18f4431, and a lookup table of 25 Entries (Which isn't enough for a smooth wave but this is for testing)


PORTB = 0
TRISB = %11000000 ' pwm0-5 output
PTCON0 = %00000000 ' (1:1) prescale , Free-Running mode
PTCON1 = %10000000 'time base is on, Counting is up
PWMCON0 = %01000000 'Pwm0-5 enbbled for pwm output, pwm0-5 complementary mode
PWMCON1 = %00000001
OVDCOND = %11111111
PTPERL = $D0
PTPERH = $07 'PTPER = 2000
Main:
PDC0L = 00
PDC0H = 10

PDC0L = 59
PDC0H = 12

PDC0L = 00
PDC0H = 15

PDC0L = 07
PDC0H = 17

PDC0L = 66
PDC0H = 18

PDC0L = 66
PDC0H = 19

PDC0L = 00
PDC0H = 20

PDC0L = 66
PDC0H = 19

PDC0L = 66
PDC0H = 18

PDC0L = 07
PDC0H = 17

PDC0L = 00
PDC0H = 15

PDC0L = 59
PDC0H = 12

PDC0L = 00
PDC0H = 10

PDC0L = 41
PDC0H = 07

PDC0L = 00
PDC0H = 05

PDC0L = 93
PDC0H = 02

PDC0L = 34
PDC0H = 01

PDC0L = 34
PDC0H = 00

PDC0L = 00
PDC0H = 00

PDC0L = 34
PDC0H = 00

PDC0L = 34
PDC0H = 01

PDC0L = 93
PDC0H = 02

PDC0L = 00
PDC0H = 05

PDC0L = 41
PDC0H = 07

PDC0L = 00
PDC0H = 10

GoTo Main
Thanks

HenrikOlsson
- 4th July 2013, 11:05
Hi,
You are currently updating the dutycycle register at a very fast rate, much much faster than the PWM frequency. Try insterting a short delay ( PAUSE 5 or whatever) between each duty cycle update. If that doesn't help try explaining what it does and what the problem is.

I'll have to look at Bruce's example to see if I can answer the other (first) question.

/Henrik.

EDIT: OK, looked at Bruce's example (http://www.picbasic.co.uk/forum/showthread.php?t=7095&p=43820#post43820) but I don't see where he's multiplying the pdcx with anything....could you clarify?

Kloney
- 4th July 2013, 12:51
I added a "1 us" delay and it worked quite well but the wave took longer duration time, the output frequency is 16.6hz instead of 50hz becaus of the delay
I'm using 25 entry and the output should be 50hz (Fpwm = 50 * 25 = 1250 I guess ?) how can I fix that ?
Also for the DTCON I can't get this part I need a 5us dead-time and I'm using (Fosc/4) settings what are the calculations needed for "5-0" bits in DTCON to set such a dead-time ?

For Bruce's Example this is the part I'm talking about :

Duty = 800 ' ~50% PDC2L = Duty.LowByte ' maintain a fixed 50% duty cycle on PWM4,5 PDC2H = Duty.HighByte
But i don't think that I'm going to need that in my case I just was wondering to understand the whole thing what are the LowByte and HighByte ?

Thanks Henrick , I can't find words to describe how much you helped me

HenrikOlsson
- 4th July 2013, 13:28
Hi,
I suspect you added 1ms delay, not 1us.
Correct, if the table is 25 entries for a full cycle and you want 50Hz output you need to index the table at 1250Hz. The delay between each update of the dutycycle should then be 1/1250=800us (so use PauseUs 800 and see what happens). Then, of course, the actual updating of the dutycycle registers takes some small amount of time so if it's critical you'll need to tweak the delay.

The first and last entry in your "lookup table" are one and the same ($1000) - you'll get a "flat spot" in the cycle.

Ah, OK, now I see what you're asking....Bruce is not multiplying anything.
He has a WORD-sized (two bytes) variable, called Duty. He's then assigning the high byte of Duty to PDC0H ( PDC0H=Duty.HighByte ) and the low byte of Duty to PDC0L ( PDC0L = Duty.LowByte ).

I'll have to read the datasheet for the deadtime thing and I don't have time for that at the moment, I'll try to get back to it later.

/Henrik.

HenrikOlsson
- 4th July 2013, 17:59
Hello again,
If Fosc=10MHz then Tosc=100ns. If you select the source for the Dead Time unit to be Fosc/2 then one "unit" of dead time equals 200ns. You want 5us deadtime which equals 25 "units" of 200ns each so the lower 6 bits of DTCON should be set to 25. If you're using the Fosc/4 setting then one "unit" of deadtime is 400ns and the lower 6 bits would have to be to set to 12.5 to get exactly 5us which obviously isn't possible - use 12 or 13.

/Henrik.

Kloney
- 4th July 2013, 18:35
Hi
-Yes it was 1ms not 1us ,and yeah 800us was just fine for dutycycle update you are right.
Tweaking delay, hmmmm .. should I try Higher frequency ?? then (Fpwm= 25 * 50) won't be correct.
that's going to be a problem since I'm going to use 100 entries for a smoother wave.

-Right , I'm going to edit this part

-oops .. I thought he was multiplying, got it now thanks :) .

-For the DTCON, it's ok I figured it out after re-reading Bruce's example.

Kloney
- 5th July 2013, 06:42
Hello again,
If Fosc=10MHz then Tosc=100ns. If you select the source for the Dead Time unit to be Fosc/2 then one "unit" of dead time equals 200ns. You want 5us deadtime which equals 25 "units" of 200ns each so the lower 6 bits of DTCON should be set to 25. If you're using the Fosc/4 setting then one "unit" of deadtime is 400ns and the lower 6 bits would have to be to set to 12.5 to get exactly 5us which obviously isn't possible - use 12 or 13.

/Henrik.

Oh .. I didn't notice this comment when did it show up :o ? thanks Henrick
but still the only left problem is the delay thing which is leading to a lower output frequency.
Also I made an array to lookup up entries and I'm not sure i I'm doing it the right way..
I had to declare each entry in the array one by one .. also compiler refuse using .Lowbyte and .Highbyte (ex : pdc0l=duty[i].lowbyte) with arrays so I made two arrays one for pdcxL and another for pdcxH
is there any other way to declare an array ? or other other function for this case ?

HenrikOlsson
- 5th July 2013, 08:23
Hi,
Without seeing your actual code it's hard to tell what you're doing and if it's "the right way" or not. If it works then I guess it's OK.
But anyway, here are couple of random thoughts....
You don't need an array (in RAM) since the values are constants, use a lookup table (which stores the values in program memory instead of RAM). If the values you're looking up are WORDs you need to use LOOKUP2.

i VAR BYTE
DUTY VAR WORD
MAIN:
For i = 0 to 8
LOOKUP2 [100,123,234,345,456,789,890,1234,2345], Duty
PDC0L = DUTY.LOWBYTE
PDC0H = DUTY.HIGHBYTE
PAUSEUS 800
NEXT
GOTO Main
As for the delay, again hard to tell what you're doing and I'm not sure I understand what the actual problem is. If you want higher output frequency (ie the sine output, not the actual PWM frequency) then reduce the delay between each update of the dutycycle. If the math doesn't add up, make SURE the PIC is running at the frequency you think it is.

/Henrik.

Kloney
- 5th July 2013, 14:20
Hi,
seems I should use Interrupt somehow which I don't have any idea about using it in such a thing..
anyway here's the code


DEFINE OSC 40
portb = 0
latb = 0
TRISB = %11000000 ' pwm0-5 output
PTCON0 = %00000000 ' (1:1) prescale , Free-running mode
PTCON1 = %10000000 'time base is on, Counting is up
PWMCON0 = %01000000 'Pwm0-5 enbbled for pwm output,Complementary
PWMCON1 = %00000001
OVDCOND = %11111111
DTCON = %01110010 '5us dead-time
PTPERL = $E7
PTPERH = $03
i var byte
x var byte
z var byte
i=0
x=33
z=66
duty var byte[101]
duty2 var byte[101]
duty[0]=05
duty2[0]=00
duty[1]=05
duty2[1]=31
duty[2]=05
duty2[2]=63
duty[3]=05
duty2[3]=94
duty[4]= 06
duty2[4]=24
duty[5]=06
duty2[5]=55
duty[6]=6
duty2[6]=84
duty[7]=7
duty2[7]=13
duty[8]=7
duty2[8]=41
duty[9]=7
duty2[9]=68
duty[10]=7
duty2[10]=94
duty[11]=8
duty2[11]=91
duty[12]=8
duty2[12]=42
duty[13]=8
duty2[13]=64
duty[14]=8
duty2[14]=85
duty[15]=09
duty2[15]=05
duty[16]=9
duty2[16]=22
duty[17]=9
duty2[17]=38
duty[18]=9
duty2[18]=52
duty[19]=9
duty2[19]=65
duty[20]=9
duty2[20]=76
duty[21]=9
duty2[21]=84
duty[22]=9
duty2[22]=91
duty[23]=9
duty2[23]=96
duty[24]=9
duty2[24]=99
duty[25]=10
duty2[25]=00
duty[26]=9
duty2[26]=99
duty[27]=9
duty2[27]=96
duty[28]=9
duty2[28]=91
duty[29]=9
duty2[29]=84
duty[30]=9
duty2[30]=76
duty[31]=9
duty2[31]=65
duty[32]=9
duty2[32]=52
duty[33]=9
duty2[33]=38
duty[34]=9
duty2[34]=22
duty[35]=9
duty2[35]=05
duty[36]=8
duty2[36]=85
duty[37]=8
duty2[37]=64
duty[38]=8
duty2[38]=42
duty[39]=8
duty2[39]=91
duty[40]=7
duty2[40]=94
duty[41]=7
duty2[41]=68
duty[42]=7
duty2[42]=41
duty[43]=7
duty2[43]=31
duty[44]=6
duty2[44]=84
duty[45]=6
duty2[45]=55
duty[46]=6
duty2[46]=42
duty[47]=5
duty2[47]=49
duty[48]=5
duty2[48]=63
duty[49]=5
duty2[49]=31
duty[50]=05
duty2[50]=00
duty[51]=4
duty2[51]=69
duty[52]=4
duty2[52]=37
duty[53]=4
duty2[53]=06
duty[54]=3
duty2[54]=76
duty[55]=3
duty2[55]=45
duty[56]=3
duty2[56]=16
duty[57]=2
duty2[57]=87
duty[58]=2
duty2[58]=59
duty[59]=2
duty2[59]=32
duty[60]=2
duty2[60]=06
duty[61]=1
duty2[61]=81
duty[62]=1
duty2[62]=58
duty[63]=1
duty2[63]=36
duty[64]=1
duty2[64]=15
duty[65]=0
duty2[65]=59
duty[66]=0
duty2[66]=78
duty[67]=0
duty2[67]=62
duty[68]=0
duty2[68]=48
duty[69]=0
duty2[69]=35
duty[70]=0
duty2[70]=24
duty[71]=0
duty2[71]=16
duty[72]=0
duty2[72]=09
duty[73]=0
duty2[73]=04
duty[74]=0
duty2[74]=01
duty[75]=0
duty2[75]=0
duty[76]=00
duty2[76]=01
duty[77]=0
duty2[77]=04
duty[78]=0
duty2[78]=09
duty[79]=0
duty2[79]=16
duty[80]=0
duty2[80]=24
duty[81]=0
duty2[81]=35
duty[82]=0
duty2[82]=48
duty[83]=0
duty2[83]=62
duty[84]=0
duty2[84]=78
duty[85]=0
duty2[85]=95
duty[86]=01
duty2[86]=15
duty[87]=01
duty2[87]=36
duty[88]=01
duty2[88]=58
duty[89]=01
duty2[89]=81
duty[90]=02
duty2[90]=06
duty[91]=02
duty2[91]=32
duty[92]=02
duty2[92]=59
duty[93]=02
duty2[93]=87
duty[94]=03
duty2[94]=16
duty[95]=03
duty2[95]=45
duty[96]=03
duty2[96]=76
duty[97]=04
duty2[97]=06
duty[98]=04
duty2[98]=37
duty[99]=04
duty2[99]=96

Main:

pdc0l=duty2[i]
pdc0h=duty[i]

pdc1l=duty2[x]
pdc1l=duty[x]

pdc2l=duty2[z]
pdc2h=duty[z]
pauseus 100
i=i+1
x=x+1
z=z+1
if (x>99) THEN X=0
IF (I>99) THEN I=0
IF (Z>99) THEN Z=0
goto main


I'm trying to generate 3 sinwaves with a 120 shift each to use it in a 3-phase inverter ,the output sinewave frequency should be 50Hz.
I think I can use the Lookup2 function to declare the array entries like this :



G VAR BYTE
Arrayd VAR WORD
For G = 0 to 99
LOOKUP2 [500, 531, 563, 594, 624, 655, 684, 713, 741, 768, 794, 819, 842, 864, 885, 905, 922, 938, 952, 965, 976, 984, 991, 996, 999, 1000, 999, 996, 991, 984, 976, 965, 952, 938, 922, 905, 885, 864, 842, 819, 794, 768, 741, 713, 684, 655, 624, 594, 563, 531, 500, 469, 437, 406, 376, 345, 316, 287, 259, 232, 206, 181, 158, 136, 115, 95, 78, 62, 48, 35, 24, 16, 9, 4, 1, 0, 1, 4, 9, 16, 24, 35, 48, 62, 78, 95, 115, 136, 158, 181, 206, 232, 259, 287, 316, 345, 376, 406, 437, 469], Arrayd
duty[G] = Arrayd.LOWBYTE
duty2[G] = Arrayd.HIGHBYTE
NEXT
not sure if that is going to work

but I think this is not the problem here

Kloney
- 5th July 2013, 15:00
update:
Hi again,
for Lookup2 it didn't compile
"Expected '['
Bad expression " but it's ok anyway since I already filled the array manually, but I can't find out how to use interrupt in my case or if there is any other solution for my problem

HenrikOlsson
- 5th July 2013, 18:36
Hi,
You are overthinking this. Why would want a lookuptable and then populate an array with the data from the lookuptable?
EITHER use an array for your data OR use a lookup table - I sugest a lookup table. Use the value from the lookupo table and load it to the dutycycle registers - don't populate an array with it's just wasting RAM. Get it working with something like 16 values, then move on the full 100 or whatever.

Then use three indexes, one for each phase to retreive the correct dutycycle value for each phase from the lookup table, just as you're doing with the arrays currently.

I know I've covered all these questions in other threads on the exact same topic. 3-phase power inverters seems to be a popular beginners project for some reason. Do a bit of searching on the forum and you should be able to find example code for 3 phase sine PWM.

/Henrik.