Re: Gizmo hardware problems
John Carmack <[email protected]>
| Newsgroups | gmane.technology.erps |
|---|---|
| Message-ID | <[email protected]> |
At 09:57 PM 7/26/2004 -0700, you wrote:
>Well, I wrote the flight control code. I have no idea if it works.
>
>We went to test it, but it didn't initialize the IMU correctly. So I
>went back to the imutest code and tried that. Works great from my laptop
>and the build server, but not from the PC/104. We checked everything we
>could think of, even brought out the O-scope to look at what was being
>sent to the IMU. The only difference between serial signals from my
>laptop and the PC/104 is the PC/104 is running +/-9V the laptop +/-5V.
>The build server runs +/-12V. Unfortunately we didn't have a breakout
>box to see both sides of the serial at the same time.
>
>Dave
Are you absolutely sure the flight computer is initializing the serial port
correctly? Have you had it talking to a laptop over a null modem
cable? Are you sure it isn't something silly like having an unkeyed 10 pin
ribbon connector on upside down to the CPU board? Make sure you can cat
junk to /dev/ttyS0 and see it on a laptop hyperterminal session.
I don't have a recent version of my flight computer code on this computer,
but here is a couple-year-old version of my xbow code for linux. This is
the previous generation FOG, things changed a bit in the updated one, and
I'm sure your version is somewhat different. The baud rate change
complicates things, and there is certainly no need to do that on the low
bandwidth MEMS gyros you have.
Every sensor and actuator I use has built in test-on-port functionality in
the flight computer executable that is separate from the normal flightcode,
which is very useful when you are reconfiguring your flight systems. Just
a few hours ago, I was using the -testIgnition function to work on a balky
ignition system that we changed around, for instance.
Out of curiosity, do you treat your flight computer like a traditional
embedded system that you cross compile for, or do you have a full
development environment on it? I really like doing my builds and debugging
directly on the flight computer, but I don't use a linux laptop like some
of the ERPS folks do.
John Carmack
/*
====================
OpenSerialPort
Opens a com port
PortNumber is 1 based, ie COM1, COM2 == /dev/ttyS0, /dev/ttyS1
Returns the file descriptor, or exits with an error
====================
*/
int OpenSerialPort( int portNumber, int cflag, int iflag, int oflag, int
lflag ) {
struct termios ios;
struct serial_struct serinfo;
char filename[1024];
int fd;
sprintf( filename, "/dev/ttyS%i", portNumber-1 );
// O_NOCTTY prevents ^c from interrupting
fd = open( filename, O_RDWR | O_NOCTTY );
if ( fd == -1 ) {
perror( "com port open " );
ExitFlightcom();
}
// set low latency mode, so we don't get 10msec scheduling
granularity
// this is the same as 'setserial /dev/ttyS0 low_latency',
// but it won't complete if we aren't root
if ( ioctl( fd, TIOCGSERIAL, &serinfo ) < 0 ) {
perror( "Cannot get serial info " );
ExitFlightcom();
}
serinfo.flags |= ASYNC_LOW_LATENCY;
if ( ioctl( fd, TIOCSSERIAL, &serinfo ) < 0 ) {
perror(" Cannot set serial info " );
ExitFlightcom();
}
// set the baud rate
memset( &ios, 0, sizeof( ios ) );
// CLOCAL : no modem control
// CREAD : enable reading
ios.c_cflag = cflag | CLOCAL | CREAD;
// IGNBRK : ignore serial breaks
// IGNPAR : ignore bytes with parity errors
ios.c_iflag = iflag | IGNBRK | IGNPAR;
// raw output
ios.c_oflag = oflag;
// ICANON : cannonical input processing
ios.c_lflag = lflag; /* ICANON; */
// dump anything in the buffer
tcflush( fd, TCIFLUSH );
// set the new data
tcsetattr( fd, TCSANOW, &ios );
// dump anything in the buffer after the change
tcflush( fd, TCIFLUSH );
return fd;
}
#include "flightcom.h"
static int crossbowHandle;
#define DATA_MASK 1023
static unsigned char data[DATA_MASK+1];
static int dataHead;
static int dataTail;
static int isSynced;
static int lastPacketDataTail;
#define PACKET_LENGTH 18
// including header and checksum
xbow_t xbow;
static int highRate = 1;
static int BaudRate = B57600;
/*
==============
SetHighBaud
==============
*/
static void SetHighBaud( void ) {
struct termios ios;
// set the baud rate
memset( &ios, 0, sizeof( ios ) );
// CLOCAL : no modem control
// CREAD : enable reading
ios.c_cflag = BaudRate | CS8 | CLOCAL | CREAD;
// IGNBRK : ignore serial breaks
// IGNPAR : ignore bytes with parity errors
ios.c_iflag = 0 | IGNBRK | IGNPAR;
// raw output
ios.c_oflag = 0;
// ICANON : cannonical input processing
ios.c_lflag = 0; /* ICANON; */
// dump anything in the buffer
tcflush( crossbowHandle, TCIFLUSH );
// set the new data
tcsetattr( crossbowHandle, TCSANOW, &ios );
// dump anything in the buffer after the change
tcflush( crossbowHandle, TCIFLUSH );
sleep( 1 );
}
/*
==============
ReadResponseWithTimeout
==============
*/
static int ReadResponseWithTimeout( int msec ) {
unsigned char c;
int ioCount;
int start, now;
c = 0;
start = Milliseconds();
while( 1 ) {
ioCount = read( crossbowHandle, &c, sizeof(c) );
if ( ioCount == 1 ) {
break;
}
now = Milliseconds();
if ( now - start > msec ) {
printf( "timeout.\n" );
return -1;
}
}
return c;
}
/*
==============
InitCrossbow
==============
*/
void OpenCrossbow( int portNumber ) {
char cmd[2];
unsigned char c;
unsigned char junk[4096];
int start, now;
printf( "OpenCrossbow on port %i\n", portNumber );
crossbowHandle = OpenSerialPort( portNumber,
B38400 | CS8, // cflag
0, // iflag
0, // oflag
0 ); // lflag (raw mode)
printf( "Trying reset at normal baud rate\n" );
// send a blind reset
cmd[0] = 'R';
write( crossbowHandle, cmd, 1 );
sleep( 1 );
// dump any buffered junk
read( crossbowHandle, &junk, sizeof(junk) );
// now send a reset that we expect to get the proper response for
cmd[0] = 'R';
write( crossbowHandle, cmd, 1 );
c = ReadResponseWithTimeout( 1000 );
if ( c != 'H' ) {
printf( "bad response\n" );
if ( highRate ) {
printf( "Trying reset at high baud rate\n" );
SetHighBaud();
// send a blind reset
cmd[0] = 'R';
write( crossbowHandle, cmd, 1 );
sleep( 1 );
// dump any buffered junk
read( crossbowHandle, &junk, sizeof(junk) );
// now send a reset that we expect to get the
proper response for
cmd[0] = 'R';
write( crossbowHandle, cmd, 1 );
c = ReadResponseWithTimeout( 1000 );
if ( c != 'H' ) {
printf( "bad response.\n" );
exit( 1 );
}
} else {
exit( 1 );
}
} else if ( highRate ) {
// change baud rate
printf( "changing baud rate\n" );
cmd[0] = 'b';
write( crossbowHandle, cmd, 1 );
sleep( 1 );
SetHighBaud();
cmd[0] = 'a';
write( crossbowHandle, cmd, 1 );
c = ReadResponseWithTimeout( 1000 );
if ( c != 'A' ) {
printf( "bad response\n" );
exit( 1 );
}
}
// zero gyro bias
cmd[0] = 'z';
cmd[1] = (char)200; // samples to average (*10)
write( crossbowHandle, cmd, 2 );
printf( "zeroing gyro bias...\n" );
c = ReadResponseWithTimeout( 10000 );
if ( c != 'Z' ) {
printf( "bad response\n" );
exit( 1 );
}
// program it for scaled data output
cmd[0] = 'c';
write( crossbowHandle, cmd, 1 );
c = ReadResponseWithTimeout( 100 );
if ( c != 'C' ) {
printf( "bad response\n" );
exit( 1 );
}
// program it for continuous data output
cmd[0] = 'C';
write( crossbowHandle, cmd, 1 );
// we must find a packet break
isSynced = 0;
memset( &xbow, 0, sizeof( xbow ) );
data[dataHead & DATA_MASK ] = 123; // fail first checksum
// get the first packet
printf( "getting first packet.\n" );
start = Milliseconds();
while( !UpdateCrossbow() ) {
now = Milliseconds();
if ( now - start > 1000 ) {
printf( "timeout.\n" );
exit( 1 );
}
}
printf( "Crossbow initialization complete\n" );
}
/*
==============
PacketChecksums
==============
*/
static int PacketChecksums( void ) {
int check;
int i;
check = 0;
for ( i = 0 ; i < 16 ; i++ ) {
check += data[ ( dataTail - 17 + i ) & DATA_MASK ];
}
check &= 255;
if ( check != data[ ( dataTail - 1 ) & DATA_MASK ] ) {
return 0;
}
return 1;
}
/*
==============
UpdateCrossbow
==============
*/
int UpdateCrossbow( void ) {
int i;
int ioCount;
unsigned char c;
unsigned char readData[PACKET_LENGTH];
// see if we have a complete packet ready to return
while( 1 ) {
while( dataTail < dataHead ) {
dataTail++;
c = data[ dataTail & DATA_MASK ];
// if we are synced, only check for packet breaks
where
// we expect them
if ( isSynced ) {
if ( dataTail != lastPacketDataTail +
PACKET_LENGTH ) {
continue;
}
// if this isn't a new packet, we lost sync
if ( c != 0xff ) {
printf( "UpdateCrossbow: lost
sync\n" );
// exit( 1 );
isSynced = 0;
continue;
}
if ( !PacketChecksums() ) {
printf( "UpdateCrossbow: failed
checksum\n" );
// exit( 1 );
isSynced = 0;
continue;
}
} else {
// see if this is a valid packet
if ( c != 0xff ) {
continue;
}
if ( !PacketChecksums() ) {
continue;
}
// this seems to be good, so we will be
synced now
isSynced = 1;
}
// we have a packet
lastPacketDataTail = dataTail;
xbow.updateNumber++;
xbow.updateTime = currentTime;
// byte swap
for ( i = 0 ; i < 8 ; i++ ) {
((char *)xbow.rate)[i*2+0] = data[ (
dataTail - 17 + i * 2 + 1 ) & DATA_MASK ];
((char *)xbow.rate)[i*2+1] = data[ (
dataTail - 17 + i * 2 + 0 ) & DATA_MASK ];
}
// scale to engineering units
#define RAW_TO_RATE ((260*1.5)/32768)
#define RAW_TO_ACCEL ((10*1.5)/32768)
xbow.scaledRate[0] = -xbow.rate[2] * RAW_TO_RATE;
xbow.scaledRate[1] = xbow.rate[0] * RAW_TO_RATE;
xbow.scaledRate[2] = -xbow.rate[1] * RAW_TO_RATE;
xbow.scaledAccel[0] = -xbow.accel[2] * RAW_TO_ACCEL;
xbow.scaledAccel[1] = xbow.accel[0] * RAW_TO_ACCEL;
xbow.scaledAccel[2] = -xbow.accel[1] * RAW_TO_ACCEL;
return 1;
}
// try to read more data from the serial port
ioCount = read( crossbowHandle, &readData, sizeof(readData) );
// if we can't read anything, return with no update
if ( ioCount == 0 ) {
return 0;
}
// copy the data into the circular buffer
for ( i = 0 ; i < ioCount ; i++ ) {
dataHead++;
data[dataHead & DATA_MASK] = readData[i];
}
// see if we have a valid packet now
}
return 0;
}
/*
==============
PrintCrossbow
==============
*/
void PrintCrossbow( void ) {
printf( "%i\t:\t%i\t%i\t%i\t%i\t%i\t%i\t%i\t%i\n",
xbow.updateNumber,
xbow.rate[0], xbow.rate[1], xbow.rate[2],
xbow.accel[0], xbow.accel[1], xbow.accel[2],
xbow.temp, xbow.time );
}
/*
==============
TestCrossbow
==============
*/
void TestCrossbow( int portNumber ) {
OpenCrossbow( portNumber );
// read the port until aborted
printf( "reading data, press a key to abort\n" );
while( 1 ) {
CheckAbort();
if ( !UpdateCrossbow() ) {
continue;
}
PrintCrossbow();
}
}