|
|
View previous topic :: View next topic |
Author |
Message |
Sergeant82d
Joined: 01 Nov 2009 Posts: 55 Location: Central Oklahoma
|
DE Sabertooth motor controller driver code |
Posted: Sun Jul 17, 2011 1:29 pm |
|
|
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
|
|
Posted: Sun Jul 17, 2011 4:52 pm |
|
|
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
|
Thanks very much |
Posted: Sun Jul 17, 2011 7:07 pm |
|
|
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
|
|
Posted: Mon Jul 18, 2011 2:54 am |
|
|
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
|
|
Posted: Mon Jul 18, 2011 5:33 am |
|
|
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. |
|
|
|
|
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
|