Hello i want to drive a RGB led with three PWM pulses, i use 3 potentiometers to control the PWM duty cycle, timer0 is used to trigger an A2D conversion and when the interrupt arrives i save the result into a buffer and configure the next channel for a conversion. I write the following code and the led is blinking... there is an error but i am unable to find it.
unsigned char t0_reload_value = 0x05; unsigned char d2a_output = 0xff; typedef enum SCH {ch0 = 0x00,ch1,ch2} A2DCHANNEL; A2DCHANNEL channel = ch0; data volatile unsigned char a2d[3]; void init_pca(void) { CMOD = 0x00; //cps1=0,cps0=0 (fosc/12),ecf=0 CH = 0x00; //clear PCA timer CH_CL CL = 0x00; CCAPM0 = 0x42; //mode 8bit PWM CCAPM1 = 0x42; //mode 8bit PWM CCAPM2 = 0x42; //mode 8bit PWM CCAP0H = d2a_output; //0.4% PWM Duty Cycle=(256-ccap2h)/256 CCAP1H = d2a_output; //0.4% PWM Duty Cycle=(256-ccap2h)/256 CCAP2H = d2a_output; //0.4% PWM Duty Cycle=(256-ccap2h)/256 CR = 1; // Turn PCA timer on } void init_timer0(void) { TMOD &= 0xF0; /* Timer 0 mode 2 with software gate */ TMOD |= 0x02; /* GATE0=0; C/T0#=0; M10=1; M00=0; */ TL0 = t0_reload_value; /* init values */ TH0 = t0_reload_value; /* reload value */ ET0=1; /* enable timer0 interrupt */ EA=1; /* enable interrupts */ TR0=1; /* timer0 run */ } void init_adc() { unsigned char i; //Configure P1[0.2] as analog channels ADCF = 0x07; /* Configure ADC clock Fadc=Fclk/4*PRS, PRS=5 Fclk=12Mhz then Fadc=600Khz and tconv=11/600=18.33usec */ ADCLK = 0x05; ADCON = MSK_ADCON_ADEN; //ADC enable (bit ADEN ) ADCON &= ~MSK_ADCON_SCH; //Clear the channel field adcon[2:0] ADCON |= channel; //Select channel0 to convert ADCON &= ~MSK_ADCON_PSIDLE; //Standard mode (Disables PSIDLE) EADC = 1; //Enable adc interrupt EA = 1; //Enable interrupts IPH1 = 0x00; IPL1 = 0x02; //Set interrupt level priority at state 1 ADCON &= ~MSK_ADCON_ADEOC; //Clear the End of conversion flag for(i=0;i<3;++i) a2d[i] = 0x00; //clean array } void main() { unsigned char r,g,b; init_pca(); init_adc(); init_timer0(); while(1) { r = a2d[ch0]; //get a copy of most recent value g = a2d[ch1]; //get a copy of most recent value b = a2d[ch2]; //get a copy of most recent value CCAP0H = r; CCAP1H = g; CCAP2H = b; } } void irq_t0_overflow(void) interrupt 1 using 1 { ADCON |= MSK_ADCON_ADSST; //start A2D conversion } void irq_a2d() interrupt 8 using 2 { a2d[channel]= ADDH; ++channel; if(channel == 0x03) channel = 0x00; ADCON &= ~MSK_ADCON_SCH; //clear the channel field ADCON[2:0] ADCON |= channel; //Select next channel for convertion ADCON &= ~MSK_ADCON_ADEOC; //clear the End of conversion flag }
Any ideas? Thanks
" there is an error but i am unable to find it."
You didn't think it would be an idea to try to elaborate exactly why you think there is an error?
What does happen? What did you expect should happen? What have you done to try to figure out what is wrong?
C B,
I am assuming that your 'problem' is that it is blinking and not smoothly changing values as you vary the adc values.
#1 Dump the 'using' directive if possible: bank switching is not needed in this example as it isn't time-critical. Be careful when using that directive otherwise.
#2 Assuming that your SFR configurations are correct ('init_XXX'), does the timer isr reset the timer 0 interrupt flag? (I can't remember if it does this automatically or not)
#3 Are your ADC values compatible with the capture/PWM values (signed values)? Are they properly scaled?
#4 I don't recall if 'interrupt 1' versus 'interrupt 0' is correct for timer 0 or not. Just double-check those.
#5 Per's questions are valid. You should explain them so we can help.
--Cpt. Vince Foster 2nd Cannon Place Fort Marcy Park, VA
I think the problem is my buffer communication between A2D_ISR and main(), somehow i do the job wrong. With the following even simpler code (when an A2D conversion ends i start a new one) the led seems to work fine(color is stable). But i update the PCA registers inside the A2D interrupt and timer0 is not used.
unsigned char d2a_output = 0xff; typedef enum SCH {ch0 = 0x00,ch1,ch2} A2DCHANNEL; A2DCHANNEL channel = ch0; data volatile unsigned char a2d[3]; void init_pca(void) { CMOD = 0x00; //cps1=0,cps0=0 (fosc/12),ecf=0 CH = 0x00; //clear PCA timer CH_CL CL = 0x00; //module 1-2 (P1.7) CCAPM0 = 0x42; //mode 8bit PWM CCAPM1 = 0x42; //mode 8bit PWM CCAPM2 = 0x42; //mode 8bit PWM CCAP0H = d2a_output; //0.4% PWM Duty Cycle=(256-ccap2h)/256 CCAP1H = d2a_output; //0.4% PWM Duty Cycle=(256-ccap2h)/256 CCAP2H = d2a_output; //0.4% PWM Duty Cycle=(256-ccap2h)/256 CR = 1; // Turn PCA timer on } void init_adc() { unsigned char i; //Configure P1[0.2] as analog channels ADCF = 0x07; /* Configure ADC clock Fadc=Fclk/4*PRS, PRS=5 Fclk=12Mhz then Fadc=600Khz and tconv=11/600=18.33usec */ ADCLK = 0x01; //<-0x00 ADCON = MSK_ADCON_ADEN; //ADC enable (bit ADEN ) ADCON &= ~MSK_ADCON_SCH; //Clear the channel field adcon[2:0] ADCON |= channel; //Select channel0 to convert ADCON &= ~MSK_ADCON_PSIDLE; //Standard mode (Disables PSIDLE) EADC = 1; //Enable adc interrupt EA = 1; //Enable interrupts IPH1 = 0x00; IPL1 = 0x02; //Set interrupt level priority at state 1 ADCON &= ~MSK_ADCON_ADEOC; //Clear the End of conversion flag ADCON |= MSK_ADCON_ADSST; //Start 8bit conversion for(i=0;i<3;++i) a2d[i] = 0x00; //clean array } void main() { init_pca(); init_adc(); while(1); } void irq_a2d() interrupt 8 { a2d[channel]= ADDH; switch(channel) { case 0: CCAP0H = a2d[channel]; break; case 1: CCAP1H = a2d[channel]; break; case 2: CCAP2H = a2d[channel]; break; } ++channel; if(channel == 0x03) channel = 0x00; ADCON &= ~MSK_ADCON_SCH; //clear the channel field ADCON[2:0] ADCON |= channel; //Select next channel for convertion ADCON &= ~MSK_ADCON_ADEOC; //clear the End of conversion flag ADCON |= MSK_ADCON_ADSST; //start conversion once again }
thanks once again !!
Cpt. Vince its hard for me to explain it, but i will give a try. When the potentiometers are at a stable position i expect a stable color too. Lets say for a given pot position i have Vpot = Va2dREF = 0xff so this means when i send this value to PCA will generates a stable 0.4% PWM. When i test the code on my breadboard i notice LED brightness changes, this means my PWM pulses are not very accurate. I feel like i missing something very important.
which chip?
erik i use at89c51ac2
I hope your problem was fixed with your new code rendition. I like it better than the first code: ISR driven.
A point about your source: don't use tabs. Use space-fill instead. It makes the code cleaner when changing editors.
Also, the enumeration should be more explicit:
#define CHAN0 0x00 // selects channel 0 in the ADCON register #define CHAN1 0x01 // selects channel 1 in the ADCON register #define CHAN3 0x02 // selects channel 2 in the ADCON register typedef enum SCH { ch0 = CHAN0, ch1 = CHAN1, ch2 = CHAN2 } A2DCHANNEL; A2DCHANNEL channel = ch0;
Since you use 'channel' to OR in to the SFR to convert the next channel, be careful that those values are compatible with the SFR specs and the ability to simply increment the ++channel value. The SFR can have different bit-values than what the compiler assigns the enum values.
I'd suggest you separate the channel selection enum values from the switch( case ) and incrementing just to de-link the SFR channel assignment from the 'portable' "C" code.
void irq_a2d() interrupt 8 { static unsigned char chan = 0; // or use a 'global' for syncing to rest of code a2d[ chan ] = ADDH; // Get converted ADC value /*--------------------------------------------------------------. ; Transfer the ADC values into the PWM registers ; '--------------------------------------------------------------*/ switch( chan ) { case 0: CCAP0H = a2d[ chan ]; // Transfer ADC to PWM 0 break; case 1: CCAP1H = a2d[ chan ]; // Transfer ADC to PWM 1 break; case 2: CCAP2H = a2d[ chan ]; // Transfer ADC to PWM 2 break; default: chan = 2; // force an error to become the last channel CCAP2H = a2d[ chan ]; break; } /*--------------------------------------------------------------. ; Manage index ; '--------------------------------------------------------------*/ chan++; if( chan > 2 ) // safer than an '==' in case chan gets a random value { chan = 0; } /*--------------------------------------------------------------. ; Update the ADC conversion for next iteration ; '--------------------------------------------------------------*/ channel++; // bump the enum value ADCON &= ~MSK_ADCON_SCH; // clear the channel field ADCON[2:0] ADCON |= channel; // Select next channel for conversion ADCON &= ~MSK_ADCON_ADEOC; // clear the End of conversion flag ADCON |= MSK_ADCON_ADSST; // start conversion once again }