Re: 3 channel PWM with customize duty cycle
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
Re: 3 channel PWM with customize duty cycle
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.
Re: 3 channel PWM with customize duty cycle
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
Re: 3 channel PWM with customize duty cycle
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:
Code:
ADCON1 = %10000110 'Right justified result, VDD/VSS used as Vref, AN9-13 set to digital
/Henrik.
Re: 3 channel PWM with customize duty cycle
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
Re: 3 channel PWM with customize duty cycle
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.
Re: 3 channel PWM with customize duty cycle
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:
Code:
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?
Re: 3 channel PWM with customize duty cycle
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.
Re: 3 channel PWM with customize duty cycle
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
Re: 3 channel PWM with customize duty cycle
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.
Re: 3 channel PWM with customize duty cycle
hi,
i try to test my simple interrupt on my real PIC simulation.
below is my codes:
Code:
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.
Re: 3 channel PWM with customize duty cycle
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.
Re: 3 channel PWM with customize duty cycle
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
Re: 3 channel PWM with customize duty cycle
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:
Code:
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:
Code:
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:
Code:
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.
Re: 3 channel PWM with customize duty cycle
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.
Code:
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,788,827,866,898,925,_
953,968,984,996,1000,996,984,968,953,925,898,866,827,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.
Re: 3 channel PWM with customize duty cycle
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.
2 Attachment(s)
Re: 3 channel PWM with customize duty cycle
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
Code:
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,730,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