Open FFBoard
Open source force feedback firmware
RmdMotorCAN.cpp
Go to the documentation of this file.
1/*
2 * RmdMotorCAN.cpp
3 *
4 * Created on: Dec 7, 2024
5 * Author: Yannick
6 */
7
8
9#include "target_constants.h"
10#ifdef RMDCAN
11#include "RmdMotorCAN.h"
12
13bool RmdMotorCAN1::inUse = false;
15 .name = "RMD MyActuator (1)" ,
16 .id=CLSID_MOT_RMD1, // 11
17};
18bool RmdMotorCAN2::inUse = false;
20 .name = "RMD MyActuator (2)" ,
21 .id=CLSID_MOT_RMD2, // 12
22};
23
25 return info;
26}
27
29 return info;
30}
31
33 return !RmdMotorCAN1::inUse; // Creatable if not already in use for example by another axis
34}
35
37 return !RmdMotorCAN2::inUse; // Creatable if not already in use for example by another axis
38}
39
40
41
42RmdMotorCAN::RmdMotorCAN(uint8_t instance) : CommandHandler("rmd", CLSID_MOT_RMD1,instance), Thread("RMD", RMD_THREAD_MEM, RMD_THREAD_PRIO), motorId(instance) {
43 if(motorId == 0){
44 nodeId = 1;
45 }else if(motorId == 1){
46 nodeId = 2; // defaults
47 }
50
51 if(port->getSpeedPreset() < 3){
52 port->setSpeedPreset(3); // Minimum 250k. Default 1M
53 }
54
55 this->port->setSilentMode(false);
56 this->registerCommands();
57 this->port->takePort();
58 this->Start();
59}
60
63 stopMotor();
64}
65
66
68 uint16_t data = 0;
69 if(Flash_Read(motorId == 0 ? ADR_RMD1_DATA1 : ADR_RMD2_DATA1, &data)){
70 this->nodeId = (data & 0x1F) + 1; // Valid ID 1-32
71 // Bits 7-15 free
72 this->activerequests = (data >> 6) & 0x1;
73 }
74 if(Flash_Read(motorId == 0 ? ADR_RMD1_TORQUE : ADR_RMD2_TORQUE, &data)){
75 maxTorque = data & 0x7FFF;
76 // Bit 15 free
77 }
78
79 // Offset. Could be stored in motor but does not work reliably
80 if(Flash_Read(motorId == 0 ? ADR_RMD1_OFFSET : ADR_RMD2_OFFSET, &data)){
81 posOffset = (int16_t)(data);
82 }
83
84}
86 // Save CAN ID and max torque
87 uint16_t data1 = (this->nodeId-1) & 0x1F;
88 data1 |= this->activerequests ? 1 << 6 : 0;
89 Flash_Write(motorId == 0 ? ADR_RMD1_DATA1 : ADR_RMD2_DATA1,data1);
90 uint16_t torquedat = maxTorque & 0x7FFF;
91 Flash_Write(motorId == 0 ? ADR_RMD1_TORQUE : ADR_RMD2_TORQUE, torquedat);
92
93 int32_t offset = (posOffset % (posOffset >= 0 ? 36000 : -36000));
94
95 Flash_Write(motorId == 0 ? ADR_RMD1_OFFSET : ADR_RMD2_OFFSET, (uint16_t)offset);
96}
97
99// bool first = true;
100 Delay(100);
101 while(true){
102 nextAvailable = false;
103 if(!available){
104 sendCmd(0xB5); // Get type
105 Delay(20);
106 }
107
108 if(activerequests){
109 updateStatus();
110 }else{
112 // Setup
113 requestConstantReports(0x92, true, 0); // Enable constant position sending
115 Delay(150);
116 }
117 }
118 Delay(1000);
119 available = nextAvailable; // should have received some replies
120 }
121}
122
124 if(filterId >= 0){
126 }
127 nodeId = std::min<uint8_t>(nodeId,32);
128 uint32_t filter_id = (nodeId + 0x240); // Reply
129 uint32_t filter_mask = 0x3FF;
130
131 CAN_filter filterConf;
132 filterConf.buffer = motorId % 2 == 0 ? 0 : 1;
133 filterConf.filter_id = filter_id;
134 filterConf.filter_mask = filter_mask;
135 this->filterId = this->port->addCanFilter(filterConf);
136}
137
139 active = false;
140 this->setTorque(0);
141 sendCmd(0x80); // Disable motor
142}
143
145 if(lastErrors.asInt == 0){ // Only allow enabling if no errors to prevent reenabling after failure
146 active = true;
147 setTorque(0); // Enable torque mode, no torque
148 }
149}
150
152 return static_cast<Encoder*>(this);
153}
154
156
157 return lastErrors.asInt == 0 && available; // Ping motor state
158}
159
160
162 return 36000;
163}
164
166 if(activerequests && HAL_GetTick() - lastAngleUpdate > angleUpdateMs){
167 // pos outdated. Should be sent without request
168 sendCmd(0x92); // request multiturn pos 0x92, 0x60
169 }
170 return (lastPos * 100) - posOffset;
171}
172
173void RmdMotorCAN::setPos(int32_t pos){
174 posOffset = (lastPos*100) - pos;
175}
176
177void RmdMotorCAN::sendMsg(std::array<uint8_t,8> &data,uint8_t len){
178 CAN_tx_msg msg;
179 memcpy(&msg.data,data.data(),std::min<uint8_t>(data.size(), CAN_MSGBUFSIZE));
180 msg.header.id = this->nodeId + 0x140;
181 msg.header.length = len;
182 if(!port->sendMessage(msg)){
183 // Nothing
184 }
185}
186
187void RmdMotorCAN::sendCmd(uint8_t cmd){
188 std::array<uint8_t,8> data = {0};
189 data[0] = cmd;
190 sendMsg(data);
191}
192
193
199 sendCmd(0x9A);
200}
201
205void RmdMotorCAN::setTorque(int16_t torque){
206 std::array<uint8_t,8> data{0xA1,0,0,0,(uint8_t)(torque & 0xff),(uint8_t)((torque >> 8) & 0xff),0,0};
207 sendMsg(data);
208}
209
210void RmdMotorCAN::turn(int16_t power){
211 if(!active || !available){
212 return;
213 }
214 int16_t torque = ((float)power / (float)0x7fff) * maxTorque;
216}
217
218
221}
222
224 if(errors.asInt){
225 stopMotor();
226 }
227}
228
233void RmdMotorCAN::requestConstantReports(uint8_t cmd,bool enable,uint16_t interval_10ms){
234
235 //0xB6 0x92 0x01 0x01 0x00 0x00 0x00 0x00 reports position
236 std::array<uint8_t,8> data{0xB6,cmd,(uint8_t)(enable & 1),(uint8_t)(interval_10ms & 0xff),(uint8_t)((interval_10ms >> 8) & 0xff),0,0,0};
237 sendMsg(data);
238}
239
241 if(msg.header.id != 0x240u + nodeId){ // Filter if message is a reply for this motor
242 return;
243 }
244 nextAvailable = true; // Got a response
245 uint8_t cmd = msg.data[0];
246 switch(cmd){
247 case 0xA1: // Torque reply
248 {
249 curTemp = msg.data[1];
250 curCurrent = (msg.data[2] | (msg.data[3] << 8)); // in 0.01A
251 int16_t speed = (msg.data[4] | (msg.data[5] << 8));
252 int16_t angle = (msg.data[6] | (msg.data[7] << 8)); // Position in 1°
253 uint32_t ustime = micros();
255 float interpolatedPos = speed / (1000000.0 * (ustime - lastTorqueStatusUpdate_us));
256 lastPos = (lastPos + (angle + interpolatedPos)) / 2; // Interpolate due to low resolution
257 }
259
260// lastPos = (lastPos + angle) / 2; // Interpolate due to low resolution
261// lastPos_torquereply = angle;
262 break;
263 }
264
265 case 0x92: // Multi turn angle in 0.01°
266 {
267 int32_t pos = (msg.data[4] | (msg.data[5] << 8) | (msg.data[6] << 16) | (msg.data[7] << 24));
268 lastAngleUpdate = HAL_GetTick();
269 lastPos = pos * 0.01;
270 break;
271 }
272 case 0x94: // Multi turn angle in 0.01°
273 {
274 int32_t pos = (msg.data[6] | (msg.data[7] << 8));
275 lastAngleUpdate = HAL_GetTick();
276 lastPos = (lastPos / 360) + (pos * 0.01);
277 break;
278 }
279// case 0x60: // Multi turn pos in enc counts
280// {
281// int32_t pos = (msg.data[4] | (msg.data[5] << 8) | (msg.data[6] << 16) | (msg.data[7] << 24));
282// lastAngleUpdate = HAL_GetTick();
283// break;
284// }
285
286 case 0x9A: // Status 1
287 {
288 curTemp = msg.data[1];
289 curVoltage = msg.data[4] | (msg.data[5] << 8);
290 lastErrors.asInt = msg.data[6] | (msg.data[7] << 8);
292 break;
293 }
294
295 case 0xB5: // Model name
296 {
297 memcpy(this->modelName,msg.data+1,7);
298 break;
299 }
300
301 }
302
303}
304
305void RmdMotorCAN::updateRequestMode(bool activerequests){
306 if(activerequests == this->activerequests){
307 return; // Nothing to do
308 }
309
310 if(activerequests){
311 // Disable constant replies
312// requestConstantReports(0x9A, false, 0); // Disable constant status sending
313 requestConstantReports(0x92, false, 0); // Disable constant position sending
314
315 }else{
317// requestConstantReports(0x92, true, 0); // Enable constant position sending
318 }
319 this->activerequests = activerequests;
320}
321
324 registerCommand("canid", RmdCAN_commands::canid, "CAN id of motor",CMDFLAG_GET | CMDFLAG_SET);
325 registerCommand("errors", RmdCAN_commands::errors, "Error flags",CMDFLAG_GET);
326 registerCommand("maxtorque", RmdCAN_commands::maxtorque, "Maximum motor current in 0.01A (When activerequests on)",CMDFLAG_GET | CMDFLAG_SET);
327 registerCommand("curr", RmdCAN_commands::current, "Current in 0.01A (When activerequests on)",CMDFLAG_GET);
328 registerCommand("temp", RmdCAN_commands::temperature, "Temperature in °C (When activerequests on)",CMDFLAG_GET);
329 registerCommand("vbus", RmdCAN_commands::voltage, "Voltage in 0.1V (When activerequests on)",CMDFLAG_GET);
330 registerCommand("requestpos", RmdCAN_commands::activerequests, "1 to send active position requests for higher rates",CMDFLAG_GET | CMDFLAG_SET);
331 registerCommand("model", RmdCAN_commands::modelname, "Name of motor",CMDFLAG_GET | CMDFLAG_STR_ONLY);
332}
333
334CommandStatus RmdMotorCAN::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
335
336 switch(static_cast<RmdCAN_commands>(cmd.cmdId)){
338 if(cmd.type == CMDtype::get){
339 replies.emplace_back(this->nodeId);
340 }else if(cmd.type == CMDtype::set){
341 if(cmd.val != this->nodeId){
342 this->nodeId = cmd.val;
343 setCanFilter(); // Removes previous filter if set automatically
344 }
345 }else{
346 return CommandStatus::ERR;
347 }
348 break;
349
351 handleGetSet(cmd, replies, this->maxTorque);
352 break;
354 if(cmd.type == CMDtype::get){
355 replies.emplace_back(curCurrent);
356 }else{
357 return CommandStatus::ERR;
358 }
359 break;
360
362 if(cmd.type == CMDtype::get){
363 replies.emplace_back(curTemp);
364 }else{
365 return CommandStatus::ERR;
366 }
367 break;
368
370 if(cmd.type == CMDtype::get){
371 replies.emplace_back(lastErrors.asInt);
372 }else{
373 return CommandStatus::ERR;
374 }
375 break;
376
378 if(cmd.type == CMDtype::get){
379 replies.emplace_back(curVoltage);
380 }else{
381 return CommandStatus::ERR;
382 }
383 break;
384
386 if(cmd.type == CMDtype::get){
387 replies.emplace_back(this->modelName);
388 }else{
389 return CommandStatus::ERR;
390 }
391 break;
392
394 if(cmd.type == CMDtype::get){
395 replies.emplace_back(activerequests);
396 }else if(cmd.type == CMDtype::set){
398 }else{
399 return CommandStatus::ERR;
400 }
401 break;
402
403 default:
405 }
406
407 return CommandStatus::OK;
408
409}
410
411
412#endif
CommandStatus
EncoderType
Definition: Encoder.h:27
RmdCAN_commands
Definition: RmdMotorCAN.h:24
uint32_t id
Definition: CAN.h:70
Definition: CAN.h:119
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)
static ClassIdentifier info
Definition: RmdMotorCAN.h:127
static bool inUse
Definition: RmdMotorCAN.h:128
static bool isCreatable()
Definition: RmdMotorCAN.cpp:32
const ClassIdentifier getInfo()
Command handlers always have class infos. Works well with ChoosableClass.
Definition: RmdMotorCAN.cpp:24
static ClassIdentifier info
Definition: RmdMotorCAN.h:140
static bool isCreatable()
Definition: RmdMotorCAN.cpp:36
const ClassIdentifier getInfo()
Command handlers always have class infos. Works well with ChoosableClass.
Definition: RmdMotorCAN.cpp:28
static bool inUse
Definition: RmdMotorCAN.h:141
const uint32_t angleUpdateMs
Definition: RmdMotorCAN.h:67
bool requestConstantReportEnable
Definition: RmdMotorCAN.h:94
uint8_t curTemp
Definition: RmdMotorCAN.h:107
uint8_t nodeId
Definition: RmdMotorCAN.h:88
bool nextAvailable
Definition: RmdMotorCAN.h:91
RmdMotorCAN(uint8_t instance)
Definition: RmdMotorCAN.cpp:42
void sendCmd(uint8_t cmd)
int32_t getPos() override
uint32_t lastAngleUpdate
Definition: RmdMotorCAN.h:98
uint16_t curVoltage
Definition: RmdMotorCAN.h:108
void sendMsg(std::array< uint8_t, 8 > &data, uint8_t len=8)
virtual ~RmdMotorCAN()
Definition: RmdMotorCAN.cpp:61
uint32_t getCpr() override
bool motorReady()
void registerCommands()
uint32_t lastTorqueStatusUpdate_us
Definition: RmdMotorCAN.h:99
void updateStatus()
int32_t posOffset
Definition: RmdMotorCAN.h:112
uint16_t maxTorque
Definition: RmdMotorCAN.h:102
void stopMotor() override
void canRxPendCallback(CANPort *port, CAN_rx_msg &msg) override
CANPort * port
Definition: RmdMotorCAN.h:87
void turn(int16_t power) override
void saveFlash() override
Definition: RmdMotorCAN.cpp:85
bool activerequests
Definition: RmdMotorCAN.h:93
ErrorStatus lastErrors
Definition: RmdMotorCAN.h:110
void updateRequestMode(bool activerequests)
void errorCb(ErrorStatus &errors)
void setPos(int32_t pos) override
float lastPos
Definition: RmdMotorCAN.h:101
bool available
Definition: RmdMotorCAN.h:92
void setTorque(int16_t torque)
void requestConstantReports(uint8_t cmd, bool enable, uint16_t interval_10ms)
void startMotor() override
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies) override
char modelName[8]
Definition: RmdMotorCAN.h:96
void setCanFilter()
Encoder * getEncoder() override
uint8_t motorId
Definition: RmdMotorCAN.h:89
int16_t curCurrent
Definition: RmdMotorCAN.h:106
int32_t filterId
Definition: RmdMotorCAN.h:104
EncoderType getEncoderType() override
void restoreFlash() override
Definition: RmdMotorCAN.cpp:67
void Delay(const TickType_t Delay)
Definition: thread.hpp:352
uint32_t micros()
Definition: cppmain.cpp:116
static struct @612 data
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
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
uint8_t data[CAN_MSGBUFSIZE]
Definition: CAN.h:90
const char * name
uint32_t cmdId