1

I am using: Curiosity LPC dev kit, PIC16F15325, MPLABX v5.35 on Mac, XC8 compiler. I am able to blink each LED on the Curiosity.

My goal is to generate a PWM signal on RC5 to drive an LED on the Curiosity board. I want to be able to vary the pulse width to adjust the brightness.

I originally was using the LFINTOSC at 31kHz, but I saw another post that said that you can't run the CCP module when TMR2 is using the LFINTOSC because it's too slow. I am happy to have the PWM at a frequency of <500Hz, so if it's possible to use the LFINTOSC, please let me know. For now I'm using the HFINTOSC at 1MHz.

I also noticed that the steps in the datasheet for configuring the PWM of the CCP module are slightly differently ordered from the steps for configuring the dedicated PWM module. Maybe this discrepancy is part of my issue.

For now I am just trying to enable the PWM in main and then loop on a blinking LED so that I know the MCU hasn't gotten stuck.

Nothing happens to the LED on RC5 when I run this code.

My code is as follows:

void main(void) {
   
    ANSC4 = 0;          //disable for analog input
    TRISC4 = 1;         //configure PORTC4 for digital input
    
    TRISA = 0x00;       //configure all PORTA to digital output
    TRISC5 = 0;         //configure PORTC5 to digital output

    ANSA5 = 0;          //disable analog inputs for LED pins
    ANSA1 = 0;
    ANSA2 = 0;
    ANSC5 = 0;
    
    LATA = 0x00;        //Set all output low
    LATC = 0x00;
    
    IOCAP = 0x00;       //Disable interrupts on PORTA
    IOCAN = 0x00;
    IOCAF = 0x00;       //Clear PORTA interrupt flags
    
    IOCCP = 0b00000000;
    IOCCN = 0b00010000; //Set PORTC4 to interrupt on negative edge
    IOCCF = 0x00;       //Clear PORTC interrupt flags
    
    GIE = 1;            //Enable global interrupts
    IOCIE = 1;          //Enable interrupts on change
    
    int duty_cycle_reg = 0b0000001100;
    enable_pwm_ccp(duty_cycle_reg);
    
    while(1){  
        LATA5 = 1;
        delay_ms(100);
        LATA5 = 0;
        delay_ms(100);
    }
}

Enable_PWM_CCP definition:

void enable_pwm_ccp(int duty_cycle_reg){
    
    int lsb = 0b0000000011;
    int msb = 0b1111111100;
    
    lsb &= duty_cycle_reg;
    msb &= duty_cycle_reg;
    
    lsb = lsb << 6;
    msb = msb >> 2;
    
    RC5PPS = 0x09;              //set CCP1 output to RC5 via PPS
    TRISC5 = 1;                 //disable output
    CCP1CONbits.MODE = 0xF;     //set CCP1 to PWM mode
    CCP1CONbits.FMT = 1;        //set CCP1 format left-aligned
    CCPR1H = msb;               //set duty cycle
    CCPR1L = lsb;
    
    PIR4bits.TMR2IF = 0;        //clear TMR2IF flag
    T2CONbits.CKPS = 0b111;     //set TMR2 prescale to 128
    T2CONbits.OUTPS = 0b000;    //set TMR2 postscaler to 1
    T2CLKCON = 0x02;
    PR2 = 0x26;                 //set PR2 to make PWM freq = 50Hz
    T2CONbits.ON = 1;           //enable TMR2
    
    while(!PIR4bits.TMR2IF){}   //wait for TMR2 to settle
    
    TRISC5 = 0;                 //set RC5 for digital output
}
  • I forgot to mention in the original question that I am using the XC8 compiler. It compiles fine and I can run it on the MCU. I have written other code to use interrupts based on pin input that works fine. I'm not really sure about where to start with the disassembly -- I was hoping to depend on the compiler for that. – Maximilian Cornell Mar 22 '21 at 18:30
  • It's best to [edit] bits that you forgot into the question rather than comment on your own post. Given that it's the first comment below your post most readers will notice it. Welcome to EE.SE. – Transistor Mar 23 '21 at 23:20
  • Will do, thanks! – Maximilian Cornell Mar 24 '21 at 16:39

1 Answers1

0

It looks like you missed setting the enable bit for the CCP1.

Try adding this statement in your Enable_PWM_CCP function:

    CCP1CONbits.CCP1EN = 1;     /* Turn on PWM */

This is my test code:

/*
 * File:   main.c
 * Author: dan1138
 * Target: PIC16F15325
 * Compiler: XC8 v2.31
 * IDE: MPLABX v5.25
 *
 * Created on March 23, 2021, 1:59 PM
 * 
 * Description:
 * 
 *  Blinky LED example for DM164137 - Curiosity LPC Demo  Board
 *
 * 
 *                        PIC16F15325
 *                 +----------:_:----------+
 * BOARD_VDD ->  1 : VDD               VSS : 14 <- GND
 *    LED_D4 <>  2 : RA5/T1CKI     PGD/RA0 : 13 <> PGD
 *           <>  3 : RA4           PGC/RA1 : 12 <> PGC/LED_D5
 *       VPP ->  4 : RA3/VPP     T0CKI/RA2 : 11 <> LED_D6
 *    LED_D7 <>  5 : RC5               RC0 : 10 <> POT1
 * SWITCH_S1 <>  6 : RC4               RC1 :  9 <> 
 *           <>  7 : RC3               RC2 :  8 <> 
 *                 +-----------------------:
 *                          DIP-14
 */

