0
\$\begingroup\$

I am using a PIC16F873A for generating an SPWM signal using CCP1 and timer 2. The SPWM is generated successfully but the signal is not stable. I have generated 50 Hz signal of SPWM frequency of 40 kHz, but after 1 or 2 SPWM cycles the pulse width (shape of SPWM) changes. The output on CCP1 pin is shown in the image below:

spwm.

The SPWM 1, 2, 3, 4 have different shapes, although the duty cycle is same for all. The code is given below. How to solve this problem?

#include <xc.h>
#pragma config WDTE = ON 
#pragma config PWRTE = OFF 
#pragma config BOREN = ON 
#pragma config FOSC = HS 
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) //END CONFIG

#define _XTAL_FREQ 20000000

#include <xc.h>

unsigned char sin_table[16] = {0, 26, 50, 73, 92, 107, 118, 123, 123, 118, 107, 92, 73, 50, 26,0}; 
unsigned int TBL_POINTER_NEW, TBL_POINTER_OLD, TBL_POINTER_SHIFT, SET_FREQ; unsigned int TBL_temp; 
unsigned char DUTY_CYCLE, FlagReg;
#define SA  PORTCbits.RC3
#define  SB PORTCbits.RC4
#define  SC PORTCbits.RC5
#define  SD PORTCbits.RC6 unsigned char Direction = 0; 
void __interrupt() isr() {
  if (TMR2IF == 1) {
    TBL_POINTER_NEW = TBL_POINTER_OLD + SET_FREQ;
    if (TBL_POINTER_NEW < TBL_POINTER_OLD) {
      //CCP1CON.P1M1 = ~CCP1CON.P1M1; //Reverse direction of full-bridge
      if (Direction == 0) {
        SA = 0;
        SD = 0;
        SB = 1;
        SC = 1;
        Direction = 1;
      } 
      else {
        SB = 0;
        SC = 0;
        SA = 1;
        SD = 1;
        Direction = 0;
      }
    }
    TBL_POINTER_SHIFT = TBL_POINTER_NEW >>12;
    DUTY_CYCLE = TBL_POINTER_SHIFT;
    CCPR1L = sin_table[DUTY_CYCLE];
    TBL_POINTER_OLD = TBL_POINTER_NEW;
    TMR2IF = 0;
  }
}

void main() {
    SET_FREQ = 162;
    PORTC = 0;
    TRISC = 0;
    PR2 = 124; // 40kHz
    CCPR1L = 0;
    CCP1CON = 0x3C; //PWM mode
    TRISC = 0xFF;
    TMR2IF = 0;
    T2CON = 0x04; //TMR2 on,
    while (TMR2IF == 0);
    TMR2IF = 0; //Clear TMR2IF
    PORTC = 0;
    TRISC = 0;
    TMR2IE = 1;
    GIE = 1;
    PEIE = 1;
    while (1);
}
\$\endgroup\$
1
  • \$\begingroup\$ You can't do a visual comparison of PWMs when you zoomed out so far. You scope or logic analyzer isn't sampling fast enough to see all of the high & low periods. Put the PWM signal through an RC filter to average it out into a voltage level and you'll almost certainly see that it's actually fine. \$\endgroup\$
    – brhans
    Commented Dec 16, 2022 at 13:40

1 Answer 1

1
\$\begingroup\$

It looks like it might be missed interrupts. This family of PICs require 4 oscillator cycles per instruction cycle, so you are only running at 5 MIPS. It appears that you are trying to interrupt at a 40 kHz rate, so you are leaving only 125 instruction cycles per interrupt. When the interrupt occurs, the state of the processor gets pushed onto the stack so that it can return to the correct instruction when it exits the interrupt service routine; then this data must be retrieved from the stack when you exit.

One of the quirks of the PIC16F family is that there is no interrupt or status bit indicating when the stack has overflowed, so you won't be able to tell if this is your problem without experimentation. If you can't service the interrupt and return before timer 2 times out, you will eventually skip an ISR.

Since you are not doing anything else except for servicing this single interrupt, you might abandon the interrupt approach and just instead poll the interrupt flag (TMR2IF) during your while(1) loop. This will save the stack overhead and might get you there.

Good luck!

\$\endgroup\$
1
  • \$\begingroup\$ You can see the ISR overhead by lighting an LED upon entry and turning it off upon exit. Also, this is an ancient PIC that does not have LAT registers, so writing to individual bits can cause the infamous RMW (Read-Modify_Write) problems. \$\endgroup\$
    – PStechPaul
    Commented Dec 16, 2022 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.