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

DE Sabertooth motor controller driver code

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



Joined: 01 Nov 2009
Posts: 55
Location: Central Oklahoma

View user's profile Send private message

DE Sabertooth motor controller driver code
PostPosted: Sun Jul 17, 2011 1:29 pm     Reply with quote

Here is my first personal contribution back to the PIC community. I am a self-taught amateur and programming novice, so if there are glaring errors I am the first to claim sole responsibility. I wrote this driver for the Dimension Engineering Sabertooth 2x25 H-Bridge Motor Controller (www.dimensionengineering.com), which I am using in a project to create a robotic lawnmower for my very large (approx 2 acres!) yard.

Please offer any constructive criticism and suggestions, and I will post the code to the Code Library after you've had a chance to look it over.

Brad

(sorry about the tabs not lining up - they look perfect in MPLAB...)


Code:


/*
==========================================================================================
   Sabertooth_Driver.c
   Version 1.0
   16 July, 2011

   Brad Hedges
   H & H Enterprises
   Stratford, Oklahoma
==========================================================================================
==========================================================================================

   This is my attempt to create a multi-purpose device driver to use with the Dimension Engineering
   Sabertooth H-Bridge motor controller, for use in controlling permanent-magnet DC motors
   between 6 & 30 volts, up to 25 amps continuous and 50 amps surge.

   The driver is written in the C language for Microchip PIC microcontrollers using CCS's
   PIC-C Compiler, and was developed using version 4.120 of that compiler. It should be
   easily convertible to other microcontrollers and compilers with minimal effort.

   This driver works in the "Packetized Serial" mode, which must be set with
   the DIP switches on the controller pcb. The driver defaults to operating only one
   Sabertooth, using the lowest address of 128. The address is configurable in the driver
   by assigning the address value to the constant "MOTOR_DRIVER_ADDRESS_1".

   See the Dimension Engineering web site at: www.dimensionengineering.com for more information

==========================================================================================
   FUNCTIONS

   *** NOTE *** if using only one single Sabertooth controller, you do not need the "address"
             variable. It is only needed if you comment out the #define ONE_SABERTOOTH
             line, due to conditional compilation of that line.
==========================================================================================

   control_motors( command1, speed1, command2, speed2, address );

   set_minimum_controller_voltage( desired_minimum_voltage, address )

   set_maximum_controller_voltage( desired_maximum_voltage, address )

   set_serial_timeout ( desired_timeout_period, address )

   set_baudrate ( desired_baudrate, address )

   set_ramping_rate ( desired_ramping_rate, address )

   set_deadband_range ( desired_deadband, address )

==========================================================================================
*/

///////////////////////////////////////////////////////////////////////////////////////////////


//#define MOTOR_STOP               PIN_D5               // Assign pin name to use in emergency stop routines


#define CRC_MASK               0b01111111

#define MOTOR_DRIVER_ADDRESS_1      128                     // If using more than one Sabretooth, assign or change
//#define MOTOR_DRIVER_ADDRESS_2      129                  // numbers as needed or desired
//#define MOTOR_DRIVER_ADDRESS_3      130
//#define MOTOR_DRIVER_ADDRESS_4      131
//#define MOTOR_DRIVER_ADDRESS_5      132
//#define MOTOR_DRIVER_ADDRESS_6      133
//#define MOTOR_DRIVER_ADDRESS_7      134
//#define MOTOR_DRIVER_ADDRESS_8      135



//==========================================================================================
// If you are using more than one Sabertooth, you must comment out the ONE_SABERTOOTH line below
// This causes a conditional compilation which changes the functions to use whatever you set as the
// default, single-controller address for your Sabertooth using the MOTOR_DRIVER_ADDRESS_1 constant.

   #define ONE_SABERTOOTH                              // Comment these two lines out if using more than Sabertooth controller

   static uint8_t address = MOTOR_DRIVER_ADDRESS_1;

///////////////////////////////////////////////////////////////////////////////////////////////

// COMMANDS

#define DRIVE_FORWARD_1               0
#define DRIVE_REVERSE_1               1

#define SET_MIN_VOLTAGE               2
#define SET_MAX_VOLTAGE               3

#define DRIVE_FORWARD_2               4
#define DRIVE_REVERSE_2               5

#define DRIVE_MOTOR_1_7_BIT            6
#define DRIVE_MOTOR_2_7_BIT            7

