0
\$\begingroup\$

I am working with PIC18F26K20 and MPLABX IDE. I wrote some code for configuring and running the PWM. However, The output I get is at 50 percent duty Cycle no matter what duty cycle I provide the application. Following is the code I have written. The description of what I have done is included in the comments:

# define TMR2Prescalar 2

// 1 - Disable the PWNM Pin
TRISCbits.RC1 = 1;

// 2 - Not used since I'm using CCP not ECCP

// 3 - Set PWNM Period by setting PR2
//The formula is as given in the datasheet of PIC18F26k20 on page 141
// PR2 = (Fosc/(4*PWM frequency* TMR2 Prescalar)) -1
PR2 = (_XTAL_FREQ)/(4* 1000 * TMR2Prescalar);

// 4 - Configure the CCP module for the PWM mode by loading the CCPxCON register with the appropriate values
CCP2CON = CCP2CON | 0x0F;

// 5 - Set the PWM duty cycle by loading the CCPRxL register and CCPx bits of the CCPxCON register
// According to the datasheet page 141,  CCPRxL:DCxB<1:0> = (PulseWidth * Fosc) / TMR2Prescalar
uint16_t bits = 0;
// 70 percent pulse width
bits = (70* _XTAL_FREQ)/ TMR2Prescalar;
//Loading values into appropriate registers
CCP2CONbits.DC2B0 = bits & 0x0001;
CCP2CONbits.DC2B1 = bits & 0x0002;
CCPR2L = bits >> 2;

// 6 - Configure and start Timer2:
// Clear the TMR2IF interrupt flag bit of the PIR1 register
PIR1bits.TMR1IF = 0;
// Set the Timer2 prescale value by loading the T2CKPS bits of the T2CON register
T2CONbits.T2CKPS = 0;

//Enable Timer2 by setting the TMR2ON bit of the T2CON register.
T2CONbits.TMR2ON = 1;

// Wait until Timer2 overflows (TMR2IF bit of the PIR1 register is set).
while (!PIR1bits.TMR2IF);

//Enable the CCPx pin output driver by clearing the associated TRIS bit.
TRISCbits.RC1 = 0;

I am using an internal oscillator. The code for configuring internal oscillator is given below:

void OSCILLATOR_Initialize(void)
{
    // SCS INTOSC; OSTS intosc; IRCF 8MHz_HFINTOSC/2; IDLEN disabled; 
    OSCCON = 0x62;
    // INTSRC disabled; PLLEN disabled; TUN 0; 
    OSCTUNE = 0x00;
    // Set the secondary oscillator 
}

Can anyone tell me whats wrong with the above code?

\$\endgroup\$
2
  • \$\begingroup\$ I would guess that the problem is not in the code you posted. Can you post all of your main so we can also see that? Have you checked carefully that the device is not resetting? Also check the end values of PR2 and bits, calculations done like that are prone to overflow and give you the wrong answer if you do not get the data types correct by using suffixes on the numbers e.g. "1000L" instead of "1000". \$\endgroup\$
    – Cobusve
    Commented Mar 7, 2019 at 2:19
  • \$\begingroup\$ Are you sure about your calculation for CCPR2L? You should have a number less than or equal to PR2 there. \$\endgroup\$
    – brhans
    Commented Mar 7, 2019 at 3:48

1 Answer 1

1
\$\begingroup\$

Your code fragments do not have enough context to sort out what you may be doing wrong.

This code seems to work for me:

/*
 * File: main.c
 * Target: PIC18F26K20
 * IDE: MPLABX v5.10
 * Compiler: XC8 v2.05
 * 
 * Description:
 *  Initialize PWM1 for output on PORTC pin RC2
 *  Initialize PWM2 for output on PORTC pin RC1
 *
 */
#include <xc.h>
/*
 * PIC configuration words
 */
#pragma config FOSC = INTIO67, FCMEN = OFF, IESO = OFF, BOREN = OFF
#pragma config WDTEN = OFF, WDTPS = 1024, PBADEN = OFF, HFOFST = ON
#pragma config LVP = OFF, STVREN = ON, XINST = OFF
#pragma config CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF
#pragma config CPB = OFF, CPD = OFF
#pragma config WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF
#pragma config WRTC = OFF, WRTB = OFF, WRTD = OFF
#pragma config EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF
#pragma config EBTRB = OFF
#pragma config PWRT = OFF, BORV = 18, MCLRE = ON, CCP2MX = PORTC
#pragma config LPT1OSC = ON
/*
 * Target specific libraries
 */
#include <xc.h>
/*
 * Standard libraries
 */
#include <stdint.h>
/*
 * Constants
 */
#define _XTAL_FREQ 8000000L
#define FCYC (_XTAL_FREQ/4L)
/*
 * Initialize this PIC hardware
 */
