|
|
View previous topic :: View next topic |
Author |
Message |
keplerforever
Joined: 23 Dec 2005 Posts: 8
|
ADC problem with multitasking |
Posted: Tue Jul 04, 2006 5:37 am |
|
|
Hi,
I'm quite a starter in pic programing, and I'm currently stuck with the following problem:
- I'm trying to run a Pololu servo driver trough serial command (back and forth movement)
- At the same time I'd like to read the value of the internal servo potentiometer (0 to 2.5 volts) using pin A0
- I would like to display on my computer the values read by the ADC (using max 233A)
- In order to manage computing capabilities efficiently, I've been using the PCM code for multitasking: http://www.ccsinfo.com/forum/viewtopic.php?t=17189&start=0&postdays=0&postorder=asc&highlight=
The code bellow (could be cleaner!) is a modification of the PCM model. I'm currently using only the functions:
- void check_AD_servo(void) for servo reading
- void check_servo_move(void) for drinving servo movment and led indicators
All the other tasks are here useless, and only kept for further developpment.
Code: | /*
simple_led_lcd_877A.c
*/
#include <16f877A.h>
//#fuses HS,NOLVP,NOWDT,NOPUT
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, NOPUT, NOLVP
#use delay(clock=19660800)
#use rs232(baud=38400,xmit=PIN_C6,rcv=PIN_C7,stream=Pololu)
#use rs232(baud=19200,xmit=PIN_B4,rcv=PIN_B5,stream=PC_RS232)
//=============================================
//In the TIMERS.H file, you should have something like this:
// timers.h
//
//----------------------------------------------------------------------
// DEFINES
//
// With a 19.6608 MHz oscillator, a RTCC pre-scaler of 256, and a RTCC
// preload of 192, we get an rtcc interrupt rate of 100 Hz (every 10 ms).
// This will be our "tick" clock that we use for various event timers.
#define RTCC_PRELOAD (256 - 192)
// Multiply the following values x 10 ms to get the delay times,
// since each timer tick is 10 ms.
#define BUTTONS_TIMER_TICKS 2 // 2=20 ms
#define AD_servo_TIMER_TICKS 50 // 100 ms
#define LED_TIMER_TICKS 20 // 200 ms
#define servo_move_TIMER_TICKS 150 // 100 ms
// GLOBALS
char gc_buttons_timer;
char gc_AD_servo_timer;
char gc_LED_timer;
char gc_servo_move_timer;
char i;
char increment;
int1 dummy_bit=1;
int prev_pot_value;
int last_pot_value;
char gc_old_button_status;
#define LED_1 PIN_E0
#define LED_2 PIN_E1
#define LED_3 PIN_E2
#define LED_4 PIN_D3
//----------------------
//--------------------------------------------------------
void servo_set_param(char servo_num_set_param, char data_set_param)
{
//Always 128
//Device ID: Always 1
//Command: 0 i.e. set parameter
//servo_num_set_param: Servo Number: 0->31
//Data: range=min:0->max:31 + FW=0 or RE=32 + OFF=0 or ON=64
//ex: data_set_param: if range=30, OFF+FW=30, OFF+RE=62, ON+FW=94, ON+RE=126
//ex: data_set_param: if range=29, OFF+FW=29, OFF+RE=61, ON+FW=93, ON+RE=125
fprintf(Pololu, "%C%C%C%C%C", 128, 1, 0, servo_num_set_param, data_set_param);
}
//--------------------------------------------------------
void servo_set_position(char servo_num_set_param, char data_set_param, char data_set_position )
{
//Always 128
//Device ID: Always 1
//Command: 2 i.e. position
//servo_num_set_param: Servo Number: 0->31
//Data: range=min:0->max:31 + FW=0 or RE=32 + OFF=0 or ON=64
//ex: data_set_param: if range=30, OFF+FW=30, OFF+RE=62, ON+FW=94, ON+RE=126
//ex: data_set_param: if range=29, OFF+FW=29, OFF+RE=61, ON+FW=93, ON+RE=125
//
//data_set_position: min=0 <=> deg=0 -> max=127 <=> deg=180, middle=63 <=> deg=90
fprintf(Pololu, "%C%C%C%C%C%C%C%C%C%C", 128, 1, 0, servo_num_set_param, data_set_param, 128, 1, 2, servo_num_set_param, data_set_position);
}
//--------------------------------------------------------
void check_buttons(void)
{
char new_status;
if(gc_buttons_timer)
return;
else
gc_buttons_timer = BUTTONS_TIMER_TICKS;
new_status = input_b(); // Read the buttons
// Have the switches changed ?
if(new_status != gc_old_button_status)
{
// If so, do something.
}
// Save button status for next time.
gc_old_button_status = new_status;
}
//--------------------------------------------------------
void check_AD_servo(void)
{
if(gc_AD_servo_timer)
return;
else
{
gc_AD_servo_timer = AD_servo_TIMER_TICKS;
set_adc_channel(0);
delay_us(20);
last_pot_value = read_adc();
//delay_us(10);
//fprintf(PC_RS232, "%s %u \n\r", "position min: ", last_pot_value);
fprintf(PC_RS232, "%u \n\r", last_pot_value);
}
}
//--------------------------------------------------------
void check_LEDs(void)
{
if(gc_LED_timer)
return;
else
{
gc_LED_timer = LED_TIMER_TICKS;
// Put your LED blinking state machine here.
/*
printf("%s \n\r", "Chucki");
output_low(LED_1);
output_low(LED_2);
output_low(LED_3);
output_low(LED_4);
delay_ms(50);
output_high(LED_1);
output_high(LED_2);
output_high(LED_3);
output_high(LED_4);
//delay_ms(500);
*/
}
}
//--------------------------------------------------------
void check_servo_move(void)
{
if(gc_servo_move_timer)
return;
else
{
gc_servo_move_timer = servo_move_TIMER_TICKS;
if(dummy_bit)
{
servo_set_position(0, 126, 127);
dummy_bit = ~dummy_bit;
output_low(LED_1);
output_high(LED_2);
}
else
{
servo_set_position(0, 126, 0);
dummy_bit = ~dummy_bit;
output_high(LED_1);
output_low(LED_2);
}
}
}
//--------------------------------------------------------
// The rtcc interrupt occurs when the rtcc rolls over from FF to 00.
// I have programmed it to interrupt at a 100 Hz rate.
//
// RTCC interrupt rate = Fosc / (4 * rtcc pre-scaler * rtcc pre-load)
//
// = 19.6608 MHz / (4 * 256 * 192)
//
// = 100 Hz
//
// This gives us a timer tick approx. every 10 ms (9.98 ms actually).
#int_rtcc
void rtcc_isr(void)
{
// Reload the RTCC, so it will keep overflowing every 10 ms.
set_rtcc(RTCC_PRELOAD);
// Decrement any timers that are running.
if(gc_buttons_timer)
gc_buttons_timer--;
if(gc_AD_servo_timer)
gc_AD_servo_timer--;
if(gc_LED_timer)
gc_LED_timer--;
if(gc_servo_move_timer)
gc_servo_move_timer--;
}
main()
{
//gc_old_button_status = input_b(); // Initialize the button status
// Initialize the software timers for each task.
// These initial values could even be different from
// the "normal" value, if you wanted the initial delay
// to be different than the normal task execution rate.
gc_buttons_timer = BUTTONS_TIMER_TICKS;
gc_AD_servo_timer = AD_servo_TIMER_TICKS;
gc_servo_move_timer = servo_move_TIMER_TICKS;
gc_LED_timer = LED_TIMER_TICKS;
// Setup the ADC.
setup_adc_ports( RA0_RA1_RA3_ANALOG);
setup_adc( ADC_CLOCK_INTERNAL );
// Setup the RTCC.
setup_counters(RTCC_INTERNAL, RTCC_DIV_256);
set_rtcc(RTCC_PRELOAD);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
// This is the main loop. You put your "tasks" here.
while(1)
{
check_buttons();
check_AD_servo();
check_LEDs();
check_servo_move();
}
}
// End of program |
My problem is that, the servo is running perfectly back and forth (at the frequency specified by the timer), but the ADC just trows me garbage on my PC terminal. I've been using different baud rates, different AD_servo_TIMER_TICKS (normally I should work with sorther timing). I'm pretty confident about my adc code, since I tested it previously on another program without multitasking and data were corretcl recivied on my computer.
Like always, I've been checking first inside the forum, but couldn't find something, maybe you could point me to a relevant topic?
Or do you know what I should correct/try inside my code?
Thanks in advance for the help.
By the way could someone point me a good refence (book, homepage), about getting started on multitasking and state machine with pic, but still for a beginner like me...thanks |
|
|
rwyoung
Joined: 12 Nov 2003 Posts: 563 Location: Lawrence, KS USA
|
|
Posted: Tue Jul 04, 2006 9:29 am |
|
|
Your code looks reasonable but since I'm not at the office I can't do a test compile.
But, the first thing I would try is to replace the "read_adc()" command with a simple increment of the variable. Then when your task runs and tries to send data to the PC, you should see an increasing number. If you don't see the count walking up, then you should concentrate on fixing your RS232 stream.
Assuming it is the RS232 stream that is the problem, if you have an oscilloscope handy, look at your TX signal and check that the bit timing seems right. Look on both sides of your MAX232 or whatever chip you are using to do the level shifting.
Confirm that your MAX232 chip is wired properly and producing value levels. Confirm that your serial cable is good.
Look at the LST file your compiler is generating (what version number by the way?) and confirm that the code you wrote produces the appropriate ASM. If you can't read PIC assembly language, now is a good time to learn. You don't need to be fluent, but at least know enough to read the code. _________________ Rob Young
The Screw-Up Fairy may just visit you but he has crashed on my couch for the last month! |
|
|
keplerforever
Joined: 23 Dec 2005 Posts: 8
|
|
Posted: Tue Jul 04, 2006 12:00 pm |
|
|
Hi rwyoung,
Thanks for the answer and the good advices.
Her is what I have tried:
Quote: | But, the first thing I would try is to replace the "read_adc()" command with a simple increment of the variable. Then when your task runs and tries to send data to the PC, you should see an increasing number. If you don't see the count walking up, then you should concentrate on fixing your RS232 stream. |
I tried this, but I still recieve garbage values on my PC. Looks like ADC is not the cause of the problem.
Quote: | Assuming it is the RS232 stream that is the problem, if you have an oscilloscope handy, look at your TX signal and check that the bit timing seems right. Look on both sides of your MAX232 or whatever chip you are using to do the level shifting.
Confirm that your MAX232 chip is wired properly and producing value levels. Confirm that your serial cable is good. |
I've been checking all the element of my RS232 PC connection and evrything seems to be fine. I burnt another pic with a simple program streaming ASCII value trough the serial line and was running OK. I'm pretty confident on my RS232 connection since I've been using it in many other project to my full satisfaction until now
Quote: | Look at the LST file your compiler is generating (what version number by the way?) and confirm that the code you wrote produces the appropriate ASM. If you can't read PIC assembly language, now is a good time to learn. You don't need to be fluent, but at least know enough to read the code. |
You are rigth and I know this is the way where I should move in the future...but I'm still a bit scared about those lines of assembler...procrastination!
My suspition is those RS232 communications get messed up only when I run them in real time. So I must have been doing something wrong in the way I wrote my application.
Any more hints?
Thanks for your time and disponibility
Olivier |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jul 04, 2006 12:45 pm |
|
|
Quote: |
My suspicion is those RS232 communications get messed up only when
I run them in real time. So I must have been doing something wrong in
the way I wrote my application |
You're using a software UART to display the A/D values, but you have
the INT_RTCC interrupt running at the same time. This will disrupt the
bit timing of the soft UART.
In the section on #use_rs232(), the CCS manual explains how to
prevent this problem by using a special parameter with #use rs232().
Quote: | DISABLE_INTS
Will cause interrupts to be
disabled when the routines
get or put a character. This
prevents character distortion
for software implemented I/O
and prevents interaction
between I/O in interrupt
handlers and the main
program when using the |
Add the parameter shown in bold below.
Quote: | #use rs232(baud=19200,xmit=PIN_B4,rcv=PIN_B5,stream=PC_RS232, DISABLE_INTS) |
-------------
Also, in your code, you're using large fixed delays. You shouldn't do
that in a multi-tasking program, because it makes the program sit there
for 500 ms, and no other task can be serviced. Instead, you should
use static variables as counters and state variables. You should create
a small state machine that runs at the tick rate. I can post an example
later.
Quote: | /*
printf("%s \n\r", "Chucki");
output_low(LED_1);
output_low(LED_2);
output_low(LED_3);
output_low(LED_4);
delay_ms(50);
output_high(LED_1);
output_high(LED_2);
output_high(LED_3);
output_high(LED_4);
//delay_ms(500);
*/
|
|
|
|
keplerforever
Joined: 23 Dec 2005 Posts: 8
|
|
Posted: Tue Jul 04, 2006 12:46 pm |
|
|
Hi,
One last comment: I've been checking on my scope the serial lines before and after the RS232 and the levels seems to be OK for both stream.
Olivier |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jul 04, 2006 12:49 pm |
|
|
I think you may have missed my post, because you posted your update
one minute after me.
So here's a "bump" for this thread so you'll see it. |
|
|
keplerforever
Joined: 23 Dec 2005 Posts: 8
|
|
Posted: Tue Jul 04, 2006 1:11 pm |
|
|
Alleluia!!
Hosanna au plus haut des cieux!!
This solved the problem, many thanks PCM. I can only thank you for this answer and all the valuable work you are doing in this forum.
If once you come to switzerland I owe you a beer.
Olivier |
|
|
treitmey
Joined: 23 Jan 2004 Posts: 1094 Location: Appleton,WI USA
|
|
Posted: Mon Jul 10, 2006 8:04 am |
|
|
For those that don't speak french.
Original Latin:Benedictus qui venit in nomine Domini.
English translation
Blessed is He who comes
in the name of the Lord. |
|
|
|
|
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
|