This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

ADC and create PWM pulses

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

Parents
  • C B,

    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
    
    }
    
    

    --Cpt. Vince Foster
    2nd Cannon Place
    Fort Marcy Park, VA

Reply
  • C B,

    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
    
    }
    
    

    --Cpt. Vince Foster
    2nd Cannon Place
    Fort Marcy Park, VA

Children
No data