Open FFBoard
Open source force feedback firmware
MotorSimplemotion.cpp
Go to the documentation of this file.
1/*
2 * MotorSimplemotion.cpp
3 *
4 * Created on: Jan 9, 2023
5 * Author: Yannick
6 */
7
8#include "target_constants.h"
9#ifdef SIMPLEMOTION
10#include "MotorSimplemotion.h"
11#include "CRC.h"
12#include "cpp_target_config.h"
13#include "array"
14
16std::array<uint8_t,256> MotorSimplemotion::tableCRC8 __attribute__((section (".ccmram")));
17std::array<uint16_t,256> MotorSimplemotion::tableCRC16 __attribute__((section (".ccmram")));
18
19bool MotorSimplemotion1::inUse = false;
21 .name = "Simplemotion 1" ,
22 .id=CLSID_MOT_SM1
23};
24bool MotorSimplemotion2::inUse = false;
26 .name = "Simplemotion 2" ,
27 .id=CLSID_MOT_SM2
28};
29
30MotorSimplemotion::MotorSimplemotion(uint8_t instance) : CommandHandler("sm2", CLSID_MOT_SM1, instance),UARTDevice(motor_uart), address(address+1){
31 //Init CRC table at runtime to save flash
33 makeCrcTable(tableCRC8, crcpoly, 8); // Generate a CRC8 table the first time an instance is created
34 makeCrcTable(tableCRC16, crcpoly16, 16,true,true); // Make a CRC16 table
36 }
37
38 // Set up uart port
39 UART_InitTypeDef uartconf;
40 uartconf.BaudRate = 460800;
41 uartconf.WordLength = UART_WORDLENGTH_8B;
42 uartconf.StopBits = UART_STOPBITS_1;
43 uartconf.Parity = UART_PARITY_NONE;
44 uartconf.Mode = UART_MODE_TX_RX;
45 uartconf.HwFlowCtl = UART_HWCONTROL_NONE;
46 uartconf.OverSampling = UART_OVERSAMPLING_8;
47 uartport->reconfigurePort(uartconf);
49
51 //getSettings();
52}
53
55
56}
57
58
63 // If not initialized try that instead
64 if(waitingReply){ // || (uartport->isTaken() && !waitingFastUpdate)
65 return; // When we wait for a complex reply we don't start the fast update. Should also wait if port was taken by another class but needs more testing
66 }
67 if(!initialized){
68 if(!getSettings()){
69 return;
70 }
71 }
72 if((HAL_GetTick()-lastSentTime>10 && waitingFastUpdate) || (HAL_GetTick() - lastTimeByteReceived > uartErrorTimeout && uartport->isTaken())){
73// uartport->abortReceive();
74// uarterrors++;
75 uartErrorOccured = true;
76 waitingFastUpdate = false;
77// uartport->giveSemaphore(true); // Force giving semaphore here so we don't cause a block. This may not be ideal
78 }
79
81 uartErrorOccured = true;
82 return; // Could not take port
83 }
85 fastbuffer.adr = this->address;
89
90 if(!uartport->transmit_IT((char*)(&fastbuffer), 7)) // Send update
91 {
92 endUartTransfer(uartport, true); // transfer aborted
94 return;
95 }
96 uartport->registerInterrupt(); // Wait for reply data
97 lastSentTime = HAL_GetTick();
98 waitingFastUpdate = true;
99}
100
101void MotorSimplemotion::turn(int16_t power){
103 sendFastUpdate((uint16_t)power, 0);
104}
105
107 return static_cast<Encoder*>(this);
108}
109
110
115 if(HAL_GetTick()-lastUpdateTime>100){
116 this->turn(lastTorque); // Sending torque updates the position
117 }
118 return position - position_offset;
119}
120
123}
124
129 if(cpr == 0 || !initialized){
130 getSettings();
131 }
132 return this->cpr;
133}
134
136
137 return initialized && !hardfault;
138}
139
141 uint32_t stat = 0;
144 }
145 return stat;
146}
147
149 if(HAL_GetTick() - lastSentTime < 150){
150 return false;
151 }
152 bool status = true;
153
154
155// if(getCumstat()){
156// pulseErrLed();
157// }
158
159 uint32_t devtype;
161 this->devicetype = devtype;
162 }else{
163 status = false;
164 }
165
166 // Clearfaults
168
169
170 uint32_t st;
172 this->status = st;
173 }else{
174 status = false;
175 }
176 if(!st){
177 return false; // If we can't get a status we have no connection
178 }
179
180 uint32_t tcpr = 0;
182 status=false;
183 }
184
185 uint32_t fbd;
188 switch(this->encodertype){
190 case MotorSimplemotion_FBR::Hall: // TODO check if this is right for hall, resolver and sincos. Seems like it also must be *4 for serial
194 this->cpr = tcpr*4;
195 break;
196
198 this->cpr = tcpr*16;
199 break;
201 this->cpr = tcpr*64;
202 break;
204 this->cpr = tcpr*256;
205 break;
206 default:
208 hardfault = true;
209 status = false;
210 }
211
212 }else{status=false;}
213
214 uint32_t cm; // This is the last transfer with length specified. all future requests with no length will reply in this format
216 if(cm != 3){
217 status = false; // control mode must be torque mode
218 }
219 }else{status=false;}
220
221
223 return status;
224}
225
227 std::array<MotorSimplemotion_param,1> paramIds = {paramId};
228 std::array<uint32_t*,1> replies = {reply_p};
229 return readParameter(paramIds, replies,replylen);
230}
231
232bool MotorSimplemotion::set1Parameter(MotorSimplemotion_param paramId,int32_t value,uint32_t* reply_p){
233 std::array<std::pair<MotorSimplemotion_param,int32_t>,1> paramIds = {std::make_pair(paramId,value)};
234 std::array<uint32_t*,1> replies = {reply_p};
236}
237
238// In mV
240 uint32_t voltage;
242 return voltage * 10;
243 }
244 return 0;
245}
246
247// Torque in mA
249 uint32_t torque_u;
251 return ((int16_t)torque_u * 1000) / 560;
252 }
253 return 0;
254}
255
257 hardfault = false;
259}
260
262 // Position is in raw encoder counts but overflows between 0 and 0xffff.
263 value += 0x7fff; // Shift half range so we don't immediately overflow and cause a glitch
264 int32_t diff = value-lastPosRep;
265 if(abs(diff) > 0x7fff){
266 // Overflow likely
267 if(diff < 0)
268 overflows += 1;
269 else
270 overflows -= 1;
271 }
272 position = value + (0xffff * overflows) - 0x7fff;
273 lastPosRep = value;
274 lastUpdateTime = HAL_GetTick();
275}
276
278 status = value;
279 lastStatusTime = HAL_GetTick();
280 hardfault = (value & (1 << 5));
281 if(!(value & (1 << 12))){
282 initialized = false; // Driver has an issue
283 }
284}
285
291// uartport->abortReceive();
292 resetBuffer();
293 }
294 return uartport->takeSemaphore(true,10);
295}
296
300bool MotorSimplemotion::sendCommand(uint8_t* buf,uint8_t len,uint8_t adr){
301 if(!prepareUartTransmit()){
302 return false; // Failed
303 }
305 txbuf[1] = len;
306 txbuf[2] = adr;
307 memcpy(txbuf+3,buf,len);
308 uint16_t crc = calculateCrc16_8_rev(tableCRC16,(uint8_t*)txbuf,len+3,0);
309 txbuf[3+len] = (crc >> 8) & 0xff;
310 txbuf[4+len] = (crc) & 0xff;
311
312 if(uartport->transmit_IT((char*)(txbuf), len+5) && adr != 0) // Send update and wait for reply if not broadcasted
313 {
314 lastSentTime = HAL_GetTick();
315 waitingReply = true;
316 uartport->registerInterrupt(); // Listen for reply
317 return true;
318 }else{
319 endUartTransfer(uartport, true); // Transfer aborted
320 uartport->giveSemaphore(true);
321 return false;
322 }
323
324}
325
332 uint8_t len = 4 - ((uint8_t)type & 0x3);
333 buf[0] = (((uint8_t)type) & 0x3) << (6); // First 2 bits
334 data &= 0x3FFFFFFF; // Make sure first 2 bits are masked
335 for(uint8_t b = 0;b<len;b++){
336 buf[b] |= (data >> (8 * (len-1-b))) & 0xff; // copy data into buffer
337 }
338 return len;
339}
340
345 memset((char*)rxbuf,0,RXBUF_SIZE);
346 rxbuf_i = 0;
347 waitingReply = false; // We failed if it was reset early
348 waitingFastUpdate = false;
349 uartErrorOccured = false;
350 uartport->giveSemaphore(true); // When the buffer is reset we are allowed to transmit again
351}
352
354 uint32_t errorcodes = uartport->getErrors();
355 lastTimeByteReceived = HAL_GetTick();
356
357 // Append to buffer while not overrun
358 if(rxbuf_i < RXBUF_SIZE && !errorcodes){
359 rxbuf[rxbuf_i++] = buf;
360 }else{
361 // Overrun
362 uarterrors++;
363 uartErrorOccured = true;
364// resetBuffer();
365 /* We should actually NOT give back the semaphore immediately because data may still be sent by the device.
366 * Instead it should only be reset after a timeout...
367 */
368 return;
369 }
370 // Check if we can parse a command
371 char byte1 = rxbuf[0];
372
373 if(waitingFastUpdate && byte1 == SMCMD_FAST_UPDATE_CYCLE_RET && rxbuf_i == 6) // Fast update reply
374 {
375 // We know the size of the fast update
376 waitingFastUpdate = false;
377
378 if(calculateCrc8(tableCRC8, (uint8_t*)rxbuf, 6, crc8init) != 0){
379 uartErrorOccured = true;
380 crcerrors++;
381 resetBuffer();
382 return;
383 }
384 Sm2FastUpdate_reply packet;
385 memcpy(&packet,(char*)rxbuf,6);
386 resetBuffer(); // Done
387 updatePosition(packet.val1);
388 updateStatus(packet.val2);
389
390 return;
391 }
392 else if(waitingReply && byte1 == SMCMD_INSTANT_CMD_RET && rxbuf_i > 5) // Standard reply
393 {
394
395 uint8_t len = rxbuf[1];
396 uint8_t adr = rxbuf[2];
397 if(rxbuf_i < len+5){ // minimum 6 bytes, 3 header, 2 checksum + len * data
398 uartport->registerInterrupt(); // Wait for next byte if nothing can be parsed yet
399 return; // Not yet ready
400 }
401 if(adr != this->address){ // Intended for a different device
402 resetBuffer();
403 return;
404 }
405
406 // check that crc is 0
407 if(calculateCrc16_8_rev(tableCRC16, (uint8_t*)rxbuf, rxbuf_i, 0)){
408 crcerrors++;
409 uartErrorOccured = true;
410 resetBuffer();
411 return;
412 }
413 // Frame should be valid from here on
414 uint8_t i = 0;
415 char* data = (char*)(rxbuf+3);
416 replyidx = 0;
417 while(i < len && replyidx < REPLYBUF_SIZE){
418 uint8_t subpacketlen = 4 - ((data[i] >> 6) & 0x3); // MotorSimplemotion_cmdtypes
419 uint32_t val = (data[i] & 0x3f) << ((subpacketlen-1)*8); // First byte contains upper 6 bits
420 for(uint8_t b = 1;b < subpacketlen;b++){
421 uint32_t data_t = (uint32_t)data[i+b];
422 val |= data_t << ((subpacketlen-1-b)*8); // Copy next bytes in reverse
423 }
424 // Pad leading bits for negative values
425// val |= ~((1 << (subpacketlen*8))-1); // 8*bytes -1
426 replyvalues[replyidx++] = val;
427 i += subpacketlen;
428 }
429 waitingReply = false;
430 resetBuffer();
431 return;
432 }
433 else
434 {
435 uartport->registerInterrupt(); // Wait for next byte if nothing can be parsed yet
436 }
437
438}
439
440
443}
444
446 turn(0);
448}
449
452}
453
455 // Semaphore must be taken before sending to lock temporary buffers
456 if(transmit){
458 }else{
459 port->takeSemaphore(transmit);
460 }
461}
463 // Disable write pin
464 if(transmit){
466 }else{
467 port->giveSemaphore(transmit); // Only give semaphore for receive action. We are only allowed to transmit again after the receive buffer is processed
468 }
469
470}
471
474
475 registerCommand("crcerr", MotorSimplemotion_commands::crcerrors, "CRC error count",CMDFLAG_GET);
476 registerCommand("uarterr", MotorSimplemotion_commands::uarterrors, "UART error count",CMDFLAG_GET);
477 registerCommand("voltage", MotorSimplemotion_commands::voltage, "Voltage in mV",CMDFLAG_GET);
478 registerCommand("torque", MotorSimplemotion_commands::torque, "Torque in mA",CMDFLAG_GET);
479 registerCommand("state", MotorSimplemotion_commands::status, "Status flags",CMDFLAG_GET);
480 registerCommand("restart", MotorSimplemotion_commands::restart, "Restart driver",CMDFLAG_GET);
481 registerCommand("reg", MotorSimplemotion_commands::reg, "Read/Write raw register",CMDFLAG_GETADR | CMDFLAG_SETADR | CMDFLAG_DEBUG);
482 registerCommand("devtype", MotorSimplemotion_commands::devtype, "Device type",CMDFLAG_GET);
483}
484
485CommandStatus MotorSimplemotion::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
486 switch(static_cast<MotorSimplemotion_commands>(cmd.cmdId)){
488 replies.emplace_back(this->crcerrors);
489 break;
491 replies.emplace_back(this->uarterrors);
492 break;
494 replies.emplace_back(getVoltage());
495 break;
497 replies.emplace_back(getTorque());
498 break;
500 replies.emplace_back(status);
501 break;
503 replies.emplace_back(devicetype);
504 break;
506 restart();
507 break;
509 if(cmd.type == CMDtype::getat){
510 uint32_t t=0;
512 replies.emplace_back((int32_t)t);
513 }else if(cmd.type == CMDtype::setat){
514 uint32_t t=0;
516 replies.emplace_back((int32_t)t);
517 }
518 break;
519 }
520
521 default:
523 }
524 return CommandStatus::OK;
525
526}
527
528#endif
void makeCrcTable(std::array< T, LEN > &table, const T crcpoly, const uint8_t bits, const bool refin=false, const bool refout=false)
Definition: CRC.h:36
uint16_t calculateCrc16_8_rev(std::array< uint16_t, 256 > &crctable, uint8_t *buf, uint16_t len, uint16_t crc=0)
Definition: CRC.cpp:33
uint8_t calculateCrc8(std::array< uint8_t, 256 > &crctable, uint8_t *buf, uint16_t len, uint8_t crc=0)
Definition: CRC.cpp:13
CommandStatus
EncoderType
Definition: Encoder.h:27
std::array< uint8_t, 256 > MotorSimplemotion::tableCRC8 __attribute__((section(".ccmram")))
uint16_t val1
uint8_t adr
uint8_t crc
uint16_t val2
void registerCommand(const char *cmd, const ID cmdid, const char *help=nullptr, uint32_t flags=0)
uint32_t cpr
Definition: Encoder.h:53
static void addError(const Error &error)
static ClassIdentifier info
static ClassIdentifier info
static const uint8_t SMCMD_INSTANT_CMD_RET
void startMotor() override
volatile uint32_t lastUpdateTime
void uartRcv(char &buf)
static bool crcTableInitialized
void turn(int16_t power) override
volatile bool uartErrorOccured
EncoderType getEncoderType() override
volatile bool waitingReply
void startUartTransfer(UARTPort *port, bool transmit)
volatile uint32_t lastSentTime
const OutputPin & writeEnablePin
volatile uint32_t crcerrors
volatile uint32_t uarterrors
static const uint32_t uartErrorTimeout
volatile uint32_t lastStatusTime
void stopMotor() override
MotorSimplemotion_FBR encodertype
static const uint16_t crcpoly16
volatile uint8_t replyidx
static const uint8_t SMCMD_INSTANT_CMD
static const uint8_t crc8init
static std::array< uint8_t, 256 > tableCRC8
volatile uint32_t replyvalues[REPLYBUF_SIZE]
static const uint8_t SMCMD_FAST_UPDATE_CYCLE
volatile char rxbuf[RXBUF_SIZE]
void updateStatus(uint16_t value)
uint8_t queueCommand(uint8_t *buf, MotorSimplemotion_cmdtypes type, uint32_t data)
char txbuf[TXBUF_SIZE]
volatile uint32_t lastTimeByteReceived
bool writeParameter(std::array< std::pair< MotorSimplemotion_param, int32_t >, params > paramIds_value, std::array< uint32_t *, replynum > replies, MotorSimplemotion_cmdtypes type, uint32_t timeout_ms=uartErrorTimeout)
void sendFastUpdate(uint16_t val1, uint16_t val2=0)
static std::array< uint16_t, 256 > tableCRC16
bool readParameter(std::array< MotorSimplemotion_param, params > paramIds, std::array< uint32_t *, replynum > replies, MotorSimplemotion_cmdtypes replylen=MotorSimplemotion_cmdtypes::none, uint32_t timeout_ms=uartErrorTimeout)
bool read1Parameter(MotorSimplemotion_param paramId, uint32_t *reply_p, MotorSimplemotion_cmdtypes replylen=MotorSimplemotion_cmdtypes::none)
static const uint8_t SMCMD_FAST_UPDATE_CYCLE_RET
static const uint8_t RXBUF_SIZE
void updatePosition(uint16_t value)
volatile uint8_t rxbuf_i
Encoder * getEncoder() override
MotorSimplemotion(uint8_t instance)
uint32_t getCpr() override
Sm2FastUpdate fastbuffer
bool sendCommand(uint8_t *buf, uint8_t len, uint8_t adr)
void endUartTransfer(UARTPort *port, bool transmit)
static const uint8_t crcpoly
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies) override
int32_t getPos() override
volatile bool waitingFastUpdate
void setPos(int32_t pos) override
static const uint8_t REPLYBUF_SIZE
bool set1Parameter(MotorSimplemotion_param paramId, int32_t value, uint32_t *reply_p=nullptr)
void set() const
Definition: GPIOPin.h:38
void reset() const
Definition: GPIOPin.h:42
UARTPort * uartport
Definition: UART.h:85
Definition: UART.h:26
uint32_t getErrors()
Definition: UART.cpp:177
bool giveSemaphore(bool txsem=true)
Definition: UART.cpp:159
bool takeSemaphore(bool txsem=true, uint32_t blocktime=portMAX_DELAY)
Definition: UART.cpp:145
bool transmit_IT(const char *txbuf, uint16_t size)
Definition: UART.cpp:78
bool reconfigurePort(UART_InitTypeDef &config)
Definition: UART.cpp:35
bool registerInterrupt()
Definition: UART.cpp:56
bool isTaken()
Definition: UART.cpp:173
UARTPort motor_uart
static struct @612 data
static void * memcpy(void *dst, const void *src, size_t n)
Definition: ringbuffer.c:8
const char * name
uint32_t cmdId