Add value tables and units to every signal in .dbc

This commit is contained in:
Paul Guenette
2022-09-25 19:58:31 -07:00
parent 8a517f8c15
commit 9a754031b3
6 changed files with 511 additions and 284 deletions

View File

@@ -8,17 +8,19 @@
* When using a load encoder, homing will reset the correct linear position. Fixes [#651](https://github.com/odriverobotics/ODrive/issues/651)
* Implemented CAN controller error message, which was previously defined but not actually implemented.
* Get Vbus Voltage message updated to match ODrive Pro's CANSimple implementation.
* vel_setpoint and torque_setpoint will be clamped to vel_limit and the active torque limit. Fixes [#647](https://github.com/odriverobotics/ODrive/issues/647)
* `vel_setpoint` and `torque_setpoint` will be clamped to `vel_limit` and the active torque limit. Fixes [#647](https://github.com/odriverobotics/ODrive/issues/647)
### Added
* Added public `controller.get_anticogging_value(uint32)` fibre function to index into the the cogging map. Fixes [#690](https://github.com/odriverobotics/ODrive/issues/690)
* Added Get ADC Voltage message to CAN (0x1C). Send the desired GPIO number in byte 1, and the ODrive will respond with the ADC voltage from that pin (if previously configured for analog)
* Added CAN heartbeat message flags for motor, controller, and encoder error. If flag is true, fetch the corresponding error with the respective message.
* Added scoped enums, e.g. `CONTROL_MODE_POSITION_CONTROL` can be used as `ControlMode.POSITION_CONTROL`
### Changed
* Improved can_generate_dbc.py file and resultant .dbc. Now supports 8 ODrive axes (0..7) natively
* Add units and value tables to every signal in odrive-cansimple.dbc
* Autogenerate odrive-cansimple.dbc on compile
## [0.5.5] - 2022-08-11

View File

@@ -33,6 +33,7 @@ all:
@tup --quiet -no-environ-check
@$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py
@$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/arduino_enums_template.j2 --output ../Arduino/ODriveArduino/ODriveEnums.h
@cd ../tools/ && $(PY_CMD) create_can_dbc.py
# Copy libfibre files to odrivetool if they were built
@ ! test -f "fibre-cpp/build/libfibre-linux-amd64.so" || cp fibre-cpp/build/libfibre-linux-amd64.so ../tools/odrive/pyfibre/fibre/

View File

@@ -1,4 +1,5 @@
from cantools.database import *
from odrive.enums import *
msgList = []
nodes = [can.Node('Master')]
@@ -11,11 +12,11 @@ for axisID in range(0, 8):
# 0x00 - NMT Message (Reserved)
# 0x001 - Heartbeat
axisError = can.Signal("Axis_Error", 0, 32, receivers=['Master'])
axisState = can.Signal("Axis_State", 32, 8, receivers=['Master'])
axisError = can.Signal("Axis_Error", 0, 32, receivers=['Master'], choices={error.value: error.name for error in AxisError})
axisState = can.Signal("Axis_State", 32, 8, receivers=['Master'], choices={state.value: state.name for state in AxisState})
motorErrorFlag = can.Signal("Motor_Error_Flag", 40, 1, receivers=['Master'])
encoderErrorFlag = can.Signal("encoder_Error_Flag", 48, 1, receivers=['Master'])
controllerErrorFlag = can.Signal("controller_Error_Flag", 56, 1, receivers=['Master'])
encoderErrorFlag = can.Signal("Encoder_Error_Flag", 48, 1, receivers=['Master'])
controllerErrorFlag = can.Signal("Controller_Error_Flag", 56, 1, receivers=['Master'])
trajectoryDoneFlag = can.Signal("Trajectory_Done_Flag", 63, 1, receivers=['Master'])
heartbeatMsg = can.Message(
@@ -34,17 +35,17 @@ for axisID in range(0, 8):
estopMsg = can.Message(0x002, "Estop", 0, [], senders=['Master'])
# 0x003 - Motor Error
motorError = can.Signal("Motor_Error", 0, 32, receivers=['Master'])
motorError = can.Signal("Motor_Error", 0, 32, receivers=['Master'], choices={error.value: error.name for error in MotorError})
motorErrorMsg = can.Message(0x003, "Get_Motor_Error", 8, [motorError], senders=[newNode.name])
# 0x004 - Encoder Error
encoderError = can.Signal("Encoder_Error", 0, 32, receivers=['Master'])
encoderError = can.Signal("Encoder_Error", 0, 32, receivers=['Master'], choices={error.value: error.name for error in EncoderError})
encoderErrorMsg = can.Message(
0x004, "Get_Encoder_Error", 8, [encoderError], senders=[newNode.name]
)
# 0x005 - Sensorless Error
sensorlessError = can.Signal("Sensorless_Error", 0, 32, receivers=['Master'])
sensorlessError = can.Signal("Sensorless_Error", 0, 32, receivers=['Master'], choices={error.value: error.name for error in SensorlessEstimatorError})
sensorlessErrorMsg = can.Message(
0x005, "Get_Sensorless_Error", 8, [sensorlessError], senders=[newNode.name]
)
@@ -54,7 +55,7 @@ for axisID in range(0, 8):
axisNodeMsg = can.Message(0x006, "Set_Axis_Node_ID", 8, [axisNodeID], senders=['Master'])
# 0x007 - Requested State
axisRequestedState = can.Signal("Axis_Requested_State", 0, 32, receivers=[newNode.name])
axisRequestedState = can.Signal("Axis_Requested_State", 0, 32, receivers=[newNode.name], choices={state.value: state.name for state in AxisState})
setAxisState = can.Message(
0x007, "Set_Axis_State", 8, [axisRequestedState], senders=['Master']
)
@@ -62,52 +63,50 @@ for axisID in range(0, 8):
# 0x008 - Startup Config (Reserved)
# 0x009 - Encoder Estimates
encoderPosEstimate = can.Signal("Pos_Estimate", 0, 32, is_float=True, receivers=['Master'])
encoderVelEstimate = can.Signal("Vel_Estimate", 32, 32, is_float=True, receivers=['Master'])
encoderPosEstimate = can.Signal("Pos_Estimate", 0, 32, is_float=True, receivers=['Master'], unit='rev')
encoderVelEstimate = can.Signal("Vel_Estimate", 32, 32, is_float=True, receivers=['Master'], unit='rev/s')
encoderEstimates = can.Message(
0x009, "Get_Encoder_Estimates", 8, [encoderPosEstimate, encoderVelEstimate], senders=[newNode.name], send_type='cyclic', cycle_time=10
)
# 0x00A - Get Encoder Count
encoderShadowCount = can.Signal("Shadow_Count", 0, 32, receivers=['Master'])
encoderCountInCPR = can.Signal("Count_in_CPR", 32, 32, receivers=['Master'])
encoderShadowCount = can.Signal("Shadow_Count", 0, 32, receivers=['Master'], unit='counts')
encoderCountInCPR = can.Signal("Count_in_CPR", 32, 32, receivers=['Master'], unit='counts')
encoderCountMsg = can.Message(
0x00A, "Get_Encoder_Count", 8, [encoderShadowCount, encoderCountInCPR], senders=[newNode.name]
)
# 0x00B - Set Controller Modes
controlMode = can.Signal("Control_Mode", 0, 32, receivers=[newNode.name])
inputMode = can.Signal("Input_Mode", 32, 32, receivers=[newNode.name])
controlMode = can.Signal("Control_Mode", 0, 32, receivers=[newNode.name], choices={state.value: state.name for state in ControlMode})
inputMode = can.Signal("Input_Mode", 32, 32, receivers=[newNode.name], choices={state.value: state.name for state in InputMode})
setControllerModeMsg = can.Message(
0x00B, "Set_Controller_Mode", 8, [controlMode, inputMode], senders=['Master']
)
# 0x00C - Set Input Pos
inputPos = can.Signal("Input_Pos", 0, 32, is_float=True, receivers=[newNode.name])
velFF = can.Signal("Vel_FF", 32, 16, is_signed=True, scale=0.001, receivers=[newNode.name])
torqueFF = can.Signal(
"Torque_FF", 48, 16, is_signed=True, scale=0.001, receivers=[newNode.name]
)
inputPos = can.Signal("Input_Pos", 0, 32, is_float=True, receivers=[newNode.name], unit='rev')
velFF = can.Signal("Vel_FF", 32, 16, is_signed=True, scale=0.001, receivers=[newNode.name], unit='rev/s')
torqueFF = can.Signal("Torque_FF", 48, 16, is_signed=True, scale=0.001, receivers=[newNode.name], unit='Nm')
setInputPosMsg = can.Message(
0x00C, "Set_Input_Pos", 8, [inputPos, velFF, torqueFF], senders=['Master']
)
# 0x00D - Set Input Vel
inputVel = can.Signal("Input_Vel", 0, 32, is_float=True, receivers=[newNode.name])
inputTorqueFF = can.Signal("Input_Torque_FF", 32, 32, is_float=True, receivers=[newNode.name])
inputVel = can.Signal("Input_Vel", 0, 32, is_float=True, receivers=[newNode.name], unit='rev')
inputTorqueFF = can.Signal("Input_Torque_FF", 32, 32, is_float=True, receivers=[newNode.name], unit='rev/s')
setInputVelMsg = can.Message(
0x00D, "Set_Input_Vel", 8, [inputVel, inputTorqueFF], senders=['Master']
)
# 0x00E - Set Input Torque
inputTorque = can.Signal("Input_Torque", 0, 32, is_float=True, receivers=[newNode.name])
inputTorque = can.Signal("Input_Torque", 0, 32, is_float=True, receivers=[newNode.name], unit='Nm')
setInputTqMsg = can.Message(
0x00E, "Set_Input_Torque", 8, [inputTorque], senders=['Master']
)
# 0x00F - Set Velocity Limit
velLimit = can.Signal("Velocity_Limit", 0, 32, is_float=True, receivers=[newNode.name])
currentLimit = can.Signal("Current_Limit", 32, 32, is_float=True, receivers=[newNode.name])
velLimit = can.Signal("Velocity_Limit", 0, 32, is_float=True, receivers=[newNode.name], unit='rev/s')
currentLimit = can.Signal("Current_Limit", 32, 32, is_float=True, receivers=[newNode.name], unit='A')
setVelLimMsg = can.Message(
0x00F, "Set_Limits", 8, [velLimit, currentLimit], senders=['Master']
)
@@ -116,70 +115,64 @@ for axisID in range(0, 8):
startAnticoggingMsg = can.Message(0x010, "Start_Anticogging", 0, [], senders=['Master'])
# 0x011 - Set Traj Vel Limit
trajVelLim = can.Signal("Traj_Vel_Limit", 0, 32, is_float=True, receivers=[newNode.name])
trajVelLim = can.Signal("Traj_Vel_Limit", 0, 32, is_float=True, receivers=[newNode.name], unit='rev/s')
setTrajVelMsg = can.Message(
0x011, "Set_Traj_Vel_Limit", 8, [trajVelLim], senders=['Master']
)
# 0x012 - Set Traj Accel Limits
trajAccelLim = can.Signal("Traj_Accel_Limit", 0, 32, is_float=True, receivers=[newNode.name])
trajDecelLim = can.Signal("Traj_Decel_Limit", 32, 32, is_float=True, receivers=[newNode.name])
trajAccelLim = can.Signal("Traj_Accel_Limit", 0, 32, is_float=True, receivers=[newNode.name], unit='rev/s^2')
trajDecelLim = can.Signal("Traj_Decel_Limit", 32, 32, is_float=True, receivers=[newNode.name], unit='rev/s^2')
setTrajAccelMsg = can.Message(
0x012, "Set_Traj_Accel_Limits", 8, [trajAccelLim, trajDecelLim], senders=['Master']
)
# 0x013 - Set Traj Inertia
trajInertia = can.Signal("Traj_Inertia", 0, 32, is_float=True, receivers=[newNode.name])
trajInertia = can.Signal("Traj_Inertia", 0, 32, is_float=True, receivers=[newNode.name], unit='Nm / (rev/s^2)')
trajInertiaMsg = can.Message(
0x013, "Set_Traj_Inertia", 8, [trajInertia], senders=['Master']
)
# 0x014 - Get Iq
iqSetpoint = can.Signal("Iq_Setpoint", 0, 32, is_float=True, receivers=['Master'])
iqMeasured = can.Signal("Iq_Measured", 32, 32, is_float=True, receivers=['Master'])
iqSetpoint = can.Signal("Iq_Setpoint", 0, 32, is_float=True, receivers=['Master'], unit='A')
iqMeasured = can.Signal("Iq_Measured", 32, 32, is_float=True, receivers=['Master'], unit='A')
getIqMsg = can.Message(0x014, "Get_Iq", 8, [iqSetpoint, iqMeasured], senders=[newNode.name])
# 0x015 - Get Sensorless Estimates
sensorlessPosEstimate = can.Signal(
"Sensorless_Pos_Estimate", 0, 32, is_float=True, receivers=['Master']
)
sensorlessVelEstimate = can.Signal(
"Sensorless_Vel_Estimate", 32, 32, is_float=True, receivers=['Master']
)
getSensorlessEstMsg = can.Message(
0x015, "Get_Sensorless_Estimates", 8, [sensorlessPosEstimate, sensorlessVelEstimate], senders=[newNode.name]
)
sensorlessPosEstimate = can.Signal("Sensorless_Pos_Estimate", 0, 32, is_float=True, receivers=['Master'], unit='rev')
sensorlessVelEstimate = can.Signal("Sensorless_Vel_Estimate", 32, 32, is_float=True, receivers=['Master'], unit='rev/s')
getSensorlessEstMsg = can.Message(0x015, "Get_Sensorless_Estimates", 8, [sensorlessPosEstimate, sensorlessVelEstimate], senders=[newNode.name])
# 0x016 - Reboot ODrive
rebootMsg = can.Message(0x016, "Reboot", 0, [], senders=['Master'])
# 0x017 - Get vbus Voltage and Current
busVoltage = can.Signal("Bus_Voltage", 0, 32, is_float=True, receivers=['Master'])
busCurrent = can.Signal("Bus_Current", 32, 32, is_float=True, receivers=['Master'])
busVoltage = can.Signal("Bus_Voltage", 0, 32, is_float=True, receivers=['Master'], unit='V')
busCurrent = can.Signal("Bus_Current", 32, 32, is_float=True, receivers=['Master'], unit='A')
getVbusVCMsg = can.Message(0x017, "Get_Bus_Voltage_Current", 8, [busVoltage, busCurrent], senders=[newNode.name])
# 0x018 - Clear Errors
clearErrorsMsg = can.Message(0x018, "Clear_Errors", 0, [], senders=['Master'])
# 0x019 - Set Linear Count
position = can.Signal("Position", 0, 32, is_signed=True, receivers=[newNode.name])
position = can.Signal("Position", 0, 32, is_signed=True, receivers=[newNode.name], unit='counts')
setLinearCountMsg = can.Message(0x019, "Set_Linear_Count", 8, [position], senders=['Master'])
# 0x01A - Set Pos gain
posGain = can.Signal("Pos_Gain", 0, 32, is_float=True, receivers=[newNode.name])
posGain = can.Signal("Pos_Gain", 0, 32, is_float=True, receivers=[newNode.name], unit='(rev/s) / rev')
setPosGainMsg = can.Message(0x01A, "Set_Pos_Gain", 8, [posGain], senders=['Master'])
# 0x01B - Set Vel Gains
velGain = can.Signal("Vel_Gain", 0, 32, is_float=True, receivers=[newNode.name])
velIntGain = can.Signal("Vel_Integrator_Gain", 32, 32, is_float=True, receivers=[newNode.name])
velGain = can.Signal("Vel_Gain", 0, 32, is_float=True, receivers=[newNode.name], unit='Nm / (rev/s)')
velIntGain = can.Signal("Vel_Integrator_Gain", 32, 32, is_float=True, receivers=[newNode.name], unit='(Nm / (rev/s)) / s')
setVelGainsMsg = can.Message(0x01B, "Set_Vel_Gains", 8, [velGain, velIntGain], senders=['Master'])
# 0x01C - Get ADC Voltage
adcVoltage = can.Signal("ADC_Voltage", 0, 32, is_float=True, receivers=['Master'])
adcVoltage = can.Signal("ADC_Voltage", 0, 32, is_float=True, receivers=['Master'], unit='V')
getADCVoltageMsg = can.Message(0x01C, "Get_ADC_Voltage", 8, [adcVoltage], senders=[newNode.name])
# 0x01D - Controller Error
controllerError = can.Signal("Controller_Error", 0, 32, receivers=['Master'])
controllerError = can.Signal("Controller_Error", 0, 32, receivers=['Master'], choices={error.value: error.name for error in ControllerError})
controllerErrorMsg = can.Message(
0x01D, "Get_Controller_Error", 8, [controllerError], senders=[newNode.name]
)
@@ -232,7 +225,7 @@ for axisID in range(0, 8):
from itertools import chain
msgList = list(chain.from_iterable(msgList))
db = can.Database(msgList, nodes, buses, version='0.5.5')
db = can.Database(msgList, nodes, buses, version='0.5.6')
dump_file(db, "odrive-cansimple.dbc")
db = load_file("odrive-cansimple.dbc")

View File

@@ -3,6 +3,8 @@
# To regenerate this file, nagivate to the top level of the ODrive repository and run:
# python Firmware/interface_generator_stub.py --definitions Firmware/odrive-interface.yaml --template tools/enums_template.j2 --output tools/odrive/enums.py
import enum
[%- for _, enum in value_types.items() %]
[%- if enum.is_enum %]
@@ -13,3 +15,11 @@
[%- endif %]
[%- endfor %]
[%- for _, enum in value_types.items() %]
[%- if enum.is_enum %]
class [[(enum.parent.name if enum.name in ['Error', 'Mode', 'Protocol'] else '') + enum.name]][% if enum.is_flags %](enum.IntFlag)[% else %](enum.Enum)[% endif %]:
[%- for k, value in enum['values'].items() %]
[[((k | to_macro_case)).ljust(40)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %]
[%- endfor %]
[%- endif %]
[%- endfor %]

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@
# To regenerate this file, nagivate to the top level of the ODrive repository and run:
# python Firmware/interface_generator_stub.py --definitions Firmware/odrive-interface.yaml --template tools/enums_template.j2 --output tools/odrive/enums.py
import enum
# ODrive.GpioMode
GPIO_MODE_DIGITAL = 0
GPIO_MODE_DIGITAL_PULL_UP = 1
@@ -165,3 +167,151 @@ ENCODER_ERROR_HALL_NOT_CALIBRATED_YET = 0x00000200
SENSORLESS_ESTIMATOR_ERROR_NONE = 0x00000000
SENSORLESS_ESTIMATOR_ERROR_UNSTABLE_GAIN = 0x00000001
SENSORLESS_ESTIMATOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT = 0x00000002
class GpioMode(enum.Enum):
DIGITAL = 0
DIGITAL_PULL_UP = 1
DIGITAL_PULL_DOWN = 2
ANALOG_IN = 3
UART_A = 4
UART_B = 5
UART_C = 6
CAN_A = 7
I2C_A = 8
SPI_A = 9
PWM = 10
ENC0 = 11
ENC1 = 12
ENC2 = 13
MECH_BRAKE = 14
STATUS = 15
class StreamProtocolType(enum.Enum):
FIBRE = 0
ASCII = 1
STDOUT = 2
ASCII_AND_STDOUT = 3
class CanProtocol(enum.IntFlag):
SIMPLE = 0x00000001
class AxisState(enum.Enum):
UNDEFINED = 0
IDLE = 1
STARTUP_SEQUENCE = 2
FULL_CALIBRATION_SEQUENCE = 3
MOTOR_CALIBRATION = 4
ENCODER_INDEX_SEARCH = 6
ENCODER_OFFSET_CALIBRATION = 7
CLOSED_LOOP_CONTROL = 8
LOCKIN_SPIN = 9
ENCODER_DIR_FIND = 10
HOMING = 11
ENCODER_HALL_POLARITY_CALIBRATION = 12
ENCODER_HALL_PHASE_CALIBRATION = 13
class EncoderMode(enum.Enum):
INCREMENTAL = 0
HALL = 1
SINCOS = 2
SPI_ABS_CUI = 256
SPI_ABS_AMS = 257
SPI_ABS_AEAT = 258
SPI_ABS_RLS = 259
SPI_ABS_MA732 = 260
class ControlMode(enum.Enum):
VOLTAGE_CONTROL = 0
TORQUE_CONTROL = 1
VELOCITY_CONTROL = 2
POSITION_CONTROL = 3
class InputMode(enum.Enum):
INACTIVE = 0
PASSTHROUGH = 1
VEL_RAMP = 2
POS_FILTER = 3
MIX_CHANNELS = 4
TRAP_TRAJ = 5
TORQUE_RAMP = 6
MIRROR = 7
TUNING = 8
class MotorType(enum.Enum):
HIGH_CURRENT = 0
GIMBAL = 2
ACIM = 3
class ODriveError(enum.IntFlag):
NONE = 0x00000000
CONTROL_ITERATION_MISSED = 0x00000001
DC_BUS_UNDER_VOLTAGE = 0x00000002
DC_BUS_OVER_VOLTAGE = 0x00000004
DC_BUS_OVER_REGEN_CURRENT = 0x00000008
DC_BUS_OVER_CURRENT = 0x00000010
BRAKE_DEADTIME_VIOLATION = 0x00000020
BRAKE_DUTY_CYCLE_NAN = 0x00000040
INVALID_BRAKE_RESISTANCE = 0x00000080
class CanError(enum.IntFlag):
NONE = 0x00000000
DUPLICATE_CAN_IDS = 0x00000001
class AxisError(enum.IntFlag):
NONE = 0x00000000
INVALID_STATE = 0x00000001
MOTOR_FAILED = 0x00000040
SENSORLESS_ESTIMATOR_FAILED = 0x00000080
ENCODER_FAILED = 0x00000100
CONTROLLER_FAILED = 0x00000200
WATCHDOG_TIMER_EXPIRED = 0x00000800
MIN_ENDSTOP_PRESSED = 0x00001000
MAX_ENDSTOP_PRESSED = 0x00002000
ESTOP_REQUESTED = 0x00004000
HOMING_WITHOUT_ENDSTOP = 0x00020000
OVER_TEMP = 0x00040000
UNKNOWN_POSITION = 0x00080000
class MotorError(enum.IntFlag):
NONE = 0x00000000
PHASE_RESISTANCE_OUT_OF_RANGE = 0x00000001
PHASE_INDUCTANCE_OUT_OF_RANGE = 0x00000002
DRV_FAULT = 0x00000008
CONTROL_DEADLINE_MISSED = 0x00000010
MODULATION_MAGNITUDE = 0x00000080
CURRENT_SENSE_SATURATION = 0x00000400
CURRENT_LIMIT_VIOLATION = 0x00001000
MODULATION_IS_NAN = 0x00010000
MOTOR_THERMISTOR_OVER_TEMP = 0x00020000
FET_THERMISTOR_OVER_TEMP = 0x00040000
TIMER_UPDATE_MISSED = 0x00080000
CURRENT_MEASUREMENT_UNAVAILABLE = 0x00100000
CONTROLLER_FAILED = 0x00200000
I_BUS_OUT_OF_RANGE = 0x00400000
BRAKE_RESISTOR_DISARMED = 0x00800000
SYSTEM_LEVEL = 0x01000000
BAD_TIMING = 0x02000000
UNKNOWN_PHASE_ESTIMATE = 0x04000000
UNKNOWN_PHASE_VEL = 0x08000000
UNKNOWN_TORQUE = 0x10000000
UNKNOWN_CURRENT_COMMAND = 0x20000000
UNKNOWN_CURRENT_MEASUREMENT = 0x40000000
UNKNOWN_VBUS_VOLTAGE = 0x80000000
UNKNOWN_VOLTAGE_COMMAND = 0x100000000
UNKNOWN_GAINS = 0x200000000
CONTROLLER_INITIALIZING = 0x400000000
UNBALANCED_PHASES = 0x800000000
class ControllerError(enum.IntFlag):
NONE = 0x00000000
OVERSPEED = 0x00000001
INVALID_INPUT_MODE = 0x00000002
UNSTABLE_GAIN = 0x00000004
INVALID_MIRROR_AXIS = 0x00000008
INVALID_LOAD_ENCODER = 0x00000010
INVALID_ESTIMATE = 0x00000020
INVALID_CIRCULAR_RANGE = 0x00000040
SPINOUT_DETECTED = 0x00000080
class EncoderError(enum.IntFlag):
NONE = 0x00000000
UNSTABLE_GAIN = 0x00000001
CPR_POLEPAIRS_MISMATCH = 0x00000002
NO_RESPONSE = 0x00000004
UNSUPPORTED_ENCODER_MODE = 0x00000008
ILLEGAL_HALL_STATE = 0x00000010
INDEX_NOT_FOUND_YET = 0x00000020
ABS_SPI_TIMEOUT = 0x00000040
ABS_SPI_COM_FAIL = 0x00000080
ABS_SPI_NOT_READY = 0x00000100
HALL_NOT_CALIBRATED_YET = 0x00000200
class SensorlessEstimatorError(enum.IntFlag):
NONE = 0x00000000
UNSTABLE_GAIN = 0x00000001
UNKNOWN_CURRENT_MEASUREMENT = 0x00000002