基于STM32的电机状态检测:转速、电流与故障判断
2026/9/1 1:23:41 网站建设 项目流程

做电机控制相关项目时,“基于STM32单片机电机状态检测”是一类很基础也很实用的题目:它不解决复杂的控制算法,而是解决一个更前置的问题——电机现在到底转没转、转多快、电流大不大、有没有堵转或过温。这篇内容适合正在做课程设计、毕业设计,或者准备把电机驱动接入自己项目的人看。我按实际落地顺序拆一遍:从传感器选型、硬件接线、CubeMX配置,到转速读取、电流采样、故障判断逻辑,最后给出一套调试验证方法和排查链路。

这类项目最常见的问题是:程序能下载,串口也有输出,但转速值始终是0,或者电流数值跳来跳去,再或者明明是堵转却没有触发报警。这些问题大多不是单片机本身的问题,而是传感器信号、配置参数和判断逻辑没有对齐。下面直接进入正题。

1. 先搞清楚电机状态检测到底要检测哪些状态

很多新手拿到题目就开始写代码,结果写了一半发现不知道要显示什么、判断什么。我建议先列一个状态清单,把检测目标和判断标准定下来,再动手。

1.1 转速检测:霍尔、编码器、反电动势三条路线

电机状态检测里,转速是最核心的一项。常见做法有三种。

第一种是霍尔传感器。直流电机或者无刷电机的转子上带磁铁时,可以用霍尔元件检测磁场变化,每经过一个磁极就输出一个脉冲。单片机通过外部中断或者定时器捕获脉冲间隔,就能算出转速。

第二种是编码器。电机尾部装了AB相正交编码器时,STM32的定时器可以工作在编码器模式,硬件自动计数,程序读一个寄存器就能拿到脉冲数,再根据每转脉冲数算出转速。这个方法最稳,也不需要频繁进中断。

第三种是反电动势法,常用于无刷电机,不需要额外传感器,通过检测电机绕组的反电动势过零点来判断转子位置。但这个方法需要比较复杂的硬件调理电路和软件算法,课程设计阶段不建议一上来就碰。

从实测体验来说,霍尔传感器方案接线最简单,AB相编码器方案数据最稳定。如果题目没有限定传感器,我建议优先考虑带霍尔或者带编码器的直流减速电机。

1.2 电流、电压、温度:母线参数与故障判据

转速只能反映电机是否在转,不能反映电机是否带载、是否吃力。要判断堵转和过流,需要检测电流。

电流检测的常见做法是:在电机供电回路上串联一个毫欧级采样电阻,把电流信号转换为电压信号,再经过运放放大后送入STM32的ADC引脚。没有运放时,也可以用电流传感器模块,比如ACS712系列,直接输出电压信号,接线更简单。

电压检测相对简单,用电阻分压把母线电压降到ADC可测量范围内就行。电压数据可以用来判断供电是否异常,也可以估算电机输入功率。

温度检测一般用NTC热敏电阻配合固定电阻分压,接ADC。电机外壳温度上升之后,NTC阻值变化,ADC读数随之变化,通过查表或者线性换算得到温度值。

1.3 堵转、过流、过温:最常见故障怎么判断

检测到的数据本身没有意义,关键是把数据变成判断结果。

堵转检测的核心逻辑是:电机在运行状态下,转速接近零但电流显著高于正常值。只判断速度为零不够,因为电机可能本来就停着;只判断电流过高也不够,因为启动瞬间电流本来就很大。所以正确做法是组合判断。

过流检测的核心是:电流超过设定阈值并持续一段时间。注意“持续一段时间”很重要,否则启动瞬间的浪涌电流会频繁触发误报。

过温检测的核心是:温度超过阈值后进入保护,等温度回落到恢复阈值以下才能重新启动。这里要用迟滞逻辑,避免温度在阈值附近震荡导致继电器反复吸合。

我建议把这些状态定义成一个枚举类型,程序里用一个状态机来管理,而不是散落一堆if判断。

typedef enum { MOTOR_STOP = 0, MOTOR_RUNNING, MOTOR_OVERCURRENT, MOTOR_STALL, MOTOR_OVERTEMP } MotorState_t;

2. 硬件准备与关键选型逻辑

硬件部分决定了项目能不能稳定跑起来。不要只看单片机型号,传感器、驱动模块、电源和接线质量对结果影响很大。

