PWM pic18F45k22

Thread Starter

Nik@

Joined Apr 1, 2021
18
Hi all
I am trying to set a PWM of 1KHz and 50% duty cycle for a buzzer but no luck and i cannot figure out why. The TRIS is on RB0 and i use the CCP4 and timer 2. If someone can help. The code as follows:

Code:
/******************************************************************************/
/* Files to Include                                                           */
/******************************************************************************/

#if defined(__XC)
    #include <xc.h>        /* XC8 General Include File */
#elif defined(HI_TECH_C)
    #include <htc.h>       /* HiTech General Include File */
#elif defined(__18CXX)
    #include <p18cxxx.h>   /* C18 General Include File */
#endif

#if defined(__XC) || defined(HI_TECH_C)

#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */

#endif

#include "system.h"        /* System funct/params, like osc/peripheral config */
#include "user.h"          /* User funct/params, such as InitApp */
/******************************************************************************/
/* User Global Variable Declaration                                           */
/******************************************************************************/
#define _XTAL_FREQ 4000000

void PWM1_Set_Duty(uint16_t DC)
{
  // Check The DC Value To Make Sure it's Within 10-Bit Range
  if(DC<1024)
  {
    CCP4CONbits.DC4B1 = (DC & 1)>>1;
    CCP4CONbits.DC4B0 = (DC & 2);
    CCPR4L = DC >> 2;
  }
}

void main(void) {
    /* Configure the oscillator for the device */
    ConfigureOscillator();
    OSCCONbits.IRCF = 0x05; // @4 MHz MCU
    OSCCONbits.SCS = 0x00; // Internal oscillator block

    /* Initialise I/O and Peripherals for application */
    InitApp();
    //--[ Configure The CCP Module For PWM Mode ]--
    CCP4CONbits.CCP4M3 = 1;
    CCP4CONbits.CCP4M2 = 1;
    TRISB0 = 0; // The CCP1 Output Pin (PWM) is RB0
    // Set The PWM Frequency (1kHz)
    PR2 = 249;
    // Set The PS For Timer2 (1:4 Ratio)
    T2CONbits.T2CKPS0 = 1;
    T2CONbits.T2CKPS1 = 0;
    // Start CCP1 PWM !
    T2CONbits.TMR2ON = 1;
  
    while (1) {
        
        PWM1_Set_Duty(500);
    }
}
 

Thread Starter

Nik@

Joined Apr 1, 2021
18
Your program has no config bits.
my config.c
Code:
/******************************************************************************/
/* Files to Include                                                           */
/******************************************************************************/

#if defined(__XC)
    #include <xc.h>         /* XC8 General Include File */
#elif defined(HI_TECH_C)
    #include <htc.h>        /* HiTech General Include File */
#elif defined(__18CXX)
    #include <p18cxxx.h>    /* C18 General Include File */
#endif

/******************************************************************************/
/* Configuration Bits                                                         */
/*                                                                            */
/* Refer to 'HI-TECH PICC and PICC18 Toolchains > PICC18 Configuration        */
/* Settings' under Help > Contents in MPLAB X IDE for available PIC18         */
/* Configuration Bit Settings for the correct macros when using the C18       */
/* compiler.  When using the Hi-Tech PICC18 compiler, refer to the compiler   */
/* manual.pdf in the compiler installation doc directory section on           */
/* 'Configuration Fuses'.  The device header file in the HiTech PICC18        */
/* compiler installation directory contains the available macros to be        */
/* embedded.  The XC8 compiler contains documentation on the configuration    */
/* bit macros within the compiler installation /docs folder in a file called  */
/* pic18_chipinfo.html.                                                       */
/*                                                                            */
/* For additional information about what the hardware configurations mean in  */
/* terms of device operation, refer to the device datasheet.                  */
/*                                                                            */
/* General C18/XC8 syntax for configuration macros:                           */
/* #pragma config <Macro Name>=<Setting>, <Macro Name>=<Setting>, ...         */
/*                                                                            */
/* General HiTech PICC18 syntax:                                              */
/* __CONFIG(n,x);                                                             */
/*                                                                            */
/* n is the config word number and x represents the anded macros from the     */
/* device header file in the PICC18 compiler installation include directory.  */
/*                                                                            */
/* A feature of MPLAB X is the 'Generate Source Code to Output' utility in    */
/* the Configuration Bits window.  Under Window > PIC Memory Views >          */
/* Configuration Bits, a user controllable configuration bits window is       */
/* available to Generate Configuration Bits source code which the user can    */
/* paste into this project.                                                   */
/*                                                                            */
/******************************************************************************/

