Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Pic18f4520 pwm

Status
Not open for further replies.

drkidd22

Member
Hello,

I've successfully implemented PWM with the PIC18F4520. But my problem now is that I'm trying to run a PWM @ 50Hz. I'm using the internal clock of the pic18 and have configured OSCCON for 8Mhz, but with these settings I have not been able to get the PWM frequency down to 50Hz I need. Any Ideas??
 
The only way to get 50Hz PWM is by using the PWM module in a different way. Have a look at this thread and check out page 2 where I show how to use the PWM module to generate 50Hz pulses using interrupts.

Mike.
 
Yea I have to find some other way either i have to lower my clock or do it the interrupt way. The timer2 can only divide by max of 16 which limits the pwm frequency i can get
 
You don't use timer2, you use timer1 with the PWM module in Special Event Trigger mode. If you explain what you are trying to do it would be much easier to advise the best method.

Mike.
 
Hi,

I had a play with this and managed to get 50Hz pwm but had to use a clock freq' of 500KHz. 157 discrete steps between 0 and 100% duty cycle.
 
Hi,

I had a play with this and managed to get 50Hz pwm but had to use a clock freq' of 500KHz. 157 discrete steps between 0 and 100% duty cycle.

Correct that's what I'll have to do if there is another way I dn't know how. What I'm trying to do is get a pwm output of 50hz to control a fan
 
This can be done as I outlined in my first reply. You can easily get 100 steps at 50Hz with this method. If you use the HSPPL at 40MHz then you could easily get 1000 discreet steps.

Mike.
 
Hi,

I found this interesting so I had a go myself.

Might not have done the best job on slicing and dicing Pommies' code, but it does work.

Code:
/*13:47 22 March 2013
* E:\Electronics\PIC Projects\Electrotech\drkidd22\4520 PWM\Pommie Main.c
* 
* This is Pommies code from here: https://www.electro-tech-online.com/threads/junebug-servo-c18-code.36127/
* Hacked about a bit. Gives 100 discrete duty cycle steps at PWM freq = 50Hz.
*
* PIC 18F4520 / MPLAB 8.88 / xc8 v1.12 (free)
*/
/*------------------------------------------------------------------------*/
#include <xc.h>

#pragma config WDT = OFF, LVP = OFF, OSC = INTIO67

#define PWMPin  LATCbits.LATC2
#define PWMTris TRISCbits.TRISC2

/*------------------------------------------------------------------------*/
#define _XTAL_FREQ  (4000000)   //4MHz
/*------------------------------------------------------------------------*/
volatile int temp;
char Duty_cycle;
/*------------------------------------------------------------------------*/
void interrupt ccpisr_int()
{
    
    if(PWMPin==1)               //Will be 1 if we are at end of pulse
    {           
        PWMPin=0;
        CCPR1 = 5000 - temp;    //Off time
        }

    else
    {
        PWMPin=1;
        CCPR1 = temp;           //On time
        }

    if(Duty_cycle == 0)
    {
        PWMPin = 0;
        }

     if(Duty_cycle == 100)
     {
         PWMPin = 1;
         }
        
    PIR1bits.CCP1IF=0;          //Clear int flag
    return;
}

void main()
{
    OSCCON=0b01100000;          //Osc=4MHz
    PWMTris=0;                  //Make bit 0 output
    PWMPin=0;                   //Output off
    CCP1CON=0b00001011;         //Special event trigger
    CCPR1=2500;                 //Set CCP initial value 
    PIR1bits.TMR1IF = 0;        //Clear the interrupt flag
    T1CON=0b10100001;           //Timer 1 on with Pre = 4
    PIE1bits.CCP1IE=1;          //Enable CCP1 interrupt
    INTCONbits.PEIE=1;          //Enable peripheral interrupts
    INTCONbits.GIE=1;           //Enable glogal interrupts
    PWMPin=1;                   //Turn on output

while(1)
{
    for(char dc = 0; dc < 100; dc++)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        __delay_ms(100);
        }

    for(char dc = 99; dc > 0; dc--)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        __delay_ms(100);
        }
}
}

