by DanAlbert » Tue Sep 26, 2006 3:23 am
by DanAlbert
Tue Sep 26, 2006 3:23 am
Progress Report:
Since I replaced the ATMEGA128 with a new chip I have.
Both serial ports working via ISR.
The speaker can generate sounds.
And most importantly...All 24 servos PWM with 5 uSec (or 1 degree) increments. All 24 servos start at almost the same time. Since this is an 8 bit processor you need to assign values to ports A,B and C one instruction apart.
Since everything is done in ISRs I can play a tune while moving servos and receiving data. Duh...what a concept. Hitec makes great servos, but when it comes to software they don't have a clue.
For those that are still reading this....and want the gorey details.
This would be a lot easier if Hitec had used a 16MHz or even a 14.7456 XTAL. With only 5 uSecs to work with, at 7.3728 MHz ... well lets do the math.
To get 200 degrees in a 1 mSec PWM pulse, thats 5 uSecs per degree.
1/7372800 = .000,000,1356336..secs per cycle
.000,005 / .000,000,1356 = 36.8 cycles in 5 uSecs.
This is all ya get at 7 MHz.
the overhead on the ISR just in/out and SREG push/pop about 11 cycles.
not much time left to do anything.
I set up the data for all 24 servos during the deadband time.
This is an array of 96 pieces of data grouped in a structure of 4 bytes.(24 * 4 = 96) the first byte is the time(OCR1A setting) until the next change of state of any of ports A,B and C bits. The next three bytes are the values of ports A,B and C at that timer match.
I have two timers. Timer 0 counts off mSecs and Timer 1 gets reset at each ISR call (when OCR1A = TCNT1) with a max resolution of 5 uSecs where I load the state of Ports A,B and C.
At 0 mSec I set all the servo channels high.
At 1 mSec 1 start the faster Timer 1.
It tics off as many times as necessary to bring all the lines to low.
Since some servos have the same setting this can be less than 24.
At 2 mSec I stop Timer 1.
Heres the ISRs
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ISR mSec routine
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER0_COMP_vect, ISR_BLOCK)
{
static U8 mSecServoCounter;
mSecServoCounter++;
if(mSecServoCounter >= 20){
mSecServoCounter = 0;
PORTA = 0xFF; //Servos S7 - S0
PORTB = 0xFF; //Servos S15 - S8
PORTC = 0xFF; //Servos S23 - S16
}
else if(mSecServoCounter == 1){
ptrTrailingEdge = &ServoTrailingEdgeList[0];
TEhigh = (((U16)ptrTrailingEdge >> 8 ) & 0xff);
TElow = ((U16)ptrTrailingEdge & 0xff);
asm ("movw r30,r16");
noServoUpdate = 1;
TIMSK |= OCIE1A; // enable output compare interrupt
OCR1A = 1;
TCNT1 = 0; // reset counter for accurate .5 uSec count
}
// at two mSecs all servos should already be off (failsafe)
else if(mSecServoCounter == 2){
PORTA = 0x00; //turn off all servos
PORTB = 0x00; //turn off all servos
PORTC = 0x00; //turn off all servos
TIMSK &= ~OCIE1A; // disable output compare interrupt
noServoUpdate = 0;
}
ms_count++;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ISR Timer 1A individual trailing edge PWM for each servo
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER1_COMPA_vect,ISR_NAKED)
{
asm volatile (
"cli\n\t"
"in R18,__SREG__\n\t"
"ld R19, Z+\n\t"
"out 42, R19\n\t"
"ld R19, Z+\n\t"
"out 27, R19\n\t"
"ld R19, Z+\n\t"
"out 24, R19\n\t"
"ld R19, Z+\n\t"
"out 21, R19\n\t"
"in R19,55\n\t"
"sbr R19,16\n\t"
"out 54,R19\n\t"
"clr R0\n\t"
"out __SREG__,r18\n\t"
"reti\n\t"
);
}
I've posted this code in the hope that someone else will help with this project. There is still a great deal of work to be done.
We should set up an open source area on this site for posting code updates.
Next.....I2C and EEPROM
I want to store servo "zero" settings and servo positions.
Then come up with a method for dynamic movements.
Progress Report:
Since I replaced the ATMEGA128 with a new chip I have.
Both serial ports working via ISR.
The speaker can generate sounds.
And most importantly...All 24 servos PWM with 5 uSec (or 1 degree) increments. All 24 servos start at almost the same time. Since this is an 8 bit processor you need to assign values to ports A,B and C one instruction apart.
Since everything is done in ISRs I can play a tune while moving servos and receiving data. Duh...what a concept. Hitec makes great servos, but when it comes to software they don't have a clue.
For those that are still reading this....and want the gorey details.
This would be a lot easier if Hitec had used a 16MHz or even a 14.7456 XTAL. With only 5 uSecs to work with, at 7.3728 MHz ... well lets do the math.
To get 200 degrees in a 1 mSec PWM pulse, thats 5 uSecs per degree.
1/7372800 = .000,000,1356336..secs per cycle
.000,005 / .000,000,1356 = 36.8 cycles in 5 uSecs.
This is all ya get at 7 MHz.
the overhead on the ISR just in/out and SREG push/pop about 11 cycles.
not much time left to do anything.
I set up the data for all 24 servos during the deadband time.
This is an array of 96 pieces of data grouped in a structure of 4 bytes.(24 * 4 = 96) the first byte is the time(OCR1A setting) until the next change of state of any of ports A,B and C bits. The next three bytes are the values of ports A,B and C at that timer match.
I have two timers. Timer 0 counts off mSecs and Timer 1 gets reset at each ISR call (when OCR1A = TCNT1) with a max resolution of 5 uSecs where I load the state of Ports A,B and C.
At 0 mSec I set all the servo channels high.
At 1 mSec 1 start the faster Timer 1.
It tics off as many times as necessary to bring all the lines to low.
Since some servos have the same setting this can be less than 24.
At 2 mSec I stop Timer 1.
Heres the ISRs
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ISR mSec routine
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER0_COMP_vect, ISR_BLOCK)
{
static U8 mSecServoCounter;
mSecServoCounter++;
if(mSecServoCounter >= 20){
mSecServoCounter = 0;
PORTA = 0xFF; //Servos S7 - S0
PORTB = 0xFF; //Servos S15 - S8
PORTC = 0xFF; //Servos S23 - S16
}
else if(mSecServoCounter == 1){
ptrTrailingEdge = &ServoTrailingEdgeList[0];
TEhigh = (((U16)ptrTrailingEdge >> 8 ) & 0xff);
TElow = ((U16)ptrTrailingEdge & 0xff);
asm ("movw r30,r16");
noServoUpdate = 1;
TIMSK |= OCIE1A; // enable output compare interrupt
OCR1A = 1;
TCNT1 = 0; // reset counter for accurate .5 uSec count
}
// at two mSecs all servos should already be off (failsafe)
else if(mSecServoCounter == 2){
PORTA = 0x00; //turn off all servos
PORTB = 0x00; //turn off all servos
PORTC = 0x00; //turn off all servos
TIMSK &= ~OCIE1A; // disable output compare interrupt
noServoUpdate = 0;
}
ms_count++;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ISR Timer 1A individual trailing edge PWM for each servo
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ISR(TIMER1_COMPA_vect,ISR_NAKED)
{
asm volatile (
"cli\n\t"
"in R18,__SREG__\n\t"
"ld R19, Z+\n\t"
"out 42, R19\n\t"
"ld R19, Z+\n\t"
"out 27, R19\n\t"
"ld R19, Z+\n\t"
"out 24, R19\n\t"
"ld R19, Z+\n\t"
"out 21, R19\n\t"
"in R19,55\n\t"
"sbr R19,16\n\t"
"out 54,R19\n\t"
"clr R0\n\t"
"out __SREG__,r18\n\t"
"reti\n\t"
);
}
I've posted this code in the hope that someone else will help with this project. There is still a great deal of work to be done.
We should set up an open source area on this site for posting code updates.
Next.....I2C and EEPROM
I want to store servo "zero" settings and servo positions.
Then come up with a method for dynamic movements.