// PIC18F45K22 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1H
#pragma config FOSC = INTIO67   // Oscillator Selection bits (Internal oscillator block)
#pragma config PLLCFG = OFF     // 4X PLL Enable (Oscillator used directly)
#pragma config PRICLKEN = ON    // Primary clock enable bit (Primary clock is always enabled)
#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 PWRTEN = OFF     // Power-up Timer Enable bit (Power up timer disabled)
#pragma config BOREN = SBORDIS  // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
#pragma config BORV = 190       // Brown Out Reset Voltage bits (VBOR set to 1.90 V nominal)

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer Enable bits (Watch dog timer is always disabled. SWDTEN has no effect.)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = PORTC1  // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = ON      // PORTB A/D Enable bit (PORTB<5:0> pins are configured as analog input channels on Reset)
#pragma config CCP3MX = PORTB5  // P3A/CCP3 Mux bit (P3A/CCP3 input/output is multiplexed with RB5)
#pragma config HFOFST = ON      // HFINTOSC Fast Start-up (HFINTOSC output and ready status are not delayed by the oscillator stable status)
#pragma config T3CMX = PORTC0   // Timer3 Clock input mux bit (T3CKI is on RC0)
#pragma config P2BMX = PORTD2   // ECCP2 B output mux bit (P2B is on RD2)
#pragma config MCLRE = EXTMCLR  // MCLR Pin Enable bit (MCLR pin enabled, RE3 input pin disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = ON         // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled if MCLRE is also 1)
#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>
 
The only real thing I see won't cause it to not work...

Code:
CCP4CONbits.DC4B1 = (DC & 1)>>1;
CCP4CONbits.DC4B0 = (DC & 2);
Should be...
Code:
CCP4CONbits.DC4B1 = (DC & 2)>>1;
CCP4CONbits.DC4B0 = (DC & 1);
Other than that maybe... (just in case setting the bits individually is causing some strange thing to happen)
Code:
CCP4CON = 0x0c;
Can you blink an LED? With just the program you have listed it seems right... Maybe there is something else going on?!?

Also... while it shouldn't make a difference I would move
Code:
PWM1_Set_Duty(500);
to before the while{} It only needs to be set once
 

JohnInTX

Joined Jun 26, 2012
4,709
EDIT: @geekoftheweek posted while I was typing so some redundant advice but check out the port pin.
----------

I think one problem is that on a 45K22, CCP4 is output on RD1, not RB0. RB0 is CCP4 on a 2xK22 (Table 3 in the datasheet DS40001412H-page 7)

The inits and arithmetic appear OK but you should init 8-bit registers with an 8-bit value instead of a bit at a time. That way you are not relying on power on defaults and you are forced to show that you have considered ALL the bits in a register when doing your setup. It helps us too.
You should visit the ANSEL registers and disable the analog functions on all non-analog ports as part of your init.
Always initialize ALL bits of ALL IO - don't leave pins floating, even while playing around. Again, that also shows that you have visited all the pins in the various IO registers.
Microchip does not recommend single-bit writes to TRIS - for good reason. Figure out your IO directions and write ALL ports as bytes in your init phaze.

Finally, you don't need to rewrite the duty cycle over and over in the while loop. Sometimes there are consequences to writing peripheral registers that you only want to happen once e.g. clearing prescalers, resetting interrupt flags etc. Probably OK here but not a good habit.

Good luck!
 

Thread Starter

Nik@

Joined Apr 1, 2021
18
Hi all
Thanks all for the time you spend. I made all the changes that has been recommended by all with no result. If you come up with something else please let me know. I will still trying. If you have a ready code somewhere please post it to see any similarities.

thanks
 

JohnInTX