2.1 开发板与单片机选型原则

STM32系列里,F103系列是课程设计最常用的一类,C8T6和RCT6两个型号都能完成电机状态检测。如果只是检测一台电机的转速、电流和温度,F103C8T6完全够用:它有多个定时器、多个ADC通道、足够多的GPIO。

如果后续要做无刷电机控制或者多电机检测,建议用F103RCT6或者F407系列,主要是引脚数和定时器资源更多。但初学者没必要追求高配,把一块入门开发板吃透,比换高配板子更有用。

开发环境方面,建议用STM32CubeMX先生成初始化代码,再用Keil MDK编写业务逻辑。CubeMX可以做时钟树、GPIO、定时器、ADC的图形化配置,生成代码后会省掉大量寄存器配置工作,对于状态检测类项目足够高效。

低配置环境下也能开发,只要Keil能编译下载,串口能输出,就不会影响进度。唯一要注意的是电脑性能太差时,编译时间会比较长,耐心等待即可。

2.2 传感器与驱动模块接线思路

以直流有刷电机加霍尔传感器为例,典型接线是这样的:

  • 单片机PA1接霍尔传感器OUT引脚,配置为外部中断输入。
  • 单片机PA2接PWM输出到电机驱动模块IN1,控制电机转速。
  • 单片机PA3、PA4接驱动模块IN2和IN3,控制正反转。
  • 单片机PA5接OLED或者LCD的SDA,PA6接SCL,用于显示状态。
  • 单片机PA7接电流传感器输出,配置为ADC输入。
  • 单片机PB0接NTC分压电路输出,配置为ADC输入。

霍尔传感器一般用NPN开漏输出,所以要接上拉电阻到3.3V。上拉电阻选10k到47k都可以,太小会增加功耗,太大会导致边沿不够陡峭。这里最容易踩的坑是:霍尔传感器输出接了上拉但开发板内部也开了上拉,两个上拉并联后电平判断仍然正常,通常不影响;但如果霍尔传感器输出直接接5V开发板的引脚,就可能损坏IO,接线前一定要确认电平。

电流传感器模块一般输出0到5V或者0到3.3V,模块不同输出电压范围不同。ADC引脚输入范围不能超过VREF,否则会损坏单片机。如果传感器输出范围是0到5V,需要加电阻分压或者选择支持5V输入的开发板引脚。

2.3 电源和隔离注意事项

电机启动瞬间电流很大,会导致电源电压跌落。如果单片机和电机共用一个电源,单片机可能因为电压跌落复位。

稳妥的做法是:电机用单独电源供电,比如12V电池或者12V开关电源,单片机用USB供电或者独立的5V/3.3V电源。驱动模块的电源地和单片机的地要共地,否则信号无法正常传输。

如果电机功率较大,还需要考虑在电机供电端并联一个大电容滤波。容值可以从470uF到2200uF之间选择,具体要根据电流大小来定。电容的作用是缓冲电机启动时的瞬态电流,避免驱动模块过压或欠压。

操作顺序也很重要:先给单片机供电,确认程序能运行,再给电机通电。千万不要在电机运行时反复插拔信号线,否则很容易把单片机GPIO烧坏。

3. 从零搭最小工程:CubeMX配置与代码框架

这个项目的代码并不是越复杂越好。我先跑通最小功能,再逐步加功能,每加一个模块就验证一次。

3.1 用CubeMX初始化时钟、GPIO、定时器、ADC

打开STM32CubeMX,选择STM32F103C8T6,先配置RCC为外部晶振或者内部RC都行。教学环境下用内部RC也能跑,但如果对串口波特率精度有要求,还是用8MHz外部晶振更稳妥。

GPIO配置方面:

  • 霍尔输入引脚设为外部中断模式GPIO_MODE_IT_RISING,也就是上升沿触发。
  • PWM输出引脚设为复用推挽输出GPIO_MODE_AF_PP。
  • ADC引脚设为模拟输入GPIO_MODE_ANALOG。
  • OLED和LCD引脚根据模块要求配置,一般软件I2C可以选普通推挽输出。

定时器配置方面:

  • 用一个定时器做1ms系统时基,比如TIM2,用于计时和转速计算。
  • 用一个定时器产生PWM,比如TIM3的CH1,输出频率设10kHz到20kHz之间,占空比初始设为0。
  • 如果使用编码器,把TIM4设置为Encoder Mode。

