小米微电机STM32 HAL库驱动教程

小米微电机STM32 HAL库驱动教程

目录

一、相关资料

二、驱动代码

MI_motor_dev.h头文件:

MI_motor_dev.c源文件:

重定义can中断回调:

三、使用过程

一、相关资料

电机图纸、上位机、电机固件等:https://pan.quark.cn/s/7ab823969d3b

使用说明书(建议阅读)和基于STM32F4+keil+CUBE+FreeRTOS的工程文件:

https://pan.quark.cn/s/3725c8be2291

二、驱动代码

根据文档写出电机驱动文件如下(经测试可用):

MI_motor_dev.h头文件:

#ifndef MI_DEV_H

#define MI_DEV_H

#ifdef __cplusplus

extern "C"

{

#endif

#include "pid_lib.h"//pid算法库,可以不要

#define P_MIN -12.5f

#define P_MAX 12.5f

#define V_MIN -30.0f

#define V_MAX 30.0f

#define KP_MIN 0.0f

#define KP_MAX 500.0f

#define KD_MIN 0.0f

#define KD_MAX 5.0f

#define T_MIN -12.0f

#define T_MAX 12.0f

typedef enum

{

OK = 0,//无故障

BAT_LOW_ERR = 1,//欠压故障

OVER_CURRENT_ERR = 2,//过流

OVER_TEMP_ERR = 3,//过温

MAGNETIC_ERR = 4,//磁编码故障

HALL_ERR_ERR = 5,//HALL编码故障

NO_CALIBRATION_ERR = 6//未标定

}

motor_state_e;//电机状态(故障信息)

typedef enum

{

RESET_MODE = 0,//Reset[模式]

CALI_MODE = 1,//Cali 模式[标定]

RUN_MODE = 2//Motor模式[运行]

} motor_mode_e;//电机运行模式

typedef struct

{

uint32_t motor_id : 8; // 只占8位

uint32_t data : 16;

uint32_t mode : 5;

uint32_t res : 3;

} EXT_ID_t; // 32位扩展ID解析结构体

typedef struct

{

// 电机反馈

int16_t angle_temp;

int16_t speed_temp;

int16_t torque_temp;

int16_t temprature_temp;

float angle; // 连续角

float speed;

float torque;

float temprature;

uint32_t last_update_time; // 编码器时间戳

} Motor_fdb_t; // 电机编码器反馈结构体

typedef struct

{

CAN_HandleTypeDef *phcan;

motor_state_e motor_state;

motor_mode_e motor_mode;

EXT_ID_t EXT_ID;

uint8_t txdata[8];

PID_t PID;

Motor_fdb_t motor_fdb;

} MI_Motor_t;

/**********************Functions*************************8*/

void MI_motor_get_ID(MI_Motor_t* hmotor);

void MI_motor_init(MI_Motor_t* hmotor,CAN_HandleTypeDef *phcan);

void MI_motor_enable(MI_Motor_t *hmotor, uint8_t id);

void MI_motor_controlmode(MI_Motor_t* hmotor, float torque, float MechPosition , float speed , float kp , float kd);

void MI_motor_stop(MI_Motor_t *hmotor);

void MI_motor_setMechPosition2Zero(MI_Motor_t *hmotor);

void MI_motor_changeID(MI_Motor_t* hmotor,uint8_t Now_ID,uint8_t Target_ID);

void MIMotor_MotorDataDecode(uint32_t rx_EXT_id, uint8_t rxdata[]);

extern MI_Motor_t MI_Motor;

#endif

// #endif

#ifdef __cplusplus

}

#endif

MI_motor_dev.c源文件:

/**

*

* @File: MI_motor_dev.c

* @Author: 本人不帅

*

*/

/* Includes -------------------------------------------------------------------*/

#include "MI_motor_dev.h"

uint8_t MI_MASTERID = 1; //master id 发送指令时EXTID的bit8:15,反馈的bit0:7

uint8_t MI_fdbid = 0;//反馈ID,获取电机ID和识别码用

uint8_t MI_MCU_identifier[8];

MI_Motor_t MI_Motor;

/**

* @brief float转int,数据打包用

* @param x float数值

* @param x_min float数值的最小值

* @param x_max float数值的最大值

* @param bits int的数据位数

* @retval null

*/

int float_to_uint(float x, float x_min, float x_max, int bits) {

float span = x_max - x_min;

float offset = x_min;

if(x > x_max) x=x_max;

else if(x < x_min) x= x_min;

return (int) ((x-offset)*((float)((1<

}

/**

* @brief 小米电机CAN通信发送

* @param hmotor 电机结构体

* @retval null

*/

CAN_TxHeaderTypeDef CAN_TxHeader_MI;

void MI_Motor_CanTx(MI_Motor_t* hmotor) {

CAN_TxHeader_MI.DLC = 8;

CAN_TxHeader_MI.IDE = CAN_ID_EXT;

CAN_TxHeader_MI.RTR = CAN_RTR_DATA;

CAN_TxHeader_MI.ExtId = *((uint32_t*)&(hmotor->EXT_ID));

/*CAN_TxHeader_MI.ExtId = hmotor->EXT_ID.motor_id<<24 | hmotor->EXT_ID.data << 8 | hmotor->EXT_ID.mode << 5;*/

uint32_t mailbox;

/* Start the Transmission process */

uint32_t ret = HAL_CAN_AddTxMessage(hmotor->phcan, &CAN_TxHeader_MI, hmotor->txdata, &mailbox);

if (ret != HAL_OK) {

/* Transmission request Error */

while(1);

}

}

/**

* @brief 小米电机初始化

* @param hmotor 电机结构体

* @param phcan can总线句柄

* @retval null

*/

void MI_motor_init(MI_Motor_t* hmotor,CAN_HandleTypeDef *phcan)

{

hmotor->phcan = phcan;

}

/**

* @brief 小米电机使能

* @param hmotor 电机结构体

* @param id 电机id

* @retval null

*/

void MI_motor_enable(MI_Motor_t* hmotor,uint8_t id)

{

hmotor->EXT_ID.mode = 3;

hmotor->EXT_ID.motor_id = id;

hmotor->EXT_ID.data = MI_MASTERID;

hmotor->EXT_ID.res = 0;

for(uint8_t i=0; i<8; i++)

{

hmotor->txdata[i]=0;

}

MI_Motor_CanTx(hmotor);

}

/**

* @brief 获取设备ID (通信类型0),需在电机使能前使用

* @param hmotor 电机结构体

* @retval null

*/

void MI_motor_get_ID(MI_Motor_t* hmotor)

{

hmotor->EXT_ID.mode = 0;

hmotor->EXT_ID.data = 0;

hmotor->EXT_ID.motor_id = 0;

hmotor->EXT_ID.res = 0;

for(uint8_t i=0; i<8; i++)

{

hmotor->txdata[i]=0;

}

MI_Motor_CanTx(hmotor);

}

/**

* @brief 运控模式电机控制指令(通信类型1)

* @param hmotor 电机结构体

* @param motor_id 电机id

* @param master_id 主机id

* @retval null

*/

void MI_motor_controlmode(MI_Motor_t* hmotor, float torque, float MechPosition , float speed , float kp , float kd)

{

hmotor->EXT_ID.mode = 1;

hmotor->EXT_ID.data = float_to_uint(torque,T_MIN,T_MAX,16);

hmotor->EXT_ID.res = 0;

hmotor->txdata[0]=float_to_uint(MechPosition,P_MIN,P_MAX,16)>>8;

hmotor->txdata[1]=float_to_uint(MechPosition,P_MIN,P_MAX,16);

hmotor->txdata[2]=float_to_uint(speed,V_MIN,V_MAX,16)>>8;

hmotor->txdata[3]=float_to_uint(speed,V_MIN,V_MAX,16);

hmotor->txdata[4]=float_to_uint(kp,KP_MIN,KP_MAX,16)>>8;

hmotor->txdata[5]=float_to_uint(kp,KP_MIN,KP_MAX,16);

hmotor->txdata[6]=float_to_uint(kd,KD_MIN,KD_MAX,16)>>8;

hmotor->txdata[7]=float_to_uint(kd,KD_MIN,KD_MAX,16);

MI_Motor_CanTx(hmotor);

}

/**

* @brief 电机停止运行帧(通信类型4)

* @param hmotor 电机结构体

* @retval null

*/

void MI_motor_stop(MI_Motor_t* hmotor)

{

hmotor->EXT_ID.mode = 4;

hmotor->EXT_ID.data = MI_MASTERID;

hmotor->EXT_ID.res = 0;

for(uint8_t i=0; i<8; i++)

{

hmotor->txdata[i]=0;

}

MI_Motor_CanTx(hmotor);

}

/**

* @brief 设置电机机械零位(通信类型6)会把当前电机位置设为机械零位(掉电丢失)

* @param hmotor 电机结构体

* @retval null

*/

void MI_motor_setMechPosition2Zero(MI_Motor_t* hmotor)

{

hmotor->EXT_ID.mode = 6;

hmotor->EXT_ID.data = MI_MASTERID;

hmotor->EXT_ID.res = 0;

hmotor->txdata[0]=1;

for(uint8_t i=1; i<8; i++)

{

hmotor->txdata[i]=0;

}

MI_Motor_CanTx(hmotor);

}

/**

* @brief 设置电机CAN_ID(通信类型7)更改当前电机CAN_ID , 立即生效,需在电机使能前使用

* @param hmotor 电机结构体

* @param Now_ID 电机现在的ID

* @param Target_ID 想要改成的电机ID

* @retval null

*/

void MI_motor_changeID(MI_Motor_t* hmotor,uint8_t Now_ID,uint8_t Target_ID)

{

hmotor->EXT_ID.mode = 7;

hmotor->EXT_ID.motor_id = Now_ID;

hmotor->EXT_ID.data = Target_ID << 8 | MI_MASTERID;

hmotor->EXT_ID.res = 0;

for(uint8_t i=0; i<8; i++)

{

hmotor->txdata[i]=0;

}

MI_Motor_CanTx(hmotor);

}

/**

* @brief 单个参数读取(通信类型17)

* @param hmotor 电机结构体

* @param index 功能码

* @retval null

* @note 我用不着,所以没写反馈解码

*/

void MI_motor_Read_One_Para(MI_Motor_t* hmotor,uint16_t index)

{

hmotor->EXT_ID.mode = 17;

hmotor->EXT_ID.data = MI_MASTERID;

hmotor->EXT_ID.res = 0;

hmotor->txdata[0]=index;

memcpy(&hmotor->txdata[0],&index,2);

for(uint8_t i=2; i<8; i++)

{

hmotor->txdata[i]=0;

}

MI_Motor_CanTx(hmotor);

}

/**

* @brief 单个参数写入(通信类型18) (掉电丢失)

* @param hmotor 电机结构体

* @param index 功能码

* @param data[4] 参数数据缓冲

* @retval null

* @note 我用不着,所以没写反馈解码

*/

void MI_motor_Write_One_Para(MI_Motor_t* hmotor, uint16_t index ,uint8_t data[4])

{

hmotor->EXT_ID.mode = 0x12;

hmotor->EXT_ID.data = MI_MASTERID;

hmotor->EXT_ID.res = 0;

memcpy(&hmotor->txdata[0],&index,2);

memcpy(&hmotor->txdata[4],data, 4);

MI_Motor_CanTx(hmotor);

}

/**

* @brief 单电机解码

* @param hmotor 电机结构体

* @param state_byte状态字节,扩展ID的bit8:23

* @param rxdata 数据缓冲区

* @retval null

*/

uint16_t decode_temp_mi = 0;

uint8_t nsvd = 0;

void MI_motor_decode(MI_Motor_t* hmotor,uint8_t state_byte,uint8_t rxdata[]) {

nsvd = state_byte;

if((state_byte&0xC0) == 0) {

hmotor->motor_state = OK;

} else {

for(int i = 1; i < 7; i++) {

if(state_byte&0x01) {

hmotor->motor_state = i;

}

state_byte = state_byte>> 1;

}

}

hmotor->motor_mode = state_byte;

decode_temp_mi = (rxdata[0] << 8 | rxdata[1])^0x8000;

hmotor->motor_fdb.angle_temp = decode_temp_mi;

decode_temp_mi = (rxdata[2] << 8 | rxdata[3])^0x8000;

hmotor->motor_fdb.speed_temp = decode_temp_mi;

decode_temp_mi = (rxdata[4] << 8 | rxdata[5])^0x8000;

hmotor->motor_fdb.torque_temp = decode_temp_mi;

decode_temp_mi = (rxdata[6] << 8 | rxdata[7]);

hmotor->motor_fdb.temprature_temp = decode_temp_mi;

hmotor->motor_fdb.angle = (float)hmotor->motor_fdb.angle_temp/32768*4*3.1415926f;

hmotor->motor_fdb.speed = (float)hmotor->motor_fdb.speed_temp/32768*30;

hmotor->motor_fdb.torque = (float)hmotor->motor_fdb.torque_temp/32768*12.0f;

hmotor->motor_fdb.temprature = (float)hmotor->motor_fdb.temprature_temp/10.0f;

hmotor->motor_fdb.last_update_time = HAL_GetTick();

}

/**

* @brief 小米电机解码

* @param rx_EXT_id 接收到的扩展ID

* @param rxdata 数据缓冲区

* @retval null

*/

EXT_ID_t EXT_ID_tmp;//扩展ID数据结构体

void MIMotor_MotorDataDecode(uint32_t rx_EXT_id,uint8_t rxdata[])

{ EXT_ID_tmp = *((EXT_ID_t*)(&rx_EXT_id));

if(EXT_ID_tmp.mode == 0&&EXT_ID_tmp.motor_id == 0xFE) {

MI_fdbid = EXT_ID_tmp.data;

memcpy(MI_MCU_identifier,rxdata, 8);

}

if(EXT_ID_tmp.mode == 2) {

uint8_t id = EXT_ID_tmp.data&0xFF;

if(id == MI_Motor.EXT_ID.motor_id) {

MI_motor_decode(&MI_Motor,(uint8_t)(EXT_ID_tmp.data>>8),rxdata);

}

}

}

重定义can中断回调:

/**

* @brief CAN总线数据接收回调函数

* @param phcan: 指向CAN句柄的指针

* @retval 无

*/

CAN_RxHeaderTypeDef Can_rxHeader;//放函数外面只是为了便于DEBUG,可以放回去

uint8_t Can_rxData[8];

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *phcan) {

/* Get RX message */

uint32_t ret = HAL_CAN_GetRxMessage(phcan, CAN_RX_FIFO0, &Can_rxHeader, Can_rxData);

if (ret != HAL_OK) {

/* Reception Error */

}

if(phcan==&hcan1)

{

MIMotor_MotorDataDecode(Can_rxHeader.ExtId,Can_rxData);

}

}

三、使用流程

小米电机使用可以分为以下几步:

1、获取电机当前ID,电机没有设置ID的拨码开关,也没有ID指示,需要通过CAN通讯查看ID。

使用

MI_motor_init(MI_Motor_t* hmotor,CAN_HandleTypeDef *phcan);//主要是初始化can,也可以加自己的初始化代码

MI_motor_get_ID(MI_Motor_t* hmotor);//获取ID指令

可以获取电机ID,反馈的ID存在变量MI_fdbid中,MCU唯一标识码存在在MI_MCU_identifier[8]中。执行此操作不要使用电机使能命令,即保证电机未使能,电机使能时会有声音

2、更改电机ID(执行此操作不要使用电机使能命令),如果不需要更改电机ID也可以跳过

MI_motor_init(MI_Motor_t* hmotor,CAN_HandleTypeDef *phcan);//主要是初始化can,也可以加自己的初始化代码

MI_motor_changeID(MI_Motor_t* hmotor,uint8_t Now_ID,uint8_t Target_ID);//更改ID

3、电机使能

MI_motor_init(MI_Motor_t* hmotor,CAN_HandleTypeDef *phcan);//主要是初始化can,也可以加自己的初始化代码

MI_motor_enable(MI_Motor_t* hmotor,uint8_t id);

4、使能之后就可以使用

MI_motor_controlmode(MI_Motor_t* hmotor, float torque, float MechPosition , float speed , float kp , float kd)

进行控制了。

5、可以使用一下函数关闭电机

void MI_motor_stop(MI_Motor_t* hmotor);

相关推荐

如何打電話到巴基斯坦?  打長途電話的人一定要知道
速发365网址是多少

如何打電話到巴基斯坦? 打長途電話的人一定要知道

📅 07-26 👁️ 7066
【香港民间字集 传承字形版】以宋体风格整理制作古今汉字字形
空调遥控哪个好用
365bet娱乐场客户端

空调遥控哪个好用

📅 10-15 👁️ 8630
吃鸡游戏头像怎么改
365bet娱乐场客户端

吃鸡游戏头像怎么改

📅 07-03 👁️ 2171
模电常用芯片[模拟芯片的分类]
365bet娱乐场客户端

模电常用芯片[模拟芯片的分类]

📅 08-31 👁️ 6980
华为手机root权限的开启与关闭(一键获取root权限的方法及注意事项)