PDA

View Full Version : 3 channel PWM with customize duty cycle



photoelectric
- 16th April 2011, 03:30
Hi ppl,

im working on my PIC basic pro project to generate 3 channel pwm using PIC 16f737 family and i found some coding from previous thread. i test it and it works well.
but the PWM generated used fixed value of duty cycle and i want to change into customize duty cycle by using lookup command in PICBAsic pro .

my lookup table is below:

For DUTY1 = 0 TO 44

LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

next DUTY1

and here is my 3 channel coding:

ClearWDT

DEFINE OSC 20
' Word vars for 10-bit value of each PWM duty cycle
Duty1 VAR WORD ' Channel #1
Duty2 VAR WORD ' #2
Duty3 VAR WORD ' #3

' Set CCPx pins to outputs
TRISC.2=0 ' CCP1 output
TRISC.1=0 ' CCP2 output (could also be assigned to RB3)
TRISB.5=0 ' CCP3 output

' Set CCP modules to PWM mode
CCP1CON = %00001100 ' Mode select = PWM
CCP2CON = %00001100 ' Mode select = PWM
CCP3CON = %00001100 ' Mode select = PWM

' Set period up for 1.22kHz PWM freq
PR2 = 249

' Set TMR2 up for 1:16 prescale & turn it on
T2CON = %00010101 ' TMR2 ON 1:16 prescale

Duty1 = 512 ' 50% duty cycle. 1024/2 "10-bit resolution"
CCP1CON.4 = Duty1.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = Duty1.1 ' a 10-bit word
CCPR1L = Duty1 >> 2

Duty2 = 512
CCP2CON.4 = Duty2.0
CCP2CON.5 = Duty2.1
CCPR2L = Duty2 >> 2

Duty3 = 512
CCP3CON.4 = Duty3.0
CCP3CON.5 = Duty3.1
CCPR3L = Duty3 >> 2

End

and my problem is i cannot combine the lookup command and the 3channel coding to generate the customize duty cycle ??
can any1 help explaning how?

thanks
photoelectric

HenrikOlsson
- 16th April 2011, 08:39
Hi,
Well, if you have the lookup-table in a for-next loop like you are currently showing it will run thru that loop and then continue with the reset of the code. You have to move the NEXT so that it actually updates the dutycycles registers with the value retreived from the table before looking up the next value.

For duty = 0 to 44
Lookup.....
Set dutycycle
Pause whatever
Next

Then, in order to get you three phases you either need to have three lookup tables or you need to index the same lookuptable by offsetting the index by 1/3 for each phase and keep track of when to "roll over" and start at 0 again for each phase.

photoelectric
- 16th April 2011, 10:35
hi henrik, thanks for explaining.
but i ve try ur method using below coding for 1 channel but the the output ccp wont work. its doesnt come out the correct dutycyle from the lookup table for my PWM.
below are the example of my coding:

For DUTY1 = 0 TO 44

LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

Duty1 = DUTY2 ' 50% duty cycle. 1024/2 "10-bit resolution"
CCP1CON.4 = Duty1.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = Duty1.1 ' a 10-bit word
CCPR1L = Duty1 >> 2
next DUTY1

