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 support@ccsinfo.com

ADC problem with multitasking

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
keplerforever



Joined: 23 Dec 2005
Posts: 8

View user's profile Send private message

ADC problem with multitasking
PostPosted: Tue Jul 04, 2006 5:37 am     Reply with quote

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

View user's profile Send private message Send e-mail

PostPosted: Tue Jul 04, 2006 9:29 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jul 04, 2006 12:00 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jul 04, 2006 12:45 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jul 04, 2006 12:46 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jul 04, 2006 12:49 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jul 04, 2006 1:11 pm     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Mon Jul 10, 2006 8:04 am     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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