Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // $Id: MotionPlatformArduinoBoard.pde 507 2010-04-05 21:51:41Z rharder $
- /*
- This is the control software for the motion platform. The software
- is loaded onto an Arduino board with digital-to-audio converters (DAC)
- to convert signals to the +/- voltage that the motion platform requires.
- Authors:
- Robert Harder, 2010
- */
- // SPI and DAC Pins
- // It seems that Arduino requires that the
- // SPI pins (except slave select) be these
- // particular pins.
- // The DAC_CLEAR pin is to reset the AD7849
- // DAC chip that's part of the circuit.
- #define SPI_DATA_OUT 11
- #define SPI_DATA_IN 12
- #define SPI_CLOCK 13
- #define SPI_SLAVE_SELECT_1 10
- #define DAC_CLEAR_PIN 8
- unsigned int ROLL_PIN = 10;
- unsigned int PITCH_PIN = 9;
- // Serial communication back to a user's computer
- #define BAUD 9600
- #define SERIAL_BUFFER_SIZE 20
- char serialBuffer[SERIAL_BUFFER_SIZE+1]; // Ensure buffer +1 always has null
- unsigned int serialBufferPosition = 0;
- // Motion Smoothing voltages
- // When roll, pitch, etc are set, the values
- // are adjusted smoothly rather than jumping right
- // to the new voltage. This is to reduce the "violence"
- // of the motion platform.
- #define MOTION_DIMENSIONS 2
- float curr[MOTION_DIMENSIONS];
- float goal[MOTION_DIMENSIONS];
- float start[MOTION_DIMENSIONS];
- // Constants used in a few places, especially array indeces
- #define ROLL 0
- #define PITCH 1
- // Motion Smoothing, transitions
- // These values are used as part of the motion smoothing
- // procedure.
- float vel[MOTION_DIMENSIONS];
- float accel[MOTION_DIMENSIONS];
- float maxVel = 1;
- float maxAccel = 1;
- int startMillis;
- // ROLLOVER WARNING: It appears that the Arduino counter rolls over
- // every 49 days, or 9 hours, depending on where you look. Rollover
- // is not handled right now, so at 9 hours or 49 days, don't know
- // which, of run time, the platform will do something weird. This is
- // on the list of things to fix.
- // http://www.faludi.com/2007/12/18/arduino-millis-rollover-handling/
- /* ******** M A I N A R D U I N O F U N C T I O N S ******** */
- /**
- * Run when Arduino starts up or new serial connection is established.
- */
- void setup(){
- setupSPI();
- setupSerial();
- setupSmoothing();
- }
- /**
- * Runs continuously.
- */
- void loop(){
- readSerial();
- interpolatePosition(millis());
- }
- /* ******** S E R I A L ******** */
- /**
- * Setup the serial communication with the host computer
- */
- void setupSerial(){
- // Zero out the buffer
- for( int i = 0; i <= SERIAL_BUFFER_SIZE; i++ ){
- serialBuffer[i] = 0;
- }
- Serial.begin(BAUD);
- sendInstructions();
- }
- /**
- * Send instructions via serial to the user.
- */
- void sendInstructions(){
- Serial.println("Commands:");
- Serial.println(" r .. Set roll from -1..+1");
- Serial.println(" p .. Set pitch from -1..+1");
- Serial.println(" s v .. Set max velocity for moving the platform.");
- Serial.println(" The units refer to the -1..+1 range of motion.");
- }
- /**
- * Reads the serial port for incoming commands from the user.
- * When end of line is reached, the command is passed to the
- * function processIncomingCommand(..).
- */
- void readSerial(){
- if (Serial.available() > 0) {
- int inByte = Serial.read();
- // if( inByte >= 0 ) Serial.write(inByte);
- // No data
- if( inByte < 0 ){
- Serial.println("NO DATA EVEN THOUGH Serial.available() SAID YES");
- delay(5);
- }
- // End of line
- // We're not always getting end of line characters, so optionally
- // support ending a line with a semicolon. Yuck.
- else if( (inByte == 10) || (inByte == 13) || (inByte == 59) ){ // ; = 59
- Serial.println("<<< END OF LINE >>>");
- serialBuffer[ serialBufferPosition ] = 0;
- processIncomingCommand(serialBuffer);
- serialBufferPosition = 0;
- }
- // Request for instructions
- else if( (inByte == 63) ){ // ? = 63
- sendInstructions();
- }
- // Save next input byte in the line
- else {
- serialBuffer[ serialBufferPosition ] = (char)inByte;
- serialBufferPosition = (serialBufferPosition+1) % SERIAL_BUFFER_SIZE;
- }
- } // end if: serial available
- //Serial.print("SERIAL BUFFER POS: " ); Serial.println(serialBufferPosition);
- }
- /* ******** C O M M A N D S ******** */
- /**
- * Process an incoming command from the user.
- * Commands are line-oriented, intended to be entered,
- * at least during development, from a serial console.
- */
- void processIncomingCommand( char* line ){
- switch(line[0]){
- case 'R':
- case 'r':
- handleCommand_R(line);
- break; // Roll
- case 'P':
- case 'p':
- handleCommand_P(line);
- break; // Pitch
- case 'S':
- case 's':
- handleCommand_S(line);
- break; // Settings
- default:
- // Blink
- setDAC(ROLL,+1);
- delay(100);
- setDAC(ROLL,0);
- delay(100);
- setDAC(ROLL,+1);
- delay(100);
- setDAC(ROLL,0);
- delay(100);
- setDAC(ROLL,+1);
- delay(100);
- setDAC(ROLL,0);
- delay(100);
- break;
- }
- }
- /**
- * Set Roll with an ascii string, a floating point value -1..+1
- */
- void handleCommand_R( char* line ){
- if( line[0] == NULL ) return; // Make sure it's not a null string
- char * tok = strtok(line, " "); // Should be 'R'
- tok = strtok(NULL," "); // Call again to skip over the first token (R)
- while (tok != NULL) {
- float f = atof(tok);
- setRoll(f);
- tok = strtok(NULL," ");
- }
- }
- /**
- * Set Pitch with an ascii string, a floating point value -1..+1
- */
- void handleCommand_P( char* line ){
- if( line[0] == NULL ) return; // Make sure it's not a null string
- char * tok = strtok(line, " "); // Should be 'P'
- tok = strtok(NULL," "); // Call again to skip over the first token (P)
- while (tok != NULL) {
- float f = atof(tok);
- setPitch(f);
- tok = strtok(NULL," ");
- }
- }
- /**
- * Change settings.
- */
- void handleCommand_S( char* line ){
- if( line[0] == NULL ) return; // Make sure it's not a null string
- char * tok = strtok(line, " "); // Tokenize the line (should return S)
- tok = strtok(NULL," "); // What setting to change?
- switch( tok[0] ){
- case 'V':
- case 'v':
- tok = strtok(NULL," ");
- float f = atof(tok);
- maxVel = f;
- Serial.print("Max velocity: ");
- Serial.println(maxVel);
- break;
- } // end switch: setting
- } // end command S
- /* ******** P L A T F O R M P O S I T I O N ******** */
- void setupSmoothing(){
- startMillis = millis();
- for( int i = 0; i < MOTION_DIMENSIONS; i++ ){
- curr[i] = 0;
- goal[i] = 0;
- start[i] = 0;
- vel[i] = 0;
- accel[i] = 0;
- }
- }
- /**
- * Sets the new requested roll value.
- * Platform will begin to move toward that value.
- */
- void setRoll( float minusOneToPosOne ){
- // Serial.print("Setting roll to ");Serial.println(minusOneToPosOne);
- setDimension(ROLL, minusOneToPosOne);
- }
- /**
- * Sets the new requested roll value.
- * Platform will begin to move toward that value.
- */
- void setPitch( float minusOneToPosOne ){
- setDimension(PITCH, minusOneToPosOne);
- }
- /**
- * Consolidating function for setRoll and setPitch.
- */
- void setDimension( unsigned int dim, float minusOneToPosOne ){
- startMillis = millis();
- minusOneToPosOne = min(1, max(-1, minusOneToPosOne)); // Cap it
- start[dim] = curr[dim];
- goal[dim] = minusOneToPosOne;
- updateVelocity();
- /* Serial.print("\nRoll: "); Serial.println( minusOneToPosOne );
- Serial.print("\nStart: "); Serial.println( start[dim] );
- Serial.print("\nCurr: "); Serial.println( curr[dim] );
- Serial.print("\nGoal: "); Serial.println( goal[dim] );
- Serial.print("\nVelocity: "); Serial.print( vel[0] ); Serial.print(", "); Serial.println( vel[1] );
- */
- }
- /**
- * After roll and pitch are set, the velocities
- * need to be updated, so that the board moves
- * in the new direction.
- */
- void updateVelocity(){
- for( int i = 0; i < MOTION_DIMENSIONS; i++ ){
- vel[i] = goal[i] - start[i];
- }
- normalizeVector(vel, MOTION_DIMENSIONS);
- scaleVector(vel, MOTION_DIMENSIONS, maxVel);
- }
- /**
- * Takes a vector and normalizes to unit vector (length one).
- */
- void normalizeVector( float* vec, unsigned int length ){
- float accum = 0;
- for( int i = 0; i < length; i++ ){
- accum += vec[i]*vec[i];
- }
- accum = sqrt(accum);
- for( int i = 0; i < length; i++ ){
- vec[i] = vec[i] / accum;
- }
- }
- void scaleVector( float* vec, unsigned int length, float scalar ){
- for( int i = 0; i < length; i++ ){
- vec[i] *= scalar;
- }
- }
- void interpolatePosition(int atMillis){
- // If we're fed a timestamp that is too early, just give starting point
- if( atMillis < startMillis ){
- for(int i=0; i<MOTION_DIMENSIONS; i++){
- curr[i] = start[i];
- }
- }
- else {
- // Calculate based on timestamp and velocity
- for(int i=0; i<MOTION_DIMENSIONS; i++){
- curr[i] = start[i] + vel[i]*((atMillis-startMillis)*.001);
- }
- for(int i=0; i<MOTION_DIMENSIONS; i++){
- // If we're moving in the positive direction, and we've gone too far
- if( start[i] <= goal[i] && goal[i] <= curr[i] ){
- curr[i] = goal[i];
- }
- else
- // If we're moving in the negative direction, and we've gone too far
- if( goal[i] <= start[i] && curr[i] < goal[i] ){
- curr[i] = goal[i];
- }
- } // end for: each dimension
- }
- for(int i=0; i<MOTION_DIMENSIONS; i++){
- setDAC(i, curr[i]);
- // char c[50]; sprintf(c,"Curr: (%d, %d)", (int)(curr[0]*1000), (int)(curr[1]*1000)); Serial.println( c );
- }
- }
- /* ******** S P I A N D V O L T A G E ******** */
- // http://www.arduino.cc/en/Tutorial/SPIEEPROM
- // http://www.arduino.cc/en/Tutorial/SPIDigitalPot
- void setupSPI(){
- resetDAC();
- pinMode(SPI_DATA_OUT, OUTPUT); // SPI Output
- pinMode(SPI_DATA_IN, INPUT); // SPI Input
- pinMode(SPI_CLOCK,OUTPUT); // SPI Clock
- pinMode(SPI_SLAVE_SELECT_1,OUTPUT); // SPI Slave deactivated
- digitalWrite(SPI_SLAVE_SELECT_1,HIGH);
- // SPCR = 01010000
- //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
- //sample on leading edge of clk,system clock/4 rate (fastest)
- // SPCR
- // | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |0000000000000000000
- // | SPIE | SPE | DORD | MSTR | CPOL | CPHA | SPR1 | SPR0 |
- // SPIE - Enables the SPI interrupt when 1
- // SPE - Enables the SPI when 1
- // DORD - Sends data least Significant Bit First when 1, most Significant Bit first when 0
- // MSTR - Sets the Arduino in master mode when 1, slave mode when 0
- // CPOL - Sets the data clock to be idle when high if set to 1, idle when low if set to 0
- // CPHA - Samples data on the falling edge of the data clock when 1, rising edge when 0'
- // SPR1 and SPR0 - Sets the SPI speed, 00 is fastest (4MHz) 11 is slowest (250KHz)
- SPCR = 0 |
- (0<<7) | // SPIE: disables the SPI interrupt
- (1<<6) | // SPE: enables the SPI
- (0<<5) | // DORD: chooses transmission with the most significant bit going first
- (1<<4) | // MSTR: puts the Arduino in Master mode
- (0<<3) | // CPOL: sets the data clock idle when it is low
- (0<<2) | // CPHA: sets the SPI to sample data on the rising edge of the data clock
- (0<<1) | // SPR1: second and first bits set the speed of the
- (0 ); // SPR0: SPI to system speed / 4 (the fastest)
- //SPCR = (1<<SPE) | (1<<MSTR);
- SPSR = B00000000; // not double data rate
- byte clr=SPSR;
- clr=SPDR;
- delay(10);
- } // end setupSPI
- /**
- * This resets the DAC hardware.
- */
- void resetDAC(){
- pinMode(DAC_CLEAR_PIN, OUTPUT);
- digitalWrite(DAC_CLEAR_PIN,LOW);
- delay(5);
- digitalWrite(DAC_CLEAR_PIN,HIGH);
- }
- /**
- * Sets the voltage output based on one of the known
- * dimensions (roll and pitch at this point) and an
- * extent of that dimension scaled from -1 to +1.
- */
- void setDAC( unsigned int dimension, float minusOneToPositiveOne ){
- unsigned int SS_PIN = -1;
- switch( dimension ){
- case ROLL:
- SS_PIN = ROLL_PIN;
- break;
- case PITCH:
- SS_PIN = PITCH_PIN;
- break;
- default:
- Serial.print("\nError in setDAC function: Unknown dimension ");
- Serial.println(dimension);
- return;
- }
- // Serial.print("Value: " ); Serial.println( minusOneToPositiveOne );
- if( minusOneToPositiveOne > 0 ){
- // Positive values
- float f = min(minusOneToPositiveOne,1);
- int spiVal = max(0.0, min( 63.0, f*64));
- digitalWrite(SS_PIN,LOW);
- spi_transfer_16( spiVal << 8 );
- digitalWrite(SS_PIN,HIGH);
- //if( dimension == 0 ){ Serial.print("Pos, Pin: " ); Serial.print(SS_PIN); Serial.print(", Value: "); Serial.println(spiVal << 8); }
- }
- else if( minusOneToPositiveOne < 0 ){
- // Negative values
- float f = -max(minusOneToPositiveOne,-1);
- int spiVal = 127-max(0.0, min( 63.0, f*64));
- digitalWrite(SS_PIN,LOW);
- spi_transfer_16( spiVal << 8 );
- digitalWrite(SS_PIN,HIGH);
- //if( dimension == 0 ){ Serial.print("Neg, Pin: " ); Serial.print(SS_PIN); Serial.print(", Value: "); Serial.println(spiVal << 8); }
- }
- else {
- // Zero
- digitalWrite(SS_PIN,LOW);
- spi_transfer_16( 0 );
- digitalWrite(SS_PIN,HIGH);
- //if( dimension == 0 ){ Serial.print("Zero, Pin: " ); Serial.print(SS_PIN); Serial.print(", Value: "); Serial.println(0); }
- }
- }
- void spi_transfer_16(volatile int data){
- spi_transfer(data>>8);
- spi_transfer(data);
- }
- char spi_transfer(volatile char data)
- {
- SPDR = data; // Start the transmission
- while (!(SPSR & (1<<SPIF))) ; // Wait for the end of the transmission
- return SPDR; // return the received byte
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement