背景:项目中需要用到可以低速转动的电机,并且力矩需要满足项目条件,因此这里选用小米电机(CyberGear 微电机)。
本实验硬件条件:单片机,STM32F103RET6、CAN通讯芯片。
注:PCB由自己设计绘制,在设计中单片机本身的时钟频率无法与高频率CAN同步,因此需要增加8M晶振。
## 原理图设计
下图为单片机主电路图
下图为CAN通讯电路图
## 控制程序设计
控制程序都封装在.c文件中了
## 小米电机ID检查,通信类型为0
```c
/*—————————————————————————————————————————————————————————————————————————————————*/
/** @brief 小米电机ID检查,通信类型为0
* @param[in] id: 控制电机CAN_ID【出厂默认0x7F】
**/
void check_cybergear(uint8_t ID)
{
uint8_t tx_data[8] = {0};//没有数据
//扩展ID的组合,依旧是3个部分
txMsg.ExtId = Communication_Type_GetID<<24|Master_CAN_ID<<8|ID;
MyCAN_Transmit(txMsg.ExtId,txMsg.DLC,tx_data);//写入指令
}
```
### 小米运控模式指令,通信类型:1
```C
/*——————————————————————————————————————————————————————————————————————————————————*/
/** @brief 小米运控模式指令,通信类型:1
* @param[in] Motor: 目标电机结构体
* @param[in] torque: 力矩设置[-12,12] N*M
* @param[in] MechPosition: 位置设置[-12.5,12.5] rad
* @param[in] speed: 速度设置[-30,30] rpm
* @param[in] kp: 比例参数设置
* @param[in] kd: 微分参数设置
* @retval none
**/
void motor_controlmode(MI_Motor *Motor,float torque, float MechPosition, float speed, float kp, float kd)
{
uint8_t tx_data[8]={0};//发送数据初始化
//装填发送数据
//将目标角度转化为16位2进制数,对应字节0~1,假设是0x1234
tx_data[0]=float_to_uint(MechPosition,P_MIN,P_MAX,16)>>8; //取得是高位的结果,即0x12
tx_data[1]=float_to_uint(MechPosition,P_MIN,P_MAX,16); //取得是低位的结果,即0x34
//将目标速度转化为16位2进制数,对应字节2~3
tx_data[2]=float_to_uint(speed,V_MIN,V_MAX,16)>>8;
tx_data[3]=float_to_uint(speed,V_MIN,V_MAX,16);
//目标KP
tx_data[4]=float_to_uint(kp,KP_MIN,KP_MAX,16)>>8;
tx_data[5]=float_to_uint(kp,KP_MIN,KP_MAX,16);
//目标KD
tx_data[6]=float_to_uint(kd,KD_MIN,KD_MAX,16)>>8;
tx_data[7]=float_to_uint(kd,KD_MIN,KD_MAX,16);
txMsg.ExtId = Communication_Type_MotionControl<<24|float_to_uint(torque,T_MIN,T_MAX,16)<<8|Motor->CAN_ID;//装填扩展帧数据
MyCAN_Transmit(txMsg.ExtId,txMsg.DLC,tx_data);//写入指令
}
```
### 电机反馈数据,通信类型2
```C
/*——————————————————————————————————————————————————————————————————————————————————————*/
/** @brief 电机反馈数据,通信类型2
* @param[in] 信息存放的地址
* @retval none
*/
void Rx_Fifo0_Msg(MI_Motor *Motor)
{
if (MyCAN_ReceiveFlag())//判断是否接收到报文信息
{
uint32_t RxID;
uint8_t RxLength;
uint8_t RxData[8];
//接收信息放到对应的接收报文之中
MyCAN_Receive(&RxID, &RxLength, RxData);
Motor->CAN_ID=(RxID&0xFFFF)>>8;//获取接收数据的ID:保留低16位,其余全变成零,再右移8位,则获得了bit8~bit15的canid
Motor->Angle=uint16_to_float(RxData[0]<<8|RxData[1],MIN_P,MAX_P,16);//将字节0~1转化位浮点数,即为当前角度
Motor->Speed=uint16_to_float(RxData[2]<<8|RxData[3],V_MIN,V_MAX,16);//将字节2~3转化位浮点数,即为当前速度
Motor->Torque=uint16_to_float(RxData[4]<<8|RxData[5],T_MIN,T_MAX,16);//将字节4~5转化位浮点数,即为当前角度
Motor->Temp=(RxData[6]<<8|RxData[7])*Temp_Gain;//将字节4~5转化为当前温度
Motor->error_code=(RxID&0x1F0000)>>16;
}
}
```
### 使能电机,通信类型为3
```C
/*****************************使能电机,通信类型为3*******************************
* @brief 使能小米电机
* @param[in] Motor:对应控制电机结构体
* @retval none
*****************************************************/
void start_cybergear(MI_Motor *Motor)
{
uint8_t tx_data[8] = {0};
txMsg.ExtId = Communication_Type_MotorEnable<<24|Master_CAN_ID<<8|Motor->CAN_ID;
MyCAN_Transmit(txMsg.ExtId,txMsg.DLC,tx_data);//写入指令
}
```
### 电机停止运行,通信类型为4
```C
/************************电机停止运行,通信类型为4********************************
* @brief 停止电机
* @param[in] Motor:对应控制电机结构体
* @param[in] clear_error:清除错误位(0 不清除 1清除)
* @retval None
*******************************************************************************/
void stop_cybergear(MI_Motor *Motor,uint8_t clear_error)
{
uint8_t tx_data[8]={0};
tx_data[0]=clear_error;//清除错误位设置
txMsg.ExtId = Communication_Type_MotorStop<<24|Master_CAN_ID<<8|Motor->CAN_ID;
MyCAN_Transmit(txMsg.ExtId,txMsg.DLC,tx_data);//写入指令
}
```
### 小米电机初始化
```C
/**
* @brief 小米电机初始化
* @param[in] Motor: 电机结构体
* @param[in] Can_Id: 小米电机ID(默认0x7F)
* @param[in] Motor_Num: 电机编号
* @param[in] mode: 电机工作模式(0.运动模式Motion_mode 1. 位置模式Position_mode 2. 速度模式Speed_mode 3. 电流模式Current_mode)
* @retval none
*/
void init_cybergear(MI_Motor *Motor,uint8_t Can_Id, uint8_t mode)
{
txMsg.StdId = 0; //配置CAN发送:标准帧清零
txMsg.ExtId = 0; //配置CAN发送:扩展帧清零
txMsg.IDE = CAN_ID_EXT; //配置CAN发送:扩展帧
txMsg.RTR = CAN_RTR_DATA; //配置CAN发送:数据帧
txMsg.DLC = 0x08; //配置CAN发送:数据长度
Motor->CAN_ID=Can_Id; //ID设置
set_mode_cybergear(Motor,mode);//设置电机模式
start_cybergear(Motor); //使能电机
}
```
### 单个参数读取,通信类型17
```C
/*************************单个参数读取,通信类型17******************
*@brief 电机参数读取
*@param[in] ID,电机的ID号
*/
void check_param_cybergear(MI_Motor *Motor, uint8_t index)
{
uint8_t tx_data[8]={0};
txMsg.ExtId = Communication_Type_GetSingleParameter<<24|Master_CAN_ID<<8|Motor->CAN_ID;//装填扩展帧数据
tx_data[0]=index>>8;//高8位
tx_data[1]=index;//低8位
MyCAN_Transmit(txMsg.ExtId,txMsg.DLC,tx_data);//写入指令
}
```
### 对应说明书的单个参数写入,通信类型为18
```C
/******************对应说明书的单个参数写入,通信类型为18*************
* @brief 写入电机参数
* @param[in] Motor:对应控制电机结构体
* @param[in] Index:写入参数对应地址
* @param[in] Value:写入参数值
* @param[in] Value_type:写入参数数据类型,可以对照字节数进行区分
* @retval none
*/
static void Set_Motor_Parameter(MI_Motor *Motor,uint16_t Index,float Value,char Value_type)
{
uint8_t tx_data[8]={0};//写入的数据
//扩展ID,包括三个部分:通信类型、主ID、电机ID
txMsg.ExtId = Communication_Type_SetSingleParameter<<24|Master_CAN_ID<<8|Motor->CAN_ID;
//参见说明书的通信类型18里面,Index的相关应用方法,Index共
tx_data[0]=Index; //高8位
tx_data[1]=Index>>8; //低8位
tx_data[2]=0x00;
tx_data[3]=0x00;
//如果参数类型为浮点型,则对应的数据所占字节数为4,对应的数据需要转化为8个字节
if(Value_type == 'f'){
Float_to_Byte(Value);//将数值转化为4个byte 作为返回值
tx_data[4]=byte[3];//高8位
tx_data[5]=byte[2];
tx_data[6]=byte[1];
tx_data[7]=byte[0];//低8位
}
//如果参数类型为uint8,只有一个字节
else if(Value_type == 's'){
tx_data[4]=(uint8_t)Value;
tx_data[5]=0x00;
tx_data[6]=0x00;
tx_data[7]=0x00;
}
//can_txd();
MyCAN_Transmit(txMsg.ExtId,txMsg.DLC,tx_data);//写入指令
}
```
### 小米速度模式指令
此代码根据电机说明书中给出的速度模式通讯顺序编写
```C
/*——————————————————————————————————————————————————————————————————————————————————*/
/** @brief 小米速度模式指令,通信类型:2
* @param[in] Motor: 目标电机结构体
* @param[in] CurValue: 电流设置[0,23] A
* @param[in] SpeedValue: 速度设置[-30,30] rad
* @retval none
**/
void motor_speedmode(MI_Motor *Motor, float CurValue, float SpeedValue)
{
//设置limit_cur参数(最大电流指令)
Set_Motor_Parameter(Motor,Limit_Cur,CurValue,'f');
DelayMs(10);
//设置spd_ref参数(转速模式转速指令)
Set_Motor_Parameter(Motor,Spd_Ref,SpeedValue,'f');
DelayMs(10);
}
```
下图为说明书中速度模式指令顺序

