PIC 18 Phase Angle Dimmer. Stuck with my code - Page 1
Forum section: electronics / general

author: kolbep
author_rank: Frequent Contributor
timestamp: November 29, 2014, 03:32:08 pm
content: HiI am trying to build a DMX Dimmer.But I am having trouble with my code.I have stripped out all the DMX sets, so now I just have 1 Channel, that I am trying to Phase Angle control (ac - triac controlled)My Zero Cross is coming in on RB0/INT pin.But as it stands, it seems to not be seeing that the counter has reached zero, to turn the triac on.My code structure is (Sort of) as follows :Initialise Timer and Interrupts.Wait in a loop.If RB0/Int triggers (which is at 100hz due to the zener input),then it resets the channel counter to a predetermined value (ie 128)and it preloads the timerEach time the timer generates an interrupt,it preloads the timerthen it decrements the channel counterif the channel counter==0, then it must turn the triac onAnybody feeling like giving it a quick once-over to try help me figure it out.Thanks.The Pic is a PIC18f26K20I used the MikroElectronika Timer calculator as :Code://Timer0//Prescaler 1:1; TMR0 Preload = 57724; Actual Interrupt Time : 1.953 msand here is All of my code (as i say, I removed all the DMX stuff)Code:/********************************************************************* Function:Basic Phase Angle Dimmer** Timer0 Prescaler 1:1; TMR0 Preload = 61630; Actual Interrupt Time : 976.5 us* Thanks to MikroElektronika Timer Calculator*******************************************************************//** C O N F I G U R A T I O N B I T S ******************************/#pragma config FOSC = INTIO7, FCMEN = OFF, IESO = OFF // CONFIG1H#pragma config PWRT = OFF, BOREN = OFF, BORV = 18 // CONFIG2L#pragma config WDTEN = OFF, WDTPS = 32768 // CONFIG2H#pragma config MCLRE = ON, LPT1OSC = OFF, PBADEN = OFF, CCP2MX = PORTC// CONFIG3H#pragma config STVREN = ON, LVP = OFF, XINST = OFF // CONFIG4L#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF // CONFIG5L#pragma config CPB = OFF, CPD = OFF // CONFIG5H#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF // CONFIG6L#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF // CONFIG6H#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF // CONFIG7L#pragma config EBTRB = OFF // CONFIG7H/** I N C L U D E S **************************************************/#include "p18f26k20.h"// header files#include "timers.h"/** PROTOTYPES *****************************************************/void setup_timer0(void); // setup timer0 and interrupt/* declare statically allocated uinitialized variables *********/#pragma udata#pragma udata gpr1unsigned int outcntr0; //Counters for the Phase Angle Firingunsigned int outcntr1; //Counters for the Phase Angle Firing/* Prototype for Interrupts*/void high_interrupt (void); /* prototype needed for 'goto' below */#pragma code HIGH_INTERRUPT_VECTOR = 0x8void high_ISR (void){_asmgoto high_interrupt_endasm}/* Declare executable instructions ****************************/#pragma codevoid high_interrupt (void){if (INTCONbits.T0IF){ //Timer InterruptLATAbits.LATA1=1; // TURN ON LED TO SHOW ENTERING T0 INTERRUPTLATAbits.LATA1=0;TMR0H= 0xE1; //PRIME TIMERTMR0L= 0x7C;INTCONbits.T0IF = 0;outcntr0--;// if (LATCbits.LATC0==0){ // IF TRIAC OFFif (outcntr0==0){ // AND IF COUNTER DONELATCbits.LATC0=1; // TURN TRIAC ON};// };};if (INTCONbits.INT0IF) { //ZC InterruptTMR0H= 0xE1; //PRIME TIMERTMR0L= 0x7C;LATAbits.LATA2=1; // TURN ON LED TO SHOW ENTERING ZC INTERRUPTLATAbits.LATA2=0;outcntr0=128; //PRIME OUTCOUNTERLATC=0; //TURN OFF TRIACSINTCONbits.INT0IF = 0; //CLEAR FLAG TO ALLOW EXIT ISR};INTCONbits.GIEH=1;}void main (void){// set internal oscillator to 16MHzOSCCON = 0x70;// IRCFx = 111OSCTUNEbits.PLLEN = 0;// x4 PLL disabledTRISA = 0x01;TRISB = 0x00;TRISC= 0x00;LATA= 0x00;LATB= 0x00;LATC= 0x00;setup_timer0();INTCON2bits.INTEDG0=1; //INTERRUPT ON RISING EDGEINTCONbits.INT0IE = 1; //ENABLE RB0 INTERRUPTSLATC=0;while(1){// LATA=0; //RESET LED INDICATORS};}void setup_timer0(void){T0CON= 0x88;TMR0H= 0xE1;TMR0L= 0x7C;INTCONbits.GIEH =1;INTCONbits.T0IE =1;}

