|
|
View previous topic :: View next topic |
Author |
Message |
ktaye1987
Joined: 06 Nov 2012 Posts: 3
|
Multi channel servo control(Serial communication problem) |
Posted: Tue Nov 06, 2012 4:20 am |
|
|
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
|
|
Posted: Tue Nov 06, 2012 4:34 am |
|
|
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
|
|
Posted: Tue Nov 06, 2012 4:55 am |
|
|
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
|
|
Posted: Tue Nov 06, 2012 5:59 am |
|
|
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: 9272 Location: Greensville,Ontario
|
|
Posted: Tue Nov 06, 2012 6:12 am |
|
|
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
|
|
Posted: Tue Nov 06, 2012 8:34 am |
|
|
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
|
|
Posted: Tue Nov 06, 2012 8:57 am |
|
|
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
|
|
Posted: Tue Nov 06, 2012 7:21 pm |
|
|
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
|
|
Posted: Tue Nov 06, 2012 8:11 pm |
|
|
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. |
|
|
|
|
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
|