CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PIC16F877: PWM with less than 250Hz possible?
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Feb 17, 2009 3:29 pm     Reply with quote

Here is an improved program that will give a 50 Hz pwm update rate,
and allow 100 pwm steps.
Code:
#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)

#define PWM_PIN  PIN_B1

//#define TIMER0_PRELOAD 163  // Gives 10 KHz interrupt rate
#define TIMER0_PRELOAD 63     // Gives  5 KHz interrupt rate

#define LOOPCNT 100           // Gives 50 Hz PWM update rate

int8 width;

//-------------------------------
// Function prototypes

#INT_RTCC 
void tick_interrupt(void);

//====================================
main()
{
width = 10;

setup_timer_0(RTCC_INTERNAL | RTCC_DIV_1);
set_timer0(TIMER0_PRELOAD);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
 
// The following code is used to test the program:
// Increase the PWM pulse width from 0 to 100%
// with one step change every 100 ms.  Watch the
// pulse width increase on an oscilloscope.
while(1)
  {
   for(width = 0; width <= LOOPCNT; width++)
       delay_ms(100);
  }
 
}

//====================================
#INT_RTCC
void tick_interrupt(void)
{
static int8 loop = LOOPCNT;
static int8 pulse;

// To test the interrupt rate, uncomment the next line.
// Pin B0 will have a 2.5 KHz squarewave on it, if the
// interrupt rate is 5 KHz.
//output_toggle(PIN_B0); 

// This line compensates for interrupt latency.
set_timer0(get_timer0() + TIMER0_PRELOAD);


if(--loop == 0)
  {
   loop = LOOPCNT;
   pulse = width;
  }

if(pulse)
  {
   output_high(PWM_PIN);
   pulse--;
  }
else
  {
   output_low(PWM_PIN);
  }

}
doke



Joined: 09 Feb 2009
Posts: 6

View user's profile Send private message

PostPosted: Thu Feb 19, 2009 12:43 pm     Reply with quote

Thanks again very much for the answer!

I tried the code again and it works fine, however I'm not yet satisfied with the resolution of the PWM signal. With a 10 KHz interrupt rate and a 50 Hz period of the PWM signal with pulse width 1-2ms I only have 10 different values to control my servo (width 10..20). With the delay function i had a resulution from ca. 1µs (1000 different values between 1ms and 2ms).

I tried to increase the TIMER0_PRELOAD value, but values higher than 178 don't result in a good square wave signal on my pin (checked with an oscillocope).

Is it possible to get a higher resolution?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Feb 19, 2009 4:55 pm     Reply with quote

Possibly the increased resolution could be done in CCP Compare mode.
I just don't want to write the code for you.

For examples, look at this CCS driver file:
Quote:
c:\Program Files\PICC\Drivers\Servos.c

Here is a test program that calls the Servos.c driver:
http://www.ccsinfo.com/forum/viewtopic.php?t=34560&start=22

More on using CCP compare mode:
http://www.ccsinfo.com/forum/viewtopic.php?t=30607

I don't guarantee that any of this is quickly usable as a template to solve your problem.
oh1jty



Joined: 08 Mar 2010
Posts: 3

View user's profile Send private message

PostPosted: Mon Mar 08, 2010 3:45 pm     Reply with quote

PCM programmer wrote:
Here is an improved program that will give a 50 Hz pwm update rate,
and allow 100 pwm steps.
Code:
#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)

#define PWM_PIN  PIN_B1

//#define TIMER0_PRELOAD 163  // Gives 10 KHz interrupt rate
#define TIMER0_PRELOAD 63     // Gives  5 KHz interrupt rate

#define LOOPCNT 100           // Gives 50 Hz PWM update rate

int8 width;

//-------------------------------
// Function prototypes

#INT_RTCC 
void tick_interrupt(void);

//====================================
main()
{
width = 10;

setup_timer_0(RTCC_INTERNAL | RTCC_DIV_1);
set_timer0(TIMER0_PRELOAD);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
 
// The following code is used to test the program:
// Increase the PWM pulse width from 0 to 100%
// with one step change every 100 ms.  Watch the
// pulse width increase on an oscilloscope.
while(1)
  {
   for(width = 0; width <= LOOPCNT; width++)
       delay_ms(100);
  }
 
}

//====================================
#INT_RTCC
void tick_interrupt(void)
{
static int8 loop = LOOPCNT;
static int8 pulse;

// To test the interrupt rate, uncomment the next line.
// Pin B0 will have a 2.5 KHz squarewave on it, if the
// interrupt rate is 5 KHz.
//output_toggle(PIN_B0); 

// This line compensates for interrupt latency.
set_timer0(get_timer0() + TIMER0_PRELOAD);


if(--loop == 0)
  {
   loop = LOOPCNT;
   pulse = width;
  }

if(pulse)
  {
   output_high(PWM_PIN);
   pulse--;
  }
else
  {
   output_low(PWM_PIN);
  }

}