author: dannyf
author_rank: Super Contributor
timestamp: November 29, 2014, 05:49:42 pm
content: The code works perfectly,Seriously, if you don't tell people what you are expecting and what you are getting, how's anyone going to figure out what's wrong?

author: kolbep
author_rank: Frequent Contributor
timestamp: November 29, 2014, 06:09:46 pm
content: Ok, let me give some more info :I have an opto-triac, triac and lightbulb on RC0As I am priming outcntr0 with 128,I expect the light output to be 1/4 to 1/2Instead the light never turns on (at all).If I play around with some of the code (ie if I make the outcntr0==0 line intooutcntr0<=100) then the light does turn on.So my Input is working.My LED's on RA1 and RA2 are glowing (very dimly), which shows me that it is getting into (and leaving) the interrupt routines.So, I think maybe I am confused with the timings (or something), so that the outcntr0 is actually getting reset (to 128) by the ZeroCross routine, before it actually manages to get to zero (when it is supposed to fire the triac)What is supposed to happen is :On Zero Cross (Which is happening at 100hz, or twice the line frequency), The timer gets reloaded, and the triac is turned off.The outcntr0 is then primed with a number (between 0 and 255).Then, each time the Timer0 interrupts (which is supposed to be 512 times per complete cycle???)it reloads itselfand decrements outcntr0if outcntr0 is zero, then it turns on the triac.The triac then remains on until the next zero cross, when it gets shut off.

author: kolbep
author_rank: Frequent Contributor
timestamp: November 29, 2014, 06:19:54 pm
content: Having to type that out made me think of something.I was setting my timer at 512hz interrupt,but I think it is supposed to be 512*100hz or 512*50hzI am playing with the timings now, and at least the light is on,so let me play some more

author: dannyf
author_rank: Super Contributor
timestamp: November 29, 2014, 06:25:11 pm
content: QuoteThat's to be expected, since you are resetting outcntr0 to 128 every time there is an int0 interrupt (10ms). outcntr0 is reduced every 1.9ms. So outcntr0 will be about 128 - 5 before it is reloaded to 128 -> it will never reach zero, thus your triac will never fire.You do not have a sound logic as to how the circuit should behave.

