# PWM 1HZ PIC18F14K50

So, I'm trying to achieve a 1HZ PWM frequency with a Duty Cycle of about 70%. The problem that I encountered was with the minimum PWM frequency that the PIC18F14K50 can reach, which is about 1.892HZ, using PR2=255(max),FOSC equals to 31kHZ(changed from 16MHZ) and prescaler of TIMER2=16:

PWM FREQUENCY= (PR2+1)*4*TOSC*prescaler

So my question resides in a manner to achieve that 1HZ for the PWM Frequency with a Duty Cycle of 70%.

Notes: Can't use delays(function) and can't change the hardware(PIC), so the only option is via Software.

Bellow is the code used to achieve a frequency of 1.892HZ:

//Variables
int DC; // Duty cycle
//
OSCCON=0; // 31kHZ

// Configure PWM Module 1.892 HZ
OpenTimer2(T2_PS_1_16); // Prescaler 16
OpenPWM1(0xFF); //Configure PWM module and initialize 1 HZ ; PR2 =255
SetDCPWM1(0); // set duty cyle
SetOutputPWM1(SINGLE_OUT, PWM_MODE_1);

void LED(void) {

DC=70; // 70%

SetDCPWM1((int) DC * 10.23); //set the duty cycle 70% in 10bits//
}


EDIT: Trying to follow the comments of all of you I tried this with TIMER0 and now with FOSC= 16MHZ :

void highISR(void) {
// Check if there was an overflow in TIMER0
if (INTCONbits.TMR0IF == 1) {
LATCbits.LATC5=1; // LED ON

INTCONbits.TMR0IF = 0;
WriteTimer0(Ktimer);
}
}

#pragma code HighInterruptVector=0x0008

void HighInterruptVector(void) {
_asm
goto highISR
_endasm
}
#pragma code
// CONFIG TIMER0
OpenTimer0(TIMER_INT_ON & // Use interruptions
T0_16BIT & // 16 bit timer
T0_SOURCE_INT & // Use internal clock as source
T0_EDGE_FALL & // Update on falling edge
T0_PS_1_128); // 128 prescaler (lowest period possible)
// FOSC = 16 MHz ==> FTY = 16/4 = 4MHz ==> -------TCY = 250 ns
// Timer 0 prescaler = 128 ==> Count on every--------- 250 ns * 128 = 32 us
// 1 seg counting = --------1s / 32u = 31250
//31250*0,7 =21875 70% DC

Ktimer = 65536 - (21875);
//WriteTimer0(Ktimer);

// So the flag occurs
While(1){

WriteTimer0(Ktimer)
LATCbits.LATC5=0; // LED OFF
}


But led wont turn on. 100% certain that the fault is in how the code is constructed.

• Have you tried using an interrupt instead? Jan 19, 2017 at 15:11
• For something as slow as 1Hz, I'd just use a timer interrupt and bit-bang the "PWM". Jan 19, 2017 at 15:19
• I agree with the others. I personally go for a timer-interrupt approach when the MCU does not have any PWM output. For example, setup the timer for every 1ms and under interrupt routine, increment a register/variable until the desired value then set or clear the corresponding bit of the output register. Jan 19, 2017 at 15:53
• Edited in original post.
– Lip
Jan 19, 2017 at 16:31

Final answer to anyone wondering here's the code that worked for me. It's simple , because I really don't need a very specific code or precision for the work I'm doing. Wanna thank you all that commented and helped me get the right ideas.

#include <p18f14k50.h>
#include <stdlib.h>
#include <delays.h>
#include <timers.h>

int Ktimer; // variable for overflow
int time1=10; // Period 1HZ 1*0.1 from Ktimer = 1 second
int time2=7; // Duty Cycle
//--------------------------- End of Variables ------------------------------------
//*********************************************************************************

void PWM_LED(void){

if (time2 > 0)
LATAbits.LATA5=0; // LED ON
else
LATAbits.LATA5=1; // LED OFF
}
#pragma interruptlow highISR

void highISR(void) {
// Check if there was overflow by the timer
if (INTCONbits.TMR0IF == 1) {

time1--;
time2--;

INTCONbits.TMR0IF = 0; // Put Flag at zero again
WriteTimer0(Ktimer);

}
}

#pragma code HighInterruptVector=0x0008

void HighInterruptVector(void) {
_asm
goto highISR
_endasm
}
#pragma code