you see i declare the DUTY1 = DUTY2 for my dutycycle but it wont work
:(

please help assist.

thanks

HenrikOlsson
- 16th April 2011, 10:53
Hi,
You have Duty1 as the "index" for the lookup table. Duty1 is therefor expected to count 0, 1, 2, 3, 4...44 but you are overwriting Duty1 with the value of Duty2 so the indexing (the for-next loop) wont work properly.

Use Duty2 directly when setting the dutycycle register or use another variable for indexing the lookup table (and don't overwrite it in the middle of the loop).

/Henrik.

photoelectric
- 16th April 2011, 11:17
hi,

yes i got your point now and correcting my coding now but then i realize how to define my dutycycle value from the lookup above since dutycyle register only accept numbers. example

For DUTY1 = 0 TO 44

LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

Duty2 = ?????? ' duty cycle. 1024/2 "10-bit resolution"
CCP1CON.4 = Duty1.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = Duty1.1 ' a 10-bit word
CCPR1L = Duty1 >> 2
next DUTY1

what should i define the duty2 value since i want it to read from lookup table?

photoelectric

photoelectric
- 16th April 2011, 11:17
hi,

yes i got your point now and correcting my coding now but then i realize how to define my dutycycle value from the lookup above since dutycyle register only accept numbers. example

For DUTY1 = 0 TO 44

LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

Duty2 = ?????? ' duty cycle. 1024/2 "10-bit resolution"
CCP1CON.4 = Duty2.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = Duty2.1 ' a 10-bit word
CCPR1L = Duty2 >> 2
next DUTY1

what should i define the duty2 value since i want it to read from lookup table?

photoelectric

HenrikOlsson
- 16th April 2011, 11:39
Duty2 already contains the value from the lookuptable - that is what your Lookup does, it assignes the value pointed at by Duty1 to the variable Duty2 in this case.

For each iteration thru the loop Duty2 will contain the value pointed at by Duty1 and since Duty1 counts 0,1,2,3...44 Duty2 will contain 0,18,35,53...0

/Henrik.

photoelectric
- 16th April 2011, 11:50
So, i need to define new variable?? let say

Duty2 = Duty3

so my code will be like this:

For DUTY1 = 0 TO 44

LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

Duty2 = Duty3
next DUTY1

CCP1CON.4 = Duty3.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = Duty3.1 ' a 10-bit word
CCPR1L = Duty3 >> 2

so each of the duty2 value will be store at the ccp1 register?
am i right?

HenrikOlsson
- 16th April 2011, 12:12
No, why would you do that? If you do Duty2 = Duty3 you assingn the value of Duty3 TO Duty2 and overwrites the value Duty2 got from the lookup table.

I don't know how to explain this in any other way. You want Duty2 to contain the value from the Lookuptable and that's what it does after the lookup command has executed.


Main:
'Set up a loop for 45 iterations, Duty1 will count 0,1,2,3,4....44
For DUTY1 = 0 TO 44

'Go into the table and retrieve the value pointed at by Duty1 and put that value in Duty2
LookUp DUTY1, 0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

'Duty2 now contains the value from the lookup table that Duty1 points at. First time thru the loop it's 0 second time it's 18 and so on.
CCP1CON.4 = Duty2.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = Duty2.1 ' a 10-bit word
CCPR1L = Duty2 >> 2
Pause 10 'Use each dutycycle value 10ms before going to the next one.
NEXT DUTY1
Goto Main 'And do it all over again

Perhaps I don't understand the problem you're having? Have you actually tried it?

/Henrik.

photoelectric
- 16th April 2011, 12:57
yes i tried simulate the program using real pic simulator.
so my codes is below:

DEFINE OSC 20
DUTY1 VAR WORD
DUTY2 VAR WORD ' Duty cycle value (CCPR1L:CCP1CON<5:4>)

TRISC.2 = 0 ' Set PORTC.2 (CCP1) to output
CCP1CON = %00001100 ' Set CCP1 to PWM
T2CON = %00000101 ' Turn on Timer2, Prescale=4

PR2 = 249 ' Set PR2 to get 1KHz out

Main:
'Set up a loop for 45 iterations, Duty1 will count 0,1,2,3,4....44
For DUTY1 = 0 TO 44

'Go into the table and retrieve the value pointed at by Duty1 and put that value in Duty2

LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2


'Duty2 now contains the value from the lookup table that Duty1 points at. First time thru the loop it's 0 second time it's 18 and so on.
CCP1CON.4 = DUTY2.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = DUTY2.1 ' a 10-bit word
CCPR1L = DUTY2 >> 2
Pause 10 'Use each dutycycle value 10ms before going to the next one.
Next DUTY1
GoTo Main 'And do it all over again

End

but turns out the duty cycle doesnt come out fully as in the lookup table.
the PWM generated is not completed.
maybe i miss something important?
please help check

photoelectric

HenrikOlsson
- 16th April 2011, 15:15
Hi,
Then it's either a problem with how you write the value to the dutycycle register or a problem with the simulator. I can't see any problem with the code so I'm leaning towards the simulator....

I've tried the following here:

Duty1 VAR WORD
Duty2 VAR WORD

For Duty1 = 0 to 44
LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

HSEROUT [#Duty2,13]

Pause 10
NEXT

Pause 100
END
As expected this outputs the Duty2 value just fine which proves that the lookup part works. So again, the problem is either with the code that writes the value to the dutycycle register or with the simulator.

/Henrik.

photoelectric
- 16th April 2011, 15:38
hi,

thanks for your reply.
after checking its turn out the value in my lookup was abit wrong due to miscalculation.
before i starts calculating back my lookup table, can you explain abit about the timer prescale,
as you can see from my coding i decide to use timer prescale 16 instead of 4 or 1 stated in the datasheet, but i dont really understand what is timer prescale for.

FYI, i wan to set my PWM to 5kHz using osc 20MHz

please help,
tq

HenrikOlsson
- 16th April 2011, 16:06
If you're using a 20Mhz oscillator the internal clock runs at 5Mhz (FOsc/4) and this is what "ticks" the timer that is used for the PWM generation. The prescalers job is to divide this 5Mhz frequency by whatever ratios are available for the particular PIC and/or timer used (normaly 1, 4 and 16 for TMR2).

If you set the prescaler to 4 the timer is incremented at 5/4=1.25Mhz instead of 5Mhz.

The datasheet for the PIC you're using has a section dedicated to the CCPModule in which they explain in great detail how it works and how to calculate the PWM period:

PWM Period = (PR2 + 1) * 4 * TOsc * Prescaler

In order to calculate the PR2 value for a specific frequency you have to rewrite the formula a bit and solve for PR2+1:

PR2+1 = 1 / (TOsc * 4 * Prescaler * PWM Frequency)

If you settle for a prescaler of 4 then this becomes:

PR2+1 = 1 (0.00000005 * 4 * 4 * 5000) = 1/0.004 = 250 so in order to get a PWM frequency of 5kHz with a prescaler of 4 you should set PR2 to 249.

photoelectric
- 17th April 2011, 06:57
thank you for the explaning,
so, can i conclude that it doesnt matter to set prescale 1,4 or 16 since the value of PR2 will be change??

for instance,
prescale = 16 ,PWM freq =5kHz for 20M Fosc
from equation PR2+1 = 1 / (TOsc * 4 * Prescaler * PWM Frequency)
my PR2 should be 63.5

is it any different using prescale=4 with PR2=249 compare to the prescale=16 with PR2=63.5 because it turns out my PWM waveform doesnt seem to be same when i compare both

photoelectric

HenrikOlsson
- 17th April 2011, 08:58
Hi,
It won't make any difference for the PWM frequency, it'll be 5kHz in both cases except you can't really set PR2 to 63.5 so you won't get exactly 5kHz.

However, because the dutycycle is defined as the number of timer "ticks" the output is high in relation to the number of "ticks" it is low the prescaler setting also has an effect on the resolution because it makes the timer run slower so the total number of "ticks" in one period is lower.

So setting the dutycycle register to x won't give you the same dutycycle output if you switch prescaler setting. If you want the highest possible resolution you should use the lowest possible prescaler value.

photoelectric
- 17th April 2011, 10:48
tq again,

got your point now.
let say i use prescaler 1 for maximum resolution, my PR2+1 should be 1000.
So , let say i want to put 50% duty cycle in my lookup, my duty2 value should be:

based on the below eqn:
CCPR3L:CCP3CON<5:4>=(PR2+1)*4*Duty%
my duty2 should be 1000*4*0.5=2000
right?
correct me if im wrong.

photoelectric

HenrikOlsson
- 17th April 2011, 11:02
Hi,
That WOULD be correct but you can't put 1000 in PR2 since it's only 8 bits wide. This means that you can't get 5kHz with a prescaler of 1 so you'll have to use 4.

photoelectric
- 17th April 2011, 11:26
ok i got the idea right now.
what about value in the lookup table? is it 8 bit wide or more because
from my calculation of duty cycle:
let say 50%
prescaler 4
PR2+1 =250

so duty2 = 250*4*0.5 = 500

and if the value of constant in lookup table only 8bit wide , what should i do?
change to prescaler 16?? that would decrease my resolution..
please help assist

photoelectric

HenrikOlsson
- 17th April 2011, 15:50
Hi,
Have you had a look in the PBP manual?

It says: The LOOKUP statement can be used to retreive values from a table of 8-bit constants.

So, for LOOKUP you have to stick to 8bits in the table. You can do that by either use prescaler of 16 or stick with a prescaler of 4 and multiply the retreived value by 4 before putting it in the dutycycle register. Obviosuly both options reduces the effective resolution.

If you need more resolution then take a look at the LOOKUP2 statement, it's also described in the manual.

photoelectric
- 17th April 2011, 16:07
hi,
i stick with prescaler 4 and alrdy key in my dutycycle in my lookup using command lookup2 since it can support til 10bits wide.
below is my complete coding for 1 channel PWM:

DEFINE OSC 20
duty1 VAR WORD ' Duty cycle value (CCPR1L:CCP1CON<5:4>)
duty2 VAR WORD

TRISC.2 = 0 ' Set PORTC.2 (CCP1) to output
CCP1CON = %00001100 ' Set CCP1 to PWM
T2CON = %00000101 ' Turn on Timer2, Prescale=4

PR2 = 249 ' Set PR2 to get 5KHz out

'Set up a loop for 45 iterations, Duty1 will count 0,1,2,3,4....44
Main:
For duty1 = 0 TO 185

'Go into the table AND retrieve the value pointed at by Duty1 AND put that value in Duty2

LookUp2 duty1, [0,18,34,52,68,85,102,119,137,152,170,187,203,220,2 36,253,267,285,302,318,334,_
350,367,382,397,413,429,443,459,474,489,504,518,53 3,547,562,575,589,603,616,630,_
643,655,669,681,693,706,718,729,740,752,764,774,78 5,795,806,815,825,835,844,853,_
861,870,879,887,895,901,909,916,923,929,935,941,94 6,952,957,962,967,971,974,978,_
981,984,987,990,992,994,996,997,998,999,999,1000,9 99,999,998,997,997,996,994,992,_
990,987,984,981,978,974,971,967,962,957,952,946,94 1,935,929,923,916,909,901,895,_
887,879,870,861,853,844,835,825,815,806,795,785,77 4,764,752,740,729,718,706,693,_
681,669,655,643,630,616,603,589,575,562,547,533,51 8,504,489,474,459,443,429,413,_
397,382,367,350,334,318,302,285,267,253,220,203,18 7,170,152,137,119,102,85,68,_
52,34,18,0],duty2



'Duty2 now contains the value from the lookup table that Duty1 points at. First time thru the loop it's 0 second time it's 18 and so on.

CCP1CON.4 = duty2.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = duty2.1 ' a 10-bit word
CCPR1L = duty2 >> 2
'Pause 1 'Use each dutycycle value 10ms before going to the next one.
Next duty1
GoTo Main 'And do it all over again

End


i compile then use real pic simulator to check the PWM pattern.
seem like the duty cycle only read up to value 253 in my lookup table and then keep generating same dutycyle.
did i miss sumthing in my code??

attached is my screen shot of the pwm pattern. at the red area pwm duty is correct but at the yellow area it starts to generate same duty and not looking up to the value in the lookup2 command.

please help assist

photoelectric

HenrikOlsson
- 17th April 2011, 17:30
Hi,
I've tried it here, on a real physical PIC, and it works just fine.

I see that you've removed the Pause statement I had in there. If you don't have a delay there it will run thru the different dutycycles much faster then the actual PWM frequency. It tries to update the dutycycle registers several times during each PWM period which obviously won't work properly.

Your PWM frequency is 5kHz so if you really do want to see "all the steps" you must have atleast 1/5000=200us between updates. I measured the execution time of the loop to around 25us with a 20Mhz oscillator so you're basically trying to update the dutycycle 8 times every PWM period.....

photoelectric
- 17th April 2011, 17:54
yes, i remove the pause statement because i think adding it causing the pwm duty longer then the original pwm duty, hence changing my pwm pattern.
can we actually pause below 1 ms since i know pause statement can only be use for minimum 1ms right??
correct me if im wrong

when you tries on the real physical PIC, do you enable the pause statement??
i will try on real physical PIC later and feedback to you tmrw.

photoelectirc

HenrikOlsson
- 17th April 2011, 18:12
Hi,
The Pause statement won't effect the PWM since it's generated in hardware. If you have Pause 1 you'll basically get 5 periods of each dutycycle value.

You can Pause with microsecond resolution by using PauseUs (again, it's in the green book....)

When testing I used 300us and you can see the result in the attached screenshot. This is not a simulation, it's captured on real hardware.

photoelectric
- 19th April 2011, 11:52
hi, yes you are correct, i test it on real physical and it works fine.
But i dint really understand on how you calculate the execution of my FOR NEXT loop. Can you explain a liltle bit detailed on that.
below is the previous quote on how you calculate the execution time.




Your PWM frequency is 5kHz so if you really do want to see "all the steps" you must have atleast 1/5000=200us between updates. I measured the execution time of the loop to around 25us with a 20Mhz oscillator so you're basically trying to update the dutycycle 8 times every PWM period.....

HenrikOlsson
- 19th April 2011, 13:03
Hi,

I didn't calculate it, I measured it - as it says in the message ;-)

I put a TOGGLE PortC.0 just before the NEXT statement a put the scope on the pin. I got a 20kHz square wave which, because the pin is toggled each time thru loop, means it executes roughly 40000 times per second. 1/40000=25us

photoelectric
- 20th April 2011, 05:16
hi,
thks for the explanation, now my pwm works fines.
attached is my currently pwm pattern for ccp1 and ccp2.
NOw, i want to shift the CH2 pwm about 1/3 of the duration of the CH1 pwm which is

10.20ms/3 = 3.4ms

so, i intend to put pause command at:

Main1:
'For duty1 = 0 TO 44

'Go into the table AND retrieve the value pointed at by Duty1 AND put that value in Duty2
LookUp2 duty1, [0,70,137,208,275,341,408,470,529,588,643,694,745,7 88,827,866,898,925,_
953,968,984,996,1000,996,984,968,953,925,898,866,8 27,788,745,_
694,643,588,529,470,408,341,275,208,137,70,0],duty2



'Duty2 now contains the value from the lookup table that Duty1 points at. First time thru the loop it's 0 second time it's 18 and so on.

CCP1CON.4 = duty2.0 ' Setup 10-bit duty cycle as
CCP1CON.5 = duty2.1 ' a 10-bit word
CCPR1L = duty2 >> 2

Pause 4
CCP3CON.4 = duty2.0
CCP3CON.5 = duty2.1
CCPR3L = duty2 >> 2

PauseUs 210 'Use each dutycycle value 10ms before going to the next one.
'Next duty1
'PauseUs 100

GoTo Main1

but seem like ccp1 and ccp2 pwm duration increase above 10.20ms.
SO, what should i adjuzt to make pwm CH2 shift 1/3 of the pwm CH1
without increasing the duration?

thks
photoelectric53595359

HenrikOlsson
- 20th April 2011, 08:32
Hi,
If you're trying to get the 120° phase shift you can't do that with a PAUSE. You have to remember that only the PWM is done by hardware, everything else, ie. the lookup and setting the dutycycle registers are done in software.

If you put a Pause 4 in there the whole software loop will "hang" for 4ms and whatever the CCP module is outputting will keep getting output untill it gets updated again. The FOR-NEXT loop will NOT run while the pause statements executes.

If you want 120° phase shift you could do that thru an offset into the loopup table. If the table is 45 entries you want entry 0, 14 and 29 the first time. Then you want 1, 15 and 30 and so on. I'd probably skip the FOR-NEXT loop and rewrite it to so that I'd have three counters, all counting from 0 to 44 but with an offset of 15 between them, these will then get the dutycycle from the table and assingn them to the proper CCPModule.

/Henrik.

photoelectric
- 20th April 2011, 11:55
Hi,

Im still trying to understand your explaination.
So i have to use count command?
can you give me an example of code for me to refer to?

photoelectric

HenrikOlsson
- 20th April 2011, 16:41
Hi,
I'm sorry to say this but it sounds like you need to take a step back and work thru the basics of programming with PBP. We can keep giving you code here until we've basically written the whole thing for you and you still wouldn't understand how it works.

Pause does just that - it pauses - no other code (except if you're using interrupts) is running while the Pause exeuctes. Like this:

Main:
High PortB.0
Pause 100
Low PortB.0
Pause 100
Goto Main
This will blink a LED connected to PortB.0 at 5Hz. If you put another pause in there, like:

Main:
High PortB.0
Pause 100
Low PortB.0
Pause 100

Pause 1000
Goto Main
It won't blink at 5Hz because you just told it to pause for another second. The same thing happens with your 4ms pause. The FOR NEXT loop that is supposed to keep running and feeding the PWM module with a new dutycycle evert 210us will stop - completely - for the 4ms duration. But because the PWM is generated by hardware it will keep outputting whatever dutycycle it was last told to do.

The count command is used to count pulses on a pin, I don't think that is what you want?

The following is ONE way of doing it. This compiles fine but I have NOT tested it.

Phase VAR BYTE[3] ' Array used as pointers into lookuptable.
DutyCycle VAR WORD[3] ' Array storing the dutycycles retreived from the table
Temp VAR WORD ' Temporary variable to avoid hard to understand array indexing
i VAR BYTE ' General purpose counter

' The lookup table has 186 entries long, to get 120° phase shift we need to
' "start" the second phase at 1/3 of the cycle and the third phase at 2/3
' of the table. 186/3=62 so first phase starts at 0, second phase at 62
' and third phase at 123.
' Initilise pointers.
Phase[0] = 0 : Phase[1] = 61 : Phase[2] = 123

Main:
Gosub GetDuty ' Retrieve the dutycycle values for all three phases
GOSUB SetDutyCycle ' Set the three PWM modules accordingly

' Now increment the individual table pointers and make sure they wrap
' around to zero when they reach then end of the table. That way they
' will always stay 62 "steps" (120°) from each other.

For i = 0 to 2
Phase[i] = Phase[i] + 1
If Phase[i] > 185 then Phase[i] = 0 'When pointer is > 185 we need to wrap around to 0.
NEXT

PauseUs 300

Goto Main


SetDutyCycle:
' Get value from the array of dutycycles and put it in the dutycycle
' registers of the 3 PWM modules. We could have used the array directly
' but this way (using a Temp variable) is easier to understand.

Temp = DutyCycle[0] ' Get dutycyle for phase 1 from the array and store in temp.
CCP1CON.4 = Temp.0 ' Set the LSB's
CCP1CON.5 = Temp.1
CCPR1L = Temp >> 2 ' Set the 8 high bits

Temp = DutyCycle[1] ' Same procedure.
CCP2CON.4 = Temp.0
CCP2CON.5 = Temp.1
CCPR2L = Temp >> 2

Temp = DutyCyle[2] ' Same procedure.
CCP3CON.4 = Temp.0
CCP3CON.5 = Temp.1
CCPR3L = Temp >> 2
RETURN


' ------------------------------------------------------------------------------
' ---- Subroutine to retreive the three dutycycle values from the table.
' ---- Values will be stored in the DutyCycle array.
' ------------------------------------------------------------------------------
GetDuty:
' This For-Next loop runs three times. Each time it gets the value from the lookuptable
' that Phase[i] is pointing at. The Phase array pointers are incremented in the main loop
' and are always 62 "steps" appart so that a 120° phase shift is preserved.

For i = 0 to 2
LookUp2 Phase[i], [0,18,34,52,68,85,102,119,137,152,170,187,203,220,2 36,253,267,285,302,_
318,334,350,367,382,397,413,429,443,459,474,489,50 4,518,533,547,562,575,589,603,616,_
630,643,655,669,681,693,706,718,729,740,752,764,77 4,785,795,806,815,825,835,844,853,_
861,870,879,887,895,901,909,916,923,929,935,941,94 6,952,957,962,967,971,974,978,_
981,984,987,990,992,994,996,997,998,999,999,1000,9 99,999,998,997,997,996,994,992,_
990,987,984,981,978,974,971,967,962,957,952,946,94 1,935,929,923,916,909,901,895,_
887,879,870,861,853,844,835,825,815,806,795,785,77 4,764,752,740,729,718,706,693,_
681,669,655,643,630,616,603,589,575,562,547,533,51 8,504,489,474,459,443,429,413,_
397,382,367,350,334,318,302,285,267,253,220,203,18 7,170,152,137,119,102,85,68,_
52,34,18,0],Temp


' Lookup2 can't handle an array as the designator so we need to
' put the value in a temporary variable first and then move
' it to the array of dutycycles.

DutyCycle[i] = Temp

NEXT

RETURN

/Henrik.

photoelectric
- 21st April 2011, 11:34
Hi,
Thanks again for the example. This is my 1st time using PIC Basic pro languange and im trying very hard to learn it. hope you can assist me more in the future.

i test it and it works fine for all three phases.(according to my value of lookup)
Phase 1 launch for 17.6ms
Phase 2 launch at 7ms of the phase 1
Phase 3 launch at 13.2ms of the phase 1

now, im trying to add six signal from six port pin connected to the external AND gate so that i can control the the 3 phases.
Attached is the schematic diagram.

what should i adjust in the codes so that
when
RA1 is high and RA2 is low pwm from ccp1 is launch other word is Q1 is launch and then after 1 complete cycle,
RA1 is low , RA2 is high Q4 is launch.

and so on.

thanks,
photoelectric

HenrikOlsson
- 21st April 2011, 13:29
Hi,
I'm not completely sure I understand but anyway.....

You can use any of the phase counters, take Phase[0] for example. It'll count from 0 to 185 and then start over, count from 0 to 185 on and on and on. Every time it's 0 a new cycle starts. Use that as your "trig" to change the output pattern to the external logic.

photoelectric
- 21st April 2011, 14:09
Hi,

my explanation is same like coding below:



defineosc 20
DUTY1 var word
DUTY2 var word
FREK var word

frek=5000
Loop:
high port.1

GOSUB FIRE

low portb.1
high portb.2

GOSUB fire

low portb.2
gotoLoop

fire:

For DUTY1 = 0 TO 44

LookUp DUTY1, [0,18,35,53,70,87,104,120,135,150,164,177,190,201,2 11,221,229,236,_
243,247,251,254,255,254,251,247,243,236,229,221,21 1,201,189,_
177,164,150,135,120,104,87,70,53,35,18,0],DUTY2

hpwm 1,DUTY2,frek
next DUTY1

RETURN
end


but that coding only applicable for 1 phase only.
based on your coding example, i want it to be:
The 1st time it count from 0 to 185 , portc.3 will be high and portc.0 will be low then 2nd time it count from 0 to 185, portc.3 will be low and portc.0 will be high.

do i need to make new loop, let say:

loop:
IF Phase[0] =0 '1st time it count
high portc.3
low portc.0
then Phase[0]=0 'second time it count
high portc.0
low portc.3
goto loop

please help advice

photoelectric

HenrikOlsson
- 21st April 2011, 14:51
Hi,
Perhaps you could do something like:

For i = 0 to 2
Phase(i) = Phase(i) + 1
If Phase(i) > 185 then
Phase(i) = 0 'When pointer is > 185 we need to wrap around to 0.
Gosub ChangeBridgeDrive 'One phase is starting over, go change the outputs.
ENDIF
NEXT

And the ChangeBridgeDrive subroutine might then look something like:

ChangeBridgeDrive:

' When we come here the value i contains the phase counter that just got reset so we
' can use that to determine for which phase we should switch the outputs.

Select Case i
Case 0 ' It was Phase 1 that rolled over
TOGGLE PORTA.1 ' Invert the state of the pin
TOGGLE PORTA.2 ' Invert the state of the pin

Case 1 ' It was Phase 2 that rolled over
TOGGLE PortB.1 ' Invert the state of the pin
TOGGLE PortB.2 ' Invert the state of the pin


Case 2 ' It was Phase 1 that rolled over
TOGGLE PortC.1 ' Invert the state of the pin
TOGGLE PortC.2 ' Invert the state of the pin

END SELECT

RETURN

Again, it compiles but I've not tested. One concern I have is the time between the two outputs for each halfbridge switching but since the dutycycle is effectively zero at the time of switching there should be no risk of shoot-thru etc. This of course also depends on the design of the hardware which I haven't seen so I'm just raising a warning about it.

/Henrik.

photoelectric
- 21st April 2011, 15:25
hi,

i compile the additional example of yours into my codes and it works ok but at the toggle part dint works well.

attached is the simulation using the real pic simulation named result:
as you can see i want it to be the pin rc3 should be high and pin rc0 should be low at 1 cycle but result show both of pin is high.
i try to change the code a bit into: ( for phase 1 only)


ChangeBridgeDrive:

' When we come here the value i contains the phase counter that just got reset so we
' can use that to determine for which phase we should switch the outputs.

Select Case i
Case 0 ' It was Phase 1 that rolled over
High PORTC.3 ' Invert the state of the pin
Low PORTC.0 ' Invert the state of the pin
Toggle PORTC.3
Toggle PORTC.0

it end like attached file name result2.
yes im also worried about the shoot thru in the beginning but then i realise time
between each cycle to launch is about 1.8ms, so it should be enough time before it collide each others.

please help advice the code.

photoelectric
please help advice.

HenrikOlsson
- 21st April 2011, 15:34
Hi,
Well, you have to set the pins to the correct state at the start of the program - before you start the PWM-loop otherwise they will both start low and then toggle together. Set one high and the other low at the beginning, before you actually start the PWM.

Or create a subroutine which sets everything up properly and simply GOSUB that routine at startup or whenever you need to "restart" it.

photoelectric
- 21st April 2011, 15:51
Hi,

yes, i set all the pin state 1st but outside the main loop, i simulate it and my switching and my 3 phases channel all works ok.
i will test it in the real physical PIC tmrw and see the results, then feed back to you again.

photoelectric

photoelectric
- 22nd April 2011, 10:49
Hi,

ive just test it on real physical PIC and found out that phase 1 and phase 2 works fine but phase 3 wont work very well.
As you can see from the attached named Phase 1 shows the correct generated pwm, same as phase 2.
However, file Phase 3 show that the signal from portb.3 always high as a result generating wrong pwm switching.
ive recheck the circuit but nothing wrong with it.
i also simulate the program using real PIC simulation and nothing was wrong.
So, i guess maybe the code got something problem.
below is my complete coding:




DEFINE OSC 20
Phase VAR BYTE[3] ' Array used as pointers into lookuptable.
DutyCycle VAR WORD[3] ' Array storing the dutycycles retreived from the table
Temp VAR WORD ' Temporary variable to avoid hard to understand array indexing
i VAR BYTE ' General purpose counter
TRISC.2 = 0 ' Set PORTC.2 (CCP1) to output
TRISC.1 = 0
TRISB.5 = 0
CCP1CON = %00001100 ' Set CCP1 to PWM
CCP2CON = %00001100
CCP3CON = %00001100
T2CON = %00000101 ' Turn on Timer2, Prescale=4
PR2 = 249 ' Set PR2 to get 5KHz out

' The lookup table has 186 entries long, to get 120° phase shift we need to
' "start" the second phase at 1/3 of the cycle and the third phase at 2/3
' of the table. 186/3=62 so first phase starts at 0, second phase at 62
' and third phase at 123.
' Initilise pointers.
Phase[0] = 0 : Phase[1] = 15 : Phase[2] = 30


High PORTC.3 'Set the initial state for portc.3
Low PORTC.0 'Same
High PORTC.6 'Same
Low PORTC.5 'Same
High PORTB.4 'Same
Low PORTB.3 'Same

Main:

GoSub GetDuty ' Retrieve the dutycycle values for all three phases
GoSub SetDutyCycle ' Set the three PWM modules accordingly


' Now increment the individual table pointers and make sure they wrap
' around to zero when they reach the end of the table. That way they
' will always stay 62 "steps" (120°) from each other.


For i = 0 TO 2
Phase[i] = Phase[i] + 1
IF Phase[i] > 46 Then
Phase[i] = 0 'When pointer is > 185 we need to wrap around to 0.
GoSub ChangeBridgeDrive 'One phase is starting over, go change the outputs.
EndIF
Next

PauseUs 300


GoTo Main


SetDutyCycle:
' Get value from the array of dutycycles and put it in the dutycycle
' registers of the 3 PWM modules. We could have used the array directly
' but this way (using a Temp variable) is easier to understand.

Temp = DutyCycle[0] ' Get dutycyle for phase 1 from the array and store in temp.
CCP1CON.4 = Temp.0 ' Set the LSB's
CCP1CON.5 = Temp.1
CCPR1L = Temp >> 2 ' Set the 8 high bits

Temp = DutyCycle[1] ' Same procedure.
CCP2CON.4 = Temp.0
CCP2CON.5 = Temp.1
CCPR2L = Temp >> 2

Temp = DutyCycle[2] ' Same procedure.
CCP3CON.4 = Temp.0
CCP3CON.5 = Temp.1
CCPR3L = Temp >> 2
Return


' ------------------------------------------------------------------------------
' ---- Subroutine to retreive the three dutycycle values from the table.
' ---- Values will be stored in the DutyCycle array.
' ------------------------------------------------------------------------------
GetDuty:
' This For-Next loop runs three times. Each time it gets the value from the lookuptable
' that Phase[i] is pointing at. The Phase array pointers are incremented in the main loop
' and are always 62 "steps" appart so that a 120° phase shift is preserved.

For i = 0 TO 2
LookUp2 Phase[i], [0,0,70,137,208,275,341,408,470,529,588,643,694,745 ,788,827,866,898,925,_
953,968,984,996,1000,996,984,968,953,925,898,866,8 27,788,745,_
694,643,588,529,470,408,341,275,208,137,70,0,0],Temp


' Lookup2 can't handle an array as the designator so we need to
' put the value in a temporary variable first and then move
' it to the array of dutycycles.

DutyCycle[i] = Temp

Next

Return

ChangeBridgeDrive:

' When we come here the value i contains the phase counter that just got reset so we
' can use that to determine for which phase we should switch the outputs.

Select Case i
Case 0 ' It was Phase 1 that rolled over
Toggle PORTC.3 ' Invert the state of the pin
Toggle PORTC.0 ' Invert the state of the pin


Case 1 ' It was Phase 2 that rolled over
Toggle PORTC.5 ' Invert the state of the pin
Toggle PORTC.6 ' Invert the state of the pin


Case 2 ' It was Phase 1 that rolled over
Toggle PORTB.4 ' Invert the state of the pin
Toggle PORTB.3 ' Invert the state of the pin


End Select

Return


End


please help to advice.

thanks

HenrikOlsson
- 22nd April 2011, 12:04
Hi,
OK, so you're saying that it works in the simulator but not in the real world? Or does it not work in the simularor either?

What do YOU think might be the problem and what have YOU tried in order to figure it out?

Have you:
A) Checked the datasheet to see if there's anything specific with PortB.3?
B) Tried another pin, just as an experiment, to see if the logic of the code works?
C) Connect a LED to PortB.3 and write code to turn that on and off so you KNOW that the pin actually works and is configured properly
D) Disconnect the external logic and measured on the pin directly.
E) Anything else?

