你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【G431RB+IHM16M1】调用MCSDK API获取电机速度

[复制链接]
CM_STM32 发布时间:2026-7-14 23:43

在之前发布的文章之中已经阐述了通过 MC Workbench 生成电机控制源代码和 Motor Pilot 检测、调节电机参数、收集运行参数曲线等功能,本文对这部分内容不再过多讲解。

ST电机控制开发套件快速上手 使用Motor Pilot Profiler工具识别未知参数电机

1.技术文档

-在 Motor Pilot 的 Help 菜单里面内置了 MC SDK 的技术文档入口,这个文档是位于 /MC_SDK_x.x.x(版本号)/Documentation/html/modules.html 目录下的html文件。

bc82f876-4ce0-455a-bf17-8510f1c568a2.png

打开之后主页是这样的。点击 User Manual ,可以看到目录下内容非常齐全,涵盖了电机驱动中从底层硬件驱动到上层应用架构的方方面面,是一份极具参考价值的开发指南。3d9ce54b-ff0c-4b1f-b5e8-eb5955227b12.png

ce6cee09-8728-4c61-b8c1-89cd0a82cce2.png

文档首先从基础理论与核心算法入手,详细讲解了PMSM电机结构、FOC驱动原理,以及PMSM的MTPA控制、弱磁控制和PID调节器的设计背景; 在硬件实现细节上,详细讲解了PWM生成策略(包括SVPWM、过调制等)以及多种电流采样拓扑(涵盖三电阻、单电阻及隔离传感器方案); 此外,针对反馈系统与系统架构,手册不仅包含了霍尔、编码器等有感及无感算法的处理,还延伸到了六步算法、FreeRTOS应用、上位机通讯数据包协议、位置控制以及MC状态机管理等设计。2880e55a-d0cc-4525-a493-78f5a78df2bf.png

总而言之,这份手册几乎囊括了电机控制开发中需要掌握的所有关键技术点,无论是对于理解控制原理还是工程落地,都有着非常重要的指导意义。 Component 页是对MCSDK软件接口即其函数实现的介绍,我们重点查看API,利用其提供的API来进行电机控制。其底层实现有机会再一起详细学习。

b1f0e017-e8f7-453f-ba7c-bf886fe2358b.png

2. API介绍

MCSDK的API在mc_api.h/.c中提供。下面介绍的是比较常用的接口。

①启停接口:

启动电机1的启动程序。

启动电机1的停止程序。

②状态接口:

MCI_CommandState_t MC_GetCommandStateMotor1 (void)

返回电机1的最后一个缓冲命令的状态。

int16_t MC_GetMecSpeedReferenceMotor1 (<span class="hljs-keyword">void</span>)

返回电机1的当前机械转子转速参考,以SPEED_UNIT定义的单位表示。

关于SPEED_UNIT有如下解释: 转速单位用于与应用程序的接口。 这个符号定义了API函数用于其速度参数的单位中1赫兹的值。 例如,如果选择的单位是RPM,则SPEED_UNIT被定义为60,因为1 Hz等于60 RPM。默认的单位是U_01HZ,由Workbench在项目的初始生成中设置。由于此符号是在用户节中定义的,因此用户设置的自定义值将在整个项目再生过程中持续存在。 电机控制工作台计算的调速PID参数适用于01Hz的转速。电机控制子系统内部缩放它们以适应实际的速度单位。 此符号不应设置为文字数值。相反,应该将其设置为为此目的预定义的符号之一,例如U_RPM, U_01HZ,…有关详细信息,请参阅SpeedUnit。

int16_t MC_GetMecSpeedAverageMotor1 (void)

返回电机1最后一次计算的平均机械转子转速,用SPEED_UNIT定义的单位表示。

MCI_State_t MC_GetSTMStateMotor1 (void)

返回电机1状态机的当前状态。

uint16_t MC_GetCurrentFaultsMotor1 (void)

