absorb Arduino lib

This commit is contained in:
Oskar Weigl
2018-06-09 13:23:10 -07:00
parent 2ed6b3514a
commit 59549e9855
9 changed files with 310 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
#include <Wire.h>
#include "odrive.h"
// See odrive.h for a description
bool I2C_transaction(uint8_t slave_addr, const uint8_t * tx_buffer, size_t tx_length, uint8_t * rx_buffer, size_t rx_length) {
// transmit
if (tx_buffer) {
Wire.beginTransmission(slave_addr);
if (Wire.write(tx_buffer, tx_length) != tx_length)
return false;
bool should_stop = !rx_buffer;
if (Wire.endTransmission(should_stop) != 0)
return false;
}
// receive
if (rx_buffer) {
while(Wire.available()) Wire.read(); // flush input buffer
if (Wire.requestFrom(slave_addr, (uint8_t)rx_length, (uint8_t)true /* stop after receiving */) != rx_length)
return false;
for (size_t i = 0; i < rx_length; ++i)
rx_buffer[i] = Wire.read();
}
return true;
}
int set_and_save_configuration(uint8_t odrive_num, uint8_t axis_num) {
bool success;
success = odrive::clear_errors(odrive_num, axis_num);
if (!success)
return __LINE__;
// select hall effect mode
bool user_config_loaded = false;
success = odrive::read_property<odrive::USER_CONFIG_LOADED>(odrive_num, &user_config_loaded);
if (!success)
return __LINE__;
if (user_config_loaded) {
Serial.println("ODrive already configured");
return 0;
}
// select hall effect mode
success = odrive::write_axis_property<odrive::AXIS__ENCODER__CONFIG__MODE>(odrive_num, axis_num, 1);
if (!success)
return __LINE__;
// configure encoder counts per revolution (6 hall effect states * 12 pole pairs)
success = odrive::write_axis_property<odrive::AXIS__ENCODER__CONFIG__CPR>(odrive_num, axis_num, 72);
if (!success)
return __LINE__;
// disable velocity integrator
success = odrive::write_axis_property<odrive::AXIS__CONTROLLER__CONFIG__VEL_INTEGRATOR_GAIN>(odrive_num, axis_num, 0);
if (!success)
return __LINE__;
// select velocity control
success = odrive::write_axis_property<odrive::AXIS__CONTROLLER__CONFIG__CONTROL_MODE>(odrive_num, axis_num, 2);
if (!success)
return __LINE__;
// set velocity controller P-gain
success = odrive::write_axis_property<odrive::AXIS__CONTROLLER__CONFIG__VEL_GAIN>(odrive_num, axis_num, 0.005f);
if (!success)
return __LINE__;
// request state: motor calibration
success = odrive::write_axis_property<odrive::AXIS__REQUESTED_STATE>(odrive_num, axis_num, 4);
if (!success)
return __LINE__;
delay(6000);
// check if the axis is in idle and no errors occurred
if (!odrive::check_axis_state(odrive_num, axis_num, 1))
return __LINE__;
// ensure that the motor calibration is considered valid after power cycle
success = odrive::write_axis_property<odrive::AXIS__MOTOR__CONFIG__PRE_CALIBRATED>(odrive_num, axis_num, true);
if (!success)
return __LINE__;
// request state: encoder calibration
success = odrive::write_axis_property<odrive::AXIS__REQUESTED_STATE>(odrive_num, axis_num, 7);
if (!success)
return __LINE__;
delay(12000);
// check if the axis is in idle and no errors occurred
if (!odrive::check_axis_state(odrive_num, axis_num, 1))
return __LINE__;
// ensure that the encoder calibration is considered valid after power cycle
success = odrive::write_axis_property<odrive::AXIS__ENCODER__CONFIG__PRE_CALIBRATED>(odrive_num, axis_num, true);
if (!success)
return __LINE__;
// store the configuration to NVM
// Caution: this operation is usually instantaneous but after every couple of hundred calls it will
// take around 1 second (because a flash page needs to be erased).
success = odrive::trigger<odrive::SAVE_CONFIGURATION>(odrive_num);
if (!success)
return __LINE__;
return 0;
}
byte odrive_num = 7;
byte axis_num = 0;
bool do_setup = true;
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600);
Serial.println("Hello World!");
if (do_setup) {
Serial.println("Starting ODrive setup...");
int error_line = set_and_save_configuration(odrive_num, axis_num);
if (error_line != 0) {
Serial.print("ODrive setup failed at line ");
Serial.print(error_line);
Serial.println();
return;
}
Serial.println("ODrive setup succeeded!");
do_setup = false;
}
}
void loop() {
bool success;
delay(500);
success = odrive::check_axis_state(odrive_num, axis_num, 8);
if (!success) {
Serial.println("not in closed loop control - entering closed loop control");
// clear previous error state
success = odrive::clear_errors(odrive_num, axis_num);
if (!success) {
Serial.println("could not enter closed loop control");
return;
}
// request velocity 0
success = odrive::write_axis_property<odrive::AXIS__CONTROLLER__VEL_SETPOINT>(odrive_num, axis_num, 0);
if (!success) {
Serial.println("could not enter closed loop control");
return;
}
// request state: closed loop control
success = odrive::write_axis_property<odrive::AXIS__REQUESTED_STATE>(odrive_num, axis_num, 8);
if (!success) {
Serial.println("could not enter closed loop control");
return;
}
success = odrive::check_axis_state(odrive_num, axis_num, 8);
if (!success) {
Serial.println("could not enter closed loop control");
return;
}
}
success = odrive::write_axis_property<odrive::AXIS__CONTROLLER__VEL_SETPOINT>(odrive_num, axis_num, 72 * 5);
if (!success) {
Serial.println("error");
return;
}
delay(500);
success = odrive::write_axis_property<odrive::AXIS__CONTROLLER__VEL_SETPOINT>(odrive_num, axis_num, -72 * 5);
if (!success) {
Serial.println("error");
return;
}
// print Vbus to show liveness
float vbus;
success = odrive::read_property<odrive::VBUS_VOLTAGE>(odrive_num, &vbus);
if (!success) {
Serial.println("error");
return;
}
Serial.println(vbus);
}
+199
View File
@@ -0,0 +1,199 @@
/*
* ODrive I2C communication library
* This file implements I2C communication with the ODrive.
*
* - Implement the C function I2C_transaction to provide low level I2C access.
* - Use read_property<PropertyId>() to read properties from the ODrive.
* - Use write_property<PropertyId>() to modify properties on the ODrive.
* - Use trigger<PropertyId>() to trigger a function (such as reboot or save_configuration)
* - Use endpoint_type_t<PropertyId> to retrieve the underlying type
* of a given property.
* - Refer to PropertyId for a list of available properties.
*
* To regenerate the interface definitions, flash an ODrive with
* the new firmware, connect it to your PC via USB and then run
* ../tools/odrivetool generate-code --output [path to odrive_endpoints.h]
* This step can be done with any ODrive, it doesn't have to be the
* one that you'll be controlling over I2C.
*/
#include <limits.h>
#include <stdint.h>
#include "odrive_endpoints.h"
#ifdef __AVR__
// AVR-GCC doesn't ship with the STL, so we use our own little excerpt
#include "type_traits.h"
#else
#include <type_traits>
#endif
extern "C" {
/* @brief Send and receive data to/from an I2C slave
*
* This function carries out the following sequence:
* 1. generate a START condition
* 2. if the tx_buffer is not null:
* a. send 7-bit slave address (with the LSB 0)
* b. send all bytes in the tx_buffer
* 3. if both tx_buffer and rx_buffer are not null, generate a REPEATED START condition
* 4. if the rx_buffer is not null:
* a. send 7-bit slave address (with the LSB 1)
* b. read rx_length bytes into rx_buffer
* 5. send STOP condition
*
* @param slave_addr: 7-bit slave address (the MSB is ignored)
* @return true if all data was transmitted and received as requested by the caller, false otherwise
*/
bool I2C_transaction(uint8_t slave_addr, const uint8_t * tx_buffer, size_t tx_length, uint8_t * rx_buffer, size_t rx_length);
}
namespace odrive {
static constexpr const uint8_t i2c_addr = (0xD << 3); // write: 1101xxx0, read: 1101xxx1
template<typename T>
using bit_width = std::integral_constant<unsigned int, CHAR_BIT * sizeof(T)>;
template<typename T>
using byte_width = std::integral_constant<unsigned int, (bit_width<T>::value + 7) / 8>;
template<unsigned int IBitSize>
struct unsigned_int_of_size;
template<> struct unsigned_int_of_size<32> { typedef uint32_t type; };
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
read_le(const uint8_t buffer[byte_width<T>::value]) {
T value = 0;
for (size_t i = 0; i < byte_width<T>::value; ++i)
value |= (static_cast<T>(buffer[i]) << (i << 3));
return value;
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
read_le(const uint8_t buffer[]) {
using T_Int = typename unsigned_int_of_size<bit_width<T>::value>::type;
T_Int value = read_le<T_Int>(buffer);
return *reinterpret_cast<T*>(&value);
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
write_le(uint8_t buffer[byte_width<T>::value], T value) {
for (size_t i = 0; i < byte_width<T>::value; ++i)
buffer[i] = (value >> (i << 3)) & 0xff;
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
write_le(uint8_t buffer[byte_width<T>::value], T value) {
using T_Int = typename unsigned_int_of_size<bit_width<T>::value>::type;
write_le<T_Int>(buffer, *reinterpret_cast<T_Int*>(&value));
}
/* @brief Read from an endpoint on the ODrive.
* To read from an axis specific endpoint use read_axis_property() instead.
*
* Usage example:
* float val;
* success = odrive::read_property<odrive::VBUS_VOLTAGE>(0, &val);
*
* @param num Selects the ODrive. For instance the value 4 selects
* the ODrive that has [A2, A1, A0] connected to [VCC, GND, GND].
* @return true if the I2C transaction succeeded, false otherwise
*/
template<int IPropertyId>
bool read_property(uint8_t num, endpoint_type_t<IPropertyId>* value, uint16_t address = IPropertyId) {
uint8_t i2c_tx_buffer[4];
write_le<uint16_t>(i2c_tx_buffer, address);
write_le<uint16_t>(i2c_tx_buffer + sizeof(i2c_tx_buffer) - 2, json_crc);
uint8_t i2c_rx_buffer[byte_width<endpoint_type_t<IPropertyId>>::value];
if (!I2C_transaction(i2c_addr + num,
i2c_tx_buffer, sizeof(i2c_tx_buffer),
i2c_rx_buffer, sizeof(i2c_rx_buffer)))
return false;
if (value)
*value = read_le<endpoint_type_t<IPropertyId>>(i2c_rx_buffer);
return true;
}
/* @brief Write to an endpoint on the ODrive.
* To write to an axis specific endpoint use write_axis_property() instead.
*
* Usage example:
* success = odrive::write_property<odrive::TEST_PROPERTY>(0, 42);
*
* @param num Selects the ODrive. For instance the value 4 selects
* the ODrive that has [A2, A1, A0] connected to [VCC, GND, GND].
* @return true if the I2C transaction succeeded, false otherwise
*/
template<int IPropertyId>
bool write_property(uint8_t num, endpoint_type_t<IPropertyId> value, uint16_t address = IPropertyId) {
uint8_t i2c_tx_buffer[4 + byte_width<endpoint_type_t<IPropertyId>>::value];
write_le<uint16_t>(i2c_tx_buffer, address);
write_le<endpoint_type_t<IPropertyId>>(i2c_tx_buffer + 2, value);
write_le<uint16_t>(i2c_tx_buffer + sizeof(i2c_tx_buffer) - 2, json_crc);
return I2C_transaction(i2c_addr + num, i2c_tx_buffer, sizeof(i2c_tx_buffer), nullptr, 0);
}
/* @brief Trigger an parameter-less function on the ODrive
*
* Usage example:
* success = odrive::trigger<odrive::SAVE_CONFIGURATION>(0);
*
* @param num Selects the ODrive. For instance the value 4 selects
* the ODrive that has [A2, A1, A0] connected to [VCC, GND, GND].
* @return true if the I2C transaction succeeded, false otherwise
*/
template<int IPropertyId,
typename = typename std::enable_if<std::is_void<endpoint_type_t<IPropertyId>>::value>::type>
bool trigger(uint8_t num, uint16_t address = IPropertyId) {
uint8_t i2c_tx_buffer[4];
write_le<uint16_t>(i2c_tx_buffer, address);
write_le<uint16_t>(i2c_tx_buffer + sizeof(i2c_tx_buffer) - 2, json_crc);
return I2C_transaction(i2c_addr + num, i2c_tx_buffer, sizeof(i2c_tx_buffer), nullptr, 0);
}
template<int IPropertyId>
bool read_axis_property(uint8_t num, uint8_t axis, endpoint_type_t<IPropertyId>* value) {
return read_property<IPropertyId>(num, value, IPropertyId + axis * per_axis_offset);
}
template<int IPropertyId>
bool write_axis_property(uint8_t num, uint8_t axis, endpoint_type_t<IPropertyId> value) {
return write_property<IPropertyId>(num, value, IPropertyId + axis * per_axis_offset);
}
/* @brief Checks if the axis is in the requested state and the error register is clear */
bool check_axis_state(uint8_t num, uint8_t axis, uint8_t state) {
endpoint_type_t<odrive::AXIS__CURRENT_STATE> observed_state = 0;
endpoint_type_t<odrive::AXIS__ERROR> observed_error = 0;
if (!read_axis_property<odrive::AXIS__CURRENT_STATE>(num, axis, &observed_state))
return false;
if (!read_axis_property<odrive::AXIS__ERROR>(num, axis, &observed_error))
return false;
return (observed_error == 0) && (observed_state == state);
}
/* @brief Clears any error state of the specified axis */
bool clear_errors(uint8_t num, uint8_t axis) {
if (!write_axis_property<odrive::AXIS__ERROR>(num, axis, 0))
return false;
if (!write_axis_property<odrive::AXIS__MOTOR__ERROR>(num, axis, 0))
return false;
if (!write_axis_property<odrive::AXIS__ENCODER__ERROR>(num, axis, 0))
return false;
return true;
}
}
+299
View File
@@ -0,0 +1,299 @@
/*
* This file was autogenerated using the "odrivetool generate-code" feature.
*
* The file matches a specific firmware version. If you add/remove/rename any
* properties exposed by the ODrive, this file needs to be regenerated, otherwise
* the ODrive will ignore all commands.
*/
#ifndef __ODRIVE_ENDPOINTS_HPP
#define __ODRIVE_ENDPOINTS_HPP
namespace odrive {
static constexpr const uint16_t json_crc = 0xbe97;
static constexpr const uint16_t per_axis_offset = 101;
enum {
VBUS_VOLTAGE = 1,
SERIAL_NUMBER = 2,
HW_VERSION_MAJOR = 3,
HW_VERSION_MINOR = 4,
HW_VERSION_VARIANT = 5,
FW_VERSION_MAJOR = 6,
FW_VERSION_MINOR = 7,
FW_VERSION_REVISION = 8,
FW_VERSION_UNRELEASED = 9,
USER_CONFIG_LOADED = 10,
BRAKE_RESISTOR_ARMED = 11,
SYSTEM_STATS__UPTIME = 12,
SYSTEM_STATS__MIN_HEAP_SPACE = 13,
SYSTEM_STATS__MIN_STACK_SPACE_AXIS0 = 14,
SYSTEM_STATS__MIN_STACK_SPACE_AXIS1 = 15,
SYSTEM_STATS__MIN_STACK_SPACE_COMMS = 16,
SYSTEM_STATS__MIN_STACK_SPACE_USB = 17,
SYSTEM_STATS__MIN_STACK_SPACE_UART = 18,
SYSTEM_STATS__MIN_STACK_SPACE_USB_IRQ = 19,
SYSTEM_STATS__MIN_STACK_SPACE_STARTUP = 20,
SYSTEM_STATS__USB__RX_CNT = 21,
SYSTEM_STATS__USB__TX_CNT = 22,
SYSTEM_STATS__USB__TX_OVERRUN_CNT = 23,
SYSTEM_STATS__I2C__ADDR = 24,
SYSTEM_STATS__I2C__ADDR_MATCH_CNT = 25,
SYSTEM_STATS__I2C__RX_CNT = 26,
SYSTEM_STATS__I2C__ERROR_CNT = 27,
CONFIG__BRAKE_RESISTANCE = 28,
CONFIG__ENABLE_UART = 29,
CONFIG__ENABLE_I2C_INSTEAD_OF_CAN = 30,
CONFIG__DC_BUS_UNDERVOLTAGE_TRIP_LEVEL = 31,
CONFIG__DC_BUS_OVERVOLTAGE_TRIP_LEVEL = 32,
TEST_PROPERTY = 235,
ADC_GPIO1 = 242,
ADC_GPIO2 = 243,
SAVE_CONFIGURATION = 244,
ERASE_CONFIGURATION = 245,
REBOOT = 246,
ENTER_DFU_MODE = 247,
// Per-Axis endpoints (to be used with read_axis_property and write_axis_property)
AXIS__ERROR = 33,
AXIS__ENABLE_STEP_DIR = 34,
AXIS__CURRENT_STATE = 35,
AXIS__REQUESTED_STATE = 36,
AXIS__LOOP_COUNTER = 37,
AXIS__CONFIG__STARTUP_MOTOR_CALIBRATION = 38,
AXIS__CONFIG__STARTUP_ENCODER_INDEX_SEARCH = 39,
AXIS__CONFIG__STARTUP_ENCODER_OFFSET_CALIBRATION = 40,
AXIS__CONFIG__STARTUP_CLOSED_LOOP_CONTROL = 41,
AXIS__CONFIG__STARTUP_SENSORLESS_CONTROL = 42,
AXIS__CONFIG__ENABLE_STEP_DIR = 43,
AXIS__CONFIG__COUNTS_PER_STEP = 44,
AXIS__CONFIG__RAMP_UP_TIME = 45,
AXIS__CONFIG__RAMP_UP_DISTANCE = 46,
AXIS__CONFIG__SPIN_UP_CURRENT = 47,
AXIS__CONFIG__SPIN_UP_ACCELERATION = 48,
AXIS__CONFIG__SPIN_UP_TARGET_VEL = 49,
AXIS__MOTOR__ERROR = 50,
AXIS__MOTOR__ARMED_STATE = 51,
AXIS__MOTOR__IS_CALIBRATED = 52,
AXIS__MOTOR__CURRENT_MEAS_PHB = 53,
AXIS__MOTOR__CURRENT_MEAS_PHC = 54,
AXIS__MOTOR__DC_CALIB_PHB = 55,
AXIS__MOTOR__DC_CALIB_PHC = 56,
AXIS__MOTOR__PHASE_CURRENT_REV_GAIN = 57,
AXIS__MOTOR__CURRENT_CONTROL__P_GAIN = 58,
AXIS__MOTOR__CURRENT_CONTROL__I_GAIN = 59,
AXIS__MOTOR__CURRENT_CONTROL__V_CURRENT_CONTROL_INTEGRAL_D = 60,
AXIS__MOTOR__CURRENT_CONTROL__V_CURRENT_CONTROL_INTEGRAL_Q = 61,
AXIS__MOTOR__CURRENT_CONTROL__IBUS = 62,
AXIS__MOTOR__CURRENT_CONTROL__FINAL_V_ALPHA = 63,
AXIS__MOTOR__CURRENT_CONTROL__FINAL_V_BETA = 64,
AXIS__MOTOR__CURRENT_CONTROL__IQ_SETPOINT = 65,
AXIS__MOTOR__CURRENT_CONTROL__IQ_MEASURED = 66,
AXIS__MOTOR__CURRENT_CONTROL__MAX_ALLOWED_CURRENT = 67,
AXIS__MOTOR__GATE_DRIVER__DRV_FAULT = 68,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_GENERAL = 69,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_ADC_CB_I = 70,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_ADC_CB_DC = 71,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_MEAS_R = 72,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_MEAS_L = 73,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_ENC_CALIB = 74,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_IDX_SEARCH = 75,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_FOC_VOLTAGE = 76,
AXIS__MOTOR__TIMING_LOG__TIMING_LOG_FOC_CURRENT = 77,
AXIS__MOTOR__CONFIG__PRE_CALIBRATED = 78,
AXIS__MOTOR__CONFIG__POLE_PAIRS = 79,
AXIS__MOTOR__CONFIG__CALIBRATION_CURRENT = 80,
AXIS__MOTOR__CONFIG__RESISTANCE_CALIB_MAX_VOLTAGE = 81,
AXIS__MOTOR__CONFIG__PHASE_INDUCTANCE = 82,
AXIS__MOTOR__CONFIG__PHASE_RESISTANCE = 83,
AXIS__MOTOR__CONFIG__DIRECTION = 84,
AXIS__MOTOR__CONFIG__MOTOR_TYPE = 85,
AXIS__MOTOR__CONFIG__CURRENT_LIM = 86,
AXIS__CONTROLLER__POS_SETPOINT = 87,
AXIS__CONTROLLER__VEL_SETPOINT = 88,
AXIS__CONTROLLER__VEL_INTEGRATOR_CURRENT = 89,
AXIS__CONTROLLER__CURRENT_SETPOINT = 90,
AXIS__CONTROLLER__CONFIG__CONTROL_MODE = 91,
AXIS__CONTROLLER__CONFIG__POS_GAIN = 92,
AXIS__CONTROLLER__CONFIG__VEL_GAIN = 93,
AXIS__CONTROLLER__CONFIG__VEL_INTEGRATOR_GAIN = 94,
AXIS__CONTROLLER__CONFIG__VEL_LIMIT = 95,
AXIS__CONTROLLER__START_ANTICOGGING_CALIBRATION = 105,
AXIS__ENCODER__ERROR = 106,
AXIS__ENCODER__IS_READY = 107,
AXIS__ENCODER__INDEX_FOUND = 108,
AXIS__ENCODER__SHADOW_COUNT = 109,
AXIS__ENCODER__COUNT_IN_CPR = 110,
AXIS__ENCODER__OFFSET = 111,
AXIS__ENCODER__INTERPOLATION = 112,
AXIS__ENCODER__PHASE = 113,
AXIS__ENCODER__POS_ESTIMATE = 114,
AXIS__ENCODER__POS_CPR = 115,
AXIS__ENCODER__HALL_STATE = 116,
AXIS__ENCODER__PLL_VEL = 117,
AXIS__ENCODER__PLL_KP = 118,
AXIS__ENCODER__PLL_KI = 119,
AXIS__ENCODER__CONFIG__MODE = 120,
AXIS__ENCODER__CONFIG__USE_INDEX = 121,
AXIS__ENCODER__CONFIG__PRE_CALIBRATED = 122,
AXIS__ENCODER__CONFIG__IDX_SEARCH_SPEED = 123,
AXIS__ENCODER__CONFIG__CPR = 124,
AXIS__ENCODER__CONFIG__OFFSET = 125,
AXIS__ENCODER__CONFIG__OFFSET_FLOAT = 126,
AXIS__ENCODER__CONFIG__CALIB_RANGE = 127,
AXIS__SENSORLESS_ESTIMATOR__ERROR = 128,
AXIS__SENSORLESS_ESTIMATOR__PHASE = 129,
AXIS__SENSORLESS_ESTIMATOR__PLL_POS = 130,
AXIS__SENSORLESS_ESTIMATOR__PLL_VEL = 131,
AXIS__SENSORLESS_ESTIMATOR__PLL_KP = 132,
AXIS__SENSORLESS_ESTIMATOR__PLL_KI = 133,
};
template<int I>
struct endpoint_type;
template<> struct endpoint_type<VBUS_VOLTAGE> { typedef float type; };
template<> struct endpoint_type<SERIAL_NUMBER> { typedef uint64_t type; };
template<> struct endpoint_type<HW_VERSION_MAJOR> { typedef uint8_t type; };
template<> struct endpoint_type<HW_VERSION_MINOR> { typedef uint8_t type; };
template<> struct endpoint_type<HW_VERSION_VARIANT> { typedef uint8_t type; };
template<> struct endpoint_type<FW_VERSION_MAJOR> { typedef uint8_t type; };
template<> struct endpoint_type<FW_VERSION_MINOR> { typedef uint8_t type; };
template<> struct endpoint_type<FW_VERSION_REVISION> { typedef uint8_t type; };
template<> struct endpoint_type<FW_VERSION_UNRELEASED> { typedef uint8_t type; };
template<> struct endpoint_type<USER_CONFIG_LOADED> { typedef bool type; };
template<> struct endpoint_type<BRAKE_RESISTOR_ARMED> { typedef bool type; };
template<> struct endpoint_type<SYSTEM_STATS__UPTIME> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_HEAP_SPACE> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_STACK_SPACE_AXIS0> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_STACK_SPACE_AXIS1> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_STACK_SPACE_COMMS> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_STACK_SPACE_USB> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_STACK_SPACE_UART> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_STACK_SPACE_USB_IRQ> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__MIN_STACK_SPACE_STARTUP> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__USB__RX_CNT> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__USB__TX_CNT> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__USB__TX_OVERRUN_CNT> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__I2C__ADDR> { typedef uint8_t type; };
template<> struct endpoint_type<SYSTEM_STATS__I2C__ADDR_MATCH_CNT> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__I2C__RX_CNT> { typedef uint32_t type; };
template<> struct endpoint_type<SYSTEM_STATS__I2C__ERROR_CNT> { typedef uint32_t type; };
template<> struct endpoint_type<CONFIG__BRAKE_RESISTANCE> { typedef float type; };
template<> struct endpoint_type<CONFIG__ENABLE_UART> { typedef bool type; };
template<> struct endpoint_type<CONFIG__ENABLE_I2C_INSTEAD_OF_CAN> { typedef bool type; };
template<> struct endpoint_type<CONFIG__DC_BUS_UNDERVOLTAGE_TRIP_LEVEL> { typedef float type; };
template<> struct endpoint_type<CONFIG__DC_BUS_OVERVOLTAGE_TRIP_LEVEL> { typedef float type; };
template<> struct endpoint_type<TEST_PROPERTY> { typedef uint32_t type; };
template<> struct endpoint_type<ADC_GPIO1> { typedef uint16_t type; };
template<> struct endpoint_type<ADC_GPIO2> { typedef uint16_t type; };
template<> struct endpoint_type<SAVE_CONFIGURATION> { typedef void type; };
template<> struct endpoint_type<ERASE_CONFIGURATION> { typedef void type; };
template<> struct endpoint_type<REBOOT> { typedef void type; };
template<> struct endpoint_type<ENTER_DFU_MODE> { typedef void type; };
// Per-axis endpoints
template<> struct endpoint_type<AXIS__ERROR> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__ENABLE_STEP_DIR> { typedef bool type; };
template<> struct endpoint_type<AXIS__CURRENT_STATE> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__REQUESTED_STATE> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__LOOP_COUNTER> { typedef uint32_t type; };
template<> struct endpoint_type<AXIS__CONFIG__STARTUP_MOTOR_CALIBRATION> { typedef bool type; };
template<> struct endpoint_type<AXIS__CONFIG__STARTUP_ENCODER_INDEX_SEARCH> { typedef bool type; };
template<> struct endpoint_type<AXIS__CONFIG__STARTUP_ENCODER_OFFSET_CALIBRATION> { typedef bool type; };
template<> struct endpoint_type<AXIS__CONFIG__STARTUP_CLOSED_LOOP_CONTROL> { typedef bool type; };
template<> struct endpoint_type<AXIS__CONFIG__STARTUP_SENSORLESS_CONTROL> { typedef bool type; };
template<> struct endpoint_type<AXIS__CONFIG__ENABLE_STEP_DIR> { typedef bool type; };
template<> struct endpoint_type<AXIS__CONFIG__COUNTS_PER_STEP> { typedef float type; };
template<> struct endpoint_type<AXIS__CONFIG__RAMP_UP_TIME> { typedef float type; };
template<> struct endpoint_type<AXIS__CONFIG__RAMP_UP_DISTANCE> { typedef float type; };
template<> struct endpoint_type<AXIS__CONFIG__SPIN_UP_CURRENT> { typedef float type; };
template<> struct endpoint_type<AXIS__CONFIG__SPIN_UP_ACCELERATION> { typedef float type; };
template<> struct endpoint_type<AXIS__CONFIG__SPIN_UP_TARGET_VEL> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__ERROR> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__ARMED_STATE> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__MOTOR__IS_CALIBRATED> { typedef bool type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_MEAS_PHB> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_MEAS_PHC> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__DC_CALIB_PHB> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__DC_CALIB_PHC> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__PHASE_CURRENT_REV_GAIN> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__P_GAIN> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__I_GAIN> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__V_CURRENT_CONTROL_INTEGRAL_D> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__V_CURRENT_CONTROL_INTEGRAL_Q> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__IBUS> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__FINAL_V_ALPHA> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__FINAL_V_BETA> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__IQ_SETPOINT> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__IQ_MEASURED> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CURRENT_CONTROL__MAX_ALLOWED_CURRENT> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__GATE_DRIVER__DRV_FAULT> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_GENERAL> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_ADC_CB_I> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_ADC_CB_DC> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_MEAS_R> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_MEAS_L> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_ENC_CALIB> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_IDX_SEARCH> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_FOC_VOLTAGE> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__TIMING_LOG__TIMING_LOG_FOC_CURRENT> { typedef uint16_t type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__PRE_CALIBRATED> { typedef bool type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__POLE_PAIRS> { typedef int32_t type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__CALIBRATION_CURRENT> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__RESISTANCE_CALIB_MAX_VOLTAGE> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__PHASE_INDUCTANCE> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__PHASE_RESISTANCE> { typedef float type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__DIRECTION> { typedef int32_t type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__MOTOR_TYPE> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__MOTOR__CONFIG__CURRENT_LIM> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__POS_SETPOINT> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__VEL_SETPOINT> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__VEL_INTEGRATOR_CURRENT> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__CURRENT_SETPOINT> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__CONFIG__CONTROL_MODE> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__CONTROLLER__CONFIG__POS_GAIN> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__CONFIG__VEL_GAIN> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__CONFIG__VEL_INTEGRATOR_GAIN> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__CONFIG__VEL_LIMIT> { typedef float type; };
template<> struct endpoint_type<AXIS__CONTROLLER__START_ANTICOGGING_CALIBRATION> { typedef void type; };
template<> struct endpoint_type<AXIS__ENCODER__ERROR> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__ENCODER__IS_READY> { typedef bool type; };
template<> struct endpoint_type<AXIS__ENCODER__INDEX_FOUND> { typedef bool type; };
template<> struct endpoint_type<AXIS__ENCODER__SHADOW_COUNT> { typedef int32_t type; };
template<> struct endpoint_type<AXIS__ENCODER__COUNT_IN_CPR> { typedef int32_t type; };
template<> struct endpoint_type<AXIS__ENCODER__OFFSET> { typedef int32_t type; };
template<> struct endpoint_type<AXIS__ENCODER__INTERPOLATION> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__PHASE> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__POS_ESTIMATE> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__POS_CPR> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__HALL_STATE> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__ENCODER__PLL_VEL> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__PLL_KP> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__PLL_KI> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__MODE> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__USE_INDEX> { typedef bool type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__PRE_CALIBRATED> { typedef bool type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__IDX_SEARCH_SPEED> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__CPR> { typedef int32_t type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__OFFSET> { typedef int32_t type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__OFFSET_FLOAT> { typedef float type; };
template<> struct endpoint_type<AXIS__ENCODER__CONFIG__CALIB_RANGE> { typedef float type; };
template<> struct endpoint_type<AXIS__SENSORLESS_ESTIMATOR__ERROR> { typedef uint8_t type; };
template<> struct endpoint_type<AXIS__SENSORLESS_ESTIMATOR__PHASE> { typedef float type; };
template<> struct endpoint_type<AXIS__SENSORLESS_ESTIMATOR__PLL_POS> { typedef float type; };
template<> struct endpoint_type<AXIS__SENSORLESS_ESTIMATOR__PLL_VEL> { typedef float type; };
template<> struct endpoint_type<AXIS__SENSORLESS_ESTIMATOR__PLL_KP> { typedef float type; };
template<> struct endpoint_type<AXIS__SENSORLESS_ESTIMATOR__PLL_KI> { typedef float type; };
template<int I>
using endpoint_type_t = typename endpoint_type<I>::type;
}
#endif // __ODRIVE_ENDPOINTS_HPP
+267
View File
@@ -0,0 +1,267 @@
/*
* This file is a very small part of the GCC STL because AVR-GCC ships
* without the STL.
*/
namespace std
{
/**
* @defgroup metaprogramming Metaprogramming
* @ingroup utilities
*
* Template utilities for compile-time introspection and modification,
* including type classification traits, type property inspection traits
* and type transformation traits.
*
* @{
*/
/// integral_constant
template<typename _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
typedef _Tp value_type;
typedef integral_constant<_Tp, __v> type;
constexpr operator value_type() const noexcept { return value; }
#if __cplusplus > 201103L
#define __cpp_lib_integral_constant_callable 201304
constexpr value_type operator()() const noexcept { return value; }
#endif
};
template<typename _Tp, _Tp __v>
constexpr _Tp integral_constant<_Tp, __v>::value;
/// The type used as a compile-time boolean with true value.
typedef integral_constant<bool, true> true_type;
/// The type used as a compile-time boolean with false value.
typedef integral_constant<bool, false> false_type;
template<bool __v>
using __bool_constant = integral_constant<bool, __v>;
#if __cplusplus > 201402L
# define __cpp_lib_bool_constant 201505
template<bool __v>
using bool_constant = integral_constant<bool, __v>;
#endif
// Primary type categories.
template<typename>
struct remove_cv;
template<typename>
struct __is_void_helper
: public false_type { };
template<>
struct __is_void_helper<void>
: public true_type { };
/// is_void
template<typename _Tp>
struct is_void
: public __is_void_helper<typename remove_cv<_Tp>::type>::type
{ };
template<typename>
struct __is_integral_helper
: public false_type { };
template<>
struct __is_integral_helper<bool>
: public true_type { };
template<>
struct __is_integral_helper<char>
: public true_type { };
template<>
struct __is_integral_helper<signed char>
: public true_type { };
template<>
struct __is_integral_helper<unsigned char>
: public true_type { };
#ifdef _GLIBCXX_USE_WCHAR_T
template<>
struct __is_integral_helper<wchar_t>
: public true_type { };
#endif
template<>
struct __is_integral_helper<char16_t>
: public true_type { };
template<>
struct __is_integral_helper<char32_t>
: public true_type { };
template<>
struct __is_integral_helper<short>
: public true_type { };
template<>
struct __is_integral_helper<unsigned short>
: public true_type { };
template<>
struct __is_integral_helper<int>
: public true_type { };
template<>
struct __is_integral_helper<unsigned int>
: public true_type { };
template<>
struct __is_integral_helper<long>
: public true_type { };
template<>
struct __is_integral_helper<unsigned long>
: public true_type { };
template<>
struct __is_integral_helper<long long>
: public true_type { };
template<>
struct __is_integral_helper<unsigned long long>
: public true_type { };
// Conditionalizing on __STRICT_ANSI__ here will break any port that
// uses one of these types for size_t.
#if defined(__GLIBCXX_TYPE_INT_N_0)
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_0>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_0>
: public true_type { };
#endif
#if defined(__GLIBCXX_TYPE_INT_N_1)
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_1>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_1>
: public true_type { };
#endif
#if defined(__GLIBCXX_TYPE_INT_N_2)
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_2>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_2>
: public true_type { };
#endif
#if defined(__GLIBCXX_TYPE_INT_N_3)
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_3>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_3>
: public true_type { };
#endif
/// is_integral
template<typename _Tp>
struct is_integral
: public __is_integral_helper<typename remove_cv<_Tp>::type>::type
{ };
template<typename>
struct __is_floating_point_helper
: public false_type { };
template<>
struct __is_floating_point_helper<float>
: public true_type { };
template<>
struct __is_floating_point_helper<double>
: public true_type { };
template<>
struct __is_floating_point_helper<long double>
: public true_type { };
#if !defined(__STRICT_ANSI__) && defined(_GLIBCXX_USE_FLOAT128)
template<>
struct __is_floating_point_helper<__float128>
: public true_type { };
#endif
/// is_floating_point
template<typename _Tp>
struct is_floating_point
: public __is_floating_point_helper<typename remove_cv<_Tp>::type>::type
{ };
// Const-volatile modifications.
/// remove_const
template<typename _Tp>
struct remove_const
{ typedef _Tp type; };
template<typename _Tp>
struct remove_const<_Tp const>
{ typedef _Tp type; };
/// remove_volatile
template<typename _Tp>
struct remove_volatile
{ typedef _Tp type; };
template<typename _Tp>
struct remove_volatile<_Tp volatile>
{ typedef _Tp type; };
/// remove_cv
template<typename _Tp>
struct remove_cv
{
typedef typename
remove_const<typename remove_volatile<_Tp>::type>::type type;
};
// Primary template.
/// Define a member typedef @c type only if a boolean constant is true.
template<bool, typename _Tp = void>
struct enable_if
{ };
// Partial specialization for true.
template<typename _Tp>
struct enable_if<true, _Tp>
{ typedef _Tp type; };
// Type relations.
/// is_same
template<typename, typename>
struct is_same
: public false_type { };
template<typename _Tp>
struct is_same<_Tp, _Tp>
: public true_type { };
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Oskar Weigl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+116
View File
@@ -0,0 +1,116 @@
#include "Arduino.h"
#include "ODriveArduino.h"
static const int kMotorOffsetFloat = 2;
static const int kMotorStrideFloat = 28;
static const int kMotorOffsetInt32 = 0;
static const int kMotorStrideInt32 = 4;
static const int kMotorOffsetBool = 0;
static const int kMotorStrideBool = 4;
static const int kMotorOffsetUint16 = 0;
static const int kMotorStrideUint16 = 2;
// Print with stream operator
template<class T> inline Print& operator <<(Print &obj, T arg) { obj.print(arg); return obj; }
template<> inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; }
ODriveArduino::ODriveArduino(Stream& serial)
: serial_(serial) {}
void ODriveArduino::SetPosition(int motor_number, float position) {
SetPosition(motor_number, position, 0.0f, 0.0f);
}
void ODriveArduino::SetPosition(int motor_number, float position, float velocity_feedforward) {
SetPosition(motor_number, position, velocity_feedforward, 0.0f);
}
void ODriveArduino::SetPosition(int motor_number, float position, float velocity_feedforward, float current_feedforward) {
serial_ << "$p " << motor_number << " " << position << " " << velocity_feedforward << " " << current_feedforward << "!";
}
void ODriveArduino::SetVelocity(int motor_number, float velocity) {
SetVelocity(motor_number, velocity, 0.0f);
}
void ODriveArduino::SetVelocity(int motor_number, float velocity, float current_feedforward) {
serial_ << "$v " << motor_number << " " << velocity << " " << current_feedforward << "!";
}
float ODriveArduino::getBusVoltage() {
serial_ << "$g 0 0!";
return readFloat();
}
float ODriveArduino::GetParameter(int motor_number, ParamNamesFloat parameter) {
int idx = kMotorOffsetFloat + kMotorStrideFloat * motor_number + parameter;
serial_ << "$g 0 " << idx << "!";
return readString().toFloat();
}
int32_t ODriveArduino::GetParameter(int motor_number, ParamNamesInt32 parameter) {
int idx = kMotorOffsetInt32 + kMotorStrideInt32 * motor_number + parameter;
serial_ << "$g 1 " << idx << "!";
return readString().toInt();
}
bool ODriveArduino::GetParameter(int motor_number, ParamNamesBool parameter) {
int idx = kMotorOffsetBool + kMotorStrideBool * motor_number + parameter;
serial_ << "$g 2 " << idx << "!";
return readString().toInt();
}
uint16_t ODriveArduino::GetParameter(int motor_number, ParamNamesUint16 parameter) {
int idx = kMotorOffsetUint16 + kMotorStrideUint16 * motor_number + parameter;
serial_ << "$g 3 " << idx << "!";
return readString().toInt();
}
void ODriveArduino::SetParameter(int motor_number, ParamNamesFloat parameter, float value) {
int idx = kMotorOffsetFloat + kMotorStrideFloat * motor_number + parameter;
serial_ << "$s 0 " << idx << " " << value << "!";
}
void ODriveArduino::SetParameter(int motor_number, ParamNamesInt32 parameter, int32_t value) {
int idx = kMotorOffsetInt32 + kMotorStrideInt32 * motor_number + parameter;
serial_ << "$s 1 " << idx << " " << value << "!";
}
void ODriveArduino::SetParameter(int motor_number, ParamNamesBool parameter, bool value) {
int idx = kMotorOffsetBool + kMotorStrideBool * motor_number + parameter;
serial_ << "$s 2 " << idx << " " << value << "!";
}
void ODriveArduino::SetParameter(int motor_number, ParamNamesUint16 parameter, uint16_t value) {
int idx = kMotorOffsetUint16 + kMotorStrideUint16 * motor_number + parameter;
serial_ << "$s 3 " << idx << " " << value << "!";
}
float ODriveArduino::readFloat() {
return readString().toFloat();
}
int32_t ODriveArduino::readInt() {
return readString().toInt();
}
String ODriveArduino::readString() {
String str = "";
static const unsigned long timeout = 1000;
unsigned long timeout_start = millis();
for (;;) {
while (!serial_.available()) {
if (millis() - timeout_start >= timeout) {
return str;
}
}
char c = serial_.read();
if (c == '\n')
break;
str += c;
}
return str;
}
+88
View File
@@ -0,0 +1,88 @@
#ifndef ODriveArduino_h
#define ODriveArduino_h
#include "Arduino.h"
class ODriveArduino {
public:
enum ParamNamesFloat {
PARAM_FLOAT_POS_SETPOINT,
PARAM_FLOAT_POS_GAIN,
PARAM_FLOAT_VEL_SETPOINT,
PARAM_FLOAT_VEL_GAIN,
PARAM_FLOAT_VEL_INTEGRATOR_GAIN,
PARAM_FLOAT_VEL_INTEGRATOR_CURRENT,
PARAM_FLOAT_VEL_LIMIT,
PARAM_FLOAT_CURRENT_SETPOINT,
PARAM_FLOAT_CALIBRATION_CURRENT,
PARAM_FLOAT_PHASE_INDUCTANCE,
PARAM_FLOAT_PHASE_RESISTANCE,
PARAM_FLOAT_CURRENT_MEAS_PHB,
PARAM_FLOAT_CURRENT_MEAS_PHC,
PARAM_FLOAT_DC_CALIB_PHB,
PARAM_FLOAT_DC_CALIB_PHC,
PARAM_FLOAT_SHUNT_CONDUCTANCE,
PARAM_FLOAT_PHASE_CURRENT_REV_GAIN,
PARAM_FLOAT_CURRENT_CONTROL_CURRENT_LIM,
PARAM_FLOAT_CURRENT_CONTROL_P_GAIN,
PARAM_FLOAT_CURRENT_CONTROL_I_GAIN,
PARAM_FLOAT_CURRENT_CONTROL_V_CURRENT_CONTROL_INTEGRAL_D,
PARAM_FLOAT_CURRENT_CONTROL_V_CURRENT_CONTROL_INTEGRAL_Q,
PARAM_FLOAT_CURRENT_CONTROL_IBUS,
PARAM_FLOAT_ENCODER_PHASE,
PARAM_FLOAT_ENCODER_PLL_POS,
PARAM_FLOAT_ENCODER_PLL_VEL,
PARAM_FLOAT_ENCODER_PLL_KP,
PARAM_FLOAT_ENCODER_PLL_KI,
};
enum ParamNamesInt32 {
PARAM_INT_CONTROL_MODE,
PARAM_INT_ENCODER_ENCODER_OFFSET,
PARAM_INT_ENCODER_ENCODER_STATE,
PARAM_INT_ERROR,
};
enum ParamNamesBool{
PARAM_BOOL_THREAD_READY,
PARAM_BOOL_ENABLE_CONTROL,
PARAM_BOOL_DO_CALIBRATION,
PARAM_BOOL_CALIBRATION_OK,
};
enum ParamNamesUint16{
PARAM_UINT16_CONTROL_DEADLINE,
PARAM_UINT16_LAST_CPU_TIME,
};
ODriveArduino(Stream& serial);
// Get
float getBusVoltage();
float GetParameter(int motor_number, ParamNamesFloat parameter);
int32_t GetParameter(int motor_number, ParamNamesInt32 parameter);
bool GetParameter(int motor_number, ParamNamesBool parameter);
uint16_t GetParameter(int motor_number, ParamNamesUint16 parameter);
// Set
void SetPosition(int motor_number, float position);
void SetPosition(int motor_number, float position, float velocity_feedforward);
void SetPosition(int motor_number, float position, float velocity_feedforward, float current_feedforward);
void SetVelocity(int motor_number, float velocity);
void SetVelocity(int motor_number, float velocity, float current_feedforward);
void SetParameter(int motor_number, ParamNamesFloat parameter, float value);
void SetParameter(int motor_number, ParamNamesInt32 parameter, int32_t value);
void SetParameter(int motor_number, ParamNamesBool parameter, bool value);
void SetParameter(int motor_number, ParamNamesUint16 parameter, uint16_t value);
private:
float readFloat();
int32_t readInt();
String readString();
Stream& serial_;
};
#endif //ODriveArduino_h
+10
View File
@@ -0,0 +1,10 @@
# ODriveArduino
Arduino library for the ODrive
To install the library, first clone this repository. In the Arduino IDE select: *Sketch -> Include Library -> Add .ZIP Library...*
Select the enclosing folder (e.g. ODriveArduino) to add it. Restarting the Arduino IDE may be necessary to see the examples in the *File* dropdown. Check the included example *ODriveArduinoTest* for basic usage.
**Important Note: The Arduino library currently only supports the legacy UART protocol. This is selected in the firmware in [MotorControl/commands.h](https://github.com/madcowswe/ODrive/blob/master/Firmware/MotorControl/commands.h), with UART_PROTOCOL_LEGACY**
For most applications, this protocol is sufficient. Future versions of the Arduino library will be upgraded to support the current binary protocol.
@@ -0,0 +1,75 @@
#include <SoftwareSerial.h>
#include <ODriveArduino.h>
// Printing with stream operator
template<class T> inline Print& operator <<(Print &obj, T arg) { obj.print(arg); return obj; }
template<> inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; }
// Serial to the ODrive
SoftwareSerial odrive_serial(8, 9); //RX (ODrive TX), TX (ODrive RX)
// ODrive object
ODriveArduino odrive(odrive_serial);
void setup() {
// ODrive uses 115200 baud
odrive_serial.begin(115200);
// Serial to PC
Serial.begin(115200);
while (!Serial) ; // wait for Arduino Serial Monitor to open
Serial.println("ODriveArduino alpha.");
Serial.println("Setting parameters...");
// In this example we set the same parameters to both motors.
// You can of course set them different if you want.
for (int motor = 0; motor < 2; ++motor) {
odrive.SetParameter(motor, odrive.PARAM_FLOAT_CURRENT_CONTROL_CURRENT_LIM, 10.0f); // [A]
odrive.SetParameter(motor, odrive.PARAM_FLOAT_VEL_LIMIT, 20000.0f); // [counts/s]
odrive.SetParameter(motor, odrive.PARAM_FLOAT_POS_GAIN, 20.0f); // [(counts/s) / counts]
odrive.SetParameter(motor, odrive.PARAM_FLOAT_VEL_GAIN, 15.0f/10000.0f); // [A/(counts/s)]
odrive.SetParameter(motor, odrive.PARAM_FLOAT_VEL_INTEGRATOR_GAIN, 0.0f/10000.0f); // [A/(counts/s * s)]
}
Serial.println("Ready!");
Serial.println("Send the character 's' to exectue test move");
Serial.println("Send the character 'b' to read bus voltage");
Serial.println("Send the character 'p' to read motor positions in a 10s loop");
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
// Sinusoidal test move
if (c == 's') {
for (float ph = 0.0f; ph < 6.28318530718f; ph += 0.01f) {
float pos_m0 = 20000.0f * cos(ph);
float pos_m1 = 20000.0f * sin(ph);
odrive.SetPosition(0, pos_m0);
odrive.SetPosition(1, pos_m1);
delay(5);
}
}
// Read bus voltage
if (c == 'b') {
Serial << "Vbus voltage: " << odrive.getBusVoltage() << '\n';
}
// print motor positions in a 10s loop
if (c == 'p') {
static const unsigned long duration = 10000;
unsigned long start = millis();
while(millis() - start < duration) {
for (int motor = 0; motor < 2; ++motor) {
Serial << odrive.GetParameter(motor, odrive.PARAM_FLOAT_ENCODER_PLL_POS) << '\t';
}
Serial << '\n';
}
}
}
}