main函数中根据此顺序进行小米电机初始化,然后设置速度模式,使能电机,写入单个参数limit_cur(最大电流),然后写入单个参数spd_ref(最大速度)。即可让电机按照速度模式转动。
下面为main函数:
```C
MI_Motor Cyber;
uint8_t Motor_ID=0x01; //电机ID,初始状态默认为0x7F,这里调整为0x01
uint8_t Mode = Speed_mode;//电机工作模式(0.运动模式Motion_mode 1. 位置模式Position_mode 2. 速度模式Speed_mode 3. 电流模式Current_mode)
uint32_t id;
/*
************************************************************
* 函数名称: Hardware_Init
*
* 函数功能: 硬件初始化
************************************************************
*/
void Hardware_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断控制器分组设置
Delay_Init(); //systick初始化
uart1_init(115200); //串口1
uart2_init(115200); //串口2
LED_Init();
MyCAN_Init();
}
/*
************************************************************
* 函数名称: main
*
* 函数功能:
************************************************************
*/
int main(void)
{
Hardware_Init(); //初始化外围硬件
DelayMs(1000);
//以下用于调用程序验证相应的通信数据是否准确
init_cybergear(&Cyber, 0X01, Mode);//初始化电机
//*校验用的内容
check_cybergear(Cyber.CAN_ID);//小米电机ID检查,通信类型为0
//电机运控模式
// motor_controlmode(&Cyber, 0, 0, 1, 0, 0.5);
//电机速度模式
motor_speedmode(&Cyber,23,1);
while(1)
{
LED_STA();
// MyCAN_Transmit(0X555, 8 ,AA);
DelayMs(10);
}
}
原理图: 无
仿真: 无
代码:
STM32控制小米电机.7z
(198.2 KB, 下载次数: 3)
|