• 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.

PWM with PIC24FJ256GB110

drkidd22

Member
Hello all,

I've never played around with PWMon the pic24, so I've taken a look at the data sheet and came up with the below code, but I can't seem to obtain the output. I have a scope and I'm using the explorer 16 to see if I can get this working. The code compiles ok. I'm using the FRC as main clock signal, don't think this should be a problem. I'm trying to generate a 5KHz signal, but don't really understand how the values for OC1R = 1000;
OC1RS = 2000; should be set.

Code:
#include <P24FJ256GB110.h>

_CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx2 ) 
_CONFIG2( FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMOD_NONE & FNOSC_FRC & DISUVREG_ON & IOL1WAY_ON & IESO_OFF)

//Fast Internal RC (FRC) Oscillator 8MHz Clock Source

main ()
{

TRISAbits.TRISA0 = 0x00;
PORTAbits.RA0 = 0x00;

RPOR15bits.RP30R = 1;      //Comparator 1 Output to Pin RF2 PWM0

TRISFbits.TRISF8 = 0xFF;   //Set RF8 as input
TRISFbits.TRISF2 = 0x00;   //Set RF2 as Output for PWM0


void PWM0 (void)
{
OC1CON1 = 0;
OC1CON2 = 0;

OC1CON1bits.OCTSEL = 0x07;
OC1R = 1000;
OC1RS = 2000;

OC1CON1bits.OCM = 6;

}//endPWM0

 //PWM0 ();

while (1)
{
if (PORTFbits.RF8 == 1)
{
    PORTAbits.RA0 = 0xFF;
}

else if (PORTFbits.RF8 == 0)
{
PORTAbits.RA0 = 0x00;
}
}
}
 

phalanx

Member
You shouldn't define a function inside of main(). Move "void PWM0 (void)" and its associated code outside of main() or remove all the declaration syntax.

When you configure your peripheral pin select, the PWM functionality is generated by one of the output compare modules, not an analog comparator. As such, you need to change your PPS configuration to RPOR15bits.RP30R = 18; so Output Compare module #1 is selected.

OC1RS is used to set the period of your PWM signal and OC1R is used for pulse width. Assuming edge aligned mode, at time=0, the output pin of OC1 is set high and OC1TMR begins to increment based on your clock selection. When OC1TMR matches the value in OC1R, the output pin is set low. OC1TMR continues to increment until it matches OC1RS. When those values match OC1TMR is reset to zero, the output pin is set high, and the process starts all over again.

In your case, you are running at Fosc=8MHz and want a 5KHz PWM signal. Since you selected Fcy as your clock source and Fosc/2=Fcy, that means your timer will increment 4,000,000 times a second. To get 5KHz as per the datasheet: OC1RS = (Fcy/Fpwm)-1 = (4,000,000/5000)-1 = 799. You can then load any value into OC1R from 0-799 to set the dutycycle. OC1R = 399 would be roughly a 50% dutycycle.

The compare modules in the PIC you are using have lots of extra configurations to allow for synchronization and triggering that you don't need to worry about at the moment but they still need to be set up correctly to get basic PWM functionality. Here is how I would configure your output pin for a 5KHz 50% dutycycle waveform

Code:
RPOR15bits.RP30R = 18;               //OC1 Output to Pin RF2 PWM0
 
OC1CON1 = 0;                         // Clear registers
OC1CON2 = 0;
OC1RS   = 799;                       //5000Hz PWM period
OC1R = 399;                         //50% initial duty cycle
OC1CON2bits.SYNCSEL = 0b11111;     //synchronized by itself
OC1CON1bits.OCTSEL = 0b111;        //Peripheral clock is the source for output Compare
OC1CON1bits.OCM = 0b110;           // Edge-aligned PWM mode on OC
Unless you want to change frequency, there should be no reason to chance the OC1RS value while operating. Dutycycle changes can be made on the fly by updating the value stored in OC1R.
 
Last edited:

drkidd22

Member
Nice thanks. I will give that a shot when I get home from work. I now have better understanding of setting up PWM. I did all the calculations as per the datasheet and as you posted them, but didn't really know where to place the numbers. I also never used the PPS and got confused when selecting for PWM. Thanks again.
 

drkidd22

Member
That did the trick, setting everything as you stated worked. I got an output of 4.8Khz on the scope. Another question I have is, should the output be at the 3.3V signal level? I was getting more like 200mVp-p on the scope and not 3.3V.
 
Last edited:

drkidd22

Member
I've changed over to the PIC24FJ64GB002 since this is the device I will be using. Below is how I have the code setup. I don't get an output at PWMRef(); If I change to OC1 (18) and comment out PWMStep (); in the main function then the PWM output at RP14R (RB14) works ok. I'm not sure why this is not working or if it's even supposed to work.

Code:
#include <P24FJ64GB002.h>
 
_CONFIG1( FWDTEN_OFF  & ICS_PGx1 & GWRP_OFF & GCP_OFF & GWRP_OFF & JTAGEN_OFF) 
_CONFIG2( POSCMOD_NONE  & I2C1SEL_PRI & IOL1WAY_ON & OSCIOFNC_ON & FCKSM_CSDCMD  & FNOSC_FRC & PLL96MHZ_OFF & IESO_OFF)
 _CONFIG3(SOSCSEL_IO)

//Fast Internal RC (FRC) Oscillator 8MHz Clock Source
 
void PWMStep (void)
{

RPOR2bits.RP4R = 18;                               //OC1 Output to Pin RB4 PWM
OC1CON1 = 0;                                             // Clear registers
OC1CON2 = 0;
OC1RS   = 199;                                             //20KHz PWM period
OC1R = 100;                                                 //50% initial duty cycle
OC1CON2bits.SYNCSEL = 0x1F;               //synchronized by itself
OC1CON1bits.OCTSEL = 0x07;                //Peripheral clock is the source for output Compare
OC1CON1bits.OCM = 0b110;                     // Edge-aligned PWM mode on OC
} //endPWMStep 

void PWMRef (void)
{

RPOR7bits.RP14R = 19;                             //OC2 Output to Pin RB14 PWM
OC1CON1 = 0;                                             // Clear registers
OC1CON2 = 0;
OC1RS   = 199;                                             //20KHz PWM period
OC1R = 100;                                                 //50% initial duty cycle
OC1CON2bits.SYNCSEL = 0x1F;               //Synchronized by itself
OC1CON1bits.OCTSEL = 0x07;                  //Peripheral clock is the source for output Compare
OC1CON1bits.OCM = 0b110;                     // Edge-aligned PWM mode on OC
} //endPWMRef

main ()
{

PWMStep (); 
PWMRef ();
 
while (1)
{

}
}
 

phalanx

Member
You're going to kick yourself for this but in your PWMRef() function, you are setting your PPS output to OC2 but the registers you are configuring are for OC1.

OC1CON1 should be OC2CON1
OC1RS should be OC2RS
OC1R should be OC2R
etc.
etc.
 

drkidd22

Member
Yeah, I think I should punch my face really hard for that...but hey I'm just trying to make it here, I didn't look at the x in OCxCON1 or any of the other registers.. Thanks again.
 
Last edited:

drkidd22

Member
I get this positive spikes at the rising edges and negative spike at falling edges of the PWM signal when looking at the signal with an oscilloscope right at the PWM output pin with no other external circuit. Is this normal operation or is it an effect from me using the internal oscillator? or maybe is my DSO scope?
 
Last edited:

phalanx

Member
It could be a number of things. Make sure your project board is properly decoupled. Make sure all GND pins have a low impedance path to the common GND. Make sure you are using the correct probe on the scope. Make sure it has its GND (both power and signal) connected properly. Make sure the probes have been properly compensated.

Those are just a few ideas to start.
 

drkidd22

Member
It's me again, another little issue here, might be something I've over looked.

Code:
main ()
{

AD1PCFG = 0xFFFF;          //All pins are digital

TRISBbits.TRISB4 = 0;       //Set RB4 as output
PORTBbits.RB4 = 0;           //Initialize RB4 as low

TRISAbits.TRISA2 = 1;  //Set RA2 as input to trigger the PWM on RB4 when hi. Turn PWM off when RA2 low.

//PWMRef ();
//PWMVpfd();
 
while (1)
{

if (PORTAbits.RA2 == 1)
    {

PWMStep (); 
//PORTBbits.RB4 = 1;
  //              PORTBbits.RB4 = 0;
   }
else

PORTAbits.RB4 = 0;
}
}
So the basic thing I'm trying to accomplish is to start the PWM signal on RB4 (OC1) when the input on RA2 is HI and PWM should be off when RA2 is LOW. The code I have above is not doing what I want. What it does is I get a signal of about 100kHz PWM at OC1 when RA2 is high and the 20Khz PWM when RA2 is low. I've already assigned RB4 as digital and assigned all pins as analog. Not sure what else I'm missing. I'm simply applying 3.3V to RA2 to test and look for the changes on OC1, RA2 is pulled low with 10K resistor.
 