Modified the code a bit because could not get exactly 0 and 100% duty cycles. Bit of a code bodge but it seems to work.

https://s178.photobucket.com/user/gruntstripe/media/Electrotech/MVI_1310.mp4.html
 
Last edited:
I tried your code and it works thanks. But I get a syntax error if I try to run it C18 Toolsuite. When I run it with XC8 it works just fine. I need to use the C18 Compiler and not the newer XC8 as I'm using some of the libraries and they don't seem to be compatible. Below is the code I have, the problem happens when I put in the for loops in the while function, other than that it compiles fine, but can't confirm the PWM output yet.

Code:
/*************INCLUDES****************
**************************************/

#include <p18f4520.h>
#include <delays.h>
#include <stdio.h>
#include <adc.h>
#include <math.h>
#include <pwm.h>
#include <timers.h>
#include "XLCD/xlcd.h"

#pragma config OSC = INTIO67
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config BOREN = OFF
#pragma config PBADEN = ON

#define PWM1_DIR TRISCbits.TRISC1
#define PWM1_OUT LATCbits.LATC1
#define PWM2_DIR TRISCbits.TRISC2
#define PWM2_OUT LATCbits.LATC2

/*************FUNCTION PROTOTYPES**************
**********************************************/
void ReadTemp (void);

int TempCi;
volatile int temp;
char Duty_cycle;
/*********************************************/


#pragma interrupt CCP2_ISR
void CCP2_ISR()
{
    if(PWM1_OUT == 1)               //Will be 1 if we are at end of pulse
    {
        PWM1_OUT = 0;
        CCPR1 = 5000 - temp;    //Off time
        }
    else
    {
        PWM1_OUT = 1;
        CCPR1 = temp;           //On time
        }

    if(Duty_cycle == 0)
    {
        PWM1_OUT = 0;
        }

     if(Duty_cycle == 100)
     {
         PWM1_OUT = 1;
         }

    PIR1bits.CCP1IF=0;          //Clear int flag
    return;
}

void main (void)
{

    //Open ADC CH7 for thermistor readings
OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_4_TAD,
ADC_CH7 & ADC_INT_OFF & ADC_REF_VDD_VSS, ADC_7ANA);

//Open ADC CH11 for pressure readings
OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_4_TAD,
ADC_CH11 & ADC_INT_OFF & ADC_REF_VDD_VSS, ADC_11ANA);

ADCON1 = 0b0001110;
OSCCON=0b01100000;          //Osc=4MHz
//OSCCON = 0xF2;  //configure for internal oscillator, 8MHz
PWM1_DIR = 0;           //Make bit 0 output
PWM1_OUT = 0;           //Output off
CCP1CON = 0b00001011;   //Special event trigger
CCPR1 = 2500;           //Set CCP initial value
PIR1bits.TMR1IF = 0;    //Clear the interrupt flag
T1CON = 0b10100001;     //Timer 1 on with Pre = 4
PIE1bits.CCP1IE=1;      //Enable CCP1 interrupt
INTCONbits.PEIE=1;      //Enable peripheral interrupts
INTCONbits.GIE=1;       //Enable glogal interrupts
PWM1_OUT = 1;           //Turn on output

XLCDInit();  	//Initialize the LCD display
XLCDClear();	//Clear the LCD display

while (1)
{

        for(char dc = 0; dc < 100; dc++)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        //__delay_ms(100);
        }

    for(char dc = 99; dc > 0; dc--)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        //__delay_ms(100);
        }
        
ReadTemp ();


}
CloseADC();			//Close the ADC

}

