Maker Pro
Maker Pro

PIC16F877A - PWM Sine wave coding problem

ggbc11

Apr 23, 2013
8
Joined
Apr 23, 2013
Messages
8
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,
17
};
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
}
else
{
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*/
a=0;
di=49;

/*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

while(1);
}
 

ggbc11

Apr 23, 2013
8
Joined
Apr 23, 2013
Messages
8

this is what it looks like at the start
 
Last edited:

BobK

Jan 5, 2010
7,682
Joined
Jan 5, 2010
Messages
7,682
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?

Bob
 

ggbc11

Apr 23, 2013
8
Joined
Apr 23, 2013
Messages
8
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.
Ta,
GG
 
Top