Joined Jun 26, 2012
4,709
I ported your original code to a PICDEM 2 PLUS board and 18F45K22 I have and it works as shown. The main problem was the uint_16_t type for DC in the duty cycle select. On my version of XC8, it threw warnings and always generated 0000. ???? Declaring DC as unsigned int fixed it. I fixed and verified the DC->duty cycle code by pausing the code and inspecting CCP4CON. I also scoped it and works as expected.
You'll need to tweak the IO setup for your board.
Have fun!

Standard PWM setup 18F45K22:
// PWM4 Setup for PICDEM 2 PLUS board
// PIC18F45K22 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1H
#pragma config FOSC = INTIO7    // Oscillator Selection bits (Internal oscillator block, CLKOUT function on OSC2)
#pragma config PLLCFG = OFF     // 4X PLL Enable (Oscillator used directly)
#pragma config PRICLKEN = ON    // Primary clock enable bit (Primary clock is always enabled)
#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 PWRTEN = ON      // Power-up Timer Enable bit (Power up timer enabled)
#pragma config BOREN = SBORDIS  // Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
#pragma config BORV = 190       // Brown Out Reset Voltage bits (VBOR set to 1.90 V nominal)

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer Enable bits (Watch dog timer is always disabled. SWDTEN has no effect.)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = PORTC1  // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<5:0> pins are configured as digital I/O on Reset)
#pragma config CCP3MX = PORTB5  // P3A/CCP3 Mux bit (P3A/CCP3 input/output is multiplexed with RB5)
#pragma config HFOFST = ON      // HFINTOSC Fast Start-up (HFINTOSC output and ready status are not delayed by the oscillator stable status)
#pragma config T3CMX = PORTC0   // Timer3 Clock input mux bit (T3CKI is on RC0)
#pragma config P2BMX = PORTD2   // ECCP2 B output mux bit (P2B is on RD2)
#pragma config MCLRE = EXTMCLR  // MCLR Pin Enable bit (MCLR pin enabled, RE3 input pin disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = ON         // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled if MCLRE is also 1)
#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>
#include <stdint.h>
#define _XTAL_FREQ 4000000      // 4 mhz internal osc

void initIO(void)            // for PICDEM 2 PLUS board, edit as necessary
{
    ANSELA = 0x00000001;    // RA0 is analog pot, rest are digital IO
    ANSELB = 0;             // PWM worked without ANSELs but still needed
    ANSELC = 0;
    ANSELD = 0;
    ANSELE = 0;

    LATA =  0b00010001;
    LATB =  0b00011001; // LED3 ON as a run test
    LATC =  0b10000000;
    LATD =  0b00010000;
    LATE =  0x0;

    TRISA = 0b00010001; // RA4: SW2 IN, RA0 is analog pot
    TRISB = 0b00000001; // LED 1-3: RB1-RB3, SW3Input:RB0
    TRISC = 0b10000000; // RC7: TXD, Rest out
    TRISD = 0b00000000; // RD1 is CCP4 output (and LCD DATA)
    TRISE = 0x00;
}

void setupPWM(void)
{
    CCP4CONbits.CCP4M3 = 1;
    CCP4CONbits.CCP4M2 = 1;
    PR2 = 249;
    T2CONbits.T2CKPS0 = 1;  // div by 4 prescaler
    T2CONbits.T2CKPS1 = 0;
    T2CONbits.TMR2ON = 1;   // start timer
}

void PWM4_Set_Duty(unsigned int DC)
{
    if(DC > 1023) DC = 1023;            // clip to 10 bits

    CCP4CONbits.DC4B1 = (DC & 2)>> 1;   // original code had bits interchanged
    CCP4CONbits.DC4B0 = (DC & 1);
    CCPR4L = DC >> 2;
}


void main(void)
{
    OSCCON = 0b01010000;            // 4MHz osc, 1MHz sys clock

    initIO();
    setupPWM();
    PWM4_Set_Duty(504);             // change by 1 to see bits in CCP4CON

    while (1);
}
scope_18.jpg
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,709
Hi all
Thanks for all efforts. Problem solved using a bit of all the sugestions. The thread can be closed.
regards
One last thing to consider would be clamping of the DC value to 1000. Since the PWM base counter is 1000 (249+1)*4, 1000 will be the limit.

Glad it is working. Well done!
 
Last edited:
Top