View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Feb 17, 2009 3:29 pm |
|
|
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
|
|
Posted: Thu Feb 19, 2009 12:43 pm |
|
|
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
|
|
|
oh1jty
Joined: 08 Mar 2010 Posts: 3
|
|
Posted: Mon Mar 08, 2010 3:45 pm |
|
|
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
|
|
Posted: Mon Mar 08, 2010 3:59 pm |
|
|
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
|
Resolution |
Posted: Mon Mar 08, 2010 4:39 pm |
|
|
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
|
|
Posted: Mon Mar 08, 2010 5:57 pm |
|
|
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
|
|
Posted: Mon Mar 08, 2010 6:11 pm |
|
|
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
|
|
Posted: Tue Mar 09, 2010 12:32 pm |
|
|
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 |
|
|
Brian
Joined: 29 Jun 2010 Posts: 10
|
|
Posted: Tue Aug 31, 2010 1:27 pm |
|
|
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
|
|
Posted: Tue Aug 31, 2010 1:51 pm |
|
|
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
|
|
Posted: Tue Aug 31, 2010 2:11 pm |
|
|
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
|
|
Posted: Tue Aug 31, 2010 3:01 pm |
|
|
I didn't actually write the inner loop code. Someone else 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
|
|
Posted: Tue Aug 31, 2010 4:56 pm |
|
|
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
|
|
Posted: Tue Aug 31, 2010 5:17 pm |
|
|
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):
|
|
|
|