Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

16F877A CCP PWM odd output

Status
Not open for further replies.

Dale Gregg

Junior Member level 3
Joined
Jul 23, 2013
Messages
29
Helped
2
Reputation
4
Reaction score
2
Trophy points
3
Location
Leeds, United Kingdom
Activity points
263
LA_end pulse.PNG

So, I thought I'd finished this part of the project and was just tidying up when the above PWM output starting happening. The PWM signal is a half sine wave and was working fine but now has a pulse for about 2 periods at the start/finish of each cycle. This therefore messes up my output sine. I've spent most of the day looking at it, I've stripped the code back to the bare minimum played around with char string lengths, order of the code and its still there! Could this just be an issue with Proteus?

Code:
unsigned char sinedc[39]={  0,  20,  60,  79,  98, 116, 134, 150, 166, 180,
                          194, 206, 217, 226, 234, 240, 245, 248, 250, 250, 
                          248, 245, 240, 234, 226, 217, 206, 194, 180, 166, 
                          150, 134, 116,  98,  79,  60,  40,  20,   0
                          };

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

void interrupt()             //Interrupt Function
   {
   if(TMR2IF_bit==1)       //At PWM period end
      {
      if(a>=di)             //when counter reaches end of string
         {
           a=0;              //reset counter
         }
      else
         {
         CCPR1L = sinedc[a];   //Loads values from sine char string
                                       //to DC
         a++;                        //increment counter
         }

      TMR2IF_bit = 0;             //TMR2IF reset
      }
   }
     
void main() {

/*DC Loading*/
a=0;
di=38;
}

I've tried removing the else statement and putting the code from there above the if statement, this for some reason loses the first 3 or 4 pulses from the beginning and end of the signal. di limits the count to 38 as there are 39 values hence 0-38.

Anyone any ideas?
 

After just glancing at your code, it seems the majority of the issues you describe are located in the following section of code:

Code:
if(TMR2IF_bit==1)       //At PWM period end
    {
      if([COLOR="#FF0000"]a>=di[/COLOR])             //when counter reaches end of string
         {
           a=0;              //reset counter
         }
      [COLOR="#FF0000"]else[/COLOR]
         {
         CCPR1L = sinedc[a];   //Loads values from sine char string
                                       //to DC
         a++;                        //increment counter
         }

      TMR2IF_bit = 0;             //TMR2IF reset
    }

First the conditional test should be written:

Code:
if(a>di)

Rather than the former:

Code:
if(a>=di)

As the latter indicates, once variable a has obtained the value of 38 or greater, reset to zero, which prevents the value contained in sinedc[38] from ever being accessed.

Also, rather than an if/else conditional structure, consider using the following instead:

Code:
if(TMR2IF_bit==1)       //At PWM period end
    {
      if(a>di)             //when counter reaches end of string
         {
           a=0;              //reset counter
         }
      
       CCPR1L = sinedc[a];   //Loads values from sine char string
                                       //to DC
       a++;                        //increment counter

      TMR2IF_bit = 0;             //TMR2IF reset
    }

As your original code also prevented the value contained in sinedc[0] from being accessed, after the first iteration.

There maybe other issues, however the above quickly come to mind.


BigDog
 
Hi,

Thanks for that. I changed those bits but its still the same, I tried taking di down to 36 which then replaced that area with a flat spot which suggests that it something with the loading of values to CCPR1L? I thought it might have something to do with it having 0's at each end but tried some other values with the same result. Might just re-write it :sad:
 

Zip and post your Project files and Proteus file. A 40 is missing while sine wave is increasing.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
unsigned char sinedc[40]={  0,  20,  40, 60,  79,  98, 116, 134, 150, 166, 180,
                          194, 206, 217, 226, 234, 240, 245, 248, 250, 250, 
                          248, 245, 240, 234, 226, 217, 206, 194, 180, 166, 
                          150, 134, 116,  98,  79,  60,  40,  20,   0
                          };
 
unsigned char a;       //Loop counter
unsigned char di;      //Number of divisions
 
void interrupt()             //Interrupt Function
   {
   if(TMR2IF_bit==1)       //At PWM period end
      {
      if(a>=di)             //when counter reaches end of string
         {
           a=0;              //reset counter
         }
      else
         {
         CCPR1L = sinedc[a];   //Loads values from sine char string
                                       //to DC
         a++;                        //increment counter
         }
 
      TMR2IF_bit = 0;             //TMR2IF reset
      }
   }
     
void main() {
 
/*DC Loading*/
a=0;
di=39;
}

 

Yep, probably the missing "40" was the guilt one, you are acessing a value out of the array....

Simao
 