返回一个位字段,显示电机1上的所有当前故障。

span

uint16_t MC_GetCurrentFaultsMotor1 (void)

修改电机1的目标速度。

如果电机1的状态机处于RUN状态,则立即执行。可以使用MC_GetCommandStateMotor1() 检查命令的状态。如果应用程序使用无传感器电机控制技术,则不能反转旋转方向。如果hFinalSpeed参数的符号与当前速度的符号不同,则当转速即将达到0rpm时,将不会完成速度斜坡并发生速度反馈错误(MC_SPEED_FDBK)。

其他的函数接口可以自行查阅。

span

为了直观地看到电机运行的状态以及速度,我连接了一个1.28英寸,128x128像素的SPI屏幕,驱动芯片是ST7735S。 值得注意的是,在分配引脚的时候不能只根据CubeMX中的空闲引脚分配,而需要根据原理图进行分配。我最开始分配引脚的时候将MOSI放到了PB5引脚,这个引脚在CubeMX里是未初始化的,但是实际上在扩展板上连接到了STSPIN830芯片的STBY引脚。这个错误导致了STBY不稳定,同时也影响了SPI通信的稳定性,通信速率达到5MHz以上屏幕就花屏或白屏;同时电机完全无法正常启动。

同时,最开始分配背光BLK到引脚PB3、数据/指令D/C到引脚PC7,其实在扩展板上也连接到了霍尔传感器的接口上,通过两个0R电阻短接。这也导致了SPI通信失败。最开始一直以为是软件问题,直到用示波器测量的时候才发现了异常所在。所以使用扩展板的时候检查硬件连接是十分必要的。

最终确定的引脚分配如下:

span

其中I2C1是用于连接AS5600磁编码器的;最开始分配的是PC8和PC9,后面发现开发板上PC9用于反电动势测量,虽然当前FOC应用不需要使用PC9,但是I2C总线还是重新分配到I2C1的PB8和PB9两个引脚,避免后续的应用中发生冲突。

span

我们的程序主要新增的功能:

  • 调用MCSDK接口,显示电机状态、观测器测量的速度到屏幕;
  • 新增AS5600驱动,读取并显示编码器测量的速度到屏幕。

这边使用的是VSCode+CMake开发环境,新增的文件需要在CmakeList.txt中添加文件路径。

# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user sources here
    Hardware/AS5600_I2C.c
    Hardware/BSP_LCD.c
    Hardware/ST7735.c
)

# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined include paths
    Hardware
)

显示方面,我的软件封装层级是这样的: BSP_LCD 用于描述LCD接口的宏定义,ST7735.h则封装了用于驱动ST7735的初始化、显示、绘图等等函数。修改硬件之后只需要修改BSP_LCD中的定义即可。

//BSP_LCD.h

#define RST_L   HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET)
#define RST_H   HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET)

#define DC_CMD  HAL_GPIO_WritePin(LCD_DC_GPIO_Port,  LCD_DC_Pin,  GPIO_PIN_RESET)
#define DC_DATA HAL_GPIO_WritePin(LCD_DC_GPIO_Port,  LCD_DC_Pin,  GPIO_PIN_SET)

#define CS_L    HAL_GPIO_WritePin(SPI3_CS_GPIO_Port, SPI3_CS_Pin, GPIO_PIN_RESET)
#define CS_H    HAL_GPIO_WritePin(SPI3_CS_GPIO_Port, SPI3_CS_Pin, GPIO_PIN_SET)

#ifndef LCD_BLK_ACTIVE_LOW
#define LCD_BLK_ACTIVE_LOW 0
#endif

#if LCD_BLK_ACTIVE_LOW
#define BLK_L   HAL_GPIO_WritePin(LCD_BLK_GPIO_Port, LCD_BLK_Pin, GPIO_PIN_SET)
#define BLK_H   HAL_GPIO_WritePin(LCD_BLK_GPIO_Port, LCD_BLK_Pin, GPIO_PIN_RESET)
#else
#define BLK_L   HAL_GPIO_WritePin(LCD_BLK_GPIO_Port, LCD_BLK_Pin, GPIO_PIN_RESET)
#define BLK_H   HAL_GPIO_WritePin(LCD_BLK_GPIO_Port, LCD_BLK_Pin, GPIO_PIN_SET)
#endif

这里只截取部分代码。

//ST7735.h中的接口

void ST7735_reset(void);
void ST7735_TurnON(void);
void ST7735_TurnOFF(void);
void ST7735_write_command(uint8_t command);
void ST7735_write_data(uint8_t data);
void ST7735_write_data_u16(uint16_t data);
void ST7735_init(void);

/* 地址控制 */
void ST7735_Set_Cursor(uint16_t x, uint16_t y);
void ST7735_Set_Region(uint16_t x_start, uint16_t y_start,
                        uint16_t x_end,   uint16_t y_end);
void ST7735_SpinScreen(uint8_t locate);

/* 填充 */
void ST7735_Screen_Fill(uint16_t color);
void ST7735_Region_Fill(uint16_t x1,uint16_t y1,
                        uint16_t x2,uint16_t y2,uint16_t color);

/* 基础图形 */
void ST7735_DrawPoint(uint16_t x,uint16_t y,uint16_t Color);
void ST7735_DrawArc(uint16_t X, uint16_t Y, uint16_t R,
                    uint16_t start_angle, uint16_t end_angle,
                    uint16_t color);
void ST7735_DrawQuarterCircle(uint16_t X, uint16_t Y, uint16_t R,
                              uint8_t quadrant, uint16_t color);
void ST7735_DrawCircle(uint16_t X,uint16_t Y,uint16_t R,uint16_t fc);
void ST7735_DrawLine(uint16_t x0, uint16_t y0,
                     uint16_t x1, uint16_t y1,
                     uint16_t Color);
void ST7735_DrawRect(uint16_t x, uint16_t y,
                     uint16_t w, uint16_t h,
                     uint16_t color);
void ST7735_DrawRoundRect(uint16_t x, uint16_t y,
                          uint16_t w, uint16_t h,
                          uint16_t r, uint16_t color);
void ST7735_FillRoundRect(uint16_t x, uint16_t y,
                          uint16_t w, uint16_t h,
                          uint16_t r, uint16_t color);

/* 字符 / 字符串 / 数字 / 图片 / 中文 */
void ST7735_ShowChar(uint8_t x,uint8_t y,
                     uint16_t fc,uint16_t bc,char c);
void ST7735_ShowString(uint8_t x,uint8_t y,
                       uint16_t fc,uint16_t bc,char *c);
void ST7735_ShowNumber(uint8_t x,uint8_t y,
                       uint16_t fc,uint16_t bc,long long num);
void ST7735_ShowImage(uint16_t x, uint16_t y,
                      uint16_t length, uint16_t width,
                      const unsigned char *p);
void ST7735_ShowChinese(uint8_t x,uint8_t y,
                        uint16_t fc,uint16_t bc,char *c);
int  map(char *c);

AS5600的驱动只封装了一个读取角度值的函数:

#ifndef __AS5600_I2C_H
#define __AS5600_I2C_H
#include "main.h"
#define AS5600_ADDR (0x36U << 1)
uint16_t AS5600_ReadRawAngle(void);
#endif

在main.c中声明以下变量:

//时间变量用于主循环中非阻塞定时
static uint32_t display_update_time = 0;
static const uint32_t DISPLAY_UPDATE_INTERVAL = 100; // 100ms
static uint32_t encoder_update_time = 0;
static const uint32_t ENCODER_UPDATE_INTERVAL = 10; // 10ms

//实现了仅变化才重绘的刷新逻辑
static bool lcd_init_done = false;
static MCI_State_t last_state = (MCI_State_t)0xFF;
static int16_t last_speed_rpm = INT16_MIN;
static uint16_t last_fault_now = 0xFFFF;
static uint16_t last_fault_occ = 0xFFFF;
static uint16_t last_enc_angle = 0xFFFF;
static int16_t last_enc_speed_rpm = INT16_MIN;
static bool last_enc_valid = false;

//编码器测速用
static uint16_t prev_enc_angle = 0U;
static uint32_t prev_enc_tick = 0U;
static bool enc_speed_ready = false;
static uint16_t enc_angle_latest = 0xFFFF;
static int16_t enc_speed_rpm_latest = 0;
static bool enc_valid_latest = false;
static int16_t last_pot_target_rpm = INT16_MIN;

此函数用于将电机状态转化为字符串,在显示任务中调用:

/* Motor state to string */
static const char* MC_GetStateString(MCI_State_t state)
{
  switch (state) {
    case ICLWAIT: return "ICLWAIT";
    case IDLE: return "IDLE";
    case ALIGNMENT: return "ALIGNMENT";
    case CHARGE_BOOT_CAP: return "CHARGE_BOOT";
    case OFFSET_CALIB: return "OFFSET_CALIB";
    case START: return "START";
    case SWITCH_OVER: return "SWITCH_OVER";
    case RUN: return "RUN";
    case STOP: return "STOP";
    case FAULT_NOW: return "FAULTNOW";
    case FAULT_OVER: return "FAULTOV";
    case WAIT_STOP_MOTOR: return "WAIT_STOP";
    case OTF_DETECTION: return "OTF_DET";
    case OTF_BRAKE: return "OTF_BRAKE";
    default: return "UNKNOWN";
  }
}

更新电机参数显示的函数;主要使用

static void Display_UpdateMotorInfo(void)
{
  MCI_State_t motor_state = MC_GetSTMStateMotor1();
  int16_t speed_unit = MC_GetMecSpeedAverageMotor1();
  int16_t speed_rpm = (int16_t)(((int32_t)speed_unit * U_RPM) / SPEED_UNIT);
  uint16_t fault_now = MC_GetCurrentFaultsMotor1();
  uint16_t enc_angle = enc_angle_latest;
  int16_t enc_speed_rpm = enc_speed_rpm_latest;
  bool enc_valid = enc_valid_latest;
  char line_buf[24];

  if (motor_state != last_state)
  {
    snprintf(line_buf, sizeof(line_buf), "%-8s", MC_GetStateString(motor_state));
    ST7735_ShowString(56, 28, BLACK, WHITE, line_buf);
    last_state = motor_state;
  }

  if (speed_rpm != last_speed_rpm)
  {
    snprintf(line_buf, sizeof(line_buf), "%-8d", speed_rpm);
    ST7735_ShowString(56, 48, BLACK, WHITE, line_buf);
    last_speed_rpm = speed_rpm;
  }

  if (fault_now != last_fault_now)
  {
    snprintf(line_buf, sizeof(line_buf), "0x%04X", fault_now);
    ST7735_ShowString(56, 68, BLACK, WHITE, line_buf);
    last_fault_now = fault_now;
  }

  if ((!enc_valid && last_enc_valid) ||
      (enc_valid && (!last_enc_valid || enc_angle != last_enc_angle || enc_speed_rpm != last_enc_speed_rpm)))
  {
    if (enc_valid)
    {
      snprintf(line_buf, sizeof(line_buf), "A:%4u V:%4d", enc_angle, enc_speed_rpm);
    }
    else
    {
      snprintf(line_buf, sizeof(line_buf), "A:ERR V:ERR");
    }
    ST7735_ShowString(8, 108, BLACK, WHITE, "           ");
    ST7735_ShowString(8, 108, BLACK, WHITE, line_buf);
    last_enc_valid = enc_valid;
    last_enc_angle = enc_angle;
    last_enc_speed_rpm = enc_speed_rpm;
  }
}

AS5600编码器读到的数据是角度值,在主循环中每10ms测得一次角度数据,并换算得到速度值。这个方法最高能测到6000rpm的转速,如果转速更高的话需要减小计时值。为了方便主循环中的定时是读取SysTick值来计算的,如果要更精准的话最好启用定时器。

//编码器角度值到速度的转换
static void Encoder_UpdateFromAS5600(void)
{
  uint16_t enc_angle = AS5600_ReadRawAngle();

  if (enc_angle != 0xFFFFU)
  {
    uint32_t now = HAL_GetTick();
    int16_t enc_speed_rpm = enc_speed_rpm_latest;

    if (enc_speed_ready)
    {
      uint32_t dt_ms = now - prev_enc_tick;
      if (dt_ms > 0U)
      {
        int32_t delta = (int32_t)enc_angle - (int32_t)prev_enc_angle;
        if (delta > 2048)
        {
          delta -= 4096;
        }
        else if (delta < -2048)
        {
          delta += 4096;
        }

        /* rpm = delta_count * 60000 / (counts_per_rev * dt_ms) */
        int32_t rpm32 = (delta * 60000) / (4096 * (int32_t)dt_ms);
        if (rpm32 > INT16_MAX)
        {
          rpm32 = INT16_MAX;
        }
        else if (rpm32 < INT16_MIN)
        {
          rpm32 = INT16_MIN;
        }
        enc_speed_rpm = (int16_t)rpm32;
      }
    }

    prev_enc_angle = enc_angle;
    prev_enc_tick = now;
    enc_speed_ready = true;

    enc_angle_latest = enc_angle;
    enc_speed_rpm_latest = enc_speed_rpm;
    enc_valid_latest = true;
  }
  else
  {
    enc_speed_ready = false;
    enc_valid_latest = false;
    enc_angle_latest = 0xFFFF;
    enc_speed_rpm_latest = 0;
  }
}

主函数中初始化过程加入:

  ST7735_init();
  Display_InitLayout();
  Display_UpdateMotorInfo();
  display_update_time = HAL_GetTick();
  encoder_update_time = display_update_time;
  ST7735_TurnON();

完成ST7735的初始化以及亮屏,以及初始时间戳的记录; while循环中加入以下测速、显示的任务:

  while (1)
  {
    if (HAL_GetTick() - encoder_update_time >= ENCODER_UPDATE_INTERVAL)
    {
      Encoder_UpdateFromAS5600();
      encoder_update_time = HAL_GetTick();
    }

    if (lcd_init_done && (HAL_GetTick() - display_update_time >= DISPLAY_UPDATE_INTERVAL))
    {
      Display_UpdateMotorInfo();
      display_update_time = HAL_GetTick();
    }
  }

运行结果如下,可以看到观测器在中高速状态下和编码器测得的速度是相近的。缺点就是不能适应低速运行;在drive_parameters.h中也定义了观测器可观测的最低速度值,运行的话尽量不要低于这个转速:

/* Observer start-up output conditions  */
#define OBS_MINIMUM_SPEED_RPM               486

依旧不能放视频,放一张图片。

8626031f-b7a4-48eb-bea7-2eddb1a1d5bb.png
ecd3aae6-f374-423d-9dd9-04ddd693a066.png
dc1920b8-d366-4697-8b81-07528b64adba.png
image.png
ac56ddec-48b2-4cbc-aef9-f1f63cbff629.png
收藏 评论0 发布时间:2026-7-14 23:43

举报

0个回答

所属标签

ST 电机控制

意法半导体提供完整的电机控制解决方案,包括合适的电子器件,诸如集成式智能功率模块和系统级封装、单片式电机驱动器、快速高效的功率开关、具有电压暂态保护功能的可控硅、以及功能强大且安全的微控制器等。以及提供完整的生态系统(包括一系列评估板、参考设计、固件和开发工具),以简化和加速设计流程。


最新内容

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版