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

Multi channel servo control(Serial communication problem)

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



Joined: 06 Nov 2012
Posts: 3

View user's profile Send private message

Multi channel servo control(Serial communication problem)
PostPosted: Tue Nov 06, 2012 4:20 am     Reply with quote

Hi, I am running PIC18f46k22 to control multi servos, and get command from another micro controller. Here is my code for receiving part...
Code:

#include <18F46K22.h>
#fuses HSH,NOWDT,NOLVP,PROTECT,NOPUT,NOBROWNOUT,NOHFOFST,INTRC_IO
#use delay(clock=16MHz)
#use rs232(baud=9600,xmit =PIN_C6, rcv=PIN_C7, PARITY=N, BITS =8, STOP=1, STREAM=COM_A)
#include <constant.h>
#include <function.h>

void main(void) {
   _systInit();
   while(TRUE){
      if(kbhit()){
         _dataIn[_serialCounter] = fgetc();
         if(_serialCounter == 0){
            if(_dataIn[0] != '*'){
               _serialCounter = 8;   
            }
         }
         else if(_serialCounter == 1){
            _deseireServoNum = _dataIn[1];
         }
         else if(_serialCounter == 3){
            _deseireServoPulse = ((unsigned int16)_dataIn[2] << 8 | (unsigned int16)_dataIn[3]); 
            if(_deseireServoNum >= 0 && _deseireServoNum < 30){
               if(_deseireServoPulse > 2000)   
                  _deseireServoPulse = 2000;
               if(_deseireServoPulse < 50)     
                  _deseireServoPulse = 50;
               _tempServoPulse[_deseireServoNum] = _deseireServoPulse;
            }
         }
         _serialCounter++;
         if(_serialCounter > 3){
            _serialCounter = 0;
         }
      }
   }
}
 
void _interrupt(void){
   SETUP_TIMER_1(T1_INTERNAL); 
   SET_TIMER1(62535);
   SETUP_TIMER_3(T3_INTERNAL|T3_DIV_BY_4); 
   SET_TIMER3(0);
     
   ENABLE_INTERRUPTS(INT_TIMER1);
   ENABLE_INTERRUPTS(INT_TIMER3);
   //ENABLE_INTERRUPTS(INT_RDA);   
   ENABLE_INTERRUPTS(GLOBAL);
}

#INT_TIMER1
void _timer1(void){
   CLEAR_INTERRUPT(INT_TIMER1);
   DISABLE_INTERRUPTS(INT_TIMER1);
   _counter++;
   if(_counter > 20){
      _counter = 0;
      _setServosPin();
      _sortServos();
      _pulseServos();
   }
   SET_TIMER1(61535);                 // sets timer to interrupt in 1ms
   ENABLE_INTERRUPTS(INT_TIMER1);
}

void _setServosPin(void){
   unsigned char  i;
   
   _servoNum[0]   = _servo1;
   _servoNum[1]   = _servo2;
   _servoNum[2]   = _servo3;
   _servoNum[3]   = _servo4;
   _servoNum[4]   = _servo5;
   _servoNum[5]   = _servo6;
   _servoNum[6]   = _servo7;
   _servoNum[7]   = _servo8;
   _servoNum[8]   = _servo9;
   _servoNum[9]   = _servo10;
   _servoNum[10]  = _servo11;
   _servoNum[11]  = _servo12;
   _servoNum[12]  = _servo13;
   _servoNum[13]  = _servo14;
   _servoNum[14]  = _servo15;   
   _servoNum[15]  = _servo16;
   _servoNum[16]  = _servo17;
   _servoNum[17]  = _servo18;
   _servoNum[18]  = _servo19;
   _servoNum[19]  = _servo20;
   _servoNum[20]  = _servo21;
   _servoNum[21]  = _servo22;
   _servoNum[22]  = _servo23;
   _servoNum[23]  = _servo24;
   _servoNum[24]  = _servo25;
   _servoNum[25]  = _servo26;
   _servoNum[26]  = _servo27;
   _servoNum[27]  = _servo28;
   _servoNum[28]  = _servo29;
   _servoNum[29]  = _servo30;
   
   for(i = 0;i < _numOfServo; i++){
      _servoPulse[i] = _tempServoPulse[i];  // pulse lenght in µS
   }
}

void _sortServos(void){
   unsigned char  i,j;
   unsigned int16 _tempServoNum[1];
   unsigned int16 _tempPulse[2];
   for(i = 0;i < _numOfServo;i++){
      for(j = i+1;j < _numOfServo;j++){
         if(_servoPulse[i] > _servoPulse[j]){
            _tempServoNum[0]  = _servoNum[i];
            _tempPulse[0]     = _servoPulse[i];
           
            _servoNum[i]      = _servoNum[j];
            _servoPulse[i]    = _servoPulse[j];
           
            _servoNum[j]      = _tempServoNum[0];
            _servoPulse[j]    = _tempPulse[0];
         }
      }
   } 
}

void _pulseServos(void){
   unsigned char i;
   _setServosHigh();
   SET_TIMER3(0);
   ENABLE_INTERRUPTS(INT_TIMER3);
   for(i = 0;i < _numOfServo; i++){
      while(GET_TIMER3() < _servoPulse[i]);
         output_low(_servoNum[i]);
   }
   DISABLE_INTERRUPTS(INT_TIMER3);
}


I need to read 4 bytes of data. First byte is just to confirm. Second byte is servo number, third and fourth byte is position of servo.

Here is my TX part.
Code:

#include <18F4520.h>
#device ADC=10
#fuses HS, NOWDT, NOPROTECT, NOLVP
#use delay (clock=20MHz)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, PARITY=N, BITS =8, STOP=1, STREAM=COM_A)
#include <constant.h>
#include <lcd.h>
#include <functions.h>