#define DRIVE_FORWARD_MIXED            8
#define DRIVE_REVERSE_MIXED            9
#define DRIVE_TURN_RIGHT_MIXED         10
#define DRIVE_TURN_LEFT_MIXED         11

#define DRIVE_FWD_REV_7_BIT            12
#define DRIVE_TURN_7_BIT            13

#define SET_SERIAL_TIMEOUT            14
#define SET_BAUD_RATE               15

#define SET_RAMPING_RATE            16
#define SET_DEADBAND               17


///////////////////////////////////////////////////////////////////////////////////////////////

// COMMAND DATA

#define STOP                     0

#define SET_BAUDRATE_2400            1
#define SET_BAUDRATE_9600            2
#define SET_BAUDRATE_19200            3
#define SET_BAUDRATE_38400            4

#define MIN_VOLTAGE_MULTIPLIER         5
#define MIN_CONTROLLER_VOLTAGE         6
#define DEFAULT_MINIMUM_VOLTAGE         0                  // 0 is equal to 6 volts minimum voltage for shutdown
#define MAX_MINIMUM_VOLTAGE            120
#define MIN_MINIMUM_VOLTAGE            0

#define MAX_VOLTAGE_MULTIPLIER         5.12

#define CLEAR_SERIAL_TIMEOUT         0

#define MAX_RAMPING_VALUE            80
#define MAX_FAST_RAMPING_VALUE         10
#define DEFAULT_RAMPING_VALUE         1