Let me know what you've tried and what the results are.

photoelectric
- 22nd April 2011, 12:18
Hi,
its works on simulator for all phases but in real world only phase 3 as i mention before.

i think its the pin portb.4 and portb.3 on the PIC16F737 but im not sure.

A) yes, i have check the function of portb.3 and portb.4 -

based on the datasheet
portb.3:
-Digital I/O.
-CCP2 capture input, compare output, PWM output.
-Analog input channel 9.
portb.4:
-Digital I/O.
-Analog input channel 11.

since i declare portb.5 as the ccp3, should be no problem for portb.3.
portb.4 same function as i use for phase 1 and 2

B)i tried another pin which is portb.2 and portb.3, still not function well (seem like it wont toggle properly)

C)havent done it yet. will test using it next week and feedback to you again.

D)Yes, i measure part A and B directly to the pin PIC

btw, i test it on the breadboard. havent change the breadboard yet but i dont think its the reason.

i will test it again friday next week and feedback to you.
if u have any more idea on testing error please let me know

photoelectric

HenrikOlsson
- 22nd April 2011, 12:57
Hi,
So it says that PortB.3 can be the output of CCP2 but that's likely not the problem because you get the PWM from CCP2 on PortC.1, correct?

It also says that it (and PortB.4) can be an analog input. Have you investigated that part? Usually when the PIC starts up it defaults to analog inputs. In order to make them work as digital outputs you need to turn the analog functions off.

If you look at the PortB section of the datasheet you'll see in table 5-4 that they mention the ADCON1-register. You'll also see that it defaults to all zeros on startup. If you then turn to the Analog-to-digital converter section of the datasheet and look at the ADCON1 register you'll see that all zeros mean that AN0-AN11 are configured as analog inputs. This needs to be changed in order for the pin to function as a digital pin.

The other pins you're using (PortC.0, PortC.3, PortC.5 and PortC.6) doesn't have any analog circuitry so they work out of the box but PortB.3 and PortB.4 (as well as PortB.2 that you tried) DO have analog circuitry which is most likely the reason for them not to work as you expect.

The fact that it works in the simulator only illustrates that the simulator isn't doing what the real PIC does....

/Henrik.

photoelectric
- 29th April 2011, 10:54
hi,

im back :)

tq for your advice. i change the port b to digital by just adding:

adcon1 = 7

then it works fine. Actually, i only follow the example in the PBP book and i dint really understand the definition of it, how the PIC change the analog port to digital port and so on.
Can you help explain about the ADCON1 command and what if i want to chnge ONLY portb.4 and portb.3 to digital??
what should i do??

thanks

HenrikOlsson
- 29th April 2011, 15:55
Hi,
ADCON1 is not a command, it's register in the PIC which is used to control the ADC. When you do ADCON1 = 7 you set that register to 7 (which for the 16F737 sets AN8-13 to digital and leaves the rest as analog).

As I tried to tell you in my previous response you need to open up the datasheet for the PIC and look at the ADC section in general and specifically the description of the ADCON1 register. You'll find that setting just PortB.3 and PortB.4 to digital isn't possible.

photoelectric
- 29th April 2011, 16:31
Hi,

Tq again,
can i say if i want set AN9-AN13 to digital , i need to set the register value ADCON1 = 6 ?
sry for this newbie question, still trying to learn this PIC things etc..
correct me if im wrong

thanks

HenrikOlsson
- 29th April 2011, 16:51
Hi,

That is correct.

Since you're leaving some of the pins as analog inputs I'm guessing you're going to use them as that - ie analog inputs. In that case it might be needed to set the bits 4-7 in ADCON1 properly as well. Sometimes it's easier to "see" what you're doing if you write the value in binary form, like:

ADCON1 = %10000110 'Right justified result, VDD/VSS used as Vref, AN9-13 set to digital
/Henrik.

photoelectric
- 29th April 2011, 17:07
Hi,

tq for explaining.
Yes, actually i want to try adding 2 types of circuit protection system into my previous codes.

type 1 is basically come from outside signal called fault signal. when this fault signal is 5v, the PWM will operate in normal condition, however when fault signal is 0v PWM will stop operation (i thinking duty=0 instead of stopping the whole operation of PIC)

type 2 also from outside signal but from an op-amp. this op amp will compare the value of voltage. maximum output voltage from this opamp, i set to 3V. So when exceed 3V, the PIC will stop operation.

can you help assist what should i consider to design this kind of circuit protection in term of commands used, what should be define at PIC etc..?

thanks
photoelectric

HenrikOlsson
- 29th April 2011, 17:48
Hi,
I'd probably handle the discrete fault signal (type 1) thru an external interrupt (PortB.0) as it will give you a fast response time. If you find that tricky and/or the response time to that input isn't very important you can simpl poll an input each time thru the main loop and stop whatever needs stopping.

As for the overcurrent (I'm guessing that's what it's for) you could possibly use the onboard comparator (look at the datasheet, section 13). It too can generate an interrupt when its output flips or you can simply poll the output directly or the interrupt flag if you want a "latching" signal.

/Henrik.

photoelectric
- 29th April 2011, 21:42
Hi,

i read the ON INTERRUPT section on PBP book and still trying to understand it.
if i want to implement an interrupt command on my codes, should i declare the interrupt in the main loop?

below is the coding for type 1 :

let say:


ON INTERRUPT GoTo faultsignal
INTCON = %10010000 'Enable RB0 INTERRUPT
OPTION_REG.6 = 0 'Interrupt on rising edge of RB0/INT pin
Disable 'Disable interrupts in handler
faultsignal:
CCP1CON = %00000000 'off ccp1

INTCON.1 = 0 'Clear INTERRUPT flag
Resume 'Return TO main program
Enable 'Enable interrupts after handler

anything wrong with this codes?i need to write it in the main loop?
did really pin rb0 can detect changes from 5v to 0v using ONLY this codes?

HenrikOlsson
- 30th April 2011, 08:54
Hi,
That looks to be correct but I haven't tried it. Obviously you have to write code in the interrupt handler to take care of whatever needs to be taken care of when it sees the signal but yes, interrupts work like that.

You declare the interrupt at the beginning of the program. Then, whenever PortB.0 goes high the execution will jump to the label you specify (Faultsignal in this case). It executes the code there and when it sees the Resume keyword it returns to where it left off.

Don't try to incorporate it in your application directly, play with it a bit first. Make a LED blink continously and have the interrupt toggle another LED so you see how it works, then move on to incorporate it in your application.

With that said ON INTERRUPT isn't the most effective of handling interrupts but I won't go into that right now. There are TONS of info on interrupts on this forum, do a little searching and reading.

photoelectric
- 1st May 2011, 09:35
hi,

OK i will play it using LED first. The port B0 that i declare says that when the fault signal detect 5V is the normal operation, when suddenly changes to 0v, port B0 automatically goes to my interrupt handler to do whatever need to be done.
So, my question is:

1. did portb0 can detect directly 5v without adding extra codes that define the values of voltages?
2. if yes, can i use pull-up resistor with push-up button to create a changes signal to the portb0 and see the interrupt works or not?
3. if not, what others method to create a changes signal to the portb0?

thanks,
photoelectric

HenrikOlsson
- 1st May 2011, 10:15
Hi,
The interrupt can be triggered on either the rising or falling edge of the signal connected to PortB.0 (ie the interrupt fires when the signal changes state). You select rising/falling edge by setting or clearing OPTION_REG.6

If, during normal operation, the signal connected to PortB.0 is high and it goes low when there's a problem you want to set it up so it interrupts on the falling edge. (OPTION_REG.6 = 0)

Yes, to test it, pull up PortB.0 with a resistor and use a switch/button/whatever to pull it low and trig the interrupt. Note that you are likely to get some contact bounce with a mecanical switch/button which may trig the interrupt several time for each press of the button.

photoelectric
- 1st May 2011, 12:51
hi,
i try to test my simple interrupt on my real PIC simulation.
below is my codes:


led VAR PORTC.4

TRISB.0 = 1 'set port bo to input
ON INTERRUPT GoTo myint ' Define interrupt handler
INTCON = %10010000 'Enable RB0 INTERRUPT
OPTION_REG.6 = 0 'Interrupt on rising edge of RB0/INT pin

loop: High led ' Turn LED on
GoTo loop ' Do it forever


' Interrupt handler
Disable ' No interrupts past this point
myint:
Low led ' If we get here, turn LED off
'Pause 500 ' Pause .5 seconds
INTCON.1 = 0 ' Clear interrupt flag
Resume ' Return to main program
Enable

End

turns out when i enable Pause my interrupt works ok but i turns off pause it wont interrupt at all.
Please help explain what cause the problem.

HenrikOlsson
- 1st May 2011, 12:59
Hi,
I bet it works just fine. It's just that when you don't have the Pause there you don't see it becase the main routine keeps turning ON the LED all the time.

The interrupt routine turns it OFF and a couple of microseconds later the main routine turns it ON again.

photoelectric
- 1st May 2011, 13:47
Hi,

OIC. tq
So, i notice that the led will turn off whenever the faultsignal is change BUT after a few milisec it turns ON back.
The problem on my design is that i dont know how much time the fault signal will be interrupted before it back to normal operation

What should i do if i want the led to turn off until the fault signal back to normal operation which 5V again?

any example for me?

photoelectric

HenrikOlsson
- 1st May 2011, 14:21
Like I said, it turns ON again because YOU turn it on in your main loop - continously. If you don't want it to turn on again then stop turning it on. Set it one time, before you enter the loop.

If you want it to trig (turn off the LED) with an interrupt but then start "working" again as soon as the fault signal "goes away" then simply read the state of the pin in your Main routine and turn on the LED once it's high:

Main:
If PortB.0 = 1 THEN 'Turn on LED only when signal is high.
HIGH LED
ENDIF
'Do whatever else needs to be done.
Goto Main
If want it to trig with an interrupt and stay off untill another input "restart it" then use a flag/semaphore:

FAULT VAR BIT
LED VAR PortC.4
Restart VAR PortC.5 'Pulled up thru resistor, pull to GND to "activate"

High LED

Main:
If Fault = 1 then 'If we are in fault state...
If Restart = 0 then '...we check the restart button....
HIGH LED '....if it's pressed we turn LED on again...
Fault = 0 '...and reset the fault flag/state
ENDIF
ENDIF
'Do whatever else here.
Goto Main
Then, in your interrupt routine you turn off the LED and set the Fault flag, like:

LOW LED
Fault = 1 'Set flag

Finally, be careful with the word Loop as a label, in newer versions of PBP that is a reserved word and can't be used as a label, what version are you using.

photoelectric
- 1st May 2011, 16:04
Hi,

thank you for the example.
Then, i add-up CCP1CON = %00000000 in the interrupt handler in order to turn off the pwm for a while. Turns out that after it come back to normal operation, the pwm keep turning off.


DEFINE OSC 20
Phase VAR BYTE[3] ' Array used as pointers into lookuptable.
DutyCycle VAR WORD[3] ' Array storing the dutycycles retreived from the table
Temp VAR WORD ' Temporary variable to avoid hard to understand array indexing
i VAR BYTE ' General purpose counter
led VAR PORTC.4
TRISC.2 = 0 ' Set PORTC.2 (CCP1) to output
TRISC.1 = 0

CCP1CON = %00001100 ' Set CCP1 to PWM
CCP2CON = %00001100

T2CON = %00000101 ' Turn on Timer2, Prescale=4
PR2 = 249 ' Set PR2 to get 5KHz out
TRISB.0 = 1 'set port bo to input

' The lookup table has 45 entries long, to get 120° phase shift we need to
' "start" the second phase at 1/3 of the cycle and the third phase at 2/3
' of the table. 45/3=15 so first phase starts at 0, second phase at 15
' and third phase at 30.
' Initilise pointers.
Phase[0] = 0 : Phase[1] = 15


High PORTC.3 'Set the initial state for portc.3
Low PORTC.0 'Same
High PORTC.6 'Same
Low PORTC.5 'Same
High PORTC.4

ON INTERRUPT GoTo faultsignal
INTCON = %10010000 'Enable RB0 INTERRUPT
OPTION_REG.6 = 0 'Interrupt on rising edge of RB0/INT pin


Main:

IF PORTB.0 = 1 Then 'Turn on LED only when signal is high.
Low LED
GoSub GetDuty ' Retrieve the dutycycle values for all three phases
GoSub SetDutyCycle ' Set the three PWM modules accordingly
EndIF


' Now increment the individual table pointers and make sure they wrap
' around to zero when they reach the end of the table. That way they
' will always stay 62 "steps" (120°) from each other.


For i = 0 TO 1
Phase[i] = Phase[i] + 1
IF Phase[i] > 44 Then
Phase[i] = 0 'When pointer is > 44 we need to wrap around to 0.
GoSub ChangeBridgeDrive 'One phase is starting over, go change the outputs.

EndIF
Next

PauseUs 200


GoTo Main


SetDutyCycle:
' Get value from the array of dutycycles and put it in the dutycycle
' registers of the 3 PWM modules. We could have used the array directly
' but this way (using a Temp variable) is easier to understand.

Temp = DutyCycle[0] ' Get dutycyle for phase 1 from the array and store in temp.
CCP1CON.4 = Temp.0 ' Set the LSB's
CCP1CON.5 = Temp.1
CCPR1L = Temp >> 2 ' Set the 8 high bits

Temp = DutyCycle[1] ' Same procedure.
CCP2CON.4 = Temp.0
CCP2CON.5 = Temp.1
CCPR2L = Temp >> 2

Return


' ------------------------------------------------------------------------------
' ---- Subroutine to retreive the three dutycycle values from the table.
' ---- Values will be stored in the DutyCycle array.
' ------------------------------------------------------------------------------
GetDuty:
' This For-Next loop runs three times. Each time it gets the value from the lookuptable
' that Phase[i] is pointing at. The Phase array pointers are incremented in the main loop
' and are always 15 "steps" appart so that a 120° phase shift is preserved.

For i = 0 TO 1
LookUp2 Phase[i], [0,70,137,208,275,341,408,470,529,588,643,694,745,7 88,827,866,898,925,_
953,968,984,996,1000,996,984,968,953,925,898,866,8 27,788,745,_
694,643,588,529,470,408,341,275,208,137,70,0],Temp


' Lookup2 can't handle an array as the designator so we need to
' put the value in a temporary variable first and then move
' it to the array of dutycycles.

DutyCycle[i] = Temp

Next

Return

ChangeBridgeDrive:

' When we come here the value i contains the phase counter that just got reset so we
' can use that to determine for which phase we should switch the outputs.

Select Case i
Case 0 ' It was Phase 1 that rolled over
Toggle PORTC.3 ' Invert the state of the pin
Toggle PORTC.0 ' Invert the state of the pin


Case 1 ' It was Phase 2 that rolled over
Toggle PORTC.5 ' Invert the state of the pin
Toggle PORTC.6 ' Invert the state of the pin


End Select

Return

Disable 'Disable interrupts in handler

faultsignal:
High led
CCP1CON = %00000000
INTCON.1 = 0 'Clear INTERRUPT flag

Resume 'Return TO main program
Enable 'Enable interrupts after handler


End

seem like it wont go back to main program.

HenrikOlsson
- 1st May 2011, 16:56
Hi,
I'm starting to get frustrated here.... The PIC won't do anything you don't tell it to. If you shut down the PWM it will stay off until you turn it on again. Obviously you must turn it back on again when you want it to turn back on.

Right now you retrive the dutycycle values and set the dutycycle registers but the PWM is left off because you don't turn it on.

photoelectric
- 16th May 2011, 13:49
hi,

long time no see :D

after many times checking on my pwm using osiloskope, i found out that the pattern of the pwm isnt fixed according to the lookup table value while the PIC is running.
this definitely affecting my inverter output and produce too many harmonics..
as you can see for the attached file:

both screen shot should have same length and pattern of pwm but when the PIC is running, the PWM seem keep expanding and de-expanding repeatedly thus changing the pattern.

sumting wrong with the codes or my osiloskope resolution??i dont think so



ClearWDT
DEFINE OSC 20
Phase VAR BYTE[3] ' Array used as pointers into lookuptable.
DutyCycle VAR WORD[3] ' Array storing the dutycycles retreived from the table
Temp VAR WORD ' Temporary variable to avoid hard to understand array indexing
i VAR BYTE ' General purpose counter
TRISC.2 = 0 ' Set PORTC.2 (CCP1) to output
TRISC.1 = 0 ' Set PORTC.1 (CCP2) to output
TRISB.5 = 0 ' Set PORTB.5 (CCP3) to output
CCP1CON = %00001100 ' Set CCP1 to PWM
CCP2CON = %00001100 ' Set CCP2 to PWM
CCP3CON = %00001100 ' Set CCP3 to PWM
T2CON = %00000101 ' Turn on Timer2, Prescale=4
PR2 = 249 ' Set PR2 to get 5KHz out
ADCON1 = 7 ' Set All PortB to Digital port


' The lookup table has 50 entries long, to get 120° phase shift we need to
' "start" the second phase at 1/3 of the cycle and the third phase at 2/3
' of the table. 50/3=15 so first phase starts at 0, second phase at 15
' and third phase at 30.
' Initilise pointers.

Phase[0] = 0 : Phase[1] = 17 : Phase[2] = 34


High PORTC.3 'Set the initial state for portc.3
Low PORTC.0 'Same
High PORTC.6 'Same
Low PORTC.5 'Same
High PORTB.4 'Same
Low PORTB.3 'Same

Main:

if portb.2=1 then
GoSub GetDuty ' Retrieve the dutycycle values for all three phases
GoSub SetDutyCycle ' Set the three PWM modules accordingly


' Now increment the individual table pointers and make sure they wrap
' around to zero when they reach the end of the table. That way they
' will always stay 17 "steps" (120°) from each other.


For i = 0 TO 2
Phase[i] = Phase[i] + 1
IF Phase[i] > 50 Then
Phase[i] = 0 'When pointer is > 50 we need to wrap around to 0.
GoSub ChangeBridgeDrive 'One phase is starting over, go change the outputs.
EndIF
Next

PauseUs 77 'Use each duty cycle for 85us before going to another
endif

if portb.2=0 then
gosub GetDutyWhenFault
endif
GoTo Main


SetDutyCycle:
' Get value from the array of dutycycles and put it in the dutycycle
' registers of the 3 PWM modules. We could have used the array directly
' but this way (using a Temp variable) is easier to understand.

Temp = DutyCycle[0] ' Get dutycyle for phase 1 from the array and store in temp.
CCP1CON.4 = Temp.0 ' Set the LSB's
CCP1CON.5 = Temp.1
CCPR1L = Temp >> 2 ' Set the 8 high bits

Temp = DutyCycle[1] ' Same procedure.
CCP2CON.4 = Temp.0
CCP2CON.5 = Temp.1
CCPR2L = Temp >> 2

Temp = DutyCycle[2] ' Same procedure.
CCP3CON.4 = Temp.0
CCP3CON.5 = Temp.1
CCPR3L = Temp >> 2
Return

GetDutyWhenFault:

Temp = 0 ' Set dutycyle for phase 1 to 0%.
CCP1CON.4 = Temp.0 ' Set the LSB's
CCP1CON.5 = Temp.1
CCPR1L = Temp >> 2 ' Set the 8 high bits

Temp = 0 ' Same procedure for phase 2
CCP2CON.4 = Temp.0
CCP2CON.5 = Temp.1
CCPR2L = Temp >> 2


Temp = 0 ' Same procedure for phase 3
CCP3CON.4 = Temp.0
CCP3CON.5 = Temp.1
CCPR3L = Temp >> 2
return

' -----------------------
'-------------------------------------------------------
' ---- Subroutine to retreive the three dutycycle values from the table.
' ---- Values will be stored in the DutyCycle array.
' ------------------------------------------------------------------------------
GetDuty:
' This For-Next loop runs three times. Each time it gets the value from the lookuptable
' that Phase[i] is pointing at. The Phase array pointers are incremented in the main loop
' and are always 15 "steps" appart so that a 120° phase shift is preserved.

For i = 0 TO 2
LookUp2 Phase[i], [0,60,130,190,250,310,370,430,480,540,590,640,690,7 30,770,810,840,870,_
910,930,950,970,980,990,1000,1000,1000,990,980,970 ,950,930,910,870,840,810,770,730,690,_
640,590,540,480,430,370,310,250,190,130,60,0],Temp


' Lookup2 can't handle an array as the designator so we need to
' put the value in a temporary variable first and then move
' it to the array of dutycycles.

DutyCycle[i] = Temp

Next

Return

ChangeBridgeDrive:

' When we come here the value i contains the phase counter that just got reset so we
' can use that to determine for which phase we should switch the outputs.

Select Case i
Case 0 ' It was Phase 1 that rolled over
Toggle PORTC.3 ' Invert the state of the pin
Toggle PORTC.0 ' Invert the state of the pin


Case 1 ' It was Phase 2 that rolled over
Toggle PORTC.5 ' Invert the state of the pin
Toggle PORTC.6 ' Invert the state of the pin


Case 2 ' It was Phase 3 that rolled over
Toggle PORTB.4 ' Invert the state of the pin
Toggle PORTB.3 ' Invert the state of the pin


End Select

Return


End


thanks