This code doesn`t work on my 18F4620 design.
But it works fine on my old 16F877 design.
Any ideas ?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Mar 08, 2010 3:59 pm     Reply with quote

Timer0 is 8 bits wide in the 16F877. It can optionally be 8 or 16 bits
wide in the 18F4620. Look in the Timer0 section of the 18F4620.h
header file to see the constant for setup_timer_0() that configures
it to operate in 8-bit mode.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Resolution
PostPosted: Mon Mar 08, 2010 4:39 pm     Reply with quote

Do you know how much resolution you need?

Is 4us good enough? (i.e. 250 different values from 1ms to 2ms)
oh1jty



Joined: 08 Mar 2010
Posts: 3

View user's profile Send private message

PostPosted: Mon Mar 08, 2010 5:57 pm     Reply with quote

PCM programmer wrote:
Timer0 is 8 bits wide in the 16F877. It can optionally be 8 or 16 bits
wide in the 18F4620. Look in the Timer0 section of the 18F4620.h
header file to see the constant for setup_timer_0() that configures
it to operate in 8-bit mode.


I tried with RTCC_8_BIT, but no difference.
Measured the test interrupt rate from B6.
With 16F877 it was around 1.6KHz and with 18F4620 around 5Hz
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Mar 08, 2010 6:11 pm     Reply with quote

It works for me. I took the program you quoted above, and I changed
the #include line for the PIC to 18F4620. I edited the setup_timer_0()
statement to 'OR' in the RTCC_8_BIT constant. I then programmed it
into the 18F4620 chip on my PicDem2-Plus board. I then looked at
pin B1 with my oscilloscope. It has a rectangular waveform on it, and
the scope's frequency counter says 47.85 Hz. The duty cycle slowly
increases from 0 to 100%, and then repeats. I tested with with compiler
vs. 4.105.
Code:

#include <18F4620.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)

#define PWM_PIN  PIN_B1

//#define TIMER0_PRELOAD 163  // Gives 10 KHz interrupt rate
#define TIMER0_PRELOAD 63     // Gives  5 KHz interrupt rate

#define LOOPCNT 100           // Gives 50 Hz PWM update rate

int8 width;

//-------------------------------
// Function prototypes

#INT_RTCC 
void tick_interrupt(void);

//====================================
main()
{
width = 10;

setup_timer_0(RTCC_INTERNAL | RTCC_DIV_1 | RTCC_8_BIT);
set_timer0(TIMER0_PRELOAD);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
 
// The following code is used to test the program:
// Increase the PWM pulse width from 0 to 100%
// with one step change every 100 ms.  Watch the
// pulse width increase on an oscilloscope.
while(1)
  {
   for(width = 0; width <= LOOPCNT; width++)
       delay_ms(100);
  }
 
}

//====================================
#INT_RTCC
void tick_interrupt(void)
{
static int8 loop = LOOPCNT;
static int8 pulse;

// To test the interrupt rate, uncomment the next line.
// Pin B0 will have a 2.5 KHz squarewave on it, if the
// interrupt rate is 5 KHz.
//output_toggle(PIN_B0); 

// This line compensates for interrupt latency.
set_timer0(get_timer0() + TIMER0_PRELOAD);


if(--loop == 0)
  {
   loop = LOOPCNT;
   pulse = width;
  }

if(pulse)
  {
   output_high(PWM_PIN);
   pulse--;
  }
else
  {
   output_low(PWM_PIN);
  }

}
oh1jty



Joined: 08 Mar 2010
Posts: 3

View user's profile Send private message

PostPosted: Tue Mar 09, 2010 12:32 pm     Reply with quote

Thanks, i found the problem.

I tried with simpler code and it worked without any problems.
So i went thru the code and found this line:
"setup_counters(RTCC_INTERNAL, RTCC_DIV_8);" which the wizard had added Laughing
Brian



Joined: 29 Jun 2010
Posts: 10

View user's profile Send private message

PostPosted: Tue Aug 31, 2010 1:27 pm     Reply with quote

I am using the PIC18f2480, and this code generates a 11.95 Hz PWM frequency. Simiarily, uncommenting the debug code shows that the interrupt rate is 598 Hz, not 2.5 Khz. Not sure why?

Also, I'm confused how the interrupt function works.
Specifically:
Code:

f(--loop == 0)
  {
   loop = LOOPCNT;
   pulse = width;
  }

if(pulse)
  {
   output_high(PWM_PIN);
   pulse--;
  }
else
  {
   output_low(PWM_PIN);
  }


1) How would (--loop) every equal 0 if its initially 100 (the value of loopcount)? Wouldn't --loop just be 99?
2) What is the initial value for pulse? Initially its just declared as static int8 pulse.

I'm just a little confused how that logic works.

Thanks.


PCM programmer wrote:
It works for me. I took the program you quoted above, and I changed
the #include line for the PIC to 18F4620. I edited the setup_timer_0()
statement to 'OR' in the RTCC_8_BIT constant. I then programmed it
into the 18F4620 chip on my PicDem2-Plus board. I then looked at
pin B1 with my oscilloscope. It has a rectangular waveform on it, and
the scope's frequency counter says 47.85 Hz. The duty cycle slowly
increases from 0 to 100%, and then repeats. I tested with with compiler
vs. 4.105.
Code:

#include <18F4620.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)

#define PWM_PIN  PIN_B1

//#define TIMER0_PRELOAD 163  // Gives 10 KHz interrupt rate
#define TIMER0_PRELOAD 63     // Gives  5 KHz interrupt rate

#define LOOPCNT 100           // Gives 50 Hz PWM update rate

int8 width;

//-------------------------------
// Function prototypes

#INT_RTCC 
void tick_interrupt(void);

//====================================
main()
{
width = 10;

setup_timer_0(RTCC_INTERNAL | RTCC_DIV_1 | RTCC_8_BIT);
set_timer0(TIMER0_PRELOAD);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
 
// The following code is used to test the program:
// Increase the PWM pulse width from 0 to 100%
// with one step change every 100 ms.  Watch the
// pulse width increase on an oscilloscope.
while(1)
  {
   for(width = 0; width <= LOOPCNT; width++)
       delay_ms(100);
  }
 
}

//====================================
#INT_RTCC
void tick_interrupt(void)
{
static int8 loop = LOOPCNT;
static int8 pulse;

// To test the interrupt rate, uncomment the next line.
// Pin B0 will have a 2.5 KHz squarewave on it, if the
// interrupt rate is 5 KHz.
//output_toggle(PIN_B0); 

// This line compensates for interrupt latency.
set_timer0(get_timer0() + TIMER0_PRELOAD);


if(--loop == 0)
  {
   loop = LOOPCNT;
   pulse = width;
  }

if(pulse)
  {
   output_high(PWM_PIN);
   pulse--;
  }
else
  {
   output_low(PWM_PIN);
  }

}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Aug 31, 2010 1:51 pm     Reply with quote

It worked for me. I got 47.85 Hz on pin B1. The duty cycle slowly
increases until it's 100%, and then repeats. This was with vs. 4.111.
I copied and pasted the code (in your post above) into an MPLAB project.
I don't have an 18F2480, so I used an 18F2580, which is very similar.
I used it with a 4 MHz crystal, as specified in the code. I tested it on a
PicDem2-Plus board. I used an oscilloscope with a built-in frequency
counter to see the results.
Brian



Joined: 29 Jun 2010
Posts: 10

View user's profile Send private message

PostPosted: Tue Aug 31, 2010 2:11 pm     Reply with quote

Perhaps its because I'm not using an external crystal, just the internal clock..?..

Also, could you explain how the interrupt logic works here?

Code:

if(--loop == 0)
  {
   loop = LOOPCNT;
   pulse = width;
  }

if(pulse)
  {
   output_high(PWM_PIN);
   pulse--;
  }
else
  {
   output_low(PWM_PIN);
  }


1) How would (--loop) every equal 0 if its initially 100 (the value of loopcount)? Wouldn't --loop just be 99?
2) What is the initial value for pulse? Initially its just declared as static int8 pulse.

I'm just a little confused how that logic works.

Thanks!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Aug 31, 2010 3:01 pm     Reply with quote

I didn't actually write the inner loop code. Someone else Smile on the
forum wrote that as a fragment. I incorporated it into a test program
just to be my helpful old self. But I'll try to explain it.

The 'loop' variable is initialized to 100 (Set in LOOPCNT).

The first if() statement decrements the loop counter once per pass.
It will decrement to 99, then 98, etc., down to 0 (after 100 interrupts).
When it goes to 0, the if() statement will reload the loop counter with
the initial value of 100.

I think there's a bug with the 'pulse' variable. It's not initialized.
One quick fix would be to initialize it to 0 in the static declaration:
Quote:
#INT_RTCC
void tick_interrupt(void)
{
static int8 loop = LOOPCNT;
static int8 pulse = 0;
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Tue Aug 31, 2010 4:56 pm     Reply with quote

PCM programmer wrote:
I think there's a bug with the 'pulse' variable. It's not initialized.
In ANSI C all static and global variables will be initialized to zero before the program starts, except when the programmer specified another value.

To avoid the confusion about the variable being initialized or not I often explicitly add the initial value.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Aug 31, 2010 5:17 pm     Reply with quote

You're right, and I looked for that in the .LST file but I missed it.
It is there (with no explicit init of pulse in the code):
Quote:
012C: CLRF pulse
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group