![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
servo cycle variation between s/w and oscilloscope |
Posted: Sun Oct 23, 2005 11:57 am |
|
|
This code is being developed to control a six-legged robot with IR vision. Right now I'm just trying to center one servo using an interrupt routine off Timer_0. The rest of the program prints out values to an LCD for debugging and reads data from a IR range finder.
In the code, the servo is being sent a delay of 1500 us in the ISR. This has been confirmed with the LCD print. In addition, the interrupt has been set to cycle every 16 ms with the setup_timer_0 statement.
On an oscilloscope however, the servo is being sent a high pulse of about 1250 us with a period of 66 ms.
I'm getting kind of x-eyed looking at the code and I am a newbie so any help would be appreciated.
Also, the code snippet below, when it has positive values in it, doesn't seem to affect the interrupt timing:
Thanks....Harry
Code: | #include <16F628.h>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#include <LCD_4LINE.c> //this file set up to use port B
#define quar_mov 250
#define GP2D02_VIN PIN_B3
#define GP2D02_VOUT PIN_A2
//Interrupt Service Routine (ISR)=========================
#int_TIMER0 // This function is called every time
void servo_isr() // the RTCC (timer0) overflows (255->0).
{ // For this program this shld be :) 50 times
int reading=0; // per second.
long quarter= 2; // 4 servo positions: 1,2,3,4
int counter=9;
//Position the servo(s)===================================
long position;
position = quarter * quar_mov;
output_high(PIN_A0);
delay_ms(1);
delay_us(position);
output_low(PIN_A0);
//Collect the IR reading==================================
output_low(GP2D02_VIN); // start cycle with Vin low
delay_ms(1); // give the sensor a little time before we bug it
while (!input(GP2D02_VOUT)); //wait for Vout to go high
do {
delay_cycles(4); // minimum wait is 2us, max is 170us, 4 worked
output_low(GP2D02_VIN); // tell the sensor that we want a bit
delay_cycles(2); // might be as low as 1 (maybe), 2 worked
reading=reading<<1; // left shift the reading value
reading=reading|(input(GP2D02_VOUT)); // put the newest reading into the
// LSB of reading
output_high(GP2D02_VIN); // we read bit, and get ready for the next one
counter--;
} while (counter>0);
//LCD print statements====================================
lcd_gotoxy(1,1);
printf(lcd_putc," IR Measurement");
lcd_gotoxy(1,2);
printf(lcd_putc,"================");
lcd_gotoxy(1,3);
printf(lcd_putc,"Value = %3u", reading);
lcd_gotoxy(1,4);
printf(lcd_putc,"Position =%lu",position);
}
//Setup LCD, pins, Timer_0, interrupts====================
void main()
{
lcd_init();
setup_comparator(NC_NC_NC_NC);
set_timer0(0);
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_64);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(1);
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 23, 2005 12:50 pm |
|
|
The problem is caused by passing a 16-bit variable to a function
that is expecting an 8-bit variable.
Quote: |
In the code, the servo is being sent a delay of 1500 us in the ISR
On an oscilloscope however, the servo is being sent a high pulse
of about 1250 us
long position;
delay_us(position); |
From the CCS manual:
Quote: | DELAY_US()
Syntax: delay_us (time)
Parameters: time - a variable 0-255 or a constant 0-65535 |
See this thread for a work-around:
http://www.ccsinfo.com/forum/viewtopic.php?t=16656&highlight=longdelayus |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Sun Oct 23, 2005 1:43 pm |
|
|
Thanks...the long_delay_us works perfectly...within the accuracy of my scope anyway. I've still got a couple of questions about the following code.
Code: | set_timer0(0);
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_64);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL); |
1. By my calculations RTCC_DIV_64 should give me a little over a 16 ms period. I'm actually getting 66 ms.
2. I'm not clear on why I need "enable_interrupts(GLOBAL)" to get the interrupt working.
3."set_timer0(0)" does nothing regardless of which positive value I put in the argument. I thought I could shorten the interrupt time by putting in a value that reduces 256.
Thanks....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Sun Oct 23, 2005 2:43 pm |
|
|
Remember that all the timers clocks run off the master clock/4. So youhave clock/4/64/256. This is probably the factor of about 4* the time you are expecting.
Yes you can set the clock 'forward', but it'll ony effect that one cycle of the interrupt. If you want a longer adjustable time, look at the CTC modules, which allow timer2, to be repeatedly reset automatically.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
![](templates/subSilver/images/spacer.gif) |
dyeatman
Joined: 06 Sep 2003 Posts: 1924 Location: Norman, OK
|
|
Posted: Sun Oct 23, 2005 3:03 pm |
|
|
Harry,
In the datasheet on page 21 look at the INTCON bit 7 (GIE). Per page 16 that bit is set to 0 at POR and that disables ALL interrupts.
ENABLE_INTERUPTS(GLOBAL) sets this bit to 1 allowing all UNMASKED (individualy enabled) interrupts to occur. |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Sun Oct 23, 2005 6:42 pm |
|
|
Ttelmah wrote: | Remember that all the timers clocks run off the master clock/4. So youhave clock/4/64/256. This is probably the factor of about 4* the time you are expecting.
|
I've taken a smaller portion of the code and gotten it to work as expected, that is, a 16 ms period with a prescaler of 64. The same code, within the larger program, seems to go to 66 ms.
Harry |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Sun Oct 23, 2005 6:45 pm |
|
|
I've taken your code example and applied it to my code. At least now I've gotten the preload to work by putting it inside the ISR function. The results have gotten even more hairy but you've given me something to work with.
Thanks....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Sun Oct 23, 2005 6:47 pm |
|
|
dyeatman wrote: | Harry,
In the datasheet on page 21 look at the INTCON bit 7 (GIE). Per page 16 that bit is set to 0 at POR and that disables ALL interrupts.
ENABLE_INTERUPTS(GLOBAL) sets this bit to 1 allowing all UNMASKED (individualy enabled) interrupts to occur. |
Thanks for the explanation, all this help has taken my confidence level from zero to "tentative first steps".
Harry |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Mon Oct 24, 2005 10:06 am |
|
|
I'm stuck again! I've pulled just the timer_0 interrupt and servo centering routines out of the program in my first post and they work as designed. The oscilloscope shows a 1500 us pulse and a 20 ms period. Here is the code:
Code: | #include <16F628.h>
#fuses XT, NOWDT, NOPROTECT
#use delay(clock=4000000)
#define quar_mov 250
//Long delay function====================================
void long_delay_us(long count)
{
char i;
i = (char)(count >> 8);
while(i >= 1)
{
i--;
delay_us(253);
restart_wdt();
}
delay_us((char)count);
}
//Interrupt Service Routine (ISR)=========================
#int_TIMER0 // This function is called every time
void servo_isr() // the RTCC (timer0) overflows (255->0).
{
// For this program this shld be :) 50 times
int reading=0; // per second.
long quarter= 2; // 4 servo positions: 1,2,3,4
int counter=9;
//Position the servo(s)===================================
long position;
set_timer0 (100);
position = quarter * quar_mov;
output_high(PIN_A0);
delay_ms(1);
long_delay_us(position);
output_low(PIN_A0);
}
main()
{
setup_comparator(NC_NC_NC_NC);
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_128);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(true);
} |
The code from my first post, which includes additional IF rangefinder and LCD print routines but the same servo and interrupt routines, give the correct pulse of 1500 us but a period of around 85 ms.
Any ideas?
Thanks....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Mon Oct 24, 2005 10:07 am |
|
|
I'm stuck again! I've pulled just the timer_0 interrupt and servo centering routines out of the program in my first post and they work as designed. The oscilloscope shows a 1500 us pulse and a 20 ms period. Here is the code:
Code: | #include <16F628.h>
#fuses XT, NOWDT, NOPROTECT
#use delay(clock=4000000)
#define quar_mov 250
//Long delay function====================================
void long_delay_us(long count)
{
char i;
i = (char)(count >> 8);
while(i >= 1)
{
i--;
delay_us(253);
restart_wdt();
}
delay_us((char)count);
}
//Interrupt Service Routine (ISR)=========================
#int_TIMER0 // This function is called every time
void servo_isr() // the RTCC (timer0) overflows (255->0).
{
// For this program this shld be :) 50 times
int reading=0; // per second.
long quarter= 2; // 4 servo positions: 1,2,3,4
int counter=9;
//Position the servo(s)===================================
long position;
set_timer0 (100);
position = quarter * quar_mov;
output_high(PIN_A0);
delay_ms(1);
long_delay_us(position);
output_low(PIN_A0);
}
main()
{
setup_comparator(NC_NC_NC_NC);
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_128);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(true);
} |
The code from my first post, which includes additional IF rangefinder and LCD print routines but the same servo and interrupt routines, give the correct pulse of 1500 us but a period of around 85 ms.
I've just noticed that I've made some changes to the original code so I will post that also.
Any ideas?
Thanks....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
Please read prvious post. |
Posted: Mon Oct 24, 2005 10:14 am |
|
|
Here is a copy of the original post with a few changes. Please refer to my last post. Code: |
#include <16F628.h>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#include <LCD_4LINE.c> //this file set up to use port B
#define quar_mov 250
#define GP2D02_VIN PIN_B3
#define GP2D02_VOUT PIN_A2
//Long delay function
void long_delay_us(long count)
{
char i;
i = (char)(count >> 8);
while(i >= 1)
{
i--;
delay_us(253);
restart_wdt();
}
delay_us((char)count);
}
//Interrupt Service Routine (ISR)=========================
#int_TIMER0 // This function is called every time
void servo_isr() // the RTCC (timer0) overflows (255->0).
{
// For this program this shld be :) 50 times
int reading=0; // per second.
long quarter= 2; // 4 servo positions: 1,2,3,4
int counter=9;
//Position the servo(s)===================================
long position;
set_timer0 (100);
position = quarter * quar_mov;
output_high(PIN_A0);
delay_ms(1);
long_delay_us(position);
output_low(PIN_A0);
//Collect the IR reading==================================
output_low(GP2D02_VIN); // start cycle with Vin low
delay_ms(1); // give the sensor a little time before we bug it
while (!input(GP2D02_VOUT)); //wait for Vout to go high
do {
delay_cycles(4); // minimum wait is 2us, max is 170us, 4 worked
output_low(GP2D02_VIN); // tell the sensor that we want a bit
delay_cycles(2); // might be as low as 1 (maybe), 2 worked
reading=reading<<1; // left shift the reading value
reading=reading|(input(GP2D02_VOUT)); // put the newest reading into the
// LSB of reading
output_high(GP2D02_VIN); // we read bit, and get ready for the next one
counter--;
} while (counter>0);
//LCD print statements====================================
lcd_gotoxy(1,1);
printf(lcd_putc," IR Measurement");
lcd_gotoxy(1,2);
printf(lcd_putc,"================");
lcd_gotoxy(1,3);
printf(lcd_putc,"Value = %3u", reading);
lcd_gotoxy(1,4);
printf(lcd_putc,"Position =%lu",position);
}
//Setup LCD, pins, Timer_0, interrupts====================
void main()
{
lcd_init();
setup_comparator(NC_NC_NC_NC);
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_128);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
while(1);
}
|
Thanks....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Mon Oct 24, 2005 11:39 am |
|
|
The period gets extended from 20 ms to 85 ms when I include the IR rangefinder routine within the ISR. I guess it must use up about 55 ms of time. Back to the drawing board! |
|
![](templates/subSilver/images/spacer.gif) |
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Mon Oct 24, 2005 11:50 am |
|
|
Do as little as possible in an interrupt routine. If possible, set a flag and process it in the main loop. |
|
![](templates/subSilver/images/spacer.gif) |
kender
Joined: 09 Aug 2004 Posts: 768 Location: Silicon Valley
|
|
Posted: Mon Oct 24, 2005 12:40 pm |
|
|
Put the IR rangefinder code into the main() loop together with all of the collision avoidance intelligence and the LCD code. It's unadvisable to have delays and slow serial communication in the ISR. |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|