author: kolbep
author_rank: Frequent Contributor
timestamp: November 29, 2014, 08:02:08 pm
content: Ok, I have modified my code now,and am getting full range.If i set outcntr0preload=1, then I have Full brightness (the PIC never switches the triac off)If i set outcntr0preload=255, then I have the PIC completely off.Any value between the 2 will give me brigtness level (depending on the value)of course I still have to adjust the ranges at the beginning and end of the scale just to make it more linear.The timerpreload, how I figured that out is, I just changed that value, and programmed, until it worked right.My next step will probably also be a routine that counts the timeticks between zero crosses, and works out what the timerpreload should be, and automatically update it every cycle. (But that will be for another day.BTW, is it normal for me to see flicker on a halogen bulb, if I have it running at 50uS on 20000uS off (Or a Mark:Space of 5:2000)?Also, I do not think my circuit logic was at fault, just my maths was messed up, trying to convert from Frequency to Wavelength to Half Wavelength, to 256 points per half wavelength (512 points per wavelength),,, and basing this all on the PICS frequency which I don't know how much it could be drifting by.....My main reason for wanting to do this, is to dim stage lights (for Video and Theatrical) using a DMX512 system, but not having to pay the 150usd Each for a Chauvet 4 Channel Dimmer (of which I will need 4 off)This probably will (in the end) work out being more expensive time wise (than price wise of buying dimmers), but it is a big learning curve, and I get to customise things exactly the way I want [starting to think about using dmx to control the venues HVAC Aircons, Extractors, Smoke Machines etc, and everything that I can].Code:/********************************************************************* Function:Basic Phase Angle Dimmer** Timer0 Prescaler 1:1; TMR0 Preload = 61630; Actual Interrupt Time : 976.5 us* Thanks to MikroElektronika Timer Calculator*******************************************************************//** C O N F I G U R A T I O N B I T S ******************************/#pragma config FOSC = INTIO7, FCMEN = OFF, IESO = OFF // CONFIG1H#pragma config PWRT = OFF, BOREN = OFF, BORV = 18 // CONFIG2L#pragma config WDTEN = OFF, WDTPS = 32768 // CONFIG2H#pragma config MCLRE = ON, LPT1OSC = OFF, PBADEN = OFF, CCP2MX = PORTC// CONFIG3H#pragma config STVREN = ON, LVP = OFF, XINST = OFF // CONFIG4L#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF // CONFIG5L#pragma config CPB = OFF, CPD = OFF // CONFIG5H#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF // CONFIG6L#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF // CONFIG6H#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF // CONFIG7L#pragma config EBTRB = OFF // CONFIG7H/** I N C L U D E S **************************************************/#include "p18f26k20.h"// header files#include "timers.h"/** PROTOTYPES *****************************************************/void setup_timer0(void); // setup timer0 and interrupt/* declare statically allocated uinitialized variables *********/#pragma udata#pragma udata gpr1unsigned int outcntr0; //Counters for the Phase Angle Firingunsigned int outcntr1; //Counters for the Phase Angle Firingunsigned int timerpreload=65257;unsigned int outcntr0preload=2;/* Prototype for Interrupts*/void high_interrupt (void); /* prototype needed for 'goto' below */#pragma code HIGH_INTERRUPT_VECTOR = 0x8void high_ISR (void){_asmgoto high_interrupt_endasm}/* Declare executable instructions ****************************/#pragma codevoid high_interrupt (void){if (INTCONbits.T0IF){ //Timer InterruptWriteTimer0( timerpreload );LATAbits.LATA1=1; // TURN ON LED TO SHOW ENTERING T0 INTERRUPT// LATAbits.LATA1=0;INTCONbits.T0IF = 0;outcntr0--;// if (LATCbits.LATC0==0){ // IF TRIAC OFFif (outcntr0==0){ // AND IF COUNTER DONELATCbits.LATC0=1; // TURN TRIAC ON};// };};if (INTCONbits.INT0IF) { //ZC InterruptWriteTimer0( timerpreload );LATAbits.LATA2=1; // TURN ON LED TO SHOW ENTERING ZC INTERRUPT// LATAbits.LATA2=0;outcntr0=outcntr0preload; //PRIME OUTCOUNTERLATC=0; //TURN OFF TRIACSINTCONbits.INT0IF = 0; //CLEAR FLAG TO ALLOW EXIT ISR};INTCONbits.GIEH=1;}void main (void){// set internal oscillator to 16MHzOSCCON = 0x70;// IRCFx = 111OSCTUNEbits.PLLEN = 0;// x4 PLL disabledTRISA = 0x01;TRISB = 0x01;TRISC= 0x00;LATA= 0x00;LATB= 0x00;LATC= 0x00;setup_timer0();INTCON2bits.INTEDG0=1; //INTERRUPTINTCONbits.INT0IE = 1; //ENABLE RB0 INTERRUPTSwhile(1){};}void setup_timer0(void){T0CON= 0x88;WriteTimer0( timerpreload );INTCONbits.GIEH =1;INTCONbits.T0IE =1;}

author: IanB
author_rank: Super Contributor
timestamp: November 29, 2014, 08:11:25 pm
content: Quote from: kolbep on November 29, 2014, 08:02:08 pmIf it is switched on at 20 ms intervals it would be flashing 50 times a second. Yes, the human eye can see flickering at that rate, especially in the peripheral vision.But I'm confused. If you are creating an AC dimmer at 50 Hz there are 100 half-cycles per second. So the lamp should be switched every 10 ms, should it not?

author: dannyf
author_rank: Super Contributor
timestamp: November 29, 2014, 09:47:57 pm
content: QuoteYou are dividing 10ms into 256 periods. So the time slices are exactly 10000us / 256 = 39us. At 16Mhz, that means a period of 39x4=156 ticks.So you load up TMR0 to -156, or 65380 (0xff64) in 2's compliments. Actually you can simply use the negative value and the compiler will do the rest for you.Your approach is very processing intensive - every 39us the processor wastes 10+ ticks on the interrupt latency alone. I would say that 3/4 or more of your mcu's MIPS has gone into this task.The other issue is that because of the latency, you may not be able to completely shut off the light. It may not be a huge issue for a light source with large time constants; but for sources like LEDs, you will be visible.You should look into other approaches, like pwm or output compare -> using the INT to reset the time base periodically. So other than processing the 100hz interrupts, the mcu is free.If you insist on going down this path, at least use a timer with auto reload.

author: kolbep
author_rank: Frequent Contributor
timestamp: November 30, 2014, 05:02:53 pm
content: I have neatened up the code a bit (using some things I borrowed out of other peoples code),But no matter what I try, It misses every second half of the phase.From my Bridge rectifier on the transformer, I go through a 3v3 Zener limiter, and then through another diode, just to drop it below my +ve voltage (around 3v).So I am getting an m waveform which is going to zero every 10ms. But I have scoped the Zero Cross signal, as well as the pin that fires the triac, and I get it looking like the picture below (this was tested 3 times, with 128 as the outcntr0preload, then also 254 and 1. The bottom waveform is the zero cross waveform coming into the pic.Sometimes, when I switch the 230vac power off that is going to the Triac and Opto (MOC3021), then the signal out of the PIC pin changes (sometimes showing a trigger every halfcycle, sometimes it . Should that happen? I thought the Opto would have prevented something like this.Code:/********************************************************************* Function:Basic Phase Angle Dimmer********************************************************************//** C O N F I G U R A T I O N B I T S ******************************/#pragma config FOSC = INTIO7, FCMEN = OFF, IESO = OFF // CONFIG1H#pragma config PWRT = OFF, BOREN = OFF, BORV = 18 // CONFIG2L#pragma config WDTEN = OFF, WDTPS = 32768 // CONFIG2H#pragma config MCLRE = ON, LPT1OSC = OFF, PBADEN = OFF, CCP2MX = PORTC// CONFIG3H#pragma config STVREN = ON, LVP = OFF, XINST = OFF // CONFIG4L#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF // CONFIG5L#pragma config CPB = OFF, CPD = OFF // CONFIG5H#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF // CONFIG6L#pragma config WRTB = OFF, WRTC = OFF, WRTD = OFF // CONFIG6H#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF // CONFIG7L#pragma config EBTRB = OFF // CONFIG7H/** I N C L U D E S **************************************************/#include "p18f26k20.h"// header files#include "timers.h"#include "portb.h"/** PROTOTYPES *****************************************************/void setup_timer0(void); // setup timer0 and interruptvoid setup_portb(void);/* declare statically allocated uinitialized variables *********/#pragma udata#pragma udata gpr1unsigned int outcntr0; //Counters for the Phase Angle Firingunsigned int outcntr0preload=1 ;unsigned int osc_freq=8000000;unsigned int instruction_rate;unsigned int interrupts_s;unsigned int mains_freq=100;unsigned int resolution=256;unsigned int ticks;unsigned int timer0_count;/* Prototype for Interrupts*/void high_interrupt (void); /* prototype needed for 'goto' below */#pragma code HIGH_INTERRUPT_VECTOR = 0x8void high_ISR (void){_asmgoto high_interrupt_endasm}/* Declare executable instructions ****************************/#pragma codevoid high_interrupt (void){if (INTCONbits.T0IF){ //Timer InterruptWriteTimer0( timer0_count );INTCONbits.T0IF = 0;outcntr0++;if (outcntr0==255){ // AND IF COUNTER DONELATCbits.LATC0=1; // TURN TRIAC ON};};if (INTCONbits.INT0IF) { //ZC InterruptWriteTimer0( timer0_count );outcntr0=outcntr0preload; //PRIME OUTCOUNTERLATCbits.LATC0=0; //TURN OFF TRIACSINTCONbits.INT0IF = 0; //CLEAR FLAG TO ALLOW EXIT ISR};INTCONbits.GIEH=1;}void main (void){// set internal oscillatorOSCCON = 0x70;// IRCFx = 111OSCTUNEbits.PLLEN = 0;// x4 PLL disabledinstruction_rate = osc_freq / 4;interrupts_s = mains_freq*resolution;ticks = instruction_rate/interrupts_s;timer0_count = 256-ticks;TRISA = 0x01;TRISB = 0x01;TRISC= 0x00;LATA= 0x00;LATB= 0x00;LATC= 0x00;setup_timer0();//setup_portb();INTCONbits.GIEH =1;while(1){}; //DO NUFFING}void setup_portb(void) {OpenRB0INT( PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_OFF );}void setup_timer0(void){OpenTimer0 (TIMER_INT_ON & T0_8BIT & T0_SOURCE_INT);WriteTimer0( timer0_count );}