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
}
-
\$\begingroup\$ 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. \$\endgroup\$Maximilian Cornell– Maximilian Cornell2021年03月22日 18:30:49 +00:00Commented Mar 22, 2021 at 18:30
-
\$\begingroup\$ 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. \$\endgroup\$Transistor– Transistor2021年03月23日 23:20:22 +00:00Commented Mar 23, 2021 at 23:20
1 Answer 1
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 */
}
}
}
-
\$\begingroup\$ 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? \$\endgroup\$Maximilian Cornell– Maximilian Cornell2021年03月24日 17:08:35 +00:00Commented Mar 24, 2021 at 17:08
-
\$\begingroup\$ @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. \$\endgroup\$Dan1138– Dan11382021年03月24日 17:55:58 +00:00Commented Mar 24, 2021 at 17:55
-
\$\begingroup\$ I'm using the same version of XC8, so I'll probably follow your implementation instead. Seems like I should start learning disassembly! \$\endgroup\$Maximilian Cornell– Maximilian Cornell2021年03月24日 19:28:46 +00:00Commented Mar 24, 2021 at 19:28
Explore related questions
See similar questions with these tags.