I've managed to get the code to produced the SPWM as before (minus the pulse at the end), I reverted back to an earlier version and added the rest bit by bit. This problem started when I introduced a new bit of code to dynamically change the values of duty cycle and I'm thinking I probably changed something else in the code in trying to get it to work. I'm now trying to re-introduce this function and the pulse at the end of the signal has returned (only when this function is called to calc the values of DC).

Code:
void sinecalc(int amp)
   {
   mag=PR2-amp;
   while(id<39)
   {
      spt=sineperc[id];
      dclrg=spt*mag;
      dcsml=dclrg/100;
      sinebuff[id]=dcsml;
      id++;
   }
   id=0;

   for(idd=0;idd<39;idd++)
      sinedc[idd]=sinebuff[idd];
}

amp is the variable that controls the output amplitude. Each percentage value of sine is selected and then used to calculate the value of DC for that amplitude which is loaded into the buffer array. The buffer array is then loaded into the sinedc array which is been used in the interrupt function to load the values of DC sequentially. I use the buffer because it changes the values in 700uS instead of 20mS if done directly.

I've run through this with the debugger several times checking the char arrays and it seems to work fine. I've used a different compiler and checked the char arrays at each stage with the printf, looks fine. At one point (before I started messing around with it) it worked fine from amp=5 up to 100 then the odd pulse at the end would show then go again at higher values.

Any ideas to try?
 

I've found that this problem seems to be with the placement of the TMR2IF_bit (interrupt) reset. If I have the reset within the else statement as before the output seems fine (therefore missing the last reset when the counter is reset),
Code:
void interrupt()             //Interrupt Function
{
     if(TMR2IF_bit==1)       //At PWM period end
     {
        if(a>di)            //when counter reaches end of string
        {
           if(tog==1)     //Toggle H bridge switches
              {
              tog  = 0;
              togn = 1;
              }
           else
              {
              tog  = 1;
              togn = 0;
              }
           a=0;              //reset counter
        }
        else
        {
           CCPR1L = sinedc[a];   //Loads values from sine char string
                                       //to DC
           a++;                        //increment counter
           TMR2IF_bit = 0;             //TMR2IF reset
        }
     }
}

output,
old interruptF.PNG

If I then change the interrupt function to the more logical way as discussed,

Code:
void interrupt()             //Interrupt Function
{
     if(TMR2IF_bit==1)       //At PWM period end
     {
        CCPR1L = sinedc[a];   //Loads values from sine char string
                              //to DC
        a++;                  //increment counter
        
        if(a>di)            //when counter reaches end of string
        {
           if(tog==1)     //Toggle H bridge switches
              {
              tog  = 0;
              togn = 1;
              }
           else
              {
              tog  = 1;
              togn = 0;
              }
           a=0;              //reset counter
        }
        TMR2IF_bit = 0;             //TMR2IF reset
     }
}

output,
new interruptF.PNG

I now have large flat bits either side of the pulse sequence??

Anyone had this fault before?
 

Please post the entire current revision of your code, as it is difficult to advise you without see the whole picture.

Is the output not repeating? Or is there a lengthy pause between iteration of the pulse train?

Are you still using a lookup table?

One possible is issue is the application of sinedc[0] and sinedc[38] in succession.

If so, if you examine the lookup table you will notice each iteration begins/ends with a double pulse of 0 width:

Code:
unsigned char sinedc[40]={  [COLOR="#FF0000"]0[/COLOR],  20,  40, 60,  79,  98, 116, 134, 150, 166, 180,
                          194, 206, 217, 226, 234, 240, 245, 248, 250, 250, 
                          248, 245, 240, 234, 226, 217, 206, 194, 180, 166, 
                          150, 134, 116,  98,  79,  60,  40,  20,   [COLOR="#FF0000"]0[/COLOR]
                          };

0, 20, 40, 60, 79, 98, 116, 134, 150, ... 150, 134, 116, 98, 79, 60, 40, 20, 0, 0, 20, 40, 60, 79, 98, 116, 134, 150, ... 150, 134, 116, 98, 79, 60, 40, 20, 0, 0, 20, 40, 60, 79, 98, 116, 134, 150, ...


BigDog
 

Hi, it repeats the shown waveform every cycle, I've tried changing the end/beginning values to 1, 5 or 10 and still gives the same error.

Code:
/*
Project: Pure Sine Wave SPWM
Version: 7.1
Target: Re-arrange code, comment and tidy
Sub-Target:
Revisions: 7 - Set for 16MHz selection

Coded by: Dale Gregg

Comments:

Result: Amplitude rises and falls as programmed. Spike at either end of half
wave - possible simulation fault.
*/

#define AMP_TRANS_TEST
//#define HI_FREQ