//*********************************************************************************
//-------------------------------------- Main -------------------------------------
//*********************************************************************************
void main (void)
{

//****************************Other congigurations*********************************
OSCCON=0x70;        // Select 16 MHz internal clock

//************************************Setups***************************************

// Interrupts
INTCONbits.GIEH = 1; // Enable all high priority interrupts (also required for LOW priority)

INTCONbits.TMR0IF = 0; //TMR0 Overflow Interrupt Flag bit (must be cleared by software) AO rebentar activa a flag

INTCONbits.TMR0IE = 1; //Enable TMR0 Overflow Interrupt Enable bit
INTCON2bits.TMR0IP = 1; //Set TMR0 Overflow Interrupt Priority bit: 1 = High priority

// Configure Timer0  0,1 seconds
OpenTimer0(TIMER_INT_ON & // Use interruptions
T0_16BIT & // 16 bit timer
T0_SOURCE_INT & // Use internal clock as source
T0_EDGE_FALL & // Update on falling edge
T0_PS_1_128); // 128 prescaler (lowest period possible)
// FOSC = 16 MHz ==> FTY = 16/4 = 4MHz ==> -------TCY = 250 ns
// Timer 0 prescaler = 128 ==> Count on every--------- 250 ns * 128 = 32 us
// 0.1 seg counting = --------0.1s / 32u = 3125
// 2^16- 3125 = 62411;
//Ktimer = 62411;  0.1 seconds
Ktimer = 65536 - (3125);
WriteTimer0(Ktimer); // Timer0 will overflow in 100ms

Delay10TCYx( 5 );             // Delay for 50TCY

//*********************************************************************************
//-------------------------------------- Main cycle -------------------------------
while (1)
{

PWM_LED();

if(time1 <= 0){ //reset
time1=10;
time2=7;
}

Delay10KTCYx(10);

}
//-------------------------------------- End of Main Cycle ------------------------
//*********************************************************************************

/* Close Peripherals */

CloseTimer0();
}


You can get precise PWM at low frequencies by using the CCP module to toggle the output pin (perhaps after a set number of compare 'hit's generating interrupts).

Each time the CCP comparison is a match you set it up for the next comparison and configure it to flip the output pin (or not).

That way you get nanosecond precision over arbitrarily long times (limited only by the clock accuracy and jitter).

If you don't care that much about jitter in the output you can do everything with a fixed interrupt- for example, set a 250usec interrupt and do the PWM in the ISR (interrupt service routine). That would give you 0.025% resolution at 1Hz. That's somewhat simpler, and has the advantage that you can use any pin you like for the output.

• Edited in original post. Trying to do the ISR.
– Lip
Jan 19, 2017 at 16:31

For something as slow as 1 Hz, you can do the PWM easily enough in firmware. Go back to the full speed clock and set up a periodic 1 ms (frequency 1 kHz) interrupt. With your original 16 MHz oscillator, the instruction rate would be 4 MHz, so there would be 4000 instruction cycles per interrupt. That's a lot compared to what the interrupt needs to do.

Now you have code that gets run every 1/1000 of your PWM period. The logic to do PWM is pretty easy from here. You keep one counter for the PWM period. You start it at 999 and decrement it each 1 ms time slice. When it goes negative, you reset it to 999 instead.

When that happens, you also reset another counter to the desired duty cycle. In your case that is 700. Set the output high whenever this counter is greater than 0. Then decrement the counter, except that you leave it at 0 if already 0.

What I described above should only take a few 10s of cycles at most. Even if it took 50 cycles (way longer than it should), that's only 1¼% of the CPU. You could easily run the interrupt faster, like at 4 kHz for higher resolution, but it seems you don't even need the 10 bits of resolution that 1 kHz gets you.

• Will try this method. Will contact soon.
– Lip
Jan 19, 2017 at 17:00

Your logic isn't that clear try something like this in the use!

Clear the flag if not done by the hardware. If (DC toggle++ & 0x01) TxCNT = - DC Led on Else TxCNT = -_DC Led off End

DC is the period where the led is turned on, and _dc is the period where the led is turned off.

TXCNT is the counter, assumed to be an up counter here. An precalculate DC and _DC so you don't have to take the compliments there.

to give you a sense of how this can be done, here is an example of running TIMER0 alternately between DC and PR-DC:

    //isr
void interrupt isr(void) {
static uint8_t toggle=0;                //toggle. 1=DC, 0=PR-DC

//tmr0 isr
TMR0IF = 0;                             //clear the flag
if (++spwm_cnt==0) {                    //counter underflow
if (toggle++ & 0x01) {              //01->DC
sPWM_ON();                      //turn on the spwm
spwm_cnt = -spwm_dc;            //load the offset
} else {
sPWM_OFF();                     //DC
spwm_cnt = -spwm_pr;            //load the offset for the off period
}
}
}


Here is the output it produces on a 12f675, using the main loop below:

int main(void) {

mcu_init();                             //initialize the mcu
spwm_init();                            //reset the module
//spwm_setdc(sPWM_PR * 1 / 4);          //set dc to 1/4
//spwm_setdc(sPWM_PR * 2 / 4);          //set dc to 2/4
spwm_setdc(sPWM_PR *3 / 4);             //set dc to 3/4
ei();
while (1) {
}
}


I was running it at 75% dc, 1Hz pwm.

what it does is to isolate the execution into the isr and relieve the user of having to pay any attention to the pwm generation in the main loop. If you need to change the duty cycle, all you needed to do is to call spwm_setdc() on an as needed basis.