在稚晖君readme中,提到Ctrl-Step驱动的使用方式,通过CAN或者串口发送指令即可控制电机。关于指令的说明见源代码`UserApp`文件夹的`interface_can.cpp`和`interface_uart.cpp`。接下来,就让我们一起来学习interface_can.cpp里的内容吧!
该代码是一个CAN总线通信的回调函数,在接收到CAN消息时被调用。根据收到的命令,执行相应的操作,操作大概分三部分,控制设定操作的命令、带有内存操作的命令以及一些一些查询命令,首先,让我们来学习第一部分的内容。
首先定义了一个CAN_TxHeaderTypeDef类型的变量txHeader,用于发送CAN消息。CAN_TxHeaderTypeDef是一个结构体类型,用于配置CAN消息的发送参数
#include "common_inc.h"
#include "configurations.h"
#include <can.h>
extern Motor motor;
extern EncoderCalibrator encoderCalibrator;
CAN_TxHeaderTypeDef txHeader = //定义了一个CAN_TxHeaderTypeDef类型的变量txHeader,(CAN_TxHeaderTypeDef是一个结构体类型,用于配置CAN消息的发送参数)
{
.StdId = 0x00, //id
.ExtId = 0x00, //扩展id
.IDE = CAN_ID_STD,//标准帧结构
.RTR = CAN_RTR_DATA,//数据帧
.DLC = 8, //数据长度
.TransmitGlobalTime = DISABLE
};
在OnCanCmd函数中,根据收到的命令_cmd,执行不同的操作。
0x01: 启用电机
0x02: 执行编码器校准
switch (_cmd)
{
// 0x00~0x0F No Memory CMDs
case 0x01: // Enable Motor 启动电机
motor.controller->requestMode = (*(uint32_t*) (RxData) == 1) ? //如果收到数据为“1”,则设置为第一个模式,否则设置为第二个模式
Motor::MODE_COMMAND_VELOCITY : Motor::MODE_STOP;
break;
case 0x02: // Do Calibration(执行编码器校准)
encoderCalibrator.isTriggered = true;//将encoderCalibrator.isTriggered设置为true,表示触发编码器的校准过程。
break;
接下来是一些电流、速度、位置的设定。
只要看懂一个以后,你会发现这些参数的设定都大同小异,以设置位置设定值为例。
- 判断modeRunning是否为位置控制模式
- 不是的话先设定为该模式
- 设置位置设定值
- 经过处理后调用函数将消息发出去。
case 0x05: // Set Position SetPoint //设置位置设定值
if (motor.controller->modeRunning != Motor::MODE_COMMAND_POSITION)
{
motor.config.motionParams.ratedVelocity = boardConfig.velocityLimit;
motor.controller->SetCtrlMode(Motor::MODE_COMMAND_POSITION);
}
motor.controller->SetPositionSetPoint(
(int32_t) (*(float*) RxData * (float) motor.MOTOR_ONE_CIRCLE_SUBDIVIDE_STEPS));
if (_data[4]) // Need Position & Finished ACK 判断_data[4]的值,_data[4]的值为非零(即为真),表示需要发送位置和完成状态的确认消息
{
tmpF = motor.controller->GetPosition(); //调用GetPosition函数获取当前位置
auto* b = (unsigned char*) &tmpF;//转换为unsigned char数组,存储在_data中
for (int i = 0; i < 4; i++)
_data[i] = *(b + i);
_data[4] = motor.controller->state == Motor::STATE_FINISH ? 1 : 0; //_data[4]设置为1或0,表示是否完成状态。
txHeader.StdId = (boardConfig.canNodeId << 7) | 0x23;
CAN_Send(&txHeader, _data);//调用CAN_Send函数将确认消息发送出去
}
break;
接下来看剩下的,还有不懂的也可以看看代码中的注释哦
case 0x03: // Set Current SetPoint 设置电流设定值
if (motor.controller->modeRunning != Motor::MODE_COMMAND_CURRENT) //如果motor.controller不是电流控制模式
motor.controller->SetCtrlMode(Motor::MODE_COMMAND_CURRENT);//用SetCtrlMode函数设置为电流控制模式
motor.controller->SetCurrentSetPoint((int32_t) (*(float*) RxData * 1000)); /*通过motor.controller->SetVelocitySetPoint函数
设置速度设定值。*/
break;
case 0x04: // Set Velocity SetPoint设置速度设定值
if (motor.controller->modeRunning != Motor::MODE_COMMAND_VELOCITY)
{
motor.config.motionParams.ratedVelocity = boardConfig.velocityLimit;//将电机的额定速度设置为boardConfig.velocityLimit
motor.controller->SetCtrlMode(Motor::MODE_COMMAND_VELOCITY);
}
motor.controller->SetVelocitySetPoint( /*调用motor.controller->SetVelocitySetPoint函数
将计算得到的速度设定值设置给电机控制器*/
(int32_t) (*(float*) RxData * //解析RxData中的数据,将其转换为float类型
(float) motor.MOTOR_ONE_CIRCLE_SUBDIVIDE_STEPS)); /*乘以motor.MOTOR_ONE_CIRCLE_SUBDIVIDE_STEPS转换为int类型,
得到速度设定值*/
break;
case 0x05: // Set Position SetPoint //设置位置设定值
if (motor.controller->modeRunning != Motor::MODE_COMMAND_POSITION)
{
motor.config.motionParams.ratedVelocity = boardConfig.velocityLimit;
motor.controller->SetCtrlMode(Motor::MODE_COMMAND_POSITION);
}
motor.controller->SetPositionSetPoint(
(int32_t) (*(float*) RxData * (float) motor.MOTOR_ONE_CIRCLE_SUBDIVIDE_STEPS));
if (_data[4]) // Need Position & Finished ACK 判断_data[4]的值,_data[4]的值为非零(即为真),
//表示需要发送位置和完成状态的确认消息
{
tmpF = motor.controller->GetPosition(); //调用GetPosition函数获取当前位置
auto* b = (unsigned char*) &tmpF;//转换为unsigned char数组,存储在_data中
for (int i = 0; i < 4; i++)
_data[i] = *(b + i);
_data[4] = motor.controller->state == Motor::STATE_FINISH ? 1 : 0; //_data[4]设置为1或0,表示是否完成状态。
txHeader.StdId = (boardConfig.canNodeId << 7) | 0x23;
CAN_Send(&txHeader, _data);//调用CAN_Send函数将确认消息发送出去
}
break;
case 0x06: // Set Position with Time设置带有时间的位置设定值
if (motor.controller->modeRunning != Motor::MODE_COMMAND_POSITION)
motor.controller->SetCtrlMode(Motor::MODE_COMMAND_POSITION);
motor.controller->SetPositionSetPointWithTime(
(int32_t) (*(float*) RxData * (float) motor.MOTOR_ONE_CIRCLE_SUBDIVIDE_STEPS),
*(float*) (RxData + 4));
if (_data[4]) // Need Position & Finished ACK
{
tmpF = motor.controller->GetPosition();
auto* b = (unsigned char*) &tmpF;
for (int i = 0; i < 4; i++)
_data[i] = *(b + i);
_data[4] = motor.controller->state == Motor::STATE_FINISH ? 1 : 0;
txHeader.StdId = (boardConfig.canNodeId << 7) | 0x23;
CAN_Send(&txHeader, _data);
}
break;
case 0x07: // Set Position with Velocity-Limit//设置带有速度限制的位置设定值
{
if (motor.controller->modeRunning != Motor::MODE_COMMAND_POSITION)
{
motor.config.motionParams.ratedVelocity = boardConfig.velocityLimit;
motor.controller->SetCtrlMode(Motor::MODE_COMMAND_POSITION);
}
motor.config.motionParams.ratedVelocity =
(int32_t) (*(float*) (RxData + 4) * (float) motor.MOTOR_ONE_CIRCLE_SUBDIVIDE_STEPS);
motor.controller->SetPositionSetPoint(
(int32_t) (*(float*) RxData * (float) motor.MOTOR_ONE_CIRCLE_SUBDIVIDE_STEPS));
// Always Need Position & Finished ACK
tmpF = motor.controller->GetPosition();
auto* b = (unsigned char*) &tmpF;
for (int i = 0; i < 4; i++)
_data[i] = *(b + i);
_data[4] = motor.controller->state == Motor::STATE_FINISH ? 1 : 0;
txHeader.StdId = (boardConfig.canNodeId << 7) | 0x23;
CAN_Send(&txHeader, _data);
}
break;
是不是再看其他的就感觉轻松多啦!