#include "ClearCore.h"
#include <stdint.h>
#include <stdlib.h>
#define ioPort ConnectorUsb
#define ioPortBaudRate 115200
#define ioFlowControl false
struct FeedbackMessage{
uint32_t number;
char *message;
};
#define FB_COMMAND_OK ( 0)
#define FB_ERR_BUFFER_OVERRUN ( 1)
#define FB_ERR_INPUT_INVALID_NONLETTER ( 2)
#define FB_ERR_MOTOR_NUM_INVALID ( 3)
#define FB_ERR_CONNECTOR_NUM_INVALID ( 4)
#define FB_ERR_CONNECTOR_MODE_INCOMPATIBLE ( 5)
#define FB_ENABLED_WAITING_ON_HLFB ( 6)
#define FB_ENABLE_FAILURE ( 7)
#define FB_ERR_IO_OUTPUT ( 8)
#define FB_ERR_MOVE_NOT_ENABLED ( 9)
#define FB_ERR_MOVE_IN_ALERT (10)
#define FB_ERR_INVALID_QUERY_REQUEST (11)
#define FB_ERR_LIMIT_OUT_OF_BOUNDS (12)
#define FB_ERR_INVALID_LIMIT_REQUEST (13)
#define FB_ERR_INVALID_FEEDBACK_OPTION (14)
#define FB_ERR_UNRECOGNIZED_COMMAND (15)
#define FB_HELP (16)
char *msg_command_ok =
"Command received";
char *msg_err_buffer_overrun =
"Error: input buffer overrun.";
char *msg_err_input_invalid_nonletter =
"Error: invalid input. Commands begin with a single letter character.";
char *msg_err_motor_num_invalid =
"Error: a required motor was not specified or specified incorrectly. Acceptable motor numbers are 0, 1, 2, and 3.";
char *msg_err_connector_num_invalid =
"Error: a required connector was not specified or specified incorrectly. Acceptable connector numbers are 0 through 12, inclusive.";
char *msg_err_io_output =
"Error: an I/O output parameter is invalid. Ensure the output value is appropriate for the type of output pin.";
char *msg_err_connector_mode_incompatible =
"Error: a specified connector is of an inappropriate mode. Verify the I/O connector is configured as necessary.";
char *msg_enabled_waiting_on_hlfb =
"Motor enabled; waiting on HLFB to assert before accepting other commands.";
char *msg_enable_failure =
"Motor failed to enable due to motor fault, loss of power, or loss/absence of connection. Motor disabled.";
char *msg_err_move_not_enabled =
"Error: motion commanded while motor not enabled. Command e# to enable motor number #.";
char *msg_err_move_in_alert =
"Error: motion commanded while motor in fault. Command c# to clear alerts on motor number #.";
char *msg_err_invalid_query_request =
"Error: invalid query request. Command h for more information.";
char *msg_err_limit_out_of_bounds =
"Error: commanded limit falls outside the acceptable bounds for this limit.";
char *msg_err_invalid_limit_request =
"Error: invalid limit request. Command h for more information.";
char *msg_err_invalid_feedback_option =
"Error: invalid feedback request. Command h for more information.";
char *msg_err_unrecognized_command =
"Error: unrecognized command. Command h for more information.";
char *msg_help =
"ClearCore Command Protocol\n"
"Acceptable commands, where # specifies a motor number* (0, 1, 2, or 3): \n"
" e# | enable specified motor\n"
" d# | disable specified motor\n"
" m# distance | if(ABSOLUTE_MOVE==1) move to the specified position\n"
" if(ABSOLUTE_MOVE==0) move the specified number of steps\n"
" v# velocity | move at the specified velocity (steps/s)\n"
" q#<p/v/s> | query specified motor's position/velocity/status\n"
" l#<v/a> limit | set specified motor's velocity/acceleration limit\n"
" c# | clear alerts\n"
" z# | set the zero position for motor # to the current commanded position\n"
" i# | read input on pin #\n"
" Digital pins return 1 or 0; analog pins return [0,4095] corresponding to [0,10]V\n"
" (*note that # for this command can be 0 through 5)\n"
" o# outputVal | write output on pin #\n"
" Digital pins allow 1 or 0; analog pins allow [409,2047] corresponding to [4,20]mA\n"
" (*note that # for this command can be 0 through 12)\n"
" f fdbkType | specify the type of feedback printed:\n"
" 0 : send message number only\n"
" 1 : send verbose message\n"
" h | print this help message\n";
FeedbackMessage FeedbackMessages[17] = {
{FB_COMMAND_OK , msg_command_ok },
{FB_ERR_BUFFER_OVERRUN , msg_err_buffer_overrun },
{FB_ERR_INPUT_INVALID_NONLETTER , msg_err_input_invalid_nonletter },
{FB_ERR_MOTOR_NUM_INVALID , msg_err_motor_num_invalid },
{FB_ERR_CONNECTOR_NUM_INVALID , msg_err_connector_num_invalid },
{FB_ERR_CONNECTOR_MODE_INCOMPATIBLE,
msg_err_connector_mode_incompatible },
{FB_ENABLED_WAITING_ON_HLFB , msg_enabled_waiting_on_hlfb },
{FB_ENABLE_FAILURE , msg_enable_failure },
{FB_ERR_IO_OUTPUT , msg_err_io_output },
{FB_ERR_MOVE_NOT_ENABLED , msg_err_move_not_enabled },
{FB_ERR_MOVE_IN_ALERT , msg_err_move_in_alert },
{FB_ERR_INVALID_QUERY_REQUEST , msg_err_invalid_query_request },
{FB_ERR_LIMIT_OUT_OF_BOUNDS , msg_err_limit_out_of_bounds },
{FB_ERR_INVALID_LIMIT_REQUEST , msg_err_invalid_limit_request },
{FB_ERR_INVALID_FEEDBACK_OPTION , msg_err_invalid_feedback_option },
{FB_ERR_UNRECOGNIZED_COMMAND , msg_err_unrecognized_command },
{FB_HELP , msg_help }
};
bool verboseFeedback = true;
#define ABSOLUTE_MOVE (1)
#define IN_BUFFER_LEN 32
char input[IN_BUFFER_LEN+1];
};
Connector *const connectors[13] = {
};
#define DEFAULT_ACCEL_LIMIT (100000) // pulses per sec^2
#define MAX_ACCEL_LIMIT (1000000000)
#define MIN_ACCEL_LIMIT (1)
#define DEFAULT_VEL_LIMIT (10000) // pulses per sec
#define MAX_VEL_LIMIT (500000)
#define MIN_VEL_LIMIT (1)
void SendFeedback(int32_t messageNumber);
void SendVerboseStatus(int32_t motorNumber);
int main() {
ioPort.Mode(Connector::USB_CDC);
ioPort.Speed(ioPortBaudRate);
ioPort.PortOpen();
while (!ioPort) {
continue;
}
DEFAULT_ACCEL_LIMIT, DEFAULT_ACCEL_LIMIT, DEFAULT_ACCEL_LIMIT, DEFAULT_ACCEL_LIMIT
};
DEFAULT_VEL_LIMIT, DEFAULT_VEL_LIMIT, DEFAULT_VEL_LIMIT, DEFAULT_VEL_LIMIT
};
uint32_t i;
motors[i]->HlfbMode(MotorDriver::HLFB_MODE_HAS_BIPOLAR_PWM);
motors[i]->HlfbCarrier(MotorDriver::HLFB_CARRIER_482_HZ);
motors[i]->VelMax(velocityLimits[i]);
motors[i]->AccelMax(accelerationLimits[i]);
}
bool inputValid;
bool motorNumValid;
bool connectorNumValid;
bool motorEnabledBeforeClearingAlerts;
int32_t motorNum_in;
int32_t connectorNum_in;
int32_t moveDistance_in;
int32_t velocity_in;
int32_t limit_in;
int32_t queriedValue;
int16_t inputConnectorValue;
int16_t outputConnectorValue_in;
ioPort.SendLine("Setup successful");
ioPort.SendLine("Send 'h' to receive a list of valid commands");
while (true) {
for (i = 0; i<IN_BUFFER_LEN+1; i++){
input[i] = (char) NULL;
}
inputValid = true;
i = 0;
while(i<IN_BUFFER_LEN && ioPort.CharPeek() != -1){
input[i] = (char) ioPort.CharGet();
i++;
}
if(verboseFeedback && i!=0){
ioPort.SendLine(input);
}
if (i==0){
inputValid = false;
} else if (ioPort.CharPeek()!=-1){
SendFeedback(FB_ERR_BUFFER_OVERRUN);
inputValid = false;
while(ioPort.CharPeek()!=-1){
ioPort.FlushInput();
}
}
if (inputValid){
if (('A' <= input[0] && input[0] <= 'Z') || ('a' <= input[0] && input[0] <= 'z')){
if ('A' <= input[0] && input[0] <= 'Z'){
input[0] += 32;
}
} else {
SendFeedback(FB_ERR_INPUT_INVALID_NONLETTER);
inputValid = false;
ioPort.FlushInput();
}
}
if (inputValid){
motorNum_in = atoi(&input[1]);
motorNumValid = (0<=motorNum_in && motorNum_in<=3 && input[1]!=NULL);
connectorNum_in = atoi(&input[1]);
connectorNumValid = (0<=connectorNum_in && connectorNum_in<=12 && input[1]!=NULL);
volatile const MotorDriver::StatusRegMotor &statusReg = motors[motorNum_in]->StatusReg();
volatile const MotorDriver::AlertRegMotor &alertReg = motors[motorNum_in]->AlertReg();
switch(input[0]){
case 'e':
if (!motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
} else {
motors[motorNum_in]->EnableRequest(true);
SendFeedback(FB_ENABLED_WAITING_ON_HLFB);
while (statusReg.bit.HlfbState != MotorDriver::HLFB_ASSERTED &&
!statusReg.bit.MotorInFault){
continue;
}
if(statusReg.bit.MotorInFault){
motors[motorNum_in]->EnableRequest(false);
SendFeedback(FB_ENABLE_FAILURE);
} else {
SendFeedback(FB_COMMAND_OK);
}
}
break;
case 'd':
if (!motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
} else {
motors[motorNum_in]->EnableRequest(false);
SendFeedback(FB_COMMAND_OK);
}
break;
case 'm':
if (!motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
} else if ( statusReg.bit.ReadyState==MotorDriver::MOTOR_DISABLED ||
statusReg.bit.ReadyState==MotorDriver::MOTOR_ENABLING){
SendFeedback(FB_ERR_MOVE_NOT_ENABLED);
} else if (motors[motorNum_in]->StatusReg().bit.AlertsPresent) {
SendFeedback(FB_ERR_MOVE_IN_ALERT);
} else {
moveDistance_in = atoi(&input[2]);
if (ABSOLUTE_MOVE){
motors[motorNum_in]->Move(moveDistance_in, MotorDriver::MOVE_TARGET_ABSOLUTE);
} else {
motors[motorNum_in]->Move(moveDistance_in);
}
SendFeedback(FB_COMMAND_OK);
}
break;
case 'v':
if (!motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
} else if ( statusReg.bit.ReadyState==MotorDriver::MOTOR_DISABLED ||
statusReg.bit.ReadyState==MotorDriver::MOTOR_ENABLING){
SendFeedback(FB_ERR_MOVE_NOT_ENABLED);
} else if (motors[motorNum_in]->StatusReg().bit.AlertsPresent) {
SendFeedback(FB_ERR_MOVE_IN_ALERT);
} else {
velocity_in = atoi(&input[2]);
motors[motorNum_in]->MoveVelocity(velocity_in);
SendFeedback(FB_COMMAND_OK);
}
break;
case 'q':
if (!motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
break;
}
switch(input[2]){
case 'p':
case 'P':
queriedValue = (int32_t) motors[motorNum_in]->PositionRefCommanded();
if (verboseFeedback){
ioPort.Send("Motor ");
ioPort.Send(motorNum_in);
ioPort.Send(" is in position (steps) ");
}
ioPort.SendLine(queriedValue);
break;
case 'v':
case 'V':
queriedValue = motors[motorNum_in]->VelocityRefCommanded();
if (verboseFeedback){
ioPort.Send("Motor ");
ioPort.Send(motorNum_in);
ioPort.Send(" is at velocity (steps/s) ");
}
ioPort.SendLine(queriedValue);
break;
case 's':
case 'S':
if (verboseFeedback){
SendVerboseStatus(motorNum_in);
} else {
ioPort.SendLine(statusReg.reg, 2);
if (statusReg.bit.AlertsPresent){
ioPort.SendLine(alertReg.reg, 2);
}
}
break;
default:
SendFeedback(FB_ERR_INVALID_QUERY_REQUEST);
}
break;
case 'l':
if ( !motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
break;
}
limit_in = atoi(&input[3]);
switch(input[2]){
case 'v':
case 'V':
if (MIN_VEL_LIMIT<=limit_in && limit_in<=MAX_VEL_LIMIT){
velocityLimits[motorNum_in] = limit_in;
motors[motorNum_in]->VelMax(velocityLimits[motorNum_in]);
SendFeedback(FB_COMMAND_OK);
} else {
SendFeedback(FB_ERR_LIMIT_OUT_OF_BOUNDS);
}
break;
case 'a':
case 'A':
if (MIN_ACCEL_LIMIT<=limit_in && limit_in<=MAX_ACCEL_LIMIT){
accelerationLimits[motorNum_in] = limit_in;
motors[motorNum_in]->AccelMax(velocityLimits[motorNum_in]);
SendFeedback(FB_COMMAND_OK);
} else {
SendFeedback(FB_ERR_LIMIT_OUT_OF_BOUNDS);
}
break;
default:
SendFeedback(FB_ERR_INVALID_LIMIT_REQUEST);
break;
}
break;
case 'c':
if (!motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
break;
}
motorEnabledBeforeClearingAlerts = motors[motorNum_in]->EnableRequest();
if (statusReg.bit.MotorInFault){
motors[motorNum_in]->EnableRequest(false);
if(motorEnabledBeforeClearingAlerts){
motors[motorNum_in]->EnableRequest(true);
}
}
motors[motorNum_in]->ClearAlerts();
SendFeedback(FB_COMMAND_OK);
break;
case 'z':
if (!motorNumValid){
SendFeedback(FB_ERR_MOTOR_NUM_INVALID);
break;
}
motors[motorNum_in]->PositionRefSet(0);
SendFeedback(FB_COMMAND_OK);
break;
case 'i':
if (!connectorNumValid){
SendFeedback(FB_ERR_CONNECTOR_NUM_INVALID);
break;
}
if (connectors[connectorNum_in]->Mode() != Connector::INPUT_DIGITAL &&
connectors[connectorNum_in]->Mode() != Connector::INPUT_ANALOG){
SendFeedback(FB_ERR_CONNECTOR_MODE_INCOMPATIBLE);
break;
}
inputConnectorValue = connectors[connectorNum_in]->State();
if(verboseFeedback){
ioPort.Send("Connector ");
ioPort.Send(connectorNum_in);
ioPort.Send(" value: ");
}
ioPort.SendLine(inputConnectorValue);
break;
case 'o':
if (!connectorNumValid){
SendFeedback(FB_ERR_CONNECTOR_NUM_INVALID);
break;
}
if (connectors[connectorNum_in]->Mode() != Connector::OUTPUT_DIGITAL &&
connectors[connectorNum_in]->Mode() != Connector::OUTPUT_ANALOG){
SendFeedback(FB_ERR_CONNECTOR_MODE_INCOMPATIBLE);
break;
}
outputConnectorValue_in = atoi(&input[3]);
if(!connectors[connectorNum_in]->State(outputConnectorValue_in)){
SendFeedback(FB_ERR_IO_OUTPUT);
} else {
SendFeedback(FB_COMMAND_OK);
}
break;
case 'f':
switch(atoi(&input[1])){
case 0:
verboseFeedback = false;
SendFeedback(FB_COMMAND_OK);
break;
case 1:
verboseFeedback = true;
SendFeedback(FB_COMMAND_OK);
break;
default:
SendFeedback(FB_ERR_INVALID_FEEDBACK_OPTION);
break;
}
break;
case 'h':
SendFeedback(FB_HELP);
break;
default:
SendFeedback(FB_ERR_UNRECOGNIZED_COMMAND);
break;
}
}
}
}
void SendFeedback(int32_t messageNumber){
if (verboseFeedback){
ioPort.SendLine(FeedbackMessages[messageNumber].message);
} else {
ioPort.SendLine(FeedbackMessages[messageNumber].number);
}
}
void SendVerboseStatus(int32_t motorNumber) {
volatile const MotorDriver::StatusRegMotor &statusReg = motors[motorNumber]->StatusReg();
volatile const MotorDriver::AlertRegMotor &alertReg = motors[motorNumber]->AlertReg();
ioPort.Send("Motor status register for motor M");
ioPort.Send(motorNumber);
ioPort.Send(": ");
ioPort.SendLine(statusReg.reg, 2);
ioPort.Send("AtTargetPosition: ");
ioPort.SendLine(statusReg.bit.AtTargetPosition);
ioPort.Send("StepsActive: ");
ioPort.SendLine(statusReg.bit.StepsActive);
ioPort.Send("AtTargetVelocity: ");
ioPort.SendLine(statusReg.bit.AtTargetVelocity);
ioPort.Send("MoveDirection: ");
ioPort.SendLine(statusReg.bit.MoveDirection);
ioPort.Send("MotorInFault: ");
ioPort.SendLine(statusReg.bit.MotorInFault);
ioPort.Send("Enabled: ");
ioPort.SendLine(statusReg.bit.Enabled);
ioPort.Send("PositionalMove: ");
ioPort.SendLine(statusReg.bit.PositionalMove);
ioPort.Send("HLFB State: ");
switch (statusReg.bit.HlfbState) {
case 0:
ioPort.SendLine("HLFB_DEASSERTED");
break;
case 1:
ioPort.SendLine("HLFB_ASSERTED");
break;
case 2:
ioPort.SendLine("HLFB_HAS_MEASUREMENT");
break;
case 3:
ioPort.SendLine("HLFB_UNKNOWN");
break;
default:
ioPort.SendLine("???");
}
ioPort.Send("AlertsPresent: ");
ioPort.SendLine(statusReg.bit.AlertsPresent);
ioPort.Send("Ready state: ");
switch (statusReg.bit.ReadyState) {
case MotorDriver::MOTOR_DISABLED:
ioPort.SendLine("Disabled");
break;
case MotorDriver::MOTOR_ENABLING:
ioPort.SendLine("Enabling");
break;
case MotorDriver::MOTOR_FAULTED:
ioPort.SendLine("Faulted");
break;
case MotorDriver::MOTOR_READY:
ioPort.SendLine("Ready");
break;
case MotorDriver::MOTOR_MOVING:
ioPort.SendLine("Moving");
break;
default:
ioPort.SendLine("???");
}
ioPort.Send("Triggering: ");
ioPort.SendLine(statusReg.bit.Triggering);
ioPort.Send("InPositiveLimit: ");
ioPort.SendLine(statusReg.bit.InPositiveLimit);
ioPort.Send("InNegativeLimit: ");
ioPort.SendLine(statusReg.bit.InNegativeLimit);
ioPort.Send("InEStopSensor: ");
ioPort.SendLine(statusReg.bit.InEStopSensor);
ioPort.SendLine("--------------------------------");
if (statusReg.bit.AlertsPresent){
ioPort.Send("Alert register: ");
ioPort.SendLine(alertReg.reg, 2);
ioPort.Send("MotionCanceledInAlert: ");
ioPort.SendLine(alertReg.bit.MotionCanceledInAlert);
ioPort.Send("MotionCanceledPositiveLimit: ");
ioPort.SendLine(alertReg.bit.MotionCanceledPositiveLimit);
ioPort.Send("MotionCanceledNegativeLimit: ");
ioPort.SendLine(alertReg.bit.MotionCanceledNegativeLimit);
ioPort.Send("MotionCanceledSensorEStop: ");
ioPort.SendLine(alertReg.bit.MotionCanceledSensorEStop);
ioPort.Send("MotionCanceledMotorDisabled: ");
ioPort.SendLine(alertReg.bit.MotionCanceledMotorDisabled);
ioPort.Send("MotorFaulted: ");
ioPort.SendLine(alertReg.bit.MotorFaulted);
ioPort.SendLine("--------------------------------");
}
}