ADC配置方面:

  • 选择ADC1的CH1和CH2,对应电流和温度两个通道。
  • 开启连续转换模式,如果软件里有多种采样任务,也可以使用单次转换再用定时器触发。
  • 采样周期可以设大一点,比如55.5个周期,提高稳定性。采样周期太小,读数容易波动。

配置完成后点击生成代码,选择Keil MDK版本即可。

3.2 转速读取:编码器模式与外部中断实现

外部中断方式适合霍尔传感器,思路是:霍尔信号每产生一个上升沿,表示转子转过了固定角度,在中断里记录一次脉冲。

转速计算的公式是:

转速(RPM) = 每秒脉冲数 / 每转脉冲数 × 60

每转脉冲数取决于霍尔传感器安装方式。比如电机轴上有一个磁铁转盘,一圈有6个磁极,霍尔传感器每圈输出6个脉冲,那么每转脉冲数就是6。

在1ms定时中断里,程序读取“当前脉冲计数”,减去1秒前的脉冲计数,就能得到每秒脉冲数。

uint32_t pulse_count; uint32_t pulse_count_prev; uint32_t pulse_per_second; uint16_t rpm; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { ms_counter++; if (ms_counter >= 1000) { pulse_per_second = pulse_count - pulse_count_prev; pulse_count_prev = pulse_count; rpm = pulse_per_second * 60 / PULSES_PER_REV; ms_counter = 0; } } }

为什么用1秒计数而不是测量脉冲间隔?因为低速时脉冲间隔很长,测量间隔会占用大量定时器资源;高速时脉冲间隔太短,定时器捕获容易出错。1秒计数的方式虽然刷新慢一些,但更稳定,适合状态检测场景。

编码器方式更简单,定时器工作在编码器模式后,硬件自动根据AB相变化计数,程序在定时器中断里读取TIM_CNT寄存器即可。编码器方式的优势是不需要在中断里频繁处理边沿,CPU开销小,适合转速较高的场景。

3.3 电流采样:ADC读取与软件滤波

ADC读到的原始值是一个0到4095的数值,需要转换成实际电流值。

假设电流传感器输出电压0到3.3V对应0到5A,那么转换关系是:

voltage = adc_value * 3.3f / 4095.0f; current = voltage / 3.3f * 5.0f;

如果使用采样电阻加运放的方式,转换关系要根据放大倍数重新计算。这里需要注意:寄存器里的adc_value读出来可能跳几个到几十个数,直接用于判断很容易误触发。

我习惯先做一次简单的中位值滤波或者滑动平均滤波。比如连续采样10次,去掉最大值和最小值,然后取平均。这个操作在低速执行频率下成本很低,收益却很直接。

float read_adc_filtered(void) { uint16_t values[10]; uint16_t temp; for (int i = 0; i < 10; i++) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); values[i] = HAL_ADC_GetValue(&hadc1); } // 冒泡排序取中间6个求平均 ... }

不要在主循环里死等ADC转换完成,否则程序会卡在一个点上,其他任务无法执行。更合理的做法是:启动ADC转换后,在定时器中断里读取结果,或者每50ms读取一次即可。电流变化速度相对较慢,50ms的刷新周期足够用于状态检测。

温度采样同理,通过分压电路把NTC阻值变化转换成电压变化,再查表或者用Steinhart-Hart公式换算温度。教学项目可以用线性近似,只要温度范围不大,误差可以接受。

4. 状态判断算法与报警逻辑

数据采集到位后,核心工作就是写状态判断逻辑。这里的难点不是代码量,而是怎么避免误报。

4.1 阈值判断、迟滞和去抖

不要用单一阈值直接判断。以电流检测为例,假设电机正常运行时电流是0.5A,堵转时是2.5A,那么可以把过流阈值设在1.5A。

但实际采样值会在1.2A到1.8A之间波动,直接判断会导致继电器或者报警信号频繁通断。解决办法是加入迟滞:

  • 触发过流保护时,电流阈值设为1.5A。
  • 退出过流保护时,电流要回落到1.0A以下才允许恢复。

同理,温度保护也有两个阈值:比如85度触发保护,75度恢复。

迟滞逻辑的本质是让状态切换有一个缓冲区,避免系统在阈值边界来回跳变。