while(TRUE){
      for(i = 0; i < 16;i++){
         fputc('*',COM_A);
         //delay_ms(3);
         fputc(i,COM_A);
         //delay_ms(3);
         high_byte = _speed >> 8;
         low_byte = _speed;
         fputc(high_byte, COM_A);
         //delay_ms(3);
         fputc(low_byte, COM_A);
         //delay_ms(3);
      }
      delay_ms(10);
      _speed += 10;
      if(_speed > 2300)
         _speed = 50;
   }


In transmitting part I had to put delay at least 3ms between one command to next command in order to work properly. Without delay between, it does not work.

This is some other functions.

In my program servo part is fine itself. The problem is I can not read correct command data from other MCU without delay between sending command. Please help me check it out. My first time posting here, so please forgive me if anything not appropriate. Thanks in advance.
Mike Walne



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

View user's profile Send private message

PostPosted: Tue Nov 06, 2012 4:34 am     Reply with quote

If you want a speedy response delay_ms(xx) is bad news.

It ties up the processor so nothing else can happen, (as you have found).

Use one of the built in timers to generate delays, if you must.

Mike
ktaye1987



Joined: 06 Nov 2012
Posts: 3

View user's profile Send private message

PostPosted: Tue Nov 06, 2012 4:55 am     Reply with quote

Mike Walne wrote:
If you want a speedy response delay_ms(xx) is bad news.

It ties up the processor so nothing else can happen, (as you have found).

Use one of the built in timers to generate delays, if you must.

Mike


Hi Mike, thanks for your reply.. but my main problem is not delay part.. i want to cancel the delay part.. without delay my serial communication is not working properly.. thanks
Mike Walne



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

View user's profile Send private message

PostPosted: Tue Nov 06, 2012 5:59 am     Reply with quote

I'm not arguing about having delays as such.

It's the CCS provided delay_ms(xx) that causes most people to have problems.

The CCS (and other compiler suppliers) delay_ms(xx) type function simply locks up the processor so that nothing else can happen.

It's often better to use one of the built in timers to achieve the required delay intervals.

One way is to get a timer to generate interrupts at, say, 1ms intervals, i.e. create a 1ms tick. In the ISR you keep track of time by counting 1ms ticks. Your main() then polls a tick counter, and is thus able to other things whilst the delay takes place.

Mike
temtronic



Joined: 01 Jul 2010
Posts: 9169
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Nov 06, 2012 6:12 am     Reply with quote

ideas.
1) always add 'errors' to the use rs232(...options) when using the hardware UART. This will avoid the UART from stopping due to overrun conditions.

2) use an ISR with buffer to receive data. CCS does provide an example of this in the 'examples' folder. This will allow 'main' to process any data that comes in via serial and not 'miss' any data.

hth
jay
John P



Joined: 17 Sep 2003
Posts: 331

View user's profile Send private message

PostPosted: Tue Nov 06, 2012 8:34 am     Reply with quote

I would say that if accurate timing is important, do NOT use an ISR for the serial port, because it will unpredictably delay the operation of the timer interrupt. Instead, poll for incoming characters either in the timer-based interrupt, or in a loop in the main() function--and it should go without saying, do the polling at a rate that's faster than characters can arrive. Alternatively, if the processor has low-priority interrupts, put the serial port on one of those.

Getting a single processor to run 30 servos with 1usec resolution seems very ambitious. Won't there be times when events are too close together, i.e. servicing one servo delays service of others?
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Tue Nov 06, 2012 8:57 am     Reply with quote

Your servos would by any chance happen to be RC type, pulse width controlled variety, would they ??

since i assume they ARE:

let me say - you sure spend a lot of time between
sort_servos and pulse servos - with ints off

---------
BTW: when _counters>20 sure looks like a #timer1 INT service destroyer
of the first order - the sort routine alone is a clock cycle eater of great dimension.

It seems a LOT to cram into the handler itself.

Better to just set a flag in MAIN and do the servo messing around out there.
ktaye1987



Joined: 06 Nov 2012
Posts: 3

View user's profile Send private message

PostPosted: Tue Nov 06, 2012 7:21 pm     Reply with quote

John P wrote:
I would say that if accurate timing is important, do NOT use an ISR for the serial port, because it will unpredictably delay the operation of the timer interrupt. Instead, poll for incoming characters either in the timer-based interrupt, or in a loop in the main() function--and it should go without saying, do the polling at a rate that's faster than characters can arrive. Alternatively, if the processor has low-priority interrupts, put the serial port on one of those.

Getting a single processor to run 30 servos with 1usec resolution seems very ambitious. Won't there be times when events are too close together, i.e. servicing one servo delays service of others?


Thanks, yeah it is working fine with setting low-priority interrupt and do the servo messing around in MAIN. I don't have delay for serving different servos, but there are some jitters problem when messing all the servo around. Any idea how to improve that part? Thanks.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Tue Nov 06, 2012 8:11 pm     Reply with quote

Quote:

Any idea how to improve that part?

More EXTERNAL hardware to help mediate the pulse timing.

Much depends on whether or not the 'moves' need to be synchronous.

ONE OTHER BIGGIE

As I have said before MANY brands of RC servo DO NOT require periodic refresh!!! think about the implications of that and you will see a better way forward -- IF your brand of servo does not really need refresh.

One example:
Hitec HS-5065MG only needs TWO successive pulses to wake from sleep and assume a position.

Thereafter ONLY a NEW position change requires another pulse.

Been there - done that with 8 of the buggers on an 8 MHz 18f4525 with scads of spare time - but using some external hardware to assist with the servo addressing.
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