void Init_PIC(void)
{
    INTCON = 0;     /* disable interrupts */
    INTCON2 = 0xF5;
    INTCON3 = 0xC0;
    PIE1 = 0;
    PIE2 = 0;

    OSCCON = 0b01100000; /* Enter Sleep mode for sleep, */
                         /* set internal oscillator to 8MHz, */
                         /* use primary clock selected by config words */
    /* Turn off the PLL */
    OSCTUNEbits.PLLEN = 0;

    /* disable ADC inputs */
    ANSEL = 0;
    ANSELH = 0;

    /* disable Comparators */
    CM1CON0 = 0;
    CM2CON0 = 0;

    /* make all available GPIOs outputs and set low */
    LATA  = 0x00;
    TRISA = 0xC0; /* leave RA6,RA7 as inputs */
    LATB  = 0x00;
    TRISB = 0x00;
    LATC  = 0x00;
    TRISC = 0x03; /* leave RC0,RC1 as inputs */

    RCONbits.IPEN = 0; /* use legacy interrupt model */
}
/* 
 * Setup PWM time base for an 1000Hz period.
 * 
 * Note 1:  Duty cycle units
 * 
 *          The duty cycle is in a 10-bit register. With
 *          a resolution in time that is 4 time that of 
 *          the PWM period register (PR2). The high 8-bits
 *          of this register are in the CCPRxL register,
 *          the low 2-bits are in the CCPxCON<5:4> register.
 * 
 *          The general case math to compute a percent of 
 *          the PWM period to set the duty cycle is:
 *          DutyCycle = PwmPeriod * DutyCyclePercent / 100
 * 
 *          The specific case using integer math is:
 *          DutyCycle = (PR2+1) * 4 * DutyCyclePercent / 100
 * 
 *          Combine terms to simplify and limit range to a 16-bit interger:
 *          DutyCycle = (PR2+1) * DutyCyclePercent / 25
 * 
 * WARNING: PMW1 and PWM2 share TIMER2 as the PWM time base.
 * When using both PWM function blocks in the PIC18F26K20
 * both must use the same time base period.
 *
 */ 
#define PWM_IF PIR1bits.TMR2IF
#define PWM_CYCLES_PER_SECOND (1000L)
#define TMR2_PRESCALE_SETTING (2) /* value can be 0, 1 or 2) sets prescale to 1:1, 4:1 or 16:1 */
#define PWM_PERIOD (FCYC/(PWM_CYCLES_PER_SECOND * (1 << (2*TMR2_PRESCALE_SETTING))))
#if (PWM_PERIOD > 256)
#error PWM_CYCLES_PER_SECOND too slow for TIMER2 prescale
#elif (PWM_PERIOD <= 1)
#error PWM_CYCLES_PER_SECOND too fast for TIMER2 prescale
#endif
void PWM_TimeBase_Init(void)
{ 
    T2CON = 0; /* stop TIMER2 */
    CCP1CON = 0; /* stop PWM1 */
    CCP2CON = 0; /* stop PWM2 */
    TRISCbits.TRISC2 = 1; /* make CCP1 tri-state */
    TRISCbits.TRISC1 = 1; /* make CCP2 tri-state */

    PR2 = PWM_PERIOD - 1; /* set PWM1 period */
    T2CON = TMR2_PRESCALE_SETTING;
    T2CONbits.TMR2ON = 1; /* start PWM1 timer */
}
/* 
 * Setup PWM1 for a 70% duty cycle.
 *
 * WARNING: PMW1 and PWM2 share TIMER2 as the PWM time base.
 * When using both PWM function blocks in the PIC18F26K20
 * both must use the same time base period.
 *
 */ 
void PWM1_Init(void)
{ 
    uint16_t DutyCycle;

    TRISCbits.TRISC2 = 1; /* make CCP1 tri-state */
    ECCP1AS = 0; /* disable auto shutdown */
    CCP1CONbits.P1M = 0b00; /* set PWM1 for single output */

    /* set duty cycle to 70%  */
    DutyCycle = ((uint16_t)(PR2+1)*70)/25; /* See: Note 1: Duty cycle units */

    CCP1CONbits.DC1B =  DutyCycle & 0b11;
    CCPR1L = (unsigned char)(DutyCycle >> 2);

    CCP1CONbits.CCP1M = 0b1100; /* set PWM1 mode */

    PWM_IF = 0;
    while(!PWM_IF); /* wait for PWM1 to complete first cycle */

    /* enable the PWM1 output */
    TRISCbits.TRISC2 = 0;
} 
/* 
 * Setup PWM2 for a 30% duty cycle.
 *
 * WARNING: PMW1 and PWM2 share TIMER2 as the PWM time base.
 * When using both PWM function blocks in the PIC18F26K20
 * both must use the same time base period.
 *
 */ 
void PWM2_Init(void)
{ 
    uint16_t DutyCycle;

    TRISCbits.TRISC1 = 1; /* make CCP2 tri-state */

    /* set duty cycle to 30%  */
    DutyCycle = ((uint16_t)(PR2+1)*30)/25; /* See: Note 1: Duty cycle units */

    CCP2CONbits.DC2B =  DutyCycle & 0b11;
    CCPR2L = (unsigned char)(DutyCycle >> 2);

    CCP2CONbits.CCP2M = 0b1100; /* set PWM2 mode */

    T2CONbits.TMR2ON = 1; /* start PWM2 timer */

    PWM_IF = 0;
    while(!PWM_IF); /* wait for PWM2 to complete first cycle */

    /* enable the PWM2 output */
    TRISCbits.TRISC1 = 0;
} 
/*
 * Main application 
 */
void main(void) 
{ 
    Init_PIC();
    PWM_TimeBase_Init();
    PWM1_Init();
    PWM2_Init();
    /* 
     * Application loop 
     */
    for(;;)
    {
        PWM_IF = 0;
        while(!PWM_IF);
    }
}
\$\endgroup\$

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.