Connect with us

PIC16F877A - PWM Sine wave coding problem

Discussion in 'Microcontrollers, Programming and IoT' started by ggbc11, May 30, 2013.

Scroll to continue with content
  1. ggbc11


    Apr 23, 2013
    Hi, I'm trying to generate a half wave sinusoidal PWM signal but failing to get the right result. I've calculated values of duty cycle then loaded them sequentially into the CCPR1L (duty cycle reg). I've ran through debug and it seems to work in theory but when I run it in Proteus only the centre 20 values/pulses show with flat line for the rest of the signal. When I scroll back on the oscilloscope to the beginning it seems to start ok but once the DC starts to fall (maybe about ~231) it goes flat until the next cycle. I've tried with less values of sine, lower frequency but I'm sure I'm doing something stupid in the code. Would appreciate it if someone could have a look.

    Code was compiled with MikroC, clock 20MHz, PWM Period 204uS, Target Frequency = 49*204E-6=10mS (half wave):

    unsigned char sinedc[49]={ 0, 16, 32, 48, 64, 80, 95, 110,
    124, 138, 151, 164, 176, 187, 198, 207,
    216, 224, 231, 237, 243, 247, 250, 252,
    253, 253, 252, 250, 247, 243, 237, 231,
    224, 216, 208, 198, 187, 176, 164, 152,
    138, 124, 110, 95, 80, 64, 49, 33,
    unsigned char dctrans; //for int to char conversion

    unsigned int a; //Loop counter
    unsigned int di; //Number of divisions

    void interrupt() //Interrupt Function
    /*Create loop using counter. Reset counter after each cycle.
    Use counter as pointer to values in char string.
    if(TMR2IF_bit==1) //At PWM period end
    if(a>di) //when counter reaches end of string
    a=0; //reset counter
    dctrans=a; //converts int to char
    CCPR1L = sinedc[dctrans]; //Loads values from sine char string
    //to DC
    a++; //increment counter
    TMR2IF_bit = 0; //TMR2IF reset

    void main() {
    /*Set values for the counter (a), 0 to start and the number of divisions (di)

    /*DC Loading*/

    /*Initialize Ports*/
    TRISC = 0; //Set all Port C to outputs (O/Ps)
    PORTC = 0; //Clear all Port C O/Ps

    /*Initialize Interrupts*/
    INTCON = 0b11000000; //<7> Global Intrpt En (GIE), <6> Perip.
    //Intrpt En (PEIE)
    PIE1 = 0b00000010; //<1> TMR2IE: TMR2/PR2 Match Intrpt En
    TMR2IF_bit = 0; //Set TMR2/PR2 Match flag low

    /*Initialize PWM*/
    PR2 = 254; //Timer2 Period Register - Period of PWM (MAX 255)
    CCP1CON = 0b00111100; //<5:4> DC LSBs, <3:2> PWM Mode
    T2CON = 0b00000101; //<2> enables Timer2, <1:0> prescaler = 4

  2. ggbc11


    Apr 23, 2013
    this is what it looks like at the start
    Last edited: May 30, 2013
  3. BobK


    Jan 5, 2010
    The code looks OK to me. I did not check all the bits in the register inits, but the logic looks fine. You don't need to use ints for a and di, they can both be chars and avoid the 16-bit arithmetic, but that should not cause any problems except making it slower than it could be.

    I don't know anything about Proteus, so I could not help you there. Did you try simulating it in MPLAB?

  4. ggbc11


    Apr 23, 2013
    Hi Bob,
    Thanks, I've changed the integers to chars, I thought they needed to be ints for the compare. I'll try it on MPLAB tomorrow as its on my home PC and let you know.
Ask a Question
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Electronics Point Logo
Continue to site
Quote of the day