/*Function Definition*/
void sinecalc(int amp);
void init(void);

/*Variable Definition*/
unsigned char sineperc[39]={  0,   8,  24,  32,  39,  46,  53,  60,  66,  72,
                             77,  82,  87,  90,  94,  96,  98,  99, 100, 100,
                             99,  98,  96,  94,  90,  87,  82,  77,  72,  66,
                             60,  53,  46,  39,  32,  24,  16,   8,   0
                           };
unsigned char sinebuff[39];
unsigned char sinedc[39];


/*DC Loading variables*/
unsigned char a;       //Loop counter
unsigned char di;      //Number of divisions
unsigned char b;       //Loop counter

/*Sine Calc Variables*/
unsigned char id;      //counter for main sine calc loop
unsigned char idd;     //counter for buffer loading loop
unsigned int amp;      //amplitude value input to function
unsigned int mag;      //resulting amp magnitude
unsigned int spt;      //sine percentage table variable
unsigned int dcsml;    //calculated duty cycle value

/*Test Code Variables*/
unsigned char testcount;   //test counter
unsigned char testamp;     //amplitude value
unsigned char dir;         //direction bit

sbit tog  at RB1_bit;      //toggle output for logic control
sbit togn at RB2_bit;      //negative toggle (inverted tog)

void interrupt()             //Interrupt Function
{
     if(TMR2IF_bit==1)       //At PWM period end
        {
        #ifdef HI_FREQ
        if(b>3)
           {
           #endif
           CCPR1L = sinedc[a];   //Loads values from sine char string
                                 //to DC
           a++;                  //increment counter
           #ifdef HI_FREQ
           b=0;                  //reset b counter
           }
        else
           {
           b++;           //increment counter
           }
        #endif
        if(a>di)            //when counter reaches end of string
        {
           if(tog==1)     //Toggle control logic gates
              {
              tog  = 0;
              togn = 1;
              }
           else
              {
              tog  = 1;
              togn = 0;
              }
           a=0;              //reset counter
        }
        TMR2IF_bit = 0;             //TMR2IF reset
     }
}
     
void main() 
   {
   init();   //initialisation function

while(1)     //continuous loop
   {
   #ifdef AMP_TRANS_TEST
   /*Test change in AMP Value*/
   testcount++;         //Test counter

   if(testcount>10)     //After 10 cycles
      {
      if(testamp>254)   //If greater than max value for amp
         {
         dir=0;         //change direction of increment
         }

      if(testamp<1)     //If less than min value for amp
         {
         dir=1;         //change direction of increment
         }

      switch(dir)
         {
         case 0: testamp--; break;   //if direction is 0, decrement
         case 1: testamp++; break;   //if direction is 1, increment
         };

      sinecalc(testamp);   //send amp value to function
      testcount=0;         //reset test counter
      }
   #endif
}
}

/*Initialization Function*/
void init(void)
{
   /*DC Loading*/
   a=0;    //pointer for dc value loading
   di=38;  //number of divisions minus 1 for counter
   
   /*Test code variables*/
   testcount=0;
   testamp=0;

   /*Initialize Ports*/
   TRISC   = 0;                  //Set all Port C to outputs (O/Ps)
   PORTC   = 0;                  //Clear all Port C O/Ps
   TRISA   = 0;                  //Set all Port A to outputs (O/Ps)
   PORTA   = 0;                  //Clear all Port A O/Ps
   TRISD   = 0;                  //Set all Port D to outputs (O/Ps)
   PORTD   = 0;                  //Clear all Port D O/Ps
   TRISB   = 0;                  //Set all Port B to outputs (O/Ps)
   PORTB   = 0;                  //Clear all Port B 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     = 255;          //Timer2 Period Register - Period of PWM (MAX 255)
   CCP1CON = 0b00111100;   //<5:4> DC LSBs, <3:2> PWM Mode
   T2CON   = 0b00000100;   //<2> enables Timer2, <1:0> prescaler = 1
   
   sinecalc(0);
}

/*Sine Calculation Function*/
void sinecalc(int amp)
   {
   
   while(id<39)   //loop for amount of divisions/value in array
   {
      spt=sineperc[id];      //load spt with % of DC from sineperc
      dcsml=(spt*amp)/100;   //calculate DC for value of amplitude
      sinebuff[id]=dcsml;    //load DC value into buffer
      id++;                  //increment counter
   }
   id=0;                     //reset counter

   for(idd=0;idd<39;idd++)      //transfer values from buffer to DC array
      sinedc[idd]=sinebuff[idd];
}

Output,
new interruptF2.PNG
at low value of amp. When I started using the sinecalc function the flat spots went but the long pulse came back?
 
Last edited:

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top