背景介绍:为了利用小米电机,搭建机械臂的关节,需要学习小米电机的使用方法。计划采用STM32驱动小米电机,实现指定运动,为此需要了解他们之间的通信方式,指令写入方法等。花了很多时间学习,但网络上相关资料很少,且对于我这类基础一般的同学,注释不够详细。为此,我认真研读了电机的说明书,参考相关的博文,实现了电机的CAN通信,特作此记录。
更新日期:2024.10.19
更新内容:补充了电机实验,通过电机实验与反馈对相关内容进行了调整,电机的运控模式、位置模式、速度模式均进行了验证
更新日期:2024.10.31
更新内容:文末附上源码资源链接
一、环回测试接线图
元件:ST-Link , STM32F103C8T6, 显示屏, 按键开关*2
二、CAN通信实现过程
1.CAN收发
需要设计一个CAN的报文输入与报文接收的函数,以用来进行数据收发,这里前面已经实现了
2.小米电机协议转化
需要弄清楚小米电机的CAN通讯协议类型,封装成相应的函数,再结合CAN收发函数进行对应的指令发送以及接收
1)小米电机通信:CAN 2.0通信接口,波特率为1Mbps,采用的扩展帧格式
2)小米电机通信相应的数据格式如下:
结合说明书可以得到具体通信指令对应关系如下:
参照小米电机的说明书内容,29位扩展ID中,Bit28~Bit24用来表示通信类型,说明书关于通信类型包括10种,具体解释与对应关系如下:
//控制命令宏定义,与说明书对应
//获取设备ID(通信类型为0)
#define Communication_Type_GetID 0x00 //获取设备的ID和64位MCU唯一标识符
//运控模式电机控制指令(通信类型为1)
#define Communication_Type_MotionControl 0x01 //用来向主机发送控制指令
//电机反馈数据(通信类型为2)
#define Communication_Type_MotorRequest 0x02 //用来向主机反馈电机运行状态
//电机使能运行(通信类型为3)
#define Communication_Type_MotorEnable 0x03 //电机使能运行
//电机停止运行(通信类型为4)
#define Communication_Type_MotorStop 0x04 //电机停止运行
//电机机械零位(通信类型为6)
#define Communication_Type_SetPosZero 0x06 //设置电机机械零位
//更改电机的CAN_ID(通信类型为7)
#define Communication_Type_CanID 0x07 //更改当前电机CAN_ID
//单个参数读取(通信类型为17,即0x11)
#define Communication_Type_GetSingleParameter 0x11 //读取单个参数
//单个参数写入(通信类型为18,即0x12)
#define Communication_Type_Control_Mode 0x12
#define Communication_Type_SetSingleParameter 0x12 //设定单个参数
//故障反馈帧(通信类型为21,即0x15)
#define Communication_Type_ErrorFeedback 0x15 //故障反馈帧
当要利用STM32发送时,要结合说明书的不同通信类型对应的数据格式,整理获取扩展ID(29位)和对应的数据内容(8Byte)。
例如:下图为获取设备ID的协议使用说明,根据其特点,可以写成扩展ID如下:
//扩展ID由通信类型+主CANID+目标电机ID
txMsg.ExtId = Communication_Type_GetID<<24|Master_CAN_ID<<8|ID;
利用上述方法,参照说明书可以获得所有通讯协议的扩展ID的写法,只要对应的按位填上数据即可。同理也可以写出对应的数据区参数。则可以逐一写出各个函数,实现与小米电机的CAN通信。
三、代码实现与解释说明
1.CAN收发
==》MyCAN.c文件,包括初始化、发送报文、接收报文函数。
1) 初始化函数:MyCAN_Init(),更新于2024.10.19,更新了波特率为1Mb
用来初始化CAN,注意里面的引脚和工作模式,以及相应的波特率设置可以按照控制元件要求修改。这里有一个FIFO0,要与后面接收数据时的FIFO对应。初始化的流程与GPIO口初始化流程类似:开启时钟——定义结构体——调用初始化函数
void MyCAN_Init(void)
{
/**********************开启GPIOA和CAN1时钟************************************/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//启用GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//启用CAN1时钟
/***********************初始化GPIOA的12号引脚,CAN 输出***********************/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//模式为复用推挽输出,注意如果要用作CAN通信,需要选择复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//12号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//引脚速度
GPIO_Init(GPIOA, &GPIO_InitStructure);//引脚初始化
/*********************初始化GPIO的11号引脚,CAN输入***************************/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*************************初始化CAN1控制器************************************/
CAN_InitTypeDef CAN_InitStructure;//定义一个CAN的结构体
/*CAN工作模式选择
CAN_Mode_Normal ((uint8_t)0x00) 正常模式
CAN_Mode_LoopBack ((uint8_t)0x01) 环回模式
CAN_Mode_Silent ((uint8_t)0x02) 静默模式,只听不发
CAN_Mode_Silent_LoopBack ((uint8_t)0x03) 环回静默模式*/
//CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;//CAN工作模式为环回模式,用于自收自发测试用,实际通信时可以改成正常模式
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal ;
/*波特率计算
波特率 = 36M(时钟频率) / 48 (预分频器的值)/ (1 + 2(BS1)+ 3(BS2)) = 125K
波特率为1Mb,小米电机的波特率就是1Mb36/6/(1+3+2)*