if (state == MOTOR_RUNNING && current > OVERCURRENT_HIGH) { state = MOTOR_OVERCURRENT; } else if (state == MOTOR_OVERCURRENT && current < OVERCURRENT_LOW) { state = MOTOR_RUNNING; }

4.2 状态机设计:停止、运行、堵转、过流、过温

单独一个状态无法表达电机全貌,我建议使用前面定义的状态机。

状态切换规则可以这样定:

  • STOP状态下,如果PWM占空比大于0,进入RUNNING。
  • RUNNING状态下,如果电流超过过流阈值且持续200ms,进入OVERCURRENT。
  • RUNNING状态下,如果电机给定转速大于某值但实际转速接近0,且电流偏高,进入STALL。
  • RUNNING或STALL状态下,如果温度超过阈值,进入OVERTEMP。
  • 故障状态下,PWM输出降到0,电机停止。
  • 故障状态通过按键或者串口指令清除后,回到STOP。

这个状态机的好处是:每一条路径都是明确的,出现问题时串口打出一行日志就能知道当前状态、当前原因,不用猜。

一个关键经验是:进入故障状态后不能自动恢复。比如过流持续1秒就进入保护,如果电流马上回落就直接恢复,那电机可能反复尝试启动,导致热积累。至少等待3秒或者收到手动复位指令,才允许重新启动。

4.3 串口、OLED输出与调试方法

状态判断做完了,需要把结果展示出来。最简单的输出方式是串口打印,使用printf重定向到USART1,波特率设为115200。

调试时打印的信息最有用的是这组数据:

[1000ms] state=RUNNING rpm=1200 current=0.53A temp=32C duty=40%

有了这行日志,可以非常清晰地把数据变化和状态切换对应起来。建议在状态切换时额外打印一条事件消息,方便追溯:

[event] RUNNING -> STALL, current=3.1A, rpm=10

OLED显示适合最终展示。一般的0.96寸OLED可以显示四行内容,建议第一行显示状态,第二行显示转速,第三行显示电流和温度,第四行显示PWM占空比。显示驱动可以用软件I2C,开发成本低,接线也方便。

如果使用LCD1602,要重点注意对比度调节和背光接线,很多项目显示不清晰是因为对比度电位器没有调好,而不是I2C通信问题。

5. 实测流程与结果验证

程序写完不直接上真机,先跑最小样例,再逐步加复杂功能。实测的顺序建议按“启动电源—看串口—单条任务—批量/持续运行—故障模拟”来走。

5.1 最小样例先跑通:读取转速

第一次上电测试,只跑转速读取功能。

打开串口,给电机供电,设置PWM占空比20%,观察串口输出中转速是否为合理值。这时候不要急着调这个调那个,先把“数据链路”打通。

什么叫链路打通?就是霍尔信号经过引脚、中断、计数、计算,最终变成串口里一行数字,整个流程中间没有断点。如果串口没有转速输出,排查顺序应该是:

  1. 霍尔传感器供电是否正常。
  2. 霍尔信号引脚电压是否在转动时变化。
  3. 外部中断是否触发,可以在中断里设置一个标志位。
  4. PWM是否让电机转起来了。
  5. 每转脉冲数是否设置正确。

我见过不少情况是:霍尔传感器没坏,程序也没写错,但电机因为驱动板EN使能没拉高而根本没转。这种情况下所有检测逻辑都正常,就是电机不转,所以排查时先解决“电机是否转动”这个大前提。

5.2 加入电流和温度后观察输出

转速链路通了,再打开电流采样和温度采样。

先设置一个固定的PWM占空比,比如50%,观察电流读数的变化范围。用手按住电机轴施加阻力,电流应该明显上升。温度数据如果显示在30度左右,说明接线基本正常。

这里有个常见问题:电流ADC读数始终是0,但用万用表量传感器输出确实有电压。这种情况大多数是ADC的通道配置错了,比如CubeMX里配置的是ADC1_IN1,但代码读取的却是ADC1_IN2。还有可能是没有配置ADC扫描模式,多个通道轮流采集时没有正确切换。

如果电流读数比实际值偏高或者偏低,先确认分压电阻和运放放大倍数,再调整转换公式。不要因为读数不对就随机改代码参数,最好是先接一个可调电源或者标准恒流源,用万用表标定一下。

5.3 故障模拟与验证:人为堵转、调高电流

