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

这篇具有很好参考价值的文章主要介绍了小米微电机STM32 HAL库驱动教程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、相关资料

二、驱动代码

MI_motor_dev.h头文件:

MI_motor_dev.c源文件:

重定义can中断回调:

三、使用过程


一、相关资料

电机图纸、上位机、电机固件等可以找客服下载,这里给出使用说明书的分享链接(建议阅读)

文档链接:https://pan.baidu.com/s/1a9X6wEsewXEyym9UU40qYw?pwd=2023 
提取码:2023

二、驱动代码

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

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<<bits)-1))/span);
}
/**
  * @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、可以使用一下函数关闭电机文章来源地址https://www.toymoban.com/news/detail-715089.html

void MI_motor_stop(MI_Motor_t* hmotor);

到了这里,关于小米微电机STM32 HAL库驱动教程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数

    该篇文章中用到的步进电机闭环驱动器为Emm42_V4.0步进电机闭环驱动器。该闭环驱动器自带FOC矢量闭环控制算法,能实现力矩、速度、位置三环控制。 如下图所示,该42步进闭环电机驱动器的A+、A-、B+、B-连接步进电机,通过右侧的使能、脉冲、方向端对步进电机进行驱动控制

    2024年02月01日
    浏览(38)
  • 【STM32】cubeMX配置HAL库驱动L298N控制直流有刷电机

    目录 1.电机为什么需要驱动板呢?为什么不能用控制器直接控制电机呢? 2.H桥电路介绍 3.L298N原理介绍 4.实物接线图 5.CUBEMX配置 6.代码 7. 演示视频 L298N是常用来驱动小型直流有刷电机(两个)和步进电机(一个)的电机驱动板,其电路原理就是两个H桥电路,控制原理是使用

    2024年02月16日
    浏览(41)
  • STM32机器人控制开发教程No.4 使用串口通信控制电机(基于HAL库)

    在机器人控制中,单片机(Arduino/STM32)与上位机(Raspberry Pi/NVIDIA Jetson nano)之间的通信经常采用串口通信的方式,那应该如何使用STM32的串口通信以及根据自己定义的协议来完成数据的接收与发送呢?在本篇文章中将给你演示如何通过自定协议来完成对电机的控制以及获取编码

    2023年04月25日
    浏览(43)
  • STM32两轮差速/阿克曼转向机器人/小车电机驱动教程!

    01 前言 之前,我们出了一系列的STM32机器人控制开发教程,收到不少小伙伴的反馈,于是我们对教程进行了优化,并将增加新的内容和工具。 本教程使用的机器人控制板拥有4个带编码器的电机接口,4个舵机接口,串口通信接口、SWD下载调试接口、航模遥控接口、USB5V输出接

    2024年02月05日
    浏览(105)
  • stm32cubemx hal学习记录:电机控制

    1、配置RCC、SYS,SYS的Timebase Source选择TIM6 2、配置USART1、时钟84MHz 3、激活FreeRTOS,选择CMSIS_V1,Config parameters种USE_TIMERS选择ENABLE 1、选用TIM3的编码器模式  2、驱动使用L298N,将PB6、PB7设置为输出模式  3、使用TIM2的CH1输出1kHz的PWM 1、设置两个软件定时器,一个用于总控制,一个

    2024年02月17日
    浏览(31)
  • stm32编码器电机测速(hal库)

    记录一下今天参考别人的代码实现了四个电机的测速。   编码器被广泛应用于电机测速,实现电机闭环控制 。所以不论是自己做小车还是后续参加各种比赛,必须要学会编码器测速。         编码电机其实就是一个带有编码器的电机,我的这个电机是一个带霍尔传感器的

    2024年02月13日
    浏览(40)
  • 【STM32】使用HAL库进行电机测速,原理、代码、滤波

    参考资料: https://blog.csdn.net/lzzzzzzm/article/details/119416134 野火STM32电机开发教程 常见的编码器有两种,分别为霍尔编码器和GMR编码器。 1.1 霍尔编码器 ​ 霍尔编码器圆盘上分布有磁极,当圆盘随电机主轴转动时,会输出两路相位差90°的方波,用这两路方波可测出电机的转速和

    2024年01月24日
    浏览(31)
  • STM32 HAL库PID控制电机 第三章 PID控制双电机

    注:本文含全部PID控制代码,保证可以运行,如不能运行可以留言回复 1 基础配置 1.1 编码器电路图及配置 引脚 定时器通道 PA0 TIM2_CH1 PA1 TIM2_CH2 PB6 TIM4_CH1 PB7 TIM4_CH2 因此需要把TIM2、TIM4配置为编码器模式。在STM32CubeIDE中找到定时器2与定时器4,进行模式配置。以下以定时器2为

    2024年02月16日
    浏览(25)
  • 【STM32】使用HAL库进行电机PID位置环控制,代码+调参

    前面两篇博文已经实现了电机测速和PID速度环控制,在这篇博文中,我们主要说明位置环的代码怎么写以及PID参数怎么调。 ​ 写完速度环后位置环就很简单了。 ​ 在串级PID中,内环的控制量一般是外环控制量的微分。在我们这里,外环是控制量是电机转动的位置(也可以说

    2024年02月16日
    浏览(35)
  • 【STM32】使用HAL库进行电机速度环PID控制,代码+调参

    主控:STM32F103C8T6 在进行速度控制之前,我们首先需要进行速度采样,这里参见这篇博文 ​ 这里不细说电机驱动模块的选型和使用,而是说一个常见的误区。我们驱动电机要使用两路PWM,一般是一路给PWM信号,一路是纯低电平。但这其实是不好的,正确的做法是一路给PWM,另

    2023年04月20日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包