#define SET_DEADBAND_TO_DEFAULT         0
#define DEFAULT_DEADBAND            3
#define MIN_DEADBAND            0
#define MAX_DEADBAND            127


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMANDS 0, 1, 4 - 13
//
//   control_motors
//
///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ONE_SABERTOOTH
   void control_motors( uint8_t command1, uint8_t speed1, uint8_t command2, uint8_t speed2 ) {
#else
   void control_motors( uint8_t command1, uint8_t speed1, uint8_t command2, uint8_t speed2, uint8_t address ) {
#endif

   putc( address );
   putc( command1 );
   putc( speed1 );
   putc( ( address + command1 + speed1 ) & CRC_MASK );         // validate serial data w/ CRC sum

   putc( address );
   putc( command2 );
   putc( speed2 );
   putc( ( address + command2 + speed2 ) & CRC_MASK );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 2
//
//   set_minimum_controller_voltage
//
///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ONE_SABERTOOTH
   void set_minimum_controller_voltage ( uint8_t desired_minimum_voltage ) {
#else
   void set_minimum_controller_voltage ( uint8_t desired_minimum_voltage, uint8_t address ) {
#endif

   uint8_t new_min_voltage = 0;

   if ( desired_minimum_voltage > MAX_MINIMUM_VOLTAGE || desired_minimum_voltage < MIN_MINIMUM_VOLTAGE ) {
      desired_minimum_voltage = DEFAULT_MINIMUM_VOLTAGE;
   }

   putc( address );
   putc( SET_MIN_VOLTAGE );
   putc( new_min_voltage );
   putc( ( address + SET_MIN_VOLTAGE + new_min_voltage ) & CRC_MASK );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 3 - do not use if powering Sabertooth from batteries, only needed for power supplies - see datasheet
//
//   set_maximum_controller_voltage
//
///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ONE_SABERTOOTH
   void set_maximum_controller_voltage ( float desired_maximum_voltage ) {
#else
   void set_maximum_controller_voltage ( float desired_maximum_voltage, uint8_t address ) {
#endif

   uint8_t new_max_voltage = 0;

   (float)new_max_voltage = ( desired_maximum_voltage * MAX_VOLTAGE_MULTIPLIER );

   putc( address );
   putc( SET_MAX_VOLTAGE );
   putc( new_max_voltage );
   putc( ( address  + SET_MAX_VOLTAGE + new_max_voltage ) & CRC_MASK );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 14
//
//   set_serial_timeout
//
//   Value received by the Sabertooth is in multiples of 100 milli-seconds; i.e., '1' = 100 ms
//
///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ONE_SABERTOOTH
   void set_serial_timeout ( uint8_t desired_timeout_period ) {
#else
   void set_serial_timeout ( uint8_t desired_timeout_period, uint8_t address ) {
#endif

   uint8_t new_timeout_period = 0;

   if ( desired_timeout_period >= CLEAR_SERIAL_TIMEOUT ) {
      new_timeout_period = desired_timeout_period;
   }

   else {
      new_timeout_period = CLEAR_SERIAL_TIMEOUT;
   }

   putc( address );
   putc( SET_SERIAL_TIMEOUT );
   putc( new_timeout_period );
   putc( ( address + SET_SERIAL_TIMEOUT + new_timeout_period ) & CRC_MASK );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 15 - BAUD RATE
//
///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ONE_SABERTOOTH
   void set_baudrate ( uint8_t desired_baudrate ) {
#else
   void set_baudrate ( uint8_t desired_baudrate, uint8_t address ) {
#endif

   if ( desired_baudrate > SET_BAUDRATE_38400 || desired_baudrate < SET_BAUDRATE_2400 ) {
      desired_baudrate = SET_BAUDRATE_9600;               // set to default if out of range
//      set error code for invalid command
   }

   putc( address );
   putc( SET_BAUD_RATE );
   putc( desired_baudrate );
   putc( ( address + SET_BAUD_RATE + desired_baudrate ) & CRC_MASK );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 16 - RAMPING
//
//   set_ramping_rate accepts the desired ramping rate, which must be a value between
//   zero ( 0 ) and eighty ( 80 ).
//
///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ONE_SABERTOOTH
   void set_ramping_rate ( uint8_t desired_ramping_rate ) {
#else
   void set_ramping_rate ( uint8_t desired_ramping_rate, uint8_t address ) {
#endif

   static uint8_t new_ramping_rate = 0;

   if ( desired_ramping_rate >= STOP ) {                  // Set up a ramping profile

      if ( desired_ramping_rate <= MAX_RAMPING_VALUE ) {      // limit to valid range
         new_ramping_rate = desired_ramping_rate;
      }
      else {
         new_ramping_rate = MAX_RAMPING_VALUE;
//         set error code for command too high
      }
   }

   else {                                          // this would be a negative number
      desired_ramping_rate = DEFAULT_RAMPING_VALUE;         // if receiving an invalid command, reset to default value
//      set error code for command too low
   }

   putc( address );
   putc( SET_RAMPING_RATE );
   putc( new_ramping_rate );
   putc( ( address + SET_RAMPING_RATE + new_ramping_rate ) & CRC_MASK );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 17 - DEADBAND
//
// value is set as follows:
// 127 (minus) command value (is less than) motors_off (is less than) 128 (plus) command value
//
///////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ONE_SABERTOOTH
   void set_deadband_range ( uint8_t desired_deadband ) {
#else
   void set_deadband_range ( uint8_t desired_deadband, uint8_t address ) {
#endif

   if ( desired_deadband < MIN_DEADBAND || desired_deadband > MAX_DEADBAND ) {
      desired_deadband = DEFAULT_DEADBAND;
   }
   putc( address );
   putc( SET_DEADBAND );
   putc( desired_deadband );
   putc( ( address + SET_DEADBAND + desired_deadband ) & CRC_MASK );
}



///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////


ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Sun Jul 17, 2011 4:52 pm     Reply with quote

Thanks for your effort to share your code. For a "novice programmer" you have done a good job. Because you've asked for it I will give some comments to improve your code.

First, a bug: In set_minimum_controller_voltage() you are using the variable new_min_voltage, but this will always be set to zero. Looks like a copy/paste error from one of your other functions.

Than a few tips to make you a better programmer:
1) You have defined 18 different commands using the #define directive. Works great, but you might want to read your C manual on the 'enum' concept. The enums have the advantage that the compiler can do type checking and in the debugger you will see the value of the enum when you hover the mouse over the name.

2) You made your program flexible by allowing for multiple controllers. Great! But instead of using the #define to create multiple versions of the same function, there is another concept in C that is easier to implement and better reading: 'Default Parameters'. This feature was not present in the first versions of C but was added later and is supported by the CCS compiler.
Instead of:
Code:
#ifdef ONE_SABERTOOTH
   void set_serial_timeout ( uint8_t desired_timeout_period ) {
#else
   void set_serial_timeout ( uint8_t desired_timeout_period, uint8_t address ) {
#endif
You can then write:
Code:
   void set_serial_timeout ( uint8_t desired_timeout_period, uint8_t address = MOTOR_DRIVER_ADDRESS_1) {
For 1 motor driver you can call the function as:
Code:
set_serial_timeout(desired_timeout);   // Note that there is only 1 parameter, the second parameter will use the default

And for multiple drivers as:
Code:
set_serial_timeout(desired_timeout, MOTOR_DRIVER_ADDRESS_5);


3) Try to avoid the use of float variables in embedded systems. The small PIC processor has no floating point hardware and therefore must process a huge number of instructions to calculate the answer. You will save a lot of memory and speed by not using floats.
Code:
   (float)new_max_voltage = ( desired_maximum_voltage * MAX_VOLTAGE_MULTIPLIER );
I doubt this will yield the result you expect, the CCS compiler is a bit tricky here and often defines the calculation type based on the first parameter (here an integer) and your voltage multiplier will be truncated from 5.12 to the integer 5. A better way to write this line would have been:
Code:
   new_max_voltage = (float)desired_maximum_voltage * MAX_VOLTAGE_MULTIPLIER;
This way you cast the first variable in the calculation to a float and consequently all further calculations will be performed as a float. The result will then be truncated to an uint8 and stored in new_max_voltage.

But,.... as I stated above it is best to avoid float at all. The common trick to avoid float is by multiplying all variables by a number of decades (10, 100, 1000, ...) so that no significant data is left after the dot. So, in your example you would multiply the 5.12 by 100, results in 512. You do all your calculations, and then at the end you divide again by 100 to get the real value you want. For further reading you can search the web for 'fixed point arithmetic'.
Example:
Code:
#define MAX_VOLTAGE_MULTIPLIER         512   // Note, multiplied by 100 to get rid of the decimal dot.

   new_max_voltage = ( (uint16)desired_maximum_voltage * MAX_VOLTAGE_MULTIPLIER ) / 100;
Depending of the maximum value for desired_maximum_voltage it might be necessary to change the (int16) cast to an (int32) cast, this depends on the maximum value of the intermediate multiplication result.

4) In your code I see 8 times the same sequence for sending the command to the driver. It is only a minor issue, but good practice is to move such repeating patterns into a separate function.
Sergeant82d



Joined: 01 Nov 2009
Posts: 55
Location: Central Oklahoma

View user's profile Send private message

Thanks very much
PostPosted: Sun Jul 17, 2011 7:07 pm     Reply with quote

Here is the updated code with ( I think all of ) Ckielstra's updates:

(tabs still don't line up - sorry)

Code:


/*
==========================================================================================
   Sabertooth_Driver.c
   Version 1.1
   16 July, 2011

   Brad Hedges
   H & H Enterprises
   Stratford, Oklahoma
==========================================================================================
==========================================================================================

   This is my attempt to create a multi-purpose device driver to use with the Dimension Engineering
   Sabertooth H-Bridge motor controller, for use in controlling permanent-magnet DC motors
   between 6 & 30 volts, up to 25 amps continuous and 50 amps surge.

   The driver is written in the C language for Microchip PIC microcontrollers using CCS's
   PIC-C Compiler, and was developed using version 4.120 of that compiler. It should be
   easily convertible to other microcontrollers and compilers with minimal effort.

   This driver works in the "Packetized Serial" mode, which must be set with
   the DIP switches on the controller pcb. The driver defaults to operating only one
   Sabertooth, using the lowest address of 128. The address is configurable in the driver
   by assigning the address value to the constant "MOTOR_DRIVER_ADDRESS_1".

   See the Dimension Engineering web site at: www.dimensionengineering.com for more information

==========================================================================================
   FUNCTIONS

   *** NOTE *** if using only one single Sabertooth controller, you do not need the "address"
             variable. It is only needed if you are sending commands to more than one controller
==========================================================================================

   control_motors( command1, speed1, command2, speed2, address );

   set_minimum_controller_voltage( desired_minimum_voltage, address )

   set_maximum_controller_voltage( desired_maximum_voltage, address )

   set_serial_timeout ( desired_timeout_period, address )

   set_baudrate ( desired_baudrate, address )

   set_ramping_rate ( desired_ramping_rate, address )

   set_deadband_range ( desired_deadband, address )

==========================================================================================
*/

///////////////////////////////////////////////////////////////////////////////////////////////


//#define MOTOR_STOP            PIN_D5               // Assign pin name to use in emergency stop routines


#define CRC_MASK               0b01111111

enum{
   MOTOR_DRIVER_ADDRESS_1 = 128,
   MOTOR_DRIVER_ADDRESS_2,
   MOTOR_DRIVER_ADDRESS_3,
   MOTOR_DRIVER_ADDRESS_4,
   MOTOR_DRIVER_ADDRESS_5,
   MOTOR_DRIVER_ADDRESS_6,
   MOTOR_DRIVER_ADDRESS_7,
   MOTOR_DRIVER_ADDRESS_8
};


///////////////////////////////////////////////////////////////////////////////////////////////

// COMMANDS

enum {
   DRIVE_FORWARD_1,
   DRIVE_REVERSE_1,

   SET_MIN_VOLTAGE,
   SET_MAX_VOLTAGE,

   DRIVE_FORWARD_2,
   DRIVE_REVERSE_2,

   DRIVE_MOTOR_1_7_BIT,
   DRIVE_MOTOR_2_7_BIT,

   DRIVE_FORWARD_MIXED,
   DRIVE_REVERSE_MIXED,
   DRIVE_TURN_RIGHT_MIXED,
   DRIVE_TURN_LEFT_MIXED,

   DRIVE_FWD_REV_7_BIT,
   DRIVE_TURN_7_BIT,

   SET_SERIAL_TIMEOUT,
   SET_BAUD_RATE,

   SET_RAMPING_RATE,
   SET_DEADBAND
};


///////////////////////////////////////////////////////////////////////////////////////////////

// COMMAND DATA

#define STOP                     0

#define SET_BAUDRATE_2400            1
#define SET_BAUDRATE_9600            2
#define SET_BAUDRATE_19200            3
#define SET_BAUDRATE_38400            4

#define MIN_VOLTAGE_MULTIPLIER         5
#define MIN_CONTROLLER_VOLTAGE         6
#define DEFAULT_MINIMUM_VOLTAGE         0               // 0 is equal to 6 volts minimum voltage for shutdown
#define MAX_MINIMUM_VOLTAGE            120
#define MIN_MINIMUM_VOLTAGE            0

#define MAX_VOLTAGE_MULTIPLIER         512               // the manual calls for 5.12, this is multiplied by 100 here
                                             // to get rid of floating point math in the code. It will be div/100
                                             // in it's calling function
#define CLEAR_SERIAL_TIMEOUT         0

#define MAX_RAMPING_VALUE            80
#define MAX_FAST_RAMPING_VALUE         10
#define DEFAULT_RAMPING_VALUE         1

#define SET_DEADBAND_TO_DEFAULT         0
#define DEFAULT_DEADBAND            3
#define MIN_DEADBAND               0
#define MAX_DEADBAND               127



///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// HELPER COMMAND - Only for use by internal driver functions
//
//   send_command
//
///////////////////////////////////////////////////////////////////////////////////////////////

void send_command ( uint8_t command, uint8_t value, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {
   putc( address );
   putc( command );
   putc( value );
   putc( ( address + command + value ) & CRC_MASK );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMANDS 0, 1, 4 - 13
//
//   control_motors
//
///////////////////////////////////////////////////////////////////////////////////////////////

void control_motors( uint8_t command1, uint8_t speed1, uint8_t command2, uint8_t speed2, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {

   send_command( command1, speed1 );

   send_command( command2, speed2 );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 2
//
//   set_minimum_controller_voltage
//
///////////////////////////////////////////////////////////////////////////////////////////////

void set_minimum_controller_voltage ( uint8_t desired_minimum_voltage, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {

   uint8_t new_min_voltage = 0;

   if ( desired_minimum_voltage > MAX_MINIMUM_VOLTAGE || desired_minimum_voltage < MIN_MINIMUM_VOLTAGE ) {
      new_min_voltage = DEFAULT_MINIMUM_VOLTAGE;
   }

   else {
      new_min_voltage = desired_minimum_voltage;
   }

   send_command( SET_MIN_VOLTAGE, new_min_voltage );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 3 - do not use if powering Sabertooth from batteries, only needed for power supplies - see datasheet
//
//   set_maximum_controller_voltage
//
///////////////////////////////////////////////////////////////////////////////////////////////

void set_maximum_controller_voltage ( uint8_t desired_maximum_voltage, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {

   uint8_t new_max_voltage = 0;

   new_max_voltage = ( ( (uint16_t)desired_maximum_voltage * MAX_VOLTAGE_MULTIPLIER ) / 100 );      // divided here to scale value back
                                                                           // down to the hardware requirement
   send_command( SET_MAX_VOLTAGE, new_max_voltage );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 14
//
//   set_serial_timeout
//
//   Value received by the Sabertooth is in multiples of 100 milli-seconds; i.e., '1' = 100 ms
//
///////////////////////////////////////////////////////////////////////////////////////////////

void set_serial_timeout ( uint8_t desired_timeout_period, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {

   uint8_t new_timeout_period = 0;

   if ( desired_timeout_period >= CLEAR_SERIAL_TIMEOUT ) {
      new_timeout_period = desired_timeout_period;
   }

   else {
      new_timeout_period = CLEAR_SERIAL_TIMEOUT;
   }

   send_command( SET_SERIAL_TIMEOUT, new_timeout_period );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 15 - BAUD RATE
//
///////////////////////////////////////////////////////////////////////////////////////////////

void set_baudrate ( uint8_t desired_baudrate, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {

   if ( desired_baudrate > SET_BAUDRATE_38400 || desired_baudrate < SET_BAUDRATE_2400 ) {
      desired_baudrate = SET_BAUDRATE_9600;               // set to default if out of range
//      set error code for invalid command
   }

   send_command( SET_BAUD_RATE, desired_baudrate );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 16 - RAMPING
//
//   set_ramping_rate accepts the desired ramping rate, which must be a value between
//   zero ( 0 ) and eighty ( 80 ).
//
///////////////////////////////////////////////////////////////////////////////////////////////

void set_ramping_rate ( uint8_t desired_ramping_rate, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {

   static uint8_t new_ramping_rate = 0;

   if ( desired_ramping_rate >= STOP ) {                  // Set up a ramping profile

      if ( desired_ramping_rate <= MAX_RAMPING_VALUE ) {      // limit to valid range
         new_ramping_rate = desired_ramping_rate;
      }
      else {
         new_ramping_rate = MAX_RAMPING_VALUE;
//         set error code for command too high
      }
   }

   else {                                          // this would be a negative number
      new_ramping_rate = DEFAULT_RAMPING_VALUE;            // if receiving an invalid command, reset to default value
//      set error code for command too low
   }

   send_command( SET_RAMPING_RATE, new_ramping_rate );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// COMMAND 17 - DEADBAND
//
// value is set as follows:
// ( 127 - command value ) < motors_off < ( 128 + command value )
//
///////////////////////////////////////////////////////////////////////////////////////////////

void set_deadband_range ( uint8_t desired_deadband, uint8_t address = MOTOR_DRIVER_ADDRESS_1 ) {

   if ( desired_deadband < MIN_DEADBAND || desired_deadband > MAX_DEADBAND ) {
      desired_deadband = DEFAULT_DEADBAND;
   }

   send_command( SET_DEADBAND, desired_deadband );
}


///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////



ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Mon Jul 18, 2011 2:54 am     Reply with quote

Your calls to send_command() are missing the address parameter. You have used the Default Parameter feature a bit too enthusiastic causing the code to fail for multiple drivers. Best is to remove the Default Parameter in the send_command function (and then fix the compile errors you will get for the calls to this function).

Regarding the use of enums I want to elaborate a bit more. Automatic numbering of the enums is great when you don't care about the actual value of the enum. In your program the values are important because these are defined in the driver's datasheet. Best is to hard code all values:
Code:

enum {
   DRIVE_FORWARD_1 = 0,
   DRIVE_REVERSE_1 = 1,

   SET_MIN_VOLTAGE = 2,
   SET_MAX_VOLTAGE = 3,

....etc
Yes, the difference in using defines has now become very small. Maybe this improvement to your code was more of a personal preference than a real improvement. You also changed the addresses to enums, that was not to my advice but do I leave up to your own likings.
Sergeant82d



Joined: 01 Nov 2009
Posts: 55
Location: Central Oklahoma

View user's profile Send private message

PostPosted: Mon Jul 18, 2011 5:33 am     Reply with quote

Going through this exercise has brought up a few issues that I am not sure about, regarding this hardware. I have contacted the manufacturer and am waiting on a reply before I go any further on this.

I have added:

, address

to each of the calls to send_command(), which *should* answer the need for multi-controller operation.

I also changed the motor addresses from an enum back to #defines; that should save a few bytes when it's really not needed most of the time.

Thanks very much for your helpful advice. I had considered some of the suggestions you made, but did not go to the effort initially to implement them.
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