Last edited:

drkidd22

Member
I think I'm getting there by turning of the output compare using OC1CON1bits.OCM = 0b000;. But the while loop is killing which sky rockets the PWM by x5
 

phalanx

Member
Once you configure the OC module to output, if you want the PWM signal to shut off all you need to do is write 0 (zero) to OCxR. This sets your duty cycle to zero.

Code:
while(1)
{
     if(PORTAbits.RA2 == 1)
          OC1R = (any nonzero value <= OC1RS);
     else
          OC1R = 0;
}
On a side note, you were initially trying to write values to your I/O pins using the PORT registers. PORT is only used for reading inputs. If you want to write values out to a pin you have to use the LATCH registers. For example:

LATBbits.LATB4 = 1; instead of PORTBbits.RB4 = 1;
 

Gaurav9527

New Member
PWM in P24FJ64GA004

I am using the following code for generating a PWM wave of 20KHz on P24FJ64GA004 using Explorer 16 board. When i checked it with the DSO the PWM frequency is coming around 5KHz. I am not sure what is the mistake i am doing. Please let me know my mistake. Thanks!!!!

#include <p24fj64ga004.h>

_CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx1)
_CONFIG2( FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMOD_XT & FNOSC_PRI & I2C1SEL_SEC & IOL1WAY_ON)

//Fast Internal RC (FRC) Oscillator 8MHz Clock Source

void PWMStep (void)
{

RPOR2bits.RP4R = 18; //OC1 Output to Pin RB4 PWM
OC1CON = 0; // Clear registers
PR1=799; //20KHz PWM
OC1RS = 199;
OC1R = 69;
OC1CONbits.OCTSEL = 0x00; //Timer 2 is the source for output Compare
OC1CONbits.OCM = 0b110; // Edge-aligned PWM mode on OC
T1CONbits.TON=1;
T1CONbits.TCKPS1=0;
T1CONbits.TCKPS0=0;
} //endPWMStep

void PWMRef (void)
{

RPOR7bits.RP14R = 19; //OC2 Output to Pin RB14 PWM
OC2CON = 0; // Clear registers
//OC2CON = 0;
PR2=799; //20KHz PWM period
OC2RS = 199;
OC2R = 50;
OC2CONbits.OCTSEL = 0x00;
OC2CONbits.OCM = 0b110;
T2CONbits.TON=1;
T2CONbits.TCKPS1=0;
T2CONbits.TCKPS0=0;
} //endPWMRef

main ()
{

PWMStep ();
PWMRef ();

while (1)
{

}
}
 

drkidd22

Member
Try commenting out PR1 = 799 and PR2 = 799; I think you are redefining the timers. Or you can try changing them to 199.
 

Gaurav9527

New Member
Thanks for your reply. Actually as per the datasheet of P24FJ64GA004 PWM period is determined by PR2 register, so i was using that. I will follow your steps and check again.
 

Gaurav9527

New Member
Thanks drkidd22, your suggestions helped a lot. Now I am trying to capture a square wave using input capture module of PIC24FJ64GA004. I can't get the correct output on the LCD. Below is the code.

#include <p24FJ64ga004.h>
#include "LCD.h"
#include <stdio.h>


_CONFIG1( JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx1)
_CONFIG2( FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMOD_XT & FNOSC_PRI & I2C1SEL_SEC & IOL1WAY_ON)

int i;
unsigned int cap1; // first capture variable
unsigned int cap2; // second capture variable

void timer3init (void)
{
TMR3 = 0;
T3CONbits.TON = 0;
T3CONbits.TCS = 1; // above line is commented since it is selecting an internal timer but in the formulas below external oscillator of 16MHz is used.added by gaurav
T3CONbits.TCKPS= 0; // prescale 1:1
T3CONbits.TGATE = 0;
T3CONbits.TSIDL = 0;
T3CONbits.TON = 1; //Timer 3 Enable

}

void capinit1(void)
{
IC1CONbits.ICM = 3; //capture edge every rising edge
IC1CONbits.ICI = 1; //interrupt on every second capture
IC1CONbits.ICTMR = 0; //used tmr3
IC1CONbits.ICSIDL = 0;
}