状态检测项目必须在现场模拟一次堵转场景,否则不能算完成。

我建议的验证流程是这样的:

  1. 电机正常运行,PWM 30%,记录电流和转速。
  2. 用手捏住电机轴,模拟堵转,观察状态变成STALL是否在预期时间内。
  3. 松开电机轴,看状态是否保持STALL,等待复位指令后恢复正常。
  4. 调高PWM到80%,记录电流是否超过过流阈值,触发OVERCURRENT保护。
  5. 用热风枪或者电烙铁靠近NTC,模拟过温,观察OVERTEMP触发和恢复阈值。

每一步都要记录日志,把状态切换时间和当时的电流、转速对应起来。如果堵塞后2秒才触发报警,而项目要求是1秒内,就要把判断周期改短。如果启动瞬间就误报过流,就要增加持续时间滤波。

故障模拟验证完成后,整个项目就具备基本的实用价值了。

6. 常见问题与排查链路

最后整理一份针对本项目的问题排查清单。这些坑不是每个项目都会遇到,但一旦遇到,能快速定位能省很多时间。

6.1 转速显示为零

先确认电机是否真的在转:听声音、看电机轴、用万用表测驱动输出。

电机转了但转速读数为零,重点检查三处:

  • GPIO是否正确配置为外部中断,有没有在CubeMX里开启NVIC中断使能。
  • 霍尔传感器输出的电平有没有变化,用万用表直流电压档量,转动时电压应该会变化。
  • 每转脉冲数是否设置得过大,导致1秒内没有产生足够多的脉冲。

如果霍尔传感器输出是5V电平,而STM32引脚工作在3.3V,信号可能无法被正确识别。这时候需要确认STM32引脚的容忍电压,或者加电平转换。

6.2 电流读数跳变

ADC读数跳变的原因很多,最常见的是:

  • 采样时间太短,电容充电不充分。
  • 电源纹波太大,参考电压不稳定。
  • 电流传感器和单片机不共地。
  • 没有滤波代码,直接拿单次采样值使用。

处理方案是:加滤波代码、加去耦电容、检查共地、把采样时间调长。按这个顺序来,而不是先去换传感器。

如果电流在0.1A到0.3A之间波动,但实际电流应该很稳定,优先怀疑电源问题而不是算法问题。先把示波器或者万用表接到传感器输出端确认原始信号,再决定改软件还是改硬件。

6.3 误报警和程序卡死

误报警大多数是判断逻辑的问题。启动瞬间电流大导致的过流保护误报,可以通过增加持续时间判断解决;温度在阈值附近波动导致的过温误报,可以通过迟滞逻辑解决。

程序卡死时,先看串口是否还有输出。如果没有输出,大概率是死循环或者中断优先级配置错误。这时先把所有中断注释掉,只保留一个串口输出,看程序是否正常运行,再一个模块一个模块加回来。

如果程序在进入某状态后卡住,通常是某个函数里的等待超时太短,比如HAL_ADC_PollForConversion的超时时间设为10ms,而ADC实际没有启动,就会卡死在等待中。解决方法是把超时时间调大,或者在等待前先启动ADC。

6.4 断电重启后状态异常

有些项目在上电瞬间会误触发一次过流保护,原因是单片机上电瞬间ADC还没有稳定,读到的是随机值。

处理办法是:上电后先延时200ms,等待电源和ADC稳定,再进行ADC校准。或者加一个“初始化窗口”,上电后1秒内不进行故障判断,只允许状态初始化。

如果闭环控制项目中有EEPROM保存参数,要防止断电时写坏参数。状态检测类项目一般不需要频繁保存参数,可以省去EEPROM,但如果是保存阈值或者校准值,就要考虑写入失败和掉电保护的问题。

排查顺序可以总结为:先看现象,再看输入信号,再看供电和共地,再看ADC配置,最后看判断逻辑。很多项目看起来是单片机问题,实际是传感器信号或者电源问题,所以不要一上来就改软件参数。

电机状态检测这个主题本身不难,难点在于把转速、电流、温度、状态判断串成一个完整的闭环。只要先把最小链路跑通,再逐步叠加功能和故障保护,就能稳定交付一个真正可演示、可验收的项目。如果后续要做接口化或者上位机,可以先把状态数据通过串口JSON格式输出,再用Python或者Qt做一个简单监控界面,这些都属于同一套架构的自然扩展。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询