Generating PIC18F45K20 PWM Using ECCP - Page 1
Forum section: electronics / general

author: Lossyminor
author_rank: Newbie
timestamp: April 28, 2020, 03:01:09 pm
content: I'm new to programming and using PIC microcontrollers. I've managed to get my PicKit 44 pin demo board working with a flashing LED program, which I copied and pasted to verify that everything works. Now I would like to generate some PWM using the ECCP module, so I can steer the PWM to a particular pin. After reading through the datasheet and multiple websites, I believe I need to configure the following for a 245Hz 50% duty cycle on pin 3 (RD5).Register PR2 - to control PWM period = 0b11111111 (for 245Hz pwm frequency with a 4MHz internal osc and tmr2 prescale set to 16 (see below))Register T2CON - enable Timer2 and set prescalar (in my case 16) = 0b00000111Register CCPR1L = 0b10000000 - 512/1024 for 50% dutyRegister CCPR1H does not need to be configured as it is a read-only register in PWM mode (page 150 of datasheet).Register CCP1CON = 0b00001100 - Single output, 00 bit for LSB PWM Duty cycle (I don't care about higher resolution), PWM mode; P1A, P1C active high, P1B, P1D active high.Register TRSID = 0b00000000 - All zero as we need RD4 an outputHowever I'm struggling to understand precisely how I set these registers. I've got the follow main.c from my flashing LED test example:Code:// CONFIG1H#pragma config FOSC = INTIO67 // Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)#pragma config IESO = OFF // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)// CONFIG2L#pragma config PWRT = OFF // Power-up Timer Enable bit (PWRT disabled)#pragma config BOREN = OFF // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)#pragma config BORV = 18 // Brown Out Reset Voltage bits (VBOR set to 1.8 V nominal)// CONFIG2H#pragma config WDTEN = OFF // Watchdog Timer Enable bit (WDT is controlled by SWDTEN bit of the WDTCON register)#pragma config WDTPS = 32768 // Watchdog Timer Postscale Select bits (1:32768)// CONFIG3H#pragma config CCP2MX = PORTC // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)#pragma config PBADEN = OFF // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)#pragma config LPT1OSC = OFF // Low-Power Timer1 Oscillator Enable bit (Timer1 configured for higher power operation)#pragma config HFOFST = OFF // HFINTOSC Fast Start-up (The system clock is held off until the HFINTOSC is stable.)#pragma config MCLRE = OFF // MCLR Pin Enable bit (RE3 input pin enabled; MCLR disabled)// CONFIG4L#pragma config STVREN = OFF // Stack Full/Underflow Reset Enable bit (Stack full/underflow will not cause Reset)#pragma config LVP = OFF // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)#pragma config XINST = OFF // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))// CONFIG5L#pragma config CP0 = OFF // Code Protection Block 0 (Block 0 (000800-001FFFh) not code-protected)#pragma config CP1 = OFF // Code Protection Block 1 (Block 1 (002000-003FFFh) not code-protected)#pragma config CP2 = OFF // Code Protection Block 2 (Block 2 (004000-005FFFh) not code-protected)#pragma config CP3 = OFF // Code Protection Block 3 (Block 3 (006000-007FFFh) not code-protected)// CONFIG5H#pragma config CPB = OFF // Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)#pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM not code-protected)// CONFIG6L#pragma config WRT0 = OFF // Write Protection Block 0 (Block 0 (000800-001FFFh) not write-protected)#pragma config WRT1 = OFF // Write Protection Block 1 (Block 1 (002000-003FFFh) not write-protected)#pragma config WRT2 = OFF // Write Protection Block 2 (Block 2 (004000-005FFFh) not write-protected)#pragma config WRT3 = OFF // Write Protection Block 3 (Block 3 (006000-007FFFh) not write-protected)// CONFIG6H#pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)#pragma config WRTB = OFF // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)#pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected)// CONFIG7L#pragma config EBTR0 = OFF // Table Read Protection Block 0 (Block 0 (000800-001FFFh) not protected from table reads executed in other blocks)#pragma config EBTR1 = OFF // Table Read Protection Block 1 (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)#pragma config EBTR2 = OFF // Table Read Protection Block 2 (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks)#pragma config EBTR3 = OFF // Table Read Protection Block 3 (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks)// CONFIG7H#pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)// #pragma config statements should precede project file includes.// Use project enums instead of #define for ON and OFF.#include <xc.h>#define _XTAL_FREQ 4000000int main(){TRISD0 = 0; //RD0 as Output PIN port data direction registerwhile(1){RD0 = 1; // LED ON__delay_ms(50); // 1 Second DelayRD0 = 0; // LED ON__delay_ms(50); // 1 Second Delay}return 0;}Sorry for the long code, but it is what it is.Now I've tried searching various sources for how to configure these registers as above, but I cannot figure it out. For example, I've searched through the 18F45K20 support information for #pragma config examples, but they don't seem to control system registers - nothing for the PWM. How do I set these registers as above to get a working PWM on my Pin 3 (RD5)? Thanks for your help.

author: ggchab
author_rank: Frequent Contributor
timestamp: April 28, 2020, 04:30:13 pm
content: I don't know that specific PIC but I think you have done almost everythingJust configure the other registers as you did for TRISD:Code:TRISD = 0; // All PortD lines as outputPR2 = 0b11111111; // for 245Hz pwm frequency with a 4MHz internal osc and tmr2 prescale set to 16T2CON = 0b00000111; // enable Timer2 and set prescalar (in my case 16)CCPR1L = 0b10000000; // 512/1023 for 50% dutyCCP1CON = 0b00001100; // Single output, 00 bit for LSB PWM Duty cycle (I don't care about higher resolution), PWM mode; P1A, P1C active high, P1B, P1D active high.

author: Lossyminor
author_rank: Newbie
timestamp: April 28, 2020, 04:43:27 pm
content: Thanks ggchab - seems it very simple to set the bits! I change the main portion of my program to the following:Code:#include <xc.h>#define _XTAL_FREQ 4000000int main(){TRISD = 0; // All PortD lines as outputPR2 = 0b11111111; // for 245Hz pwm frequency with a 4MHz internal osc and tmr2 prescale set to 16T2CON = 0b00000111; // enable Timer2 and set prescalar (in my case 16)CCPR1L = 0b10000000; // 512/1023 for 50% dutyCCP1CON = 0b00001100; // Single output, 00 bit for LSB PWM Duty cycle (I don't care about higher resolution), PWM mode; P1A, P1C active high, P1B, P1D active high.while(1){//do nothing, just output the pwm as preset}return 0;}I expected to see a PWM output on RD5 (pin 3). However there is no output. Where am I going wrong?

author: MarkF
author_rank: Super Contributor
timestamp: April 28, 2020, 04:45:32 pm
content: Here is some code for a PIC18F2550.PWM should be on pin RC2.I believe the registers for PWM are the same for your PIC.Also, __delay_ms() is in milliseconds. For a 1sec delay, it needs to be __delay_ms(1000);Code:void main(void){// PWM SETTINGSuint8_t prescale=1; // Timer2 prescale (1, 2 or 4)int16_t period=100; // PWM period ( 1/freq )int16_t dutyCycle=10; // PWM duty cycle// PACK PWM CONTROL REGISTERSTRISCbits.RC2 = 0 // Set RC2 pin as output (PWM pin)PR2 = period; // PWM Period RegisterT2CON = 0x04 | prescale; // Timer2 Control Register (Timer ON + Prescale)CCPR1L = dutyCycle>>2; // Eight MSbs of Duty CycleCCP1CON = 0x0C | ((dutyCycle<<4)&0x30); // ECCP Control Register:// (PWM Mode + Two LSBs of Duty Cycle)TRISDbits.RD0 = 0 // Set RD0 pin as output (LED pin)while (1) {RD0 = 1; // LED ON__delay_ms(1000); // 1 Second DelayRD0 = 0; // LED OFF__delay_ms(1000); // 1 Second Delay}}

author: Lossyminor
author_rank: Newbie
timestamp: April 28, 2020, 05:50:50 pm
content: Thanks MarkF that example code really helped me to get a working example! I figured out I was incorrectly calculating the value for CCPR1L register - so in effect I was asking for 100% duty cycle!I have some working code as follows (using the list of #pragma config in the first code example);Code:#include <xc.h>#define _XTAL_FREQ 4000000void main(void){// PWM SETTINGSint prescale=1; // Timer2 prescale (1, 2 or 4)int period=100; // PWM period ( 1/freq )int dutyCycle=250; // PWM duty cycle// PACK PWM CONTROL REGISTERSTRISD = 0; // Set RC2 pin as output (PWM pin)PR2 = period; // PWM Period RegisterT2CON = 0x04 | prescale; // Timer2 Control Register (Timer ON + Prescale)CCPR1L = 0b00100000; // Eight MSbs of Duty CycleCCP1CON = 0b00001100; // ECCP Control Register:// (PWM Mode + Two LSBs of Duty Cycle)PSTRCON = 0b00001110;TRISDbits.RD0 = 0 ; // Set RD0 pin as output (LED pin)while(1) {CCPR1L = 4; // LED ON__delay_ms(1000); // 1 Second DelayCCPR1L = 32; // LED OFF__delay_ms(1000); // 1 Second Delay}}Now I just need to figure out how I really ought to calculate CCPR1L.

author: ggchab
author_rank: Frequent Contributor
timestamp: April 28, 2020, 06:38:16 pm
content: Yes, because of the timer prescalar, you loose 2 bits of resolution. So, usable values for the duty cycle are 0 - 255.To load the duty cycle, you could try a function like this one:Code:void PWM_LoadDC (unsigned char dc){unsigned char lsb;CCPR1L = dc >> 2; // Loads the 6 MSBits of the duty cycle.lsb = (dc & 0x0003) << 4; // Alignement of the 2 LSBits of the duty cycle.CCP1CON = (CCP1CON & 0xCF) | lsb; // Loads the 2 LSBits of the duty cycle.}