#pragma config FEXTOSC = OFF, RSTOSC = HFINT32, CLKOUTEN = OFF, CSWEN = ON
#pragma config FCMEN = OFF, MCLRE = ON, PWRTE = OFF, LPBOREN = OFF
#pragma config BOREN = OFF, BORV = LO, ZCD = OFF, PPS1WAY = OFF, STVREN = ON
#pragma config WDTCPS = WDTCPS_31, WDTE = OFF, WDTCWS = WDTCWS_7, WDTCCS = SC
#pragma config BBSIZE = BB512, BBEN = OFF, SAFEN = OFF, WRTAPP = OFF
#pragma config WRTB = OFF, WRTC = OFF, WRTSAF = OFF, LVP = ON, CP = OFF

#include <xc.h>

#define _XTAL_FREQ (32000000ul)

void set_pwm_duty_cycle(int duty_cycle) {
    unsigned char lsb;
    unsigned char msb;

    /* PWM duty cycle registers seup for left aligned operation */
    lsb = 0;
    if(duty_cycle & 1) lsb |= 0b01000000;
    if(duty_cycle & 2) lsb |= 0b10000000;
    msb = (unsigned char )(duty_cycle>>2);

    CCPR1H = msb;               //set duty cycle
    CCPR1L = lsb;
}
void enable_pwm_ccp(int duty_cycle){
    
    
    T2CON = 0;                  /* Stop PWM timer */
    
    RC5PPS = 0x09;              //set CCP1 output to RC5 via PPS
    TRISC5 = 1;                 //disable output
    CCP1CONbits.MODE = 0xF;     //set CCP1 to PWM mode
    CCP1CONbits.FMT = 1;        //set CCP1 format left-aligned
    set_pwm_duty_cycle(duty_cycle);
    
    PIR4bits.TMR2IF = 0;        //clear TMR2IF flag
    T2CONbits.CKPS = 0b111;     //set TMR2 prescale to 128
    T2CONbits.OUTPS = 0b000;    //set TMR2 postscaler to 1
    T2CLKCON = 0x01;            /* Select FOSC/4 as clock source */
    PR2 = 250-1;                /* set PR2 to make PWM freq = 250Hz with FOSC = 32MHz */
    TMR2 = 0;
    PIR4bits.TMR2IF = 0;
    T2CONbits.ON = 1;           //enable TMR2
    CCP1CONbits.CCP1EN = 1;     /* Turn on PWM */
    
    while(!PIR4bits.TMR2IF){};  //wait for TMR2 to settle
    
    TRISC5 = 0;                 //set RC5 for digital output
}
/*
 * Initialize this PIC
 */
void PIC_Init (void) {
    ANSC4 = 0;          //disable for analog input
    TRISC4 = 1;         //configure PORTC4 for digital input
    
    TRISA = 0x00;       //configure all PORTA to digital output
    TRISC5 = 0;         //configure PORTC5 to digital output

    ANSA5 = 0;          //disable analog inputs for LED pins
    ANSA1 = 0;
    ANSA2 = 0;
    ANSC5 = 0;
    
    LATA = 0x00;        //Set all output low
    LATC = 0x00;
    
    IOCAP = 0x00;       //Disable interrupts on PORTA
    IOCAN = 0x00;
    IOCAF = 0x00;       //Clear PORTA interrupt flags
    
    IOCCP = 0b00000000;
    IOCCN = 0b00010000; //Set PORTC4 to interrupt on negative edge
    IOCCF = 0x00;       //Clear PORTC interrupt flags
    
    GIE = 1;            //Enable global interrupts
    IOCIE = 1;          //Enable interrupts on change
}
/*
 * Main application
 */
void main(void) {
    int LED_duty_cycle;
    
    PIC_Init();
    
    enable_pwm_ccp(0);
    LED_duty_cycle = ((int)(PR2)+1)*2;    /* Set LED at 50% */
    set_pwm_duty_cycle( LED_duty_cycle );
    /*
     * Application loop
     */
    for(;;){
        
        LATA5 = 1;
        __delay_ms(100);
        LATA5 = 0;
        __delay_ms(100);

        /* Vary LED D7 brightness over about a 4 second interval */
        set_pwm_duty_cycle( LED_duty_cycle );
        LED_duty_cycle += 50;
        if(LED_duty_cycle >  ((int)(PR2)+1)*4) LED_duty_cycle = 0;
    }
}

void __interrupt() ISR_Handler(void) {
    if(IOCIE) {
        if(IOCIF) {
            IOCIE = 0;  /* disable interrupt on change */
        }
    }
}
Dan1138
  • 1,294
  • 7
  • 15
  • This got me working. Thank you! I'm curious -- why did you choose to implement the duty cycle lsb & msb bitwise operations in that way? – Maximilian Cornell Mar 24 '21 at 17:08
  • @MaximilianCornell, I did the lsb bitwise because I am using the "free" version of XC8 and the code it generates for multiple bit shifts on a PIC16F is awful. – Dan1138 Mar 24 '21 at 17:55
  • I'm using the same version of XC8, so I'll probably follow your implementation instead. Seems like I should start learning disassembly! – Maximilian Cornell Mar 24 '21 at 19:28