void ReadTemp (void)
{

/*************Temperature Coefficients****************
-Thermistor temperature coefficients
******************************************************/
const float coeffA = 1.12503e-3;
const float coeffB = 2.35058e-4;
const float coeffC = 7.85661e-8;

char line[16]; 				//16row LCD Disp
float Rt, TempC, ADCVal;

SetChanADC(ADC_CH7);
XLCDDelay4ms ();
ConvertADC(); 				// Start conversion
while( BusyADC() ); 		// Wait for completion
XLCDDelay4ms ();
ADCVal = (float)ReadADC(); // Read ADC
Rt = ((1024.0/ADCVal)-1)*10000.0; //10000 is the value of R21 on PCB
XLCDDelay4ms ();
TempC = (1/(coeffA+(coeffB*(log(Rt)))+(coeffC * (exp(log(Rt)),3))))-273.15;
XLCDDelay4ms ();
XLCDL1home();
XLCDPutRomString("  TEMP :: ");
TempCi = (int)TempC;

if (TempCi > 0)
{
sprintf(line,"%d",TempCi);
XLCDPutRamString(line);
	}
else
XLCDPutRomString("ERR");
XLCDPut(223); 				//Send degree symbol
XLCDPut('C');
XLCDPutRomString("                ");

}

Edit Notes about C18 for loop:
The C18 compiler doesn't seem to like the declaration of variables within the for loop and must be declared external, maybe a global variable.
 
Last edited:
Thanks guys, I finally got it working with the clock configured for 8Mhz now. But i'm not sure how the calculation is done for timer 1 to generate the 50Hz PWM signal.
The updated code is below. All I did was changed the clock frequency and timer1 pre-scaler, but I'll like to fing out what calculations take place to get to this point.

Code:
/*13:47 22 March 2013
* E:\Electronics\PIC Projects\Electrotech\drkidd22\4520 PWM\Pommie Main.c
* 
* This is Pommies code from here: https://www.electro-tech-online.com/threads/junebug-servo-c18-code.36127/
* Hacked about a bit. Gives 100 discrete duty cycle steps at PWM freq = 50Hz.
*
* PIC 18F4520 / MPLAB 8.88 / xc8 v1.12 (free)
*/
/*------------------------------------------------------------------------*/
#include <xc.h>
 
#pragma config WDT = OFF, LVP = OFF, OSC = INTIO67
 
#define PWMPin  LATCbits.LATC2
#define PWMTris TRISCbits.TRISC2
 
/*------------------------------------------------------------------------*/
#define _XTAL_FREQ  (8000000)   //4MHz
/*------------------------------------------------------------------------*/
volatile int temp;
char Duty_cycle;
/*------------------------------------------------------------------------*/
void interrupt ccpisr_int()
{
 
    if(PWMPin==1)               //Will be 1 if we are at end of pulse
    {           
        PWMPin=0;
        CCPR1 = 5000 - temp;    //Off time
        }
 
    else
    {
        PWMPin=1;
        CCPR1 = temp;           //On time
        }
 
    if(Duty_cycle == 0)
    {
        PWMPin = 0;
        }
 
     if(Duty_cycle == 100)
     {
         PWMPin = 1;
         }
 
    PIR1bits.CCP1IF=0;          //Clear int flag
    return;
}
 
void main()
{
    //OSCCONbits.IRCF = 0b110;      //Osc = 4MHz
	OSCCON = 0xF2;  				//Osc = 8MHz
    PWMTris = 0;                  //Make bit 0 output
    PWMPin = 0;                   //Output off
    CCP1CONbits.CCP1M = 0b1011;         //Special event trigger
    CCPR1 = 2500;                 //Set CCP initial value
	//CCPR1 = 2500;                 //Set CCP initial value 
    PIR1bits.TMR1IF = 0;          //Clear the interrupt flag
    T1CON = 0b10110001;           //Timer 1 on with Pre = 8
    PIE1bits.CCP1IE = 1;          //Enable CCP1 interrupt
    INTCONbits.PEIE = 1;          //Enable peripheral interrupts
    INTCONbits.GIE = 1;           //Enable glogal interrupts
    PWMPin  = 1;                   //Turn on output
 
while(1)
{
    for(char dc = 0; dc < 100; dc++)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        __delay_ms(50);
        }
 
    for(char dc = 99; dc > 0; dc--)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        __delay_ms(50);
        }
}
}
 
Last edited:
Never mind last post about calculating the PWM frequency, I figured out. Now I'm trying to make this work on C18 V.3.45. I think I'm not configuring my ISR correctly (I never used ISR before) or there's something I'm missing, but the scope doesn't display the correct frequency or the duty cycle of PWM changing, it just jumps around.

Code:
#include <p18f4520.h>
#include <delays.h>

#pragma config OSC = INTIO67		//Use internal Oscillator
#pragma config WDT = OFF                //Watchdog timer off
#pragma config LVP = OFF		//Low voltage ICSP disabled
#pragma config BOREN = OFF		//Brown-out reset disabled
#pragma config PBADEN = OFF		//Port B A/D digital on reset

void ccpisr_hand(void);

#define PWMPin  LATCbits.LATC2
#define PWMTris TRISCbits.TRISC2
volatile int temp;
char Duty_cycle;
char dc;

void main()
{
    OSCCON = 0xF2;  			//Osc = 8MHz
    PWMTris = 0;                  	//Make bit 0 output
    PWMPin = 0;                   	//Output off
    CCP1CONbits.CCP1M = 0b1011;         //Special event trigger
    CCPR1 = 2500;                 	//Set CCP initial value
    PIR1bits.TMR1IF = 0;          	//Clear the interrupt flag
    T1CON = 0b10110001;           	//Timer 1 on with Pre = 8
    PIE1bits.CCP1IE = 1;          	//Enable CCP1 interrupt
    INTCONbits.PEIE = 1;          	//Enable peripheral interrupts
    INTCONbits.GIE = 1;           	//Enable glogal interrupts
    PWMPin  = 1;                   	//Turn on output

while(1)
{
    for(dc = 0; dc < 100; dc++)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        Delay1KTCYx (210);
    }

    for(dc = 99; dc > 0; dc--)
    {
        Duty_cycle = dc;
        temp = (Duty_cycle * 50);
        Delay1KTCYx (210);
    }
}
}

#pragma code ccpisr_int = 0x08
void ccp_int (void)
{
    _asm GOTO ccpisr_hand _endasm
}
#pragma code

#pragma interrupt ccpisr_hand
void ccpisr_hand()
{

    if(PWMPin == 1)               //Will be 1 if we are at end of pulse
    {
        PWMPin = 0;
        CCPR1 = 5000 - temp;    //Off time
        }

    else
    {
        PWMPin = 1;
        CCPR1 = temp;           //On time
        }

    if(Duty_cycle == 0)
    {
        PWMPin = 0;
        }

     if(Duty_cycle == 100)
     {
         PWMPin = 1;
         }

    PIR1bits.CCP1IF = 0;          //Clear int flag
    return;
}
 
Did you run it with C18 or XC8? It works when I run it with XC8, not C18. I'll post an image of what my scope does when I run it in C18 compiler.
 
C18 is Hi-Tech C? didn't know that.

I'll try to get the same revision your using. Have to go to work shortly so might be tomorrow before I could try it.
 
C18 is Hi-Tech C? didn't know that.

I'll try to get the same revision your using. Have to go to work shortly so might be tomorrow before I could try it.

Not really... The C16,C17 and C18 compilers weren't made by Hi-tech... I have C18 v3.40 and its NOT the same code as XC8....

I apologize if this is what you meant...
 
So there is:
1.) MPLABs C18 by Microchip**broken link removed** V3.45
2.) HI-TECH C for PIC18 MCU **broken link removed** V9.8
3.) MPLAB® XC: Compiler **broken link removed** XC8 V1.12

I'm using #1 above, the latest I think is XC8 #3. I'm not sure, but I think XC8 is a mix of #1 and #2.
 
Last edited:
Status
Not open for further replies.

Latest threads

Back
Top