MD FDCAN communication#
The easiest way to communicate with MD controllers is to use a CANdle device connected to a PC. Even though we are aware some customers want to integrate the MD controllers in their product with minimal setup to reduce the costs and the system’s complexity. This manual will guide you through the process of communicating with MD actuators from your custom CAN master controller.
Hardware requirements#
From firmware version 2.5.0 upwards, MDs are capable of either FDCAN mode communication or CAN2.0 compatibile mode. FDCAN is a preffered protocol to use, as it allows for far greater flexibility and bandwidth. CAN2.0 is supported but not recommended, as the protocol was not optimized for it, and basic operations and control may require multiple CAN frames to be exchanged, significantly reducing the bandwidth, especially with multiple actuators on a single bus.
FDCAN#
The main requirement for the host system is to be equipped with an FDCAN peripheral (either a built-in one or an external one) and an FDCAN transceiver capable of speeds up to 8Mbps. Lower maximum speed transceivers can be used as well, however for the cost of limited update rates. Depending on your custom setup you should be able to integrate a 120 ohm terminating resistor on both ends of your CAN bus.
CAN2.0#
While MD was designed with FDCAN protocol in mind, CAN2.0 compability was introduced. In CAN2.0 mode, the driver can only operate in 1M baudrate, and some registers (that are more than 4 bytes in size) are not available for modification, i.e motorName register. Additionally only some of the Frame Types are supported in this mode - READ_REGISTER_CAN2.0 and WRITE_REGISTER_CAN2.0. In most cases, access in 2.0 mode has to happen 1 register at a time (one register per can frame), with an exception of accessing two U8 type registers. Apart from maximum frame length, the contents of the frames will be the same as in FDCAN version of protocol.
Note
MD controllers can be upgraded to software controlled termination on demand. Please contact us for more information before placing your order.
Communication Structure#
Comminication with MD, happen is a strict Master-Slave structure. The MD itself will never produce a CAN frame by itself, it will only respond to direct commands from a host. Each MD device has configurable CAN ID, that serves as its unique identifier on the CAN bus. There may never be more than one MD with the same CAN ID, as this will lead to conflicts and errors.
All communication with the particular drive will happen only via messeges with particular ID. For example:
Note
Host sends a request (command) to drive with Id 100, the drive will execute the command and respond with a CAN frame that also has the ID of 100
The communication stack is based on a register access using two frames - register read and register write. The list of available registers can be found at the end of this chapter. All fields are little-endian - least significant byte first, and all float fields are 4 bytes long (32 bit) encoded in IEEE-754 standard.
Warning
Wrong access, incorrect data values or other communication errors are not reported explicitly. Command that failed (regardless of the reason), will result in the drive not producing any response. Generally if the drive does not start producing a CAN frame with 100us of the the last bit of the command, the host may consider the command has failed.
Frame Structure#
All frames (FDCAN and CAN2.0) are composed in the same fasion. The first byte is a Frame Type, then a padding byte, followed by the contents of a message.
Message contents are similar in both read and write operations. They follow a pattern of repeating sequence: register id (2 bytes) and register value (1-24 bytes depending on a register). The number of registers to be accessed is only limited by a size of message - 64 bytes for FDCAN, and 8 bytes for CAN2.0.
BYTE 0 | BYTE 1 | BYTE 2-3 | BYTE 4-X | BYTE X+1-X+2 | BYTE X+4-X+Y | ... |
FRAME ID | PADDING (0x00) | REG ID 1 | VALUE 1 | REG ID 2 | VALUE 2 | ... |
For example, a frame that would read a value of the current position, velocity and torque from a drive, would, have a length of 20 bytes, and look like the following:
BYTE 0 | BYTE 1 | BYTE 2-3 | BYTE 4-7 | BYTE 8-9 | BYTE 10-13 | BYTE 14-15 | BYTE 16-19 |
FRAME ID | PADDING (0x00) | regId - mainEncoderPosition | PADDING | regId - mainEncoderVelocity | PADDING | regId - motorTorque | PADDING |
0x41 | 0x00 | 0x0062 | 0x00 00 00 00 | 0x0063 | 0x00 00 00 00 | 0x0064 | 0x00 00 00 00 |
Frame Types#
Although there are basically only two possible variants of frames - read and write register - the frames can trigger different behaviours, apart of reading and writing. Here is a brief description of the frame behaviours by their id:
Frame ID | Name | Description |
0x40 | WRITE_REGISTER_LEGACY | performs write operation, and makes the drive respond with legacy response. This is sometimes usefull in high frequency control loops, to minimize number of frames exchanged. For frame contents, refer to Write Register Frame . |
0x41 | READ_REGISTER | performs read operation, and responds with state of the registers. Refer to Read Register Frame |
0x42 | WRITE_REGISTER | performs write operation, and responds with state of the registers AFTER operation. Usefull for verification of write operation. For more info, refer to Write Register Frame |
0x43 | READ_REGISTER_CAN2.0 | performs read operation, and responds with state of the registers. Works same as Read Register Frame , but is limited to 8 bytes and produces CAN2.0 compatibile response. |
0x44 | WRITE_REGISTER_CAN2.0 | performs write operation, and responds with state of the registers AFTER operation. Usefull for verification of write operation. Works same as Write Register Frame , but is limited to 8 bytes and produces CAN2.0 compatibile response. |
0x0A | LEGACY_RESPONE | RESPONSE ONLY. A response produced as a result of some frames. Contains data, most commonly required in fast control loops - more info here. |
Write register frame#
Write register frame is used to modify values of the user-modifiable registers. Only registers with write access can be modified.
FRAME NAME | DRIVE ID | LENGTH | BYTE 0 [ID] | BYTE 1 | BYTE 2-3 | BYTE 4-X | BYTE X+1-X+2 | BYTE X+4-X+Y |
WRITE_REGISTER | 10-2000 | X (64 max) | 0x42 | 0x00 | reg ID | value | reg ID | value |
Params:
regID (uint16_t) - first register ID (please see the end of this section)
value (uint8_t/uint16_t/uint32_t/float/char[]) - first register value to be written
regID (uint16_t) - second register ID (please see the end of this section)
value (uint8_t/uint16_t/uint32_t/float/char[]) - second register value to be written
… (up to 64 bytes total)
EXAMPLE Write target position and velocity
Command, send from host to MD:
BYTE 0 | BYTE 1 | BYTE 2-3 | BYTE 4-7 | BYTE 8-9 | BYTE 10-13 |
FRAME ID | PADDING (0x00) | regId - targetPosition | (float) 0.25 | regId - targetVelocity | (float) -7.4 |
0x42 | 0x00 | 0x0150 | 0x3E 80 00 00 | 0x0151 | 0xC0 EC CC CD |
Response, send from MD to Host:
BYTE 0 | BYTE 1 | BYTE 2-3 | BYTE 4-7 | BYTE 8-9 | BYTE 10-13 |
FRAME ID | PADDING (0x00) | regId - targetPosition | (float) 0.25 | regId - targetVelocity | (float) -7.4 |
0x42 | 0x00 | 0x0150 | 0x3E 80 00 00 | 0x0151 | 0xC0 EC CC CD |
Read Register Frame#
Read register command is used to retrieve certain register values. The actuator will respond with a frame consisting of the addresses and values of the registers issued in the master request. The master request should have the following form:
FRAME NAME | DRIVE ID | LENGTH | BYTE 0 [ID] | BYTE 1 | BYTE 2-3 | BYTE 4-X | BYTE X+1-X+2 | BYTE X+4-X+Y |
READ_REGISTER | 10-2000 | X (64 max) | 0x41 | 0x00 | reg ID | 0x00 | reg ID | 0x00 |
When all read operations succeed the 0x00 fields will be filled with appropriate register data when transmitted back to master by the MDxx controller.
FRAME NAME | DRIVE ID | LENGTH | BYTE 0 [ID] | BYTE 1 | BYTE 2-3 | BYTE 4-X | BYTE X+1-X+2 | BYTE X+4-X+Y |
Response to register read | 10-2000 | X (64 max) | 0x41 | 0x00 | reg ID | reg value | reg ID | reg value |
EXAMPLE Read MD status and position
Command, send from host to MD:
BYTE 0 | BYTE 1 | BYTE 2-3 | BYTE 4-5 | BYTE 6-7 | BYTE 8-11 |
FRAME ID | PADDING (0x00) | regId - quickStatus | PADDING (2 bytes) | regId - mainEncoderPosition | PADDING (4 bytes) |
0x41 | 0x00 | 0x0805 | 0x00 00 | 0x0062 | 0x00 00 00 00 |
Response, send from MD to Host:
BYTE 0 | BYTE 1 | BYTE 2-3 | BYTE 4-5 | BYTE 6-7 | BYTE 8-11 |
FRAME ID | PADDING (0x00) | regId - quickStatus | status (u16) | regId - mainEncoderPosition | 16.74 (float) |
0x41 | 0x00 | 0x0805 | 0x80 00 | 0x0062 | 0x41 85 EB 85 |
Legacy response#
BYTE 0 | BYTE 1-2 | BYTE 3 | BYTE 4-7 | BYTE 8-11 | BYTE 12-15 | BYTE 16-19 | BYTE 20-23 | |
NAME | FRAME ID | QUICK STATUS | MOTOR TEMPERATURE | MAIN ENCODER POSITION | MAIN ENCODER VELOCITY | MOTOR TORQUE | OUTPUT ENCODER POSITION | OUTPUT ENCODER VELOCITY |
TYPE | uint8_t | uint16_t | uint8_t [*C] | float [rad] | float [rad/s] | float [Nm] | float [rad] | float [rad/s] |
VALUE | 0x0A | 0x0000 - 0xFFFF | 0 - 255 | - | - | - | - | - |
Available registers#
reg name | address | read/write | size | limits | description |
---|---|---|---|---|---|
canId | 0x001 | RW | uint32_t | [10-2000] | FDCAN bus id number |
canBaudrate | 0x002 | RW | uint32_t | [1e6;2e6;5e6;8e6] | FDCAN bus baudrate |
canWatchdog | 0x003 | RW | uint16_t | [0-2500] | FDCAN bus watchdog period in ms |
canTermination | 0x004 | RW | uint8_t | [0-1] | CAN termination (available upon request) |
motorName | 0x010 | RW | char[24] | - | motor name |
motorPolePairs | 0x011 | RW | uint32_t | [2;225] | motor pole pair count |
motorKt | 0x012 | RW | float | >0 | motor torque constant |
motorKt_a | 0x013 | RW | float | >0 | optional parameter for phase specific torque constant |
motorKt_b | 0x014 | RW | float | >0 | optional parameter for phase specific torque constant |
motorKt_c | 0x015 | RW | float | >0 | optional parameter for phase specific torque constant |
motorIMax | 0x016 | RW | float | [1 - peak controller current] | maximum phase current |
motorGearRatio | 0x017 | RW | float | - | actuator gear ratio (ex 2:1 should be 0.5) <1 - reducer >1 - multiplier |
motorTorqueBandwidth | 0x018 | RW | uint16_t | [50-2500] | torque bandwidth in Hz |
motorFriction | 0x019 | RO | float32 | - | UNUSED |
motorStiction | 0x01A | RO | float32 | - | UNUSED |
motorResistance | 0x01B | RO | float | [5mOhm-20Ohm] | motor resistance in d axis |
motorInductance | 0x01C | RO | float | [5nH-100mH] | motor inductance in d axis |
motorKV | 0x01D | RW | uint16_t | - | motor KV rating [RPM/V] |
motorCalibrationMode | 0x01E | RW | uint8_t | [0;1] | FULL = 0, NOPPDET = 1 |
motorThermistorType | 0x01F | RW | uint8_t | - | - |
outputEncoder | 0x020 | RW | uint8_t | [0;1;2;3] | NONE = 0, ME_AS_CENTER = 1, ME_AS_OFFAXIS = 2, MB053SFA17BENT00 = 3, CM_OFFAXIS = 4, M24B_CENTER = 5, M24B_OFFAXIS = 6 |
outputEncoderDir | 0x021 | RW | uint8_t | 0 | RESERVED |
outputEncoderDefaultBaud | 0x022 | RW | uint32_t | 115200 | optional parameter for setting default output encoder baudrate |
outputEncoderVelocity | 0x023 | RO | float | - | output encoder velocity in rad/s (calculated in a 5kHz loop) |
outputEncoderPosition | 0x024 | RO | float | - | output encoder position in rad (read in 5kHz loop) |
outputEncoderMode | 0x025 | RW | uint8_t | [0;1;2;3;4] | NONE = 0, STARTUP = 1, MOTION = 2, REPORT = 3, MAIN = 4 |
outputEncoderCalibrationMode | 0x026 | RW | uint8_t | [0;1] | FULL = 0, DIRONLY = 1 |
motorPosPidKp | 0x030 | RW | float | - | position PID proportional gain |
motorPosPidKi | 0x031 | RW | float | - | position PID integral gain |
motorPosPidKd | 0x032 | RW | float | - | position PID derivative gain |
motorPosPidWindup | 0x034 | RW | float | - | position PID integral windup limit |
motorVelPidKp | 0x040 | RW | float | - | velocity PID proportional gain |
motorVelPidKi | 0x041 | RW | float | - | velocity PID integral gain |
motorVelPidKd | 0x042 | RW | float | - | velocity PID derivative gain |
motorVelPidWindup | 0x044 | RW | float | - | velocity PID integral windup limit |
motorImpPidKp | 0x050 | RW | float | - | impedance PD proportional gain |
motorImpPidKd | 0x051 | RW | float | - | impedance PD derivative gain |
mainEncoderVelocity | 0x062 | RO | float | - | main encoder velocity in rad/s (calculated in a 40kHz loop) |
mainEncoderPosition | 0x063 | RO | float | - | main encoder position in rad (read in 40kHz loop) |
motorTorque | 0x064 | RO | float | - | motor output shaft torque in Nm (read in 40kHz loop) |
runSaveCmd | 0x080 | WO | uint8_t | other than 0 to run | save non-volatile memory |
runTestMainEncoderCmd | 0x081 | WO | uint8_t | other than 0 to run | runs main encoder test routine |
runTestOutputEncoderCmd | 0x082 | WO | uint8_t | other than 0 to run | runs output encoder test routine |
runCalibrateCmd | 0x083 | WO | uint8_t | other than 0 to run | runs main calibration routine |
runCalibrateOutputEncoderCmd | 0x084 | WO | uint8_t | other than 0 to run | runs output encoder calibration routine |
runCalibratePiGains | 0x085 | WO | uint8_t | other than 0 to run | runs current PI loop calibration routine |
runRestoreFactoryConfig | 0x087 | WO | uint8_t | other than 0 to run | reverts config to factory state |
runReset | 0x088 | WO | uint8_t | other than 0 to run | resets the controller |
runClearWarnings | 0x089 | WO | uint8_t | other than 0 to run | clears all warnings |
runClearErrors | 0x08A | WO | uint8_t | other than 0 to run | clears non-critical errors |
runBlink | 0x08B | WO | uint8_t | other than 0 to run | blinks onboard LEDs |
runZero | 0x08C | WO | uint8_t | other than 0 to run | sets new zero position |
runCanReinit | 0x08D | WO | uint8_t | other than 0 to run | reinitializes can peripheral |
calOutputEncoderStdDev | 0x100 | RO | float | - | output encoder test result (standard deviation) |
calOutputEncoderMinE | 0x101 | RO | float | - | output encoder test result (min error) |
calOutputEncoderMaxE | 0x102 | RO | float | - | output encoder test result (max error) |
calMainEncoderStdDev | 0x103 | RO | float | - | main encoder test result (standard deviation) |
calMainEncoderMinE | 0x104 | RO | float | - | main encoder test result (min error) |
calMainEncoderMaxE | 0x105 | RO | float | - | main encoder test result (max error) |
positionLimitMax | 0x110 | RW | float | - | maximum valid position |
positionLimitMin | 0x111 | RW | float | - | minimum valid position |
maxTorque | 0x112 | RW | float | - | maximum torque |
maxVelocity | 0x113 | RW | float | - | maximum velocity |
maxAcceleration | 0x114 | RW | float | - | maximum acceleration |
maxDeceleration | 0x115 | RW | float | - | maximum deceleration |
profileVelocity | 0x120 | RW | float | - | profile velocity |
profileAcceleration | 0x121 | RW | float | - | profile acceleration |
profileDeceleration | 0x122 | RW | float | - | profile deceleration |
quickStopDeceleration | 0x123 | RW | float | - | quick stop deceleration in case of a non-critical error |
positionWindow | 0x124 | RW | float | - | position window within position is considered to be reached |
velocityWindow | 0x125 | RW | float | - | velocity window within velocity is considered to be reached |
motionModeCommand | 0x140 | WO | uint8_t | - | commands a motion mode change: IDLE = 0x00, POSITION_PID = 0x01, VELOCITY_PID = 0x02, RAW_TORQUE = 0x03, IMPEDANCE = 0x04, POSITION_PROFILE = 0x07, VELOCITY_PROFILE = 0x08 |
motionModeStatus | 0x141 | RO | uint8_t | - | shows the currently set motion mode |
state | 0x142 | RW | uint16_t | - | returns the internal state machine state of the controller |
targetPosition | 0x150 | RW | float | - | sets target position in rad |
targetVelocity | 0x151 | RW | float | - | sets target velocity in rad/s |
targetTorque | 0x152 | RW | float | - | sets target torque in Nm |
userGpioConfiguration | 0x160 | RW | uint8_t | - | 0 - OFF, 1 - AUTO-BRAKE, 2 - GPIO INPUT |
userGpioState | 0x161 | RO | uint16_t | - | GPIO input state |
reverseDirection | 0x600 | RW | uint8_t | - | used to change the direction of the main encoder when using other encoders than the onboard one. Always recalibrate after changing this setting |
shuntResistance | 0x700 | RO | float | [0.001 - 0.01] | Current sense resistor value. Setting this register to a value that is not coherent with the hardware may damage the controller. In this cases warranty is not respected. |
buildDate | 0x800 | RO | uint32_t | - | software build date |
commitHash | 0x801 | RO | char[8] | - | commit hash |
firmwareVersion | 0x802 | RO | uint32_t | - | - |
hardwareVersion | 0x803 | RO | uint8_t | - | - |
bridgeType | 0x804 | RO | uint8_t | - | type of the mosfet driver |
quickStatus | 0x805 | RO | uint16_t | - | quick status vector |
mosfetTemperature | 0x806 | RO | float | - | power stage temperature |
motorTemperature | 0x807 | RO | float | - | motor temperature (if thermistor is mounted) |
motorShutdownTemp | 0x808 | RW | uint8_t | - | temperature at which the MD series motor controller will enter IDLE mode |
mainEncoderErrors | 0x809 | RO | uint32_t | - | main encoder errors |
outputEncoderErrors | 0x80A | RO | uint32_t | - | output encoder errors |
calibrationErrors | 0x80B | RO | uint32_t | - | calibration errors |
bridgeErrors | 0x80C | RO | uint32_t | - | bridge errors |
hardwareErrors | 0x80D | RO | uint32_t | - | hardware errors |
communicationErrors | 0x80E | RO | uint32_t | - | communication errors |
motionErrors | 0x810 | RO | uint32_t | - | motion errors |