Open FFBoard
Open source force feedback firmware
ODriveCAN.cpp
Go to the documentation of this file.
1/*
2 * OdriveCAN.cpp
3 *
4 * Created on: Jun 21, 2021
5 * Author: Yannick
6 */
7#include "target_constants.h"
8#ifdef ODRIVE
9#include <ODriveCAN.h>
10
11bool ODriveCAN1::inUse = false;
13 .name = "ODrive (M0)" ,
14 .id=CLSID_MOT_ODRV0, // 5
15};
16bool ODriveCAN2::inUse = false;
18 .name = "ODrive (M1)" ,
19 .id=CLSID_MOT_ODRV1, // 6
20};
21
22
23
25 return info;
26}
27
29 return info;
30}
31
33 return !ODriveCAN1::inUse; // Creatable if not already in use for example by another axis
34}
35
37 return !ODriveCAN2::inUse; // Creatable if not already in use for example by another axis
38}
39
40ODriveCAN::ODriveCAN(uint8_t id) : CommandHandler("odrv", CLSID_MOT_ODRV0,id), Thread("ODRIVE", ODRIVE_THREAD_MEM, ODRIVE_THREAD_PRIO), motorId(id) {
41
42 if(motorId == 0){
43 nodeId = 0;
44 }else if(motorId == 1){
45 nodeId = 1; // defaults
46 }
48
50
51 if(port->getSpeedPreset() < 3){
52 port->setSpeedPreset(3); // Minimum 250k
53 }
54
55 this->port->setSilentMode(false);
56 this->registerCommands();
57 this->port->takePort();
58 this->Start();
59}
60
62 this->setTorque(0.0);
64 this->port->freePort();
65}
66
68 // Set up a filter to receive odrive commands
69 uint32_t filter_id = (nodeId & 0x3F) << 5;
70 uint32_t filter_mask = 0x07E0;
71
72 CAN_filter filterConf;
73 filterConf.buffer = motorId % 2 == 0 ? 0 : 1;
74 filterConf.filter_id = filter_id;
75 filterConf.filter_mask = filter_mask;
76 this->filterId = this->port->addCanFilter(filterConf);
77}
78
81 registerCommand("canid", ODriveCAN_commands::canid, "CAN id of ODrive",CMDFLAG_GET | CMDFLAG_SET);
82 registerCommand("canspd", ODriveCAN_commands::canspd, "CAN baudrate",CMDFLAG_GET | CMDFLAG_SET);
83 registerCommand("errors", ODriveCAN_commands::errors, "ODrive error flags",CMDFLAG_GET);
84 registerCommand("state", ODriveCAN_commands::state, "ODrive state",CMDFLAG_GET);
85 registerCommand("maxtorque", ODriveCAN_commands::maxtorque, "Max torque to send for scaling",CMDFLAG_GET | CMDFLAG_SET);
86 registerCommand("vbus", ODriveCAN_commands::vbus, "ODrive Vbus",CMDFLAG_GET);
87 registerCommand("anticogging", ODriveCAN_commands::anticogging, "Set 1 to start anticogging calibration",CMDFLAG_SET);
88 registerCommand("connected", ODriveCAN_commands::connected, "ODrive connection state",CMDFLAG_GET);
89 registerCommand("storepos", ODriveCAN_commands::storepos, "Store encoder offset",CMDFLAG_GET | CMDFLAG_SET);
90}
91
93 uint16_t setting1addr = ADR_ODRIVE_SETTING1_M0;
94
95 uint16_t canIds = 0x3040;
96 if(Flash_Read(ADR_ODRIVE_CANID, &canIds)){
97 if(motorId == 0){
98 nodeId = canIds & 0x3f;
99 }else if(motorId == 1){
100 nodeId = (canIds >> 6) & 0x3f;
101 setting1addr = ADR_ODRIVE_SETTING1_M1;
102 }
103
104 }
105
106 uint16_t settings1 = 0;
107 if(Flash_Read(setting1addr, &settings1)){
108 maxTorque = (float)clip(settings1 & 0xfff, 0, 0xfff) / 100.0;
109 uint8_t settings1_2 = (settings1 >> 12) & 0xf;
110 reloadPosAfterStartup = (settings1_2 & 0x1) != 0;
111 }
112
114 int16_t posOfs = 0;
115 if(Flash_Read(motorId == 0 ? ADR_ODRIVE_OFS_M0 : ADR_ODRIVE_OFS_M1, (uint16_t*)&posOfs)){
116 posOffset = (float)posOfs / getCpr();
117 }
118 }
119}
120
122 uint16_t setting1addr = ADR_ODRIVE_SETTING1_M0;
123
124 uint16_t canIds = 0x3040;
125 Flash_Read(ADR_ODRIVE_CANID, &canIds); // Read again
126 if(motorId == 0){
127 canIds &= ~0x3F; // reset bits
128 canIds |= nodeId & 0x3f;
129 }else if(motorId == 1){
130 setting1addr = ADR_ODRIVE_SETTING1_M1;
131 canIds &= ~0xFC0; // reset bits
132 canIds |= (nodeId & 0x3f) << 6;
133 }
134 canIds &= ~0x7000; // reset bits
135
136 Flash_Write(ADR_ODRIVE_CANID,canIds);
137
138 uint16_t settings1 = ((int32_t)(maxTorque*100) & 0xfff);
139 uint8_t settings1_2 = reloadPosAfterStartup ? 1 : 0; // 4 bits
140 settings1 |= (settings1_2 & 0xf) << 12;
141
142 Flash_Write(setting1addr, settings1);
143
145 int32_t posOfs = posOffset * getCpr();
146 Flash_Write(motorId == 0 ? ADR_ODRIVE_OFS_M0 : ADR_ODRIVE_OFS_M1, posOfs);
147 }
148}
149
151 while(true){
152 this->Delay(500);
153
154 switch(state){
159 }
160
161 break;
162
163 // Calibration in progress. wait until its finished to enter torque mode
168
171 }
172 break;
173
176 // Odrive is active,
177 // enable torque mode
180 this->setPos(0); // Assume this as the zero position and let the user correct it
181 }
183 }
184
185 break;
186 default:
187
188 break;
189 }
190
191 // If odrive is currently performing any calibration task wait until finished
195 // If closed loop mode is on assume its ready and already calibrated
199 }
200
201 if(HAL_GetTick() - lastVoltageUpdate > 1000){
202 requestMsg(0x17); // Update voltage
203 }
204
205 if(HAL_GetTick() - lastCanMessage > 2000){ // Timeout
208 waitReady = true;
209 connected = false;
210 }else{
211 connected = true;
212 }
213
214 }
215}
216
218 active = false;
219 this->setTorque(0.0);
222}
223
225 active = true;
228}
229
231 return static_cast<Encoder*>(this);
232}
233
237void ODriveCAN::setPos(int32_t pos){
238 // Only change encoder count internally as offset
239 posOffset = lastPos - ((float)pos / (float)getCpr());
240}
241
242
243void ODriveCAN::requestMsg(uint8_t cmd){
244 CAN_tx_msg msg;
245 msg.header.rtr = true;
246 msg.header.length = 0;
247 msg.header.id = cmd | (nodeId << 5);
248 port->sendMessage(msg);
249}
250
252 // Do not start a new request if already waiting and within timeout
253 if(this->connected && (!posWaiting || HAL_GetTick() - lastPosTime > 5)){
254 posWaiting = true;
255 requestMsg(0x09);
256 }
257 return lastPos-posOffset;
258}
259
262}
264 return getCpr() * getPos_f();
265}
266
267
269 return 0xffff;
270}
271void ODriveCAN::setTorque(float torque){
272 //if(motorReady()){
273 sendMsg<float>(0x0E,torque);
274 //}
275}
276
279}
280
281
283 uint64_t mode = (uint64_t) controlMode | ((uint64_t)inputMode << 32);
284 sendMsg(0x0B,mode);
285}
286
288 sendMsg(0x07,(uint32_t)state);
289}
290
291
292void ODriveCAN::turn(int16_t power){
293 float torque = ((float)power / (float)0x7fff) * maxTorque;
294 this->setTorque(torque);
295}
296
297
302 sendMsg(0x10, (uint8_t)0);
303}
304
305
306CommandStatus ODriveCAN::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
307
308 switch(static_cast<ODriveCAN_commands>(cmd.cmdId)){
310 if(cmd.type == CMDtype::get){
311 replies.emplace_back(lastVoltage*1000);
312 }else{
313 return CommandStatus::ERR;
314 }
315 break;
316
318 if(cmd.type == CMDtype::get){
319 replies.emplace_back((uint32_t)errors);
320 }else{
321 return CommandStatus::ERR;
322 }
323 break;
324
326 handleGetSet(cmd, replies, this->nodeId);
328 setCanFilter();
329 break;
331 if(cmd.type == CMDtype::get){
332 replies.emplace_back((uint32_t)odriveCurrentState);
333 }else{
334 return CommandStatus::ERR;
335 }
336 break;
338 if(cmd.type == CMDtype::get){
339 replies.emplace_back(port->getSpeedPreset());
340 }else if(cmd.type == CMDtype::set){
341 port->setSpeedPreset(std::max<uint8_t>(3,cmd.val));
342 }else{
343 return CommandStatus::ERR;
344 }
345 break;
347 if(cmd.type == CMDtype::set && cmd.val == 1){
348 this->startAnticogging();
349 }else{
350 return CommandStatus::ERR;
351 }
352 break;
354 {
355 if(cmd.type == CMDtype::get){
356 int32_t val = maxTorque*100;
357 replies.emplace_back(val);
358 }else if(cmd.type == CMDtype::set){
359 maxTorque = (float)clip(cmd.val, 0, 0xfff) / 100.0;
360 }else{
361 return CommandStatus::ERR;
362 }
363 break;
364 }
366 if(cmd.type == CMDtype::get){
367 replies.emplace_back(connected ? 1 : 0);
368 }
369 break;
371 if(cmd.type == CMDtype::get){
372 replies.emplace_back(reloadPosAfterStartup ? 1 : 0);
373 }else if(cmd.type == CMDtype::set){
374 reloadPosAfterStartup = cmd.val != 0;
375 }
376 break;
377 default:
379 }
380
381 return CommandStatus::OK;
382
383}
384
385void ODriveCAN::canErrorCallback(CANPort* port,uint32_t errcode){
386 //pulseErrLed();
387}
388
390 uint16_t node = (msg.header.id >> 5) & 0x3F;
391 if(node != this->nodeId){
392 return;
393 }
394 uint64_t msg_int = *reinterpret_cast<uint64_t*>(msg.data);
395 uint8_t cmd = msg.header.id & 0x1F;
396
397 lastCanMessage = HAL_GetTick();
398
399 switch(cmd){
400 case 1:
401 {
402 // TODO error handling
403 errors = (msg_int & 0xffffffff);
404 odriveCurrentState = (ODriveState)( (msg_int >> 32) & 0xff);
405 odriveMotorFlags = (msg_int >> 40) & 0xff;
406 odriveEncoderFlags = ((msg_int >> 48) & 0xff);
407 odriveControllerFlags = (msg_int >> 56) & 0xff;
408
409 if(waitReady){
410 waitReady = false;
412 }
413 break;
414 }
415 case 0x09: // encoder pos float
416 {
417 uint64_t tp = msg_int & 0xffffffff;
418 memcpy(&lastPos,&tp,sizeof(float));
419
420 uint64_t ts = (msg_int >> 32) & 0xffffffff;
421 memcpy(&lastSpeed,&ts,sizeof(float));
422 lastPosTime = HAL_GetTick();
423 posWaiting = false;
424
425 break;
426 }
427
428 case 0x17: // voltage
429 {
430 lastVoltageUpdate = HAL_GetTick();
431 uint64_t t = msg_int & 0xffffffff;
432 memcpy(&lastVoltage,&t,sizeof(float));
433
434 break;
435 }
436
437
438
439 default:
440 break;
441 }
442
443}
444
445#endif
CommandStatus
EncoderType
Definition: Encoder.h:27
ODriveCAN_commands
Definition: ODriveCAN.h:31
ODriveState
Definition: ODriveCAN.h:23
@ AXIS_STATE_ENCODER_OFFSET_CALIBRATION
@ AXIS_STATE_ENCODER_INDEX_SEARCH
@ AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION
@ AXIS_STATE_CLOSED_LOOP_CONTROL
@ AXIS_STATE_MOTOR_CALIBRATION
@ AXIS_STATE_FULL_CALIBRATION_SEQUENCE
@ AXIS_STATE_UNDEFINED
ODriveInputMode
Definition: ODriveCAN.h:25
ODriveControlMode
Definition: ODriveCAN.h:24
uint32_t id
Definition: CAN.h:70
Definition: CAN.h:119
virtual void freePort()
Definition: CAN.h:164
virtual uint8_t getSpeedPreset()=0
virtual void takePort()
Definition: CAN.h:159
virtual void setSpeedPreset(uint8_t preset)=0
virtual int32_t addCanFilter(CAN_filter filter)=0
virtual bool sendMessage(CAN_tx_msg &msg)=0
virtual void removeCanFilter(uint8_t filterId)=0
virtual void setSilentMode(bool silent)=0
void registerCommand(const char *cmd, const ID cmdid, const char *help=nullptr, uint32_t flags=0)
static CommandStatus handleGetSet(const ParsedCommand &cmd, std::vector< CommandReply > &replies, TVal &value)
const ClassIdentifier getInfo()
Command handlers always have class infos. Works well with ChoosableClass.
Definition: ODriveCAN.cpp:24
static bool isCreatable()
Definition: ODriveCAN.cpp:32
static ClassIdentifier info
Definition: ODriveCAN.h:141
static bool inUse
Definition: ODriveCAN.h:142
static ClassIdentifier info
Definition: ODriveCAN.h:155
static bool isCreatable()
Definition: ODriveCAN.cpp:36
static bool inUse
Definition: ODriveCAN.h:156
const ClassIdentifier getInfo()
Command handlers always have class infos. Works well with ChoosableClass.
Definition: ODriveCAN.cpp:28
int8_t nodeId
Definition: ODriveCAN.h:111
void canRxPendCallback(CANPort *port, CAN_rx_msg &msg) override
Definition: ODriveCAN.cpp:389
void requestMsg(uint8_t cmd)
Definition: ODriveCAN.cpp:243
bool active
Definition: ODriveCAN.h:124
void setState(ODriveState state)
Definition: ODriveCAN.cpp:287
uint32_t lastVoltageUpdate
Definition: ODriveCAN.h:104
float lastSpeed
Definition: ODriveCAN.h:101
volatile ODriveLocalState state
Definition: ODriveCAN.h:127
void stopMotor() override
Definition: ODriveCAN.cpp:217
int32_t filterId
Definition: ODriveCAN.h:126
void setTorque(float torque)
Definition: ODriveCAN.cpp:271
void setPos(int32_t pos) override
Definition: ODriveCAN.cpp:237
float posOffset
Definition: ODriveCAN.h:102
void Run()
Definition: ODriveCAN.cpp:150
void startAnticogging()
Definition: ODriveCAN.cpp:301
ODriveCAN(uint8_t id)
Definition: ODriveCAN.cpp:40
void saveFlash() override
Definition: ODriveCAN.cpp:121
CANPort * port
Definition: ODriveCAN.h:99
void setCanFilter()
Definition: ODriveCAN.cpp:67
void restoreFlash() override
Definition: ODriveCAN.cpp:92
void turn(int16_t power) override
Definition: ODriveCAN.cpp:292
void canErrorCallback(CANPort *port, uint32_t errcode)
Definition: ODriveCAN.cpp:385
volatile uint32_t odriveMotorFlags
Definition: ODriveCAN.h:117
int32_t getPos() override
Definition: ODriveCAN.cpp:263
float getPos_f() override
Definition: ODriveCAN.cpp:251
void registerCommands()
Definition: ODriveCAN.cpp:79
uint32_t lastCanMessage
Definition: ODriveCAN.h:105
float lastVoltage
Definition: ODriveCAN.h:103
Encoder * getEncoder() override
Definition: ODriveCAN.cpp:230
float maxTorque
Definition: ODriveCAN.h:121
bool connected
Definition: ODriveCAN.h:128
void startMotor() override
Definition: ODriveCAN.cpp:224
bool reloadPosAfterStartup
Definition: ODriveCAN.h:109
int8_t motorId
Definition: ODriveCAN.h:112
virtual ~ODriveCAN()
Definition: ODriveCAN.cpp:61
void sendMsg(uint8_t cmd, T value)
Definition: ODriveCAN.h:52
bool posWaiting
Definition: ODriveCAN.h:108
volatile bool waitReady
Definition: ODriveCAN.h:122
volatile uint32_t odriveEncoderFlags
Definition: ODriveCAN.h:118
bool motorReady() override
Definition: ODriveCAN.cpp:260
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies) override
Definition: ODriveCAN.cpp:306
void setMode(ODriveControlMode controlMode, ODriveInputMode inputMode)
Definition: ODriveCAN.cpp:282
uint32_t lastPosTime
Definition: ODriveCAN.h:107
volatile ODriveState odriveCurrentState
Definition: ODriveCAN.h:114
EncoderType getEncoderType() override
Definition: ODriveCAN.cpp:277
uint32_t getCpr() override
Definition: ODriveCAN.cpp:268
float lastPos
Definition: ODriveCAN.h:100
volatile uint32_t errors
Definition: ODriveCAN.h:115
volatile uint32_t odriveControllerFlags
Definition: ODriveCAN.h:119
void Delay(const TickType_t Delay)
Definition: thread.hpp:352
T clip(T v, C l, C h)
Definition: cppmain.h:58
bool Flash_Write(uint16_t adr, uint16_t dat)
bool Flash_Read(uint16_t adr, uint16_t *buf, bool checkempty=true)
static void * memcpy(void *dst, const void *src, size_t n)
Definition: ringbuffer.c:8
uint32_t buffer
Definition: CAN.h:110
uint32_t filter_mask
Definition: CAN.h:109
uint32_t filter_id
Definition: CAN.h:108
bool rtr
Definition: CAN.h:63
uint32_t id
Definition: CAN.h:60
uint32_t length
Definition: CAN.h:61
Definition: CAN.h:96
uint8_t data[CAN_MSGBUFSIZE]
Definition: CAN.h:97
CAN_msg_header_rx header
Definition: CAN.h:99
Definition: CAN.h:89
CAN_msg_header_tx header
Definition: CAN.h:92
const char * name
uint32_t cmdId