ClearCore Library
Loading...
Searching...
No Matches
ClearCoreCommandProtocol.cpp

Return to SDK Examples for Microchip Studio

1/*
2 * Title: ClearCoreCommandProtocol
3 *
4 * Objective:
5 * This example demonstrates control of various functionality of the Teknic
6 * ClearCoreI/O and motion controller, including controlling ClearPath-SD
7 * motors instep and direction mode.
8 *
9 * Description:
10 * This example processes strings of characters formatted according to the
11 * specifications below and commands the corresponding action on a ClearCore device.
12 * This example is designed to be highly configurable to meet the requirements
13 * of a variety of applications. The protocol accepts input and sends output
14 * via USB connection by default but can be configured to accept commands from
15 * other streams - such as ClearCore's COM ports, ethernet port, or XBee
16 * connection - and sources - such as manual user input, input from a text
17 * file, or control from another device sending text commands to ClearCore.
18 *
19 * Setup:
20 * 0. Consult the accompanying ClearCore Command Protocol User Guide for more
21 * information on using this example project.
22 * https://teknic.com/files/downloads/ClearCoreCommandProtocol_UserGuide.pdf
23 * 1. Connect ClearCore via USB to a terminal that can send and receive ASCII
24 * (standard text encoding) messages, or modify the code and connect to a
25 * different source of messages. Other ClearCore examples demonstrate
26 * communication via these alternate connection options.
27 * 2. ClearPath-SD motors can be connected to connector M-0, M-1, M-2, and/or M-3.
28 * 3. The connected ClearPath-SD motor(s) must be configured through the MSP software
29 * for Step and Direction mode (In MSP select Mode>>Step and Direction).
30 * 4. The ClearPath-SD motor(s) must be set to use the HLFB mode "ASG-Position
31 * w/Measured Torque" with a PWM carrier frequency of 482 Hz through the MSP
32 * software (select Advanced>>High Level Feedback [Mode]... then choose
33 * "ASG-Position w/Measured Torque" from the dropdown, make sure that 482 Hz
34 * is selected in the "PWM Carrier Frequency" dropdown, and hit the OK
35 * button).
36 * 5. Set the Input Format in MSP for "Step + Direction".
37 * 6. Set the Input Resolution in MSP the same as your motor's Positioning
38 * Resolution spec if you'd like the pulses sent by ClearCore to command a
39 * move of the same number of Encoder Counts, a 1:1 ratio.
40 * 7. Input and output devices can be wired to connectors IO0-IO5, DI6-DI8, and/or
41 * AI9-AI12. A table summarizing the acceptable connector modes for each
42 * ClearCore connector can be found in the ClearCore manual and in the User
43 * Guide for this example. This example explicitly sets the operational mode
44 * for each connector, but these modes can be reconfigured according to the
45 * table in the manual. Be sure to consult the ClearCore manual and
46 * supplemental wiring and connection diagrams to view the operating modes
47 * for each pin and corresponding connection setups.
48 *
49 * Example Command Sequences
50 * The following provides an example command sequence and corresponding behavior:
51 * e1 //enable motor1
52 * e2 //enable motor2
53 * m1 1000 // if ABSOLUTE_MOVE==1, move motor1 to absolute position 1000 steps
54 * // if ABSOLUTE_MOVE==0, move motor1 1000 steps in the positive direction
55 * v2 -200 //move motor2 at -200 steps/s in the negative direction
56 * q2s //query the status of motor2
57 * l3v 100 //limit motor3's velocity (of positional moves only) to 100 steps/s
58 * z1 //zero Clear's position reference for motor1 (no motion is commanded to the motor)
59 * i6 //read the current state of connector6 (DI6)
60 * o5 1 //output a value of 1 (digital high) to connector5 (IO5)
61 * h //display the help message
62 *
63 * The following example command sequence highlights special cases and notable behavior:
64 * v0 1000 //since motor0 has not yet been enabled, no motion will be commanded
65 * e0 //enable motor0
66 * v0 1000 //move motor0 at 1000 steps/s
67 * d0 //disable motor0. since motor0 was actively moving, motor0 will fault
68 * v0 1000 //since motor0 is in fault (disabled during motion), no motion will be commanded
69 * q1s //query the status of motor0. since the motor is in alert, the alert status will also print
70 * f 0 //disable verbose feedback
71 * q1s //with verbose feedback disabled, only the numerical status (and alert, in this case)
72 * // register(s) will print, not the full status (and alert) message
73 * c0 //clear alerts on motor0
74 * v0 1000 //move motor0 at 1000 steps/s
75 *
76 * m1 2000 //assuming ABSOLUTE_MOVE is left as default, move motor1 to absolute position 2000 steps
77 * m1 2000 //since motor1 is already at absolute position 2000 steps, no motion will be commanded
78 * z1 //define motor1's current position as the zero position
79 * m1 2000 //move motor1 to absolute position 2000, which is 2000 steps
80 * // away from its freshly-zeroed current position
81 *
82 * Additional Resources:
83 * ** ClearCore Command Protocol User Guide:
84 * https://teknic.com/files/downloads/ClearCoreCommandProtocol_UserGuide.pdf
85 * ** ClearCore Documentation:
86 * https://teknic-inc.github.io/ClearCore-library/
87 * ** ClearCore Manual:
88 * https://www.teknic.com/files/downloads/clearcore_user_manual.pdf
89 * ** ClearCore System Diagram and Connection Diagrams:
90 * https://www.teknic.com/files/downloads/clearcore_user_manual.pdf
91 * ** ClearPath Manual (DC Power):
92 * https://www.teknic.com/files/downloads/clearpath_user_manual.pdf
93 * ** ClearPath Manual (AC Power):
94 * https://www.teknic.com/files/downloads/ac_clearpath-mc-sd_manual.pdf
95 *
96 * Copyright (c) 2020 Teknic Inc. This work is free to use, copy and distribute under the terms of
97 * the standard MIT permissive software license which can be found at https://opensource.org/licenses/MIT
98 */
99
100#include "ClearCore.h"
101#include <stdint.h>
102#include <stdlib.h> //for atoi (ASCII to Integer) parsing utility
103
104// specify which serial interface to use as input/output
105#define ioPort ConnectorUsb
106// select the baud rate to match the target device.
107#define ioPortBaudRate 115200
108// specify whether the target serial interface uses CTS/RTS flow control
109// set to true if your target device uses CTS/RTS flow control
110// this is only necessary if using COM ports or XBee module (not necessary for USB)
111#define ioFlowControl false
112
113// structure to hold information on the feedback messages
114struct FeedbackMessage{
115 uint32_t number;
116 char *message;
117};
118
119// feedback message numbers
120#define FB_COMMAND_OK ( 0)
121#define FB_ERR_BUFFER_OVERRUN ( 1)
122#define FB_ERR_INPUT_INVALID_NONLETTER ( 2)
123#define FB_ERR_MOTOR_NUM_INVALID ( 3)
124#define FB_ERR_CONNECTOR_NUM_INVALID ( 4)
125#define FB_ERR_CONNECTOR_MODE_INCOMPATIBLE ( 5)
126#define FB_ENABLED_WAITING_ON_HLFB ( 6)
127#define FB_ENABLE_FAILURE ( 7)
128#define FB_ERR_IO_OUTPUT ( 8)
129#define FB_ERR_MOVE_NOT_ENABLED ( 9)
130#define FB_ERR_MOVE_IN_ALERT (10)
131#define FB_ERR_INVALID_QUERY_REQUEST (11)
132#define FB_ERR_LIMIT_OUT_OF_BOUNDS (12)
133#define FB_ERR_INVALID_LIMIT_REQUEST (13)
134#define FB_ERR_INVALID_FEEDBACK_OPTION (14)
135#define FB_ERR_UNRECOGNIZED_COMMAND (15)
136#define FB_HELP (16)
137
138// verbose feedback messages
139char *msg_command_ok =
140 "Command received";
141char *msg_err_buffer_overrun =
142 "Error: input buffer overrun.";
143char *msg_err_input_invalid_nonletter =
144 "Error: invalid input. Commands begin with a single letter character.";
145char *msg_err_motor_num_invalid =
146 "Error: a required motor was not specified or specified incorrectly. Acceptable motor numbers are 0, 1, 2, and 3.";
147char *msg_err_connector_num_invalid =
148 "Error: a required connector was not specified or specified incorrectly. Acceptable connector numbers are 0 through 12, inclusive.";
149char *msg_err_io_output =
150 "Error: an I/O output parameter is invalid. Ensure the output value is appropriate for the type of output pin.";
151char *msg_err_connector_mode_incompatible =
152 "Error: a specified connector is of an inappropriate mode. Verify the I/O connector is configured as necessary.";
153char *msg_enabled_waiting_on_hlfb =
154 "Motor enabled; waiting on HLFB to assert before accepting other commands.";
155char *msg_enable_failure =
156 "Motor failed to enable due to motor fault, loss of power, or loss/absence of connection. Motor disabled.";
157char *msg_err_move_not_enabled =
158 "Error: motion commanded while motor not enabled. Command e# to enable motor number #.";
159char *msg_err_move_in_alert =
160 "Error: motion commanded while motor in fault. Command c# to clear alerts on motor number #.";
161char *msg_err_invalid_query_request =
162 "Error: invalid query request. Command h for more information.";
163char *msg_err_limit_out_of_bounds =
164 "Error: commanded limit falls outside the acceptable bounds for this limit.";
165char *msg_err_invalid_limit_request =
166 "Error: invalid limit request. Command h for more information.";
167char *msg_err_invalid_feedback_option =
168 "Error: invalid feedback request. Command h for more information.";
169char *msg_err_unrecognized_command =
170 "Error: unrecognized command. Command h for more information.";
171char *msg_help =
172 "ClearCore Command Protocol\n"
173 "Acceptable commands, where # specifies a motor number* (0, 1, 2, or 3): \n"
174 " e# | enable specified motor\n"
175 " d# | disable specified motor\n"
176 " m# distance | if(ABSOLUTE_MOVE==1) move to the specified position\n"
177 " if(ABSOLUTE_MOVE==0) move the specified number of steps\n"
178 " v# velocity | move at the specified velocity (steps/s)\n"
179 " q#<p/v/s> | query specified motor's position/velocity/status\n"
180 " l#<v/a> limit | set specified motor's velocity/acceleration limit\n"
181 " c# | clear alerts\n"
182 " z# | set the zero position for motor # to the current commanded position\n"
183 " i# | read input on pin #\n"
184 " Digital pins return 1 or 0; analog pins return [0,4095] corresponding to [0,10]V\n"
185 " (*note that # for this command can be 0 through 5)\n"
186 " o# outputVal | write output on pin #\n"
187 " Digital pins allow 1 or 0; analog pins allow [409,2047] corresponding to [4,20]mA\n"
188 " (*note that # for this command can be 0 through 12)\n"
189 " f fdbkType | specify the type of feedback printed:\n"
190 " 0 : send message number only\n"
191 " 1 : send verbose message\n"
192 " h | print this help message\n";
193
194// array of feedback messages, accessed by SendFeedback() when feedback is sent
195FeedbackMessage FeedbackMessages[17] = {
196 {FB_COMMAND_OK , msg_command_ok },
197 {FB_ERR_BUFFER_OVERRUN , msg_err_buffer_overrun },
198 {FB_ERR_INPUT_INVALID_NONLETTER , msg_err_input_invalid_nonletter },
199 {FB_ERR_MOTOR_NUM_INVALID , msg_err_motor_num_invalid },
200 {FB_ERR_CONNECTOR_NUM_INVALID , msg_err_connector_num_invalid },
201 {FB_ERR_CONNECTOR_MODE_INCOMPATIBLE,
202 msg_err_connector_mode_incompatible },
203 {FB_ENABLED_WAITING_ON_HLFB , msg_enabled_waiting_on_hlfb },
204 {FB_ENABLE_FAILURE , msg_enable_failure },
205 {FB_ERR_IO_OUTPUT , msg_err_io_output },
206 {FB_ERR_MOVE_NOT_ENABLED , msg_err_move_not_enabled },
207 {FB_ERR_MOVE_IN_ALERT , msg_err_move_in_alert },
208 {FB_ERR_INVALID_QUERY_REQUEST , msg_err_invalid_query_request },
209 {FB_ERR_LIMIT_OUT_OF_BOUNDS , msg_err_limit_out_of_bounds },
210 {FB_ERR_INVALID_LIMIT_REQUEST , msg_err_invalid_limit_request },
211 {FB_ERR_INVALID_FEEDBACK_OPTION , msg_err_invalid_feedback_option },
212 {FB_ERR_UNRECOGNIZED_COMMAND , msg_err_unrecognized_command },
213 {FB_HELP , msg_help }
214};
215
216// global variable to select between printing only feedback number or verbose feedback message
217bool verboseFeedback = true;
218
219// macro to select between commanding absolute positional moves or relative
220// positional moves. See User Guide for more information.
221#define ABSOLUTE_MOVE (1)
222
223// container for the char stream to be read-in.
224// allows for IN_BUFFER_LEN characters to be stored followed by a NULL terminator
225//todo thoughts on this?
226#define IN_BUFFER_LEN 32
227char input[IN_BUFFER_LEN+1];
228
229// motor connectors
230MotorDriver *const motors[MOTOR_CON_CNT] = {
232};
233
234// I/O connectors
235Connector *const connectors[13] = {
239};
240
241// acceleration and velocity limit bounds
242// (note that velocity limits take effect only on positional moves)
243#define DEFAULT_ACCEL_LIMIT (100000) // pulses per sec^2
244#define MAX_ACCEL_LIMIT (1000000000)
245#define MIN_ACCEL_LIMIT (1)
246#define DEFAULT_VEL_LIMIT (10000) // pulses per sec
247#define MAX_VEL_LIMIT (500000)
248#define MIN_VEL_LIMIT (1)
249
250// helper functions to print feedback and status information
251// the implementations of these functions are at the bottom of this example
252void SendFeedback(int32_t messageNumber);
253void SendVerboseStatus(int32_t motorNumber);
254
255
256int main() {
257
258 // configure serial communication to USB port and wait for the port to open
259 ioPort.Mode(Connector::USB_CDC);
260 ioPort.Speed(ioPortBaudRate);
261 // ioPort.FlowControl(ioFlowControl); //only necessary if using COM ports or XBee module
262 ioPort.PortOpen();
263 while (!ioPort) {
264 continue;
265 }
266
267 // configure input clocking rate
268 // this normal rate is ideal for ClearPath step and direction applications
269 MotorMgr.MotorInputClocking(MotorManager::CLOCK_RATE_NORMAL);
270
271 // set all motor connectors into step and direction mode
272 MotorMgr.MotorModeSet(MotorManager::MOTOR_ALL, Connector::CPM_MODE_STEP_AND_DIR);
273
274 // local storage for velocity and acceleration limits
275 // (note that velocity limits only take effect on positional moves)
276 uint32_t accelerationLimits[MOTOR_CON_CNT] = {
277 DEFAULT_ACCEL_LIMIT, DEFAULT_ACCEL_LIMIT, DEFAULT_ACCEL_LIMIT, DEFAULT_ACCEL_LIMIT
278 };
279 uint32_t velocityLimits[MOTOR_CON_CNT] = {
280 DEFAULT_VEL_LIMIT, DEFAULT_VEL_LIMIT, DEFAULT_VEL_LIMIT, DEFAULT_VEL_LIMIT
281 };
282
283 // generic iterator
284 uint32_t i;
285
286 // configure all motor connectors for bipolar PWM HLFB mode at 482Hz, and set
287 // velocity and acceleration limits
288 for (i=0; i<MOTOR_CON_CNT; i++){
289 motors[i]->HlfbMode(MotorDriver::HLFB_MODE_HAS_BIPOLAR_PWM);
290 motors[i]->HlfbCarrier(MotorDriver::HLFB_CARRIER_482_HZ);
291 motors[i]->VelMax(velocityLimits[i]);
292 motors[i]->AccelMax(accelerationLimits[i]);
293 }
294
295 // configure I/O pins
296 // these defaults can be modified according to application needs
297 // for more information and to view a list of configurable modes for each pin,
298 // see the ClearCore manual or the User Guide for this example
299 ConnectorIO0.Mode(Connector::OUTPUT_ANALOG );
300 ConnectorIO1.Mode(Connector::OUTPUT_DIGITAL);
301 ConnectorIO2.Mode(Connector::OUTPUT_DIGITAL);
302 ConnectorIO3.Mode(Connector::OUTPUT_DIGITAL);
303 ConnectorIO4.Mode(Connector::OUTPUT_DIGITAL);
304 ConnectorIO5.Mode(Connector::OUTPUT_DIGITAL);
305 ConnectorDI6.Mode(Connector::INPUT_DIGITAL );
306 ConnectorDI7.Mode(Connector::INPUT_DIGITAL );
307 ConnectorDI8.Mode(Connector::INPUT_DIGITAL );
308 ConnectorA9 .Mode(Connector::INPUT_ANALOG );
309 ConnectorA10.Mode(Connector::INPUT_ANALOG );
310 ConnectorA11.Mode(Connector::INPUT_ANALOG );
311 ConnectorA12.Mode(Connector::INPUT_ANALOG );
312
313 // program control variables
314 bool inputValid;
315 bool motorNumValid;
316 bool connectorNumValid;
317 bool motorEnabledBeforeClearingAlerts;
318
319 // parsing variables
320 int32_t motorNum_in;
321 int32_t connectorNum_in;
322 int32_t moveDistance_in;
323 int32_t velocity_in;
324 int32_t limit_in;
325 int32_t queriedValue;
326 int16_t inputConnectorValue;
327 int16_t outputConnectorValue_in;
328
329 ioPort.SendLine("Setup successful");
330 ioPort.SendLine("Send 'h' to receive a list of valid commands");
331
332 // main loop to read and process input
333 while (true) {
334
335 // reset the input buffer by populating each index with a NULL character
336 for (i = 0; i<IN_BUFFER_LEN+1; i++){
337 input[i] = (char) NULL;
338 }
339
340 // read and store the input character by character
341 // input buffer has a default maximum size, defined by IN_BUFFER_LEN, of 32.
342 // if more characters are provided by the user, program will reject input.
343 inputValid = true;
344 i = 0;
345 while(i<IN_BUFFER_LEN && ioPort.CharPeek() != -1){
346 input[i] = (char) ioPort.CharGet();
347 i++;
348 Delay_ms(1);
349 }
350
351 //-------------------------
352 // This line divides the command reading and storing section of the code from
353 // the command parsing section of the code. To use this protocol to accept commands
354 // from another source than the default USB input, ensure that the command is stored
355 // in a character array called "input" and is ready to be parsed after this line.
356 //-------------------------
357
358 // echo nonempty input when in verbose feedback mode
359 if(verboseFeedback && i!=0){
360 ioPort.SendLine(input);
361 }
362
363 if (i==0){
364 // user did not input any characters. input invalid, but no need to report
365 inputValid = false;
366 } else if (ioPort.CharPeek()!=-1){
367 // buffer overflow (there is no space left in the buffer, but there are still characters to read from ioPort)
368 // report error, reject input, and flush input stream (so the leftover characters aren't read as part of the next command)
369 SendFeedback(FB_ERR_BUFFER_OVERRUN);
370 inputValid = false;
371 while(ioPort.CharPeek()!=-1){
372 ioPort.FlushInput();
373 Delay_ms(10);
374 }
375 }
376
377 // verify first character of command is a letter
378 if (inputValid){
379 if (('A' <= input[0] && input[0] <= 'Z') || ('a' <= input[0] && input[0] <= 'z')){
380 // if letter is uppercase, convert to lowercase
381 if ('A' <= input[0] && input[0] <= 'Z'){
382 // to convert an ASCII character from upper to lower case, add 32 to the character
383 input[0] += 32;
384 }
385 } else {
386 // if first letter of command is not letter, reject input
387 SendFeedback(FB_ERR_INPUT_INVALID_NONLETTER);
388 inputValid = false;
389 ioPort.FlushInput();
390 }
391 }
392
393 if (inputValid){
394
395 // store and validate motor number input from command
396 motorNum_in = atoi(&input[1]);
397 motorNumValid = (0<=motorNum_in && motorNum_in<=3 && input[1]!=NULL);
398
399 // store and validate connector number input from command
400 connectorNum_in = atoi(&input[1]);
401 connectorNumValid = (0<=connectorNum_in && connectorNum_in<=12 && input[1]!=NULL);
402
403 // status register for accessing motor status information
404 volatile const MotorDriver::StatusRegMotor &statusReg = motors[motorNum_in]->StatusReg();
405 volatile const MotorDriver::AlertRegMotor &alertReg = motors[motorNum_in]->AlertReg();
406
407
408 // process the command based on the command letter (first character of input)
409 switch(input[0]){
410
411 // enable
412 case 'e':
413 // verify motor number valid
414 if (!motorNumValid){
415 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
416 } else {
417 // enable the motor
418 motors[motorNum_in]->EnableRequest(true);
419 SendFeedback(FB_ENABLED_WAITING_ON_HLFB);
420 // wait until motor is ready before accepting other commands
421 // (allows any automatic homing move to complete if configured)
422 // (loop will exit on fault during homing, or if motor is disconnected or loses power)
423 while (statusReg.bit.HlfbState != MotorDriver::HLFB_ASSERTED &&
424 !statusReg.bit.MotorInFault){
425 continue;
426 }
427 if(statusReg.bit.MotorInFault){
428 // if there is a fault while trying to enable, disable and report
429 motors[motorNum_in]->EnableRequest(false);
430 SendFeedback(FB_ENABLE_FAILURE);
431 } else {
432 SendFeedback(FB_COMMAND_OK);
433 }
434 }
435 break;
436
437 // disable
438 case 'd':
439 // verify motor number valid
440 if (!motorNumValid){
441 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
442 } else {
443 // disable the motor
444 motors[motorNum_in]->EnableRequest(false);
445 SendFeedback(FB_COMMAND_OK);
446 }
447 break;
448
449 // position move
450 case 'm':
451 // verify motor number valid, motor enabled, and no faults present
452 if (!motorNumValid){
453 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
454 } else if ( statusReg.bit.ReadyState==MotorDriver::MOTOR_DISABLED ||
455 statusReg.bit.ReadyState==MotorDriver::MOTOR_ENABLING){
456 SendFeedback(FB_ERR_MOVE_NOT_ENABLED);
457 } else if (motors[motorNum_in]->StatusReg().bit.AlertsPresent) {
458 SendFeedback(FB_ERR_MOVE_IN_ALERT);
459 } else {
460 // command the move
461 // #define ABSOLUTE_MOVE (0) commands absolute moves.
462 // #define ABSOLUTE_MOVE (1) commands relative moves.
463 // absolute moves are configured by default.
464 // See User Guide for more information.
465 moveDistance_in = atoi(&input[2]);
466 if (ABSOLUTE_MOVE){
467 motors[motorNum_in]->Move(moveDistance_in, MotorDriver::MOVE_TARGET_ABSOLUTE);
468 } else {
469 motors[motorNum_in]->Move(moveDistance_in);
470 }
471 SendFeedback(FB_COMMAND_OK);
472 }
473 break;
474
475 // velocity move
476 case 'v':
477 // verify motor number valid, motor enabled, and no faults present
478 if (!motorNumValid){
479 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
480 } else if ( statusReg.bit.ReadyState==MotorDriver::MOTOR_DISABLED ||
481 statusReg.bit.ReadyState==MotorDriver::MOTOR_ENABLING){
482 SendFeedback(FB_ERR_MOVE_NOT_ENABLED);
483 } else if (motors[motorNum_in]->StatusReg().bit.AlertsPresent) {
484 SendFeedback(FB_ERR_MOVE_IN_ALERT);
485 } else {
486 // command the move
487 velocity_in = atoi(&input[2]);
488 motors[motorNum_in]->MoveVelocity(velocity_in);
489 SendFeedback(FB_COMMAND_OK);
490 }
491 break;
492
493 // query position, velocity, or status
494 case 'q':
495 // verify motor number valid
496 if (!motorNumValid){
497 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
498 break;
499 }
500 switch(input[2]){
501 // query commanded position
502 case 'p':
503 case 'P':
504 // send commanded position.
505 // this can differ from the position counter in MSP if the ClearCore
506 // position reference has not been synced with ClearPath's position
507 queriedValue = (int32_t) motors[motorNum_in]->PositionRefCommanded();
508 if (verboseFeedback){
509 ioPort.Send("Motor ");
510 ioPort.Send(motorNum_in);
511 ioPort.Send(" is in position (steps) ");
512 }
513 ioPort.SendLine(queriedValue);
514 break;
515
516 // query velocity
517 case 'v':
518 case 'V':
519 // send motor velocity
520 queriedValue = motors[motorNum_in]->VelocityRefCommanded();
521 if (verboseFeedback){
522 ioPort.Send("Motor ");
523 ioPort.Send(motorNum_in);
524 ioPort.Send(" is at velocity (steps/s) ");
525 }
526 ioPort.SendLine(queriedValue);
527 break;
528
529 // query status
530 case 's':
531 case 'S':
532 // send motor status
533 if (verboseFeedback){
534 SendVerboseStatus(motorNum_in);
535 } else {
536 ioPort.SendLine(statusReg.reg, 2); // prints the status register in binary
537 if (statusReg.bit.AlertsPresent){
538 ioPort.SendLine(alertReg.reg, 2); // prints the alert register in binary
539 }
540 }
541 break;
542
543 // invalid query request
544 default:
545 SendFeedback(FB_ERR_INVALID_QUERY_REQUEST);
546 }
547 break;
548
549 // set acceleration or velocity limit
550 case 'l':
551 // verify motor number valid
552 if ( !motorNumValid){
553 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
554 break;
555 }
556 // store limit input from command
557 limit_in = atoi(&input[3]);
558
559 switch(input[2]){
560 // velocity limit
561 case 'v':
562 case 'V':
563 //verify limit is valid, store, then propagate the change to the motor
564 if (MIN_VEL_LIMIT<=limit_in && limit_in<=MAX_VEL_LIMIT){
565 velocityLimits[motorNum_in] = limit_in;
566 motors[motorNum_in]->VelMax(velocityLimits[motorNum_in]);
567 SendFeedback(FB_COMMAND_OK);
568 } else {
569 SendFeedback(FB_ERR_LIMIT_OUT_OF_BOUNDS);
570 }
571 break;
572
573 // acceleration limit
574 case 'a':
575 case 'A':
576 //verify limit is valid, store, then propagate the change to the motor
577 if (MIN_ACCEL_LIMIT<=limit_in && limit_in<=MAX_ACCEL_LIMIT){
578 accelerationLimits[motorNum_in] = limit_in;
579 motors[motorNum_in]->AccelMax(velocityLimits[motorNum_in]);
580 SendFeedback(FB_COMMAND_OK);
581 } else {
582 SendFeedback(FB_ERR_LIMIT_OUT_OF_BOUNDS);
583 }
584 break;
585
586 // invalid limit request
587 default:
588 SendFeedback(FB_ERR_INVALID_LIMIT_REQUEST);
589 break;
590 }
591 break;
592
593 // clear alerts
594 case 'c':
595 // verify motor number valid
596 if (!motorNumValid){
597 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
598 break;
599 }
600
601 // capture the current state of enable
602 // this value will be restored after alerts are cleared
603 motorEnabledBeforeClearingAlerts = motors[motorNum_in]->EnableRequest();
604
605 // to clear all ClearCore alerts (which can include motor faults):
606 // - cycle enable if faults present (clears faults, if any)
607 // - clear alert register (clears alerts)
608 // this command clears both ClearCore motor alerts and motor faults
609 if (statusReg.bit.MotorInFault){
610 motors[motorNum_in]->EnableRequest(false);
611 Delay_ms(10);
612 if(motorEnabledBeforeClearingAlerts){
613 motors[motorNum_in]->EnableRequest(true);
614 }
615 }
616 motors[motorNum_in]->ClearAlerts();
617 SendFeedback(FB_COMMAND_OK);
618 break;
619
620 // set the zero position for motor # to the current commanded position
621 case 'z':
622 // verify motor number valid
623 if (!motorNumValid){
624 SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
625 break;
626 }
627 // zero position
628 motors[motorNum_in]->PositionRefSet(0);
629 SendFeedback(FB_COMMAND_OK);
630 break;
631
632 // read input from ClearCore connector
633 case 'i':
634 // verify connector number valid
635 if (!connectorNumValid){
636 SendFeedback(FB_ERR_CONNECTOR_NUM_INVALID);
637 break;
638 }
639 // verify connector mode valid
640 if (connectors[connectorNum_in]->Mode() != Connector::INPUT_DIGITAL &&
641 connectors[connectorNum_in]->Mode() != Connector::INPUT_ANALOG){
642 SendFeedback(FB_ERR_CONNECTOR_MODE_INCOMPATIBLE);
643 break;
644 }
645 inputConnectorValue = connectors[connectorNum_in]->State();
646 if(verboseFeedback){
647 ioPort.Send("Connector ");
648 ioPort.Send(connectorNum_in);
649 ioPort.Send(" value: ");
650 }
651 ioPort.SendLine(inputConnectorValue);
652 break;
653
654
655 // write digital output to ClearCore digital output connector
656 case 'o':
657 // verify connector number valid
658 if (!connectorNumValid){
659 SendFeedback(FB_ERR_CONNECTOR_NUM_INVALID);
660 break;
661 }
662 // verify connector type valid
663 if (connectors[connectorNum_in]->Mode() != Connector::OUTPUT_DIGITAL &&
664 connectors[connectorNum_in]->Mode() != Connector::OUTPUT_ANALOG){
665 SendFeedback(FB_ERR_CONNECTOR_MODE_INCOMPATIBLE);
666 break;
667 }
668 // store and write value
669 outputConnectorValue_in = atoi(&input[3]);
670 if(!connectors[connectorNum_in]->State(outputConnectorValue_in)){
671 SendFeedback(FB_ERR_IO_OUTPUT);
672 } else {
673 SendFeedback(FB_COMMAND_OK);
674 }
675 break;
676
677
678 // change feedback type
679 case 'f':
680 switch(atoi(&input[1])){
681
682 // feedback number only
683 case 0:
684 verboseFeedback = false;
685 SendFeedback(FB_COMMAND_OK);
686 break;
687
688 // verbose feedback messages
689 case 1:
690 verboseFeedback = true;
691 SendFeedback(FB_COMMAND_OK);
692 break;
693
694 // invalid feedback request
695 default:
696 SendFeedback(FB_ERR_INVALID_FEEDBACK_OPTION);
697 break;
698 }
699 break;
700
701 // help
702 case 'h':
703 SendFeedback(FB_HELP);
704 break;
705
706 // invalid command letter
707 default:
708 SendFeedback(FB_ERR_UNRECOGNIZED_COMMAND);
709 break;
710
711 } // switch(command letter)
712 } // if (inputValid)
713 } // while(true)
714} // main()
715
716
717/*------------------------------------------------------------------------------
718 * SendFeedback
719 *
720 * Helper function to send feedback for specified feedback message.
721 *
722 * Parameters:
723 * int messageNumber - The feedback identification number corresponding the
724 * feedback struct array index
725 *
726 * Returns: None (feedback message is sent directly to output)
727*/
728 void SendFeedback(int32_t messageNumber){
729 // send either the verbose message or only the message number, based on verboseFeedback bool
730 if (verboseFeedback){
731 ioPort.SendLine(FeedbackMessages[messageNumber].message);
732 } else {
733 ioPort.SendLine(FeedbackMessages[messageNumber].number);
734 }
735 } // SendFeedback()
736//------------------------------------------------------------------------------
737
738
739
740/*------------------------------------------------------------------------------
741 * SendVerboseStatus
742 *
743 * Outputs verbose status information.
744 * Functionality adapted from MotorStatusRegister example project
745 *
746 * Parameters:
747 * int motorNumber - The motor whose status should be printed
748 *
749 * Returns: None (status message is sent directly to output)
750 */
751void SendVerboseStatus(int32_t motorNumber) {
752 // status register for accessing motor status information
753 volatile const MotorDriver::StatusRegMotor &statusReg = motors[motorNumber]->StatusReg();
754 volatile const MotorDriver::AlertRegMotor &alertReg = motors[motorNumber]->AlertReg();
755
756 ioPort.Send("Motor status register for motor M");
757 ioPort.Send(motorNumber);
758 ioPort.Send(": ");
759 ioPort.SendLine(statusReg.reg, 2); // prints the status register in binary
760
761 ioPort.Send("AtTargetPosition: ");
762 ioPort.SendLine(statusReg.bit.AtTargetPosition);
763
764 ioPort.Send("StepsActive: ");
765 ioPort.SendLine(statusReg.bit.StepsActive);
766
767 ioPort.Send("AtTargetVelocity: ");
768 ioPort.SendLine(statusReg.bit.AtTargetVelocity);
769
770 ioPort.Send("MoveDirection: ");
771 ioPort.SendLine(statusReg.bit.MoveDirection);
772
773 ioPort.Send("MotorInFault: ");
774 ioPort.SendLine(statusReg.bit.MotorInFault);
775
776 ioPort.Send("Enabled: ");
777 ioPort.SendLine(statusReg.bit.Enabled);
778
779 ioPort.Send("PositionalMove: ");
780 ioPort.SendLine(statusReg.bit.PositionalMove);
781
782 ioPort.Send("HLFB State: ");
783 switch (statusReg.bit.HlfbState) {
784 case 0:
785 ioPort.SendLine("HLFB_DEASSERTED");
786 break;
787 case 1:
788 ioPort.SendLine("HLFB_ASSERTED");
789 break;
790 case 2:
791 ioPort.SendLine("HLFB_HAS_MEASUREMENT");
792 break;
793 case 3:
794 ioPort.SendLine("HLFB_UNKNOWN");
795 break;
796 default:
797 // something has gone wrong if this is printed
798 ioPort.SendLine("???");
799 }
800
801 ioPort.Send("AlertsPresent: ");
802 ioPort.SendLine(statusReg.bit.AlertsPresent);
803
804 ioPort.Send("Ready state: ");
805 switch (statusReg.bit.ReadyState) {
806 case MotorDriver::MOTOR_DISABLED:
807 ioPort.SendLine("Disabled");
808 break;
809 case MotorDriver::MOTOR_ENABLING:
810 ioPort.SendLine("Enabling");
811 break;
812 case MotorDriver::MOTOR_FAULTED:
813 ioPort.SendLine("Faulted");
814 break;
815 case MotorDriver::MOTOR_READY:
816 ioPort.SendLine("Ready");
817 break;
818 case MotorDriver::MOTOR_MOVING:
819 ioPort.SendLine("Moving");
820 break;
821 default:
822 // something has gone wrong if this is printed
823 ioPort.SendLine("???");
824 }
825
826 ioPort.Send("Triggering: ");
827 ioPort.SendLine(statusReg.bit.Triggering);
828
829 ioPort.Send("InPositiveLimit: ");
830 ioPort.SendLine(statusReg.bit.InPositiveLimit);
831
832 ioPort.Send("InNegativeLimit: ");
833 ioPort.SendLine(statusReg.bit.InNegativeLimit);
834
835 ioPort.Send("InEStopSensor: ");
836 ioPort.SendLine(statusReg.bit.InEStopSensor);
837
838 ioPort.SendLine("--------------------------------");
839
840
841 if (statusReg.bit.AlertsPresent){
842 ioPort.Send("Alert register: ");
843 ioPort.SendLine(alertReg.reg, 2); // prints the alert register in binary
844
845 ioPort.Send("MotionCanceledInAlert: ");
846 ioPort.SendLine(alertReg.bit.MotionCanceledInAlert);
847
848 ioPort.Send("MotionCanceledPositiveLimit: ");
849 ioPort.SendLine(alertReg.bit.MotionCanceledPositiveLimit);
850
851 ioPort.Send("MotionCanceledNegativeLimit: ");
852 ioPort.SendLine(alertReg.bit.MotionCanceledNegativeLimit);
853
854 ioPort.Send("MotionCanceledSensorEStop: ");
855 ioPort.SendLine(alertReg.bit.MotionCanceledSensorEStop);
856
857 ioPort.Send("MotionCanceledMotorDisabled: ");
858 ioPort.SendLine(alertReg.bit.MotionCanceledMotorDisabled);
859
860 ioPort.Send("MotorFaulted: ");
861 ioPort.SendLine(alertReg.bit.MotorFaulted);
862
863 ioPort.SendLine("--------------------------------");
864 }
865
866
867}// SendVerboseStatus()
868//------------------------------------------------------------------------------
869
870
871
#define MOTOR_CON_CNT
Definition SysConnectors.h:42
void Delay_ms(uint32_t ms)
Blocks operations for ms milliseconds.
Definition SysTiming.h:287
virtual ConnectorModes Mode() override
Get the connector's operational mode.
Definition DigitalInAnalogIn.h:89
virtual ConnectorModes Mode() override
Get the connector's operational mode.
Definition DigitalIn.h:161
virtual ConnectorModes Mode() override
Get the connector's operational mode.
Definition DigitalInOutAnalogOut.h:70
virtual ConnectorModes Mode() override
Get the connector's operational mode.
Definition DigitalInOutHBridge.h:124
virtual ConnectorModes Mode() override
Get the connector's operational mode.
Definition DigitalInOut.h:85
bool MotorInputClocking(MotorClockRates newRate)
Sets the output step rate for the motor step generators.
bool MotorModeSet(MotorPair motorPair, Connector::ConnectorModes newMode)
Sets the operational mode for the specified MotorDriver connectors.
DigitalInAnalogIn ConnectorA10
A-10 connector instance.
DigitalInOutAnalogOut ConnectorIO0
IO-0 connector instance.
DigitalInAnalogIn ConnectorA12
A-12 connector instance.
MotorDriver ConnectorM2
M-2 connector instance.
DigitalIn ConnectorDI6
DI-6 connector instance.
DigitalInOut ConnectorIO3
IO-3 connector instance.
DigitalInAnalogIn ConnectorA9
A-9 connector instance.
DigitalInOut ConnectorIO1
IO-1 connector instance.
MotorDriver ConnectorM0
M-0 connector instance.
DigitalInOutHBridge ConnectorIO4
IO-4 connector instance.
MotorDriver ConnectorM1
M-1 connector instance.
MotorManager & MotorMgr
Motor connector manager.
DigitalInOutHBridge ConnectorIO5
IO-5 connector instance.
MotorDriver ConnectorM3
M-3 connector instance.
DigitalInAnalogIn ConnectorA11
A-11 connector instance.
DigitalInOut ConnectorIO2
IO-2 connector instance.
DigitalIn ConnectorDI7
DI-7 connector instance.
DigitalIn ConnectorDI8
DI-8 connector instance.