void capinit2(void)
{
IC1CONbits.ICM = 4; //capture edge every 4th rising edge
IC1CONbits.ICI = 1; //interrupt on every second capture
IC1CONbits.ICTMR = 0; //used tmr3
IC1CONbits.ICSIDL = 0;
}

void capinit3(void)
{
IC1CONbits.ICM = 5; //capture edge every 16th rising edge
IC1CONbits.ICI = 1; //interrupt on every second capture
IC1CONbits.ICTMR = 0; //used tmr3
IC1CONbits.ICSIDL = 0;
}

void capclear(void)
{
IC1CON = 0x0000;
}

void __attribute__((interrupt,no_auto_psv)) _IC1Interrupt(void)
{


cap1 = IC1BUF;
cap2 = IC1BUF;

IFS0bits.IC1IF = 0;

}





int main(void)

{
unsigned long frequency;
unsigned int counts;


char results[100];
char less[]="No pulse input";

IPC0bits.IC1IP = 1; //level 1 priority
IFS0bits.IC1IF = 0; //clear the IC1 interrupt status flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupts


// TRISDbits.TRISD8=1; // Make RD8/ IC1 pin as an imput
// TRISCbits.TRISC14=1;
TRISBbits.TRISB14=1;

timer3init();
capclear();
capinit1();

resetLCD();
initLCD();


while (1)

{

if(cap1==0&&cap2==0)
{
// dispfirstLine(0);
dispsecLine(0);//added by gaurav
putstringLCD(less);
capclear();
DELAY();
DELAY();
clrLCD();
}


if (((cap1==0)&&(cap2!= 0)) || (cap1>cap2)) // reset Timer3 if cap1 is greater than cap2
{
T3CONbits.TON = 0;
TMR3=0;
T3CONbits.TON = 1;
}



if (frequency <=700000)

{
capclear();
capinit1();

counts = cap2-cap1;


// frequency = ((16000000)/counts);
frequency = ((8000000)/counts);
}

else if ((frequency>700000)&&(frequency<3000000))

{
capclear();
capinit2();

counts = cap2-cap1;
//frequency = ((16000000*4)/counts);
frequency = ((8000000*4)/counts);

}

else if (frequency>=3000000)
{

capclear();
capinit3();

counts = cap2-cap1;
//frequency = ((16000000*16)/counts);
frequency = ((8000000*16)/counts);

}




sprintf(results,"Freq:%ld Hz",frequency); // Put the value of frequency in an char array
clrLCD();

dispsecLine(0);
putstringLCD(results);
}
}
 

eyepatch

New Member
Hello, I am using pic24fj256gb110gb mcu.
I am also generating pwm & incrementing the frequency by 2% .
I think there is some jitteer in the pwm.
As far as I know I need to update the Pwm period when the timer rolls over.
Code:-
while(***)
{
PWM();
}
void PWM(void)
{
T3CONbits.TON=0;
T3CONbits.TCKPS=0;
T3CONbits.TON=0;
T3CONbits.TCS=0;
IFS0bits.T3IF = 0;
PR3=spreiod;
IEC0bits.T3IE = 1;
T3CONbits.TON=1;

TRISFbits.TRISF2 = 0; //Set RF2 as output
RPOR15bits.RP30R = 18; //OC1 Output to Pin PWM
LATFbits.LATF2 = 0; //Initialize RF2 as low
OC1CON1 = 0; // Clear registers
OC1CON2 = 0;
OC1CON2bits.SYNCSEL = 0b11111; //synchronized by itself
OC1CON1bits.OCTSEL = 0b111; //system clock is the source for output Compare
OC1CON1bits.OCM = 0b111;
OC1CON1bits.OCSIDL=1;
OC1RS = (int)(PWMpreiod-1); // PWM period
OC1R = (int)((PWMpreiod/2)-1 ); //50% initial duty cycle
--------------
----
-------------
----
}
but I am using system ckock. So How i am supposed to do that?
Incase if I use timer 3, //octsel= 001 , syncsel=01101
What will be the event /syntax to update OC1RS?
 

Nigel Goodwin

Super Moderator
Most Helpful Member
Hello, I am using pic24fj256gb110gb mcu.
I am also generating pwm & incrementing the frequency by 2% .
I think there is some jitteer in the pwm.
As far as I know I need to update the Pwm period when the timer rolls over.
Well the thread is now ten years old!.

But the PWM modules are transparent to changes, you simply update their value and the change happens seamlessly - read the datasheet.
 

EE World Online Articles

Loading

 
Top