Author Topic: Generating PIC18F45K20 PWM Using ECCP  (Read 1077 times)

0 Members and 1 Guest are viewing this topic.

Offline LossyminorTopic starter

  • Newbie
  • Posts: 3
  • Country: gb
Generating PIC18F45K20 PWM Using ECCP
« on: April 28, 2020, 03:01:09 pm »
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) = 0b00000111
Register CCPR1L = 0b10000000 - 512/1024 for 50% duty
Register 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 output

However 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: [Select]
// 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 4000000

int main()
{
  TRISD0 = 0; //RD0 as Output PIN port data direction register

  while(1)
  {
    RD0 = 1;  // LED ON
   
      __delay_ms(50); // 1 Second Delay
    RD0 = 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.  ^-^

« Last Edit: April 28, 2020, 04:44:22 pm by Lossyminor »
 

Offline ggchab

  • Frequent Contributor
  • **
  • Posts: 276
  • Country: be
Re: Generating PIC18F45K20 PWM Using ECCP
« Reply #1 on: April 28, 2020, 04:30:13 pm »
I don't know that specific PIC but I think you have done almost everything  :) Just configure the other registers as you did for TRISD:

Code: [Select]
TRISD = 0;            // All PortD lines as output
PR2 = 0b11111111;     // for 245Hz pwm frequency with a 4MHz internal osc and tmr2 prescale set to 16
T2CON = 0b00000111;   // enable Timer2 and set prescalar (in my case 16)
CCPR1L = 0b10000000;  // 512/1023 for 50% duty
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.
 
The following users thanked this post: Lossyminor

Offline LossyminorTopic starter

  • Newbie
  • Posts: 3
  • Country: gb
Re: Generating PIC18F45K20 PWM Using ECCP
« Reply #2 on: April 28, 2020, 04:43:27 pm »
Thanks ggchab - seems it very simple to set the bits! I change the main portion of my program to the following:

Code: [Select]
#include <xc.h>

#define _XTAL_FREQ 4000000

int main()
{
TRISD = 0;            // All PortD lines as output
PR2 = 0b11111111;     // for 245Hz pwm frequency with a 4MHz internal osc and tmr2 prescale set to 16
T2CON = 0b00000111;   // enable Timer2 and set prescalar (in my case 16)
CCPR1L = 0b10000000;  // 512/1023 for 50% duty
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.

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?
 

Offline MarkF

  • Super Contributor
  • ***
  • Posts: 2539
  • Country: us
Re: Generating PIC18F45K20 PWM Using ECCP
« Reply #3 on: April 28, 2020, 04:45:32 pm »
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: [Select]
void main(void)
{
   // PWM SETTINGS
   uint8_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 REGISTERS
   TRISCbits.RC2 = 0                        // Set RC2 pin as output (PWM pin)
   PR2 = period;                            // PWM Period Register
   T2CON = 0x04 | prescale;                 // Timer2 Control Register (Timer ON + Prescale)
   CCPR1L = dutyCycle>>2;                   // Eight MSbs of Duty Cycle
   CCP1CON = 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 Delay
      RD0 = 0;           // LED OFF
      __delay_ms(1000);  // 1 Second Delay
   }
}

« Last Edit: April 28, 2020, 04:51:31 pm by MarkF »
 
The following users thanked this post: Lossyminor

Offline LossyminorTopic starter

  • Newbie
  • Posts: 3
  • Country: gb
Re: Generating PIC18F45K20 PWM Using ECCP
« Reply #4 on: April 28, 2020, 05:50:50 pm »
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: [Select]
#include <xc.h>

#define _XTAL_FREQ 4000000

void main(void)
{
   // PWM SETTINGS
   int prescale=1;    // Timer2 prescale (1, 2 or 4)
   int period=100;    // PWM period ( 1/freq )
   int dutyCycle=250;  // PWM duty cycle

   // PACK PWM CONTROL REGISTERS
   TRISD = 0;                        // Set RC2 pin as output (PWM pin)
           
   PR2 = period;                            // PWM Period Register
   T2CON = 0x04 | prescale;                 // Timer2 Control Register (Timer ON + Prescale)
   CCPR1L = 0b00100000;                   // Eight MSbs of Duty Cycle
   CCP1CON = 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 Delay
      CCPR1L = 32;           // LED OFF
      __delay_ms(1000);  // 1 Second Delay
   }
}

Now I just need to figure out how I really ought to calculate CCPR1L.  ;D
 

Offline ggchab

  • Frequent Contributor
  • **
  • Posts: 276
  • Country: be
Re: Generating PIC18F45K20 PWM Using ECCP
« Reply #5 on: April 28, 2020, 06:38:16 pm »
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: [Select]
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.
 }
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf