资讯动态

基于STM32的电机状态检测系统:电流、转速与温度异常诊断方案

发布时间:2026/9/1 18:54:50 来源:尧图企业网站定制
在很多单片机课设、毕设和电子竞赛题目中“基于STM32单片机电机状态检测”是一个很经典的方向。起初很多同学会把它简单理解成“用霍尔传感器测转速然后显示在屏幕上”但实际做一个完整项目时你会发现电机堵转、过流、过热、停转这些异常状态往往比单纯的转速值更有价值。本文就以这套题目为主线从硬件选型、检测原理、代码实现到调试排错整理出一套可以直接参考的闭环方案。阅读本文后你可以掌握电机电流的ADC采样与换算、测速脉冲的输入捕获测量、DS18B20温度读取、状态判断与报警逻辑以及串口调试方法。代码基于STM32F103C8T6和标准外设库编写适合项目实战和二次扩展。1. 项目背景为什么需要电机状态检测1.1 电机状态检测解决什么问题电机在运行时不是只有“转”和“不转”两种状态。实际工程中常见的异常情况包括电机启动后堵转电流迅速升高若不及时处理会导致绕组过热烧毁。负载过大导致电机过流转速下降但电机仍在运行时间久了会损坏机械结构。电机线圈或驱动电路异常导致温度升高表面可能看不出来。电机因供电异常、驱动故障而停止转动但上位机可能仍然认为它处于工作状态。电机状态检测系统的作用就是通过传感器采集电机的电流、转速、温度等物理量并把它们转换为可处理的电信号再由单片机进行判断和报警。这样系统不仅能知道“电机在不在转”还能知道“电机是否处于健康状态”。1.2 需要检测哪些核心状态从项目可实现性和工程实用角度来看无需一开始就做振动分析、噪声分析等复杂手段建议从这三个物理量入手检测量选型方案能反映什么问题电流ACS712 霍尔电流传感器模块过流、堵转、负载异常转速霍尔测速模块或光电编码器停转、超速、转速异常温度DS18B20 数字温度传感器过热、散热不良这三个量互相结合后就可以形成比较完整的电机健康状态判断逻辑转速为 0 且电流接近 0说明电机处于停止状态。转速为 0 但电流很大说明电机可能堵转需要立即保护。电流超过额定值但转速正常说明负载偏大需要观察。温度持续上升并超过阈值说明散热异常或连续过载。1.3 系统整体方案本文采用 STM32F103C8T6 作为主控整体工作流程如下电流传感器输出模拟电压接入 STM32 的 ADC 引脚。测速模块输出脉冲信号接入 STM32 定时器输入捕获引脚。DS18B20 通过单总线协议把温度数据传给 STM32。单片机周期性采集数据经过软件滤波和公式换算得到真实物理量。程序根据设定阈值判断当前电机状态。串口输出调试数据蜂鸣器在异常状态时报警。这个方案成本低、原理清晰、代码可读性强后续如果要扩展 PID 调速、WiFi 监控、触摸屏显示也都有足够的升级空间。2. 硬件选型与系统框图2.1 主控芯片选择项目中常用的 STM32 芯片有 F103C8T6、F103ZET6、F407 等。对于电机状态检测这种中低速采集场景STM32F103C8T6 已经完全够用72MHz 主频满足 ADC 采样和输入捕获实时性要求。3 个 USART方便串口调试。2 个高级定时器、4 个通用定时器输入捕获测速很灵活。10 个 ADC 通道后续扩展多路电流检测也没有压力。需要注意的是如果使用 STM32F103C8T6 最小系统板板载通常已经包含 8MHz 晶振和复位电路使用时只需要接 ST-Link 下载器即可。2.2 传感器与模块选型电流检测模块选型时要关注量程和灵敏度。ACS712 模块常见量程有 5A、20A、30A。本文示例使用 ACS712-05B灵敏度约 185mV/A供电电压 5V零电流时输出约 2.5V。这个输出范围在 STM32 的 3.3V ADC 量程内可以直接连接。转速检测模块使用霍尔测速模块常见方案有两种带霍尔传感器的测速盘模块电机转动时输出方波脉冲。光电编码器模块通过码盘遮挡输出脉冲。两者对 STM32 来说都是“脉冲输入”处理方式一致。本文以霍尔测速模块作为示例输出脉冲接入定时器输入捕获引脚。温度检测推荐 DS18B20单总线数字输出不需要 ADC 通道而且测量精度可以做到 0.5℃ 左右非常适合电机外壳温度检测。2.3 模块接线表参考接线如下模块模块引脚STM32 引脚说明ACS712 电流模块VCC5V外部 5V 供电ACS712 电流模块OUTPA0ADC1 通道 0ACS712 电流模块GNDGND与 STM32 共地霍尔测速模块VCC3.3V 或 5V根据模块要求霍尔测速模块OUTPA6TIM3_CH1 输入捕获DS18B20DQPB0单总线数据线需接 4.7k 上拉蜂鸣器模块I/OPB1低电平触发根据模块调整串口模块TXPA10USART1_RX串口模块RXPA9USART1_TX接线时一定要保证所有模块与 STM32 共地否则传感器输出的信号电平没有参考基准ADC 读数和脉冲判断都会异常。3. 开发环境准备与工程搭建3.1 开发工具说明本文代码基于以下环境编写芯片STM32F103C8T6IDEKeil MDK5标准外设库STM32F10x 标准外设库 V3.5下载调试器ST-Link V2串口调试助手任意支持 ASCII/HEX 显示的软件即可标准外设库虽然相比 HAL 库略显繁琐但它的寄存器封装更接近硬件底层适合学习 ADC、定时器、GPIO 这些核心外设的工作原理。如果项目时间紧也可以改到 HAL 库但底层的采集逻辑和换算公式是一样的。3.2 新建标准库工程的要点STM32 标准库工程的搭建通常包含以下部分启动文件startup_stm32f10x_md.s。标准外设库的src和inc目录。CMSIS 核心文件core_cm3.c、system_stm32f10x.c。自己编写的main.c、adc.c、timer.c、delay.c等应用文件。如果使用 Keil MDK5还可以利用 RTE 环境直接添加标准库组件。但无论使用哪种方式都要注意芯片型号选择为 STM32F103C8并在 C/C 选项中定义STM32F10X_MD宏。没有这个宏标准外设库的很多配置结构体不会被正确编译。3.3 工程目录规划为了让后续维护方便建议建立清晰的文件结构Project/ ├── User/ │ ├── main.c │ ├── adc.c │ ├── adc.h │ ├── timer.c │ ├── timer.h │ ├── ds18b20.c │ ├── ds18b20.h │ ├── delay.c │ └── delay.h ├── STM32F10x_FWLib/ │ ├── inc/ │ └── src/ ├── CMSIS/ │ ├── core_cm3.c │ ├── core_cm3.h │ ├── system_stm32f10x.c │ └── startup_stm32f10x_md.s └── Project.uvprojx4. 核心检测原理拆解4.1 电流检测ADC采样与换算公式ACS712-05B 在无电流时输出电压约为 2.5V灵敏度为 185mV/A。STM32 的 ADC 是 12 位参考电压为 3.3V因此 ADC 值与电压的换算关系为电压值 ADC值 / 4096.0 × 3.3V已知电压值后再用以下公式反推电流电流值 (电压值 - 2.5V) / 0.185V/A这里有两个容易踩坑的点ACS712 的 2.5V 是典型中点电压实际模块会有偏差最好在无电流时实测校准。如果电流方向接反计算出的电流可能为负数。这并不一定代表故障而是传感器输出反向偏移所致可根据需求做绝对值处理。为了提高采样稳定性程序中可以对多次 ADC 采样结果取平均值这样可以有效滤除部分随机噪声。4.2 转速检测输入捕获与周期换算测速模块输出的是脉冲信号电机每转一圈会产生一定数量的脉冲。假设每转脉冲数为 P那么相邻两个上升沿之间的时间差就是一个脉冲周期 T。转速公式为转速 nrpm 60 / (T × P)例如每转输出 1 个脉冲如果相邻脉冲周期为 0.1 秒那么转速就是 60 / 0.1 600rpm。在 STM32 中可以使用定时器输入捕获功能测量脉冲周期。输入捕获的原理是定时器自由计数当检测到引脚上升沿时硬件自动把当前计数值保存到捕获寄存器。前后两次捕获寄存器值的差值就是脉冲周期对应的计数值。本文示例使用 TIM3_CH1配置预分频为 720 分频。系统时钟为 72MHz 时计数频率为 100kHz每个计数单位代表 0.01ms。这样一来如果捕获差值为 period脉冲周期为T period × 0.01ms period × 0.00001s代入转速公式后得到转速 nrpm 6000000.0 / (period × P)4.3 温度检测DS18B20单总线读取DS18B20 使用单总线协议数据线和时钟线复用为一根线。通信过程分成三个步骤复位脉冲主机把总线拉低一段时间然后释放等待 DS18B20 应答。写入 ROM 指令和功能指令例如跳过 ROM0xCC、启动温度转换0x44、读取暂存器0xBE。读取温度数据DS18B20 返回两个字节低字节和高字节按 0.0625℃/LSB 换算。DS18B20 驱动代码比较固定核心读取函数可以写成下面这种形式float DS18B20_Get_Temperature(void) { uint8_t temp_l 0; uint8_t temp_h 0; int16_t temp 0; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0x44); // 启动温度转换 Delay_Us(750); DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读取暂存器 temp_l DS18B20_ReadByte(); temp_h DS18B20_ReadByte(); temp (temp_h 8) | temp_l; return temp * 0.0625f; }需要注意的是DS18B20 对时序要求比较严格。如果从网上移植驱动一定要确认底层延时函数的精度尤其是微秒级延时。很多“读出来永远是 85℃”的问题本质上就是复位时序或者读写时序不满足要求。4.4 状态判断策略状态判断是整套软件的核心。本文把电机状态定义为一个枚举类型方便后续扩展状态判断条件正常转速大于停止阈值电流小于过流阈值温度正常停止转速低于停止阈值电流接近 0堵转转速低于停止阈值电流超过堵转阈值过流电流超过过流阈值但转速仍存在过热温度超过温度报警阈值超速转速超过速度上限判断时需要特别注意顺序。堵转通常同时满足“转速低”和“电流大”如果先判断过流可能把堵转误判为普通过流。因此建议先判断堵转再判断过流。5. 软件设计与完整代码实现5.1 代码结构说明代码采用模块化设计adc.c负责 ADC 初始化和电流采样。timer.c负责 TIM3 输入捕获初始化在中断中计算脉冲周期。ds18b20.c负责温度读取。main.c负责系统初始化、数据汇总、状态判断、串口输出和报警控制。下面给出每个模块的核心代码。示例中使用的是标准外设库 V3.5如果你使用 HAL 库配置思路仍然一致只是 API 名称不同。5.2 ADC电流采集模块文件adc.h#ifndef __ADC_H #define __ADC_H #include stm32f10x.h void ADC_Config(void); float Read_Current_A(void); #endif文件adc.c#include adc.h #include delay.h #define ACS712_VREF 2.5f #define ACS712_SENSITIVITY 0.185f void ADC_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); } float Read_Current_A(void) { uint32_t sum 0; float voltage; float current; uint8_t i; for (i 0; i 20; i) { ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); sum ADC_GetConversionValue(ADC1); } voltage (sum / 20.0f) / 4096.0f * 3.3f; current (voltage - ACS712_VREF) / ACS712_SENSITIVITY; return current; }代码中采样了 20 次 ADC 并求平均目的是减少电压抖动。采样周期也可以根据实际情况调整如果检测响应要求更快可以减少采样次数。5.3 定时器输入捕获测速模块文件timer.h#ifndef __TIMER_H #define __TIMER_H #include stm32f10x.h void TIM3_Cap_Init(void); float Get_Speed_RPM(uint16_t pulses_per_rev); #endif文件timer.c#include timer.h volatile uint16_t cap_1 0; volatile uint16_t cap_2 0; volatile uint16_t cap_period 0; void TIM3_Cap_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period 0xFFFF; TIM_TimeBaseStructure.TIM_Prescaler 720 - 1; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); TIM_ICInitStructure.TIM_Channel TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter 0x0F; TIM_ICInit(TIM3, TIM_ICInitStructure); NVIC_InitStructure.NVIC_IRQChannel TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); TIM_Cmd(TIM3, ENABLE); TIM_ClearFlag(TIM3, TIM_FLAG_CC1); } void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_CC1) ! RESET) { cap_2 cap_1; cap_1 TIM_GetCapture1(TIM3); if (cap_1 cap_2) { cap_period cap_1 - cap_2; } TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); } } float Get_Speed_RPM(uint16_t pulses_per_rev) { if (cap_period 0) { return 0.0f; } return 6000000.0f / ((float)cap_period * pulses_per_rev); }这里采用测周法计算转速即通过测量相邻脉冲的时间间隔来算出转速。测周法适合中高速测量转速越低单个脉冲周期越长测量精度越高。如果电机转速非常高脉冲频率很高则更适合改用测频法例如在固定时间窗口内统计脉冲个数。5.4 主函数与状态判断逻辑文件main.c#include stm32f10x.h #include adc.h #include timer.h #include ds18b20.h #include delay.h #include stdio.h #define PULSES_PER_REV 1 #define CURRENT_OVERLOAD_THRESH 1.5f #define CURRENT_BLOCK_THRESH 2.0f #define SPEED_STOP_THRESHOLD 5.0f #define TEMP_ALARM_THRESHOLD 60.0f typedef enum { STATE_NORMAL 0, STATE_STOP, STATE_BLOCKED, STATE_OVER_CURRENT, STATE_OVER_TEMP, STATE_OVER_SPEED } MotorState; void USART1_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitStructure.USART_BaudRate baudrate; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_RX | USART_Mode_TX; USART_Init(USART1, USART_InitStructure); USART_Cmd(USART1, ENABLE); } int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, (uint8_t)ch); return ch; } void BEEP_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_1); } void BEEP_ON(void) { GPIO_ResetBits(GPIOB, GPIO_Pin_1); } void BEEP_OFF(void) { GPIO_SetBits(GPIOB, GPIO_Pin_1); } MotorState Judge_MotorState(float current, float speed, float temperature) { if (speed SPEED_STOP_THRESHOLD current CURRENT_BLOCK_THRESHOLD) { return STATE_BLOCKED; } if (current CURRENT_OVERLOAD_THRESHOLD) { return STATE_OVER_CURRENT; } if (temperature TEMP_ALARM_THRESHOLD) { return STATE_OVER_TEMP; } if (speed SPEED_STOP_THRESHOLD) { return STATE_STOP; } return STATE_NORMAL; } int main(void) { float current 0.0f; float speed 0.0f; float temperature 0.0f; MotorState state STATE_NORMAL; Delay_Init(); USART1_Init(115200); ADC_Config(); TIM3_Cap_Init(); DS18B20_Init(); BEEP_Init(); printf(Motor State Detect System Start\r\n); while (1) { current Read_Current_A(); speed Get_Speed_RPM(PULSES_PER_REV); temperature DS18B20_Get_Temperature(); state Judge_MotorState(current, speed, temperature); printf(I%.2fA S%.1frpm T%.1fC State%d\r\n, current, speed, temperature, state); if (state STATE_BLOCKED || state STATE_OVER_TEMP) { BEEP_ON(); } else { BEEP_OFF(); } Delay_Ms(200); } }主函数中的printf重定向依赖fputc函数使用串口助手可以在 PC 上直接看到采集数据。如果项目要求使用 LCD1602 或 OLED 显示只需要在状态判断完成之后增加一个Display_Update()接口在接口内部根据state刷新屏幕内容即可核心检测逻辑无需改动。5.5 温度读取模块说明由于 DS18B20 底层驱动代码量较大且网上成熟代码很多这里不重复粘贴完整文件。你需要在自己的工程中新建ds18b20.h和ds18b20.c至少实现以下接口void DS18B20_Init(void); float DS18B20_Get_Temperature(void);需要注意以下几点数据线必须接上拉电阻一般使用 4.7kΩ。初始化时序的复位脉冲时间要符合手册要求480us 以上。读写位时序要求严格建议使用微秒级延时函数。如果读取结果一直为 85℃说明复位或读取时序有问题优先检查延时。6. 运行调试与结果分析6.1 串口观察数据程序下载后打开串口调试助手波特率设置为 115200正常情况下可以看到类似输出Motor State Detect System Start I0.03A S0.0rpm T28.5C State1 I0.05A S0.0rpm T28.6C State1此时电机没有启动电流接近 0转速为 0状态为停止。当电机启动后输出可能变为I0.82A S320.5rpm T31.2C State0 I0.81A S321.0rpm T31.4C State0此时状态为正常。6.2 人为模拟故障验证调试阶段可以人为制造故障验证判断逻辑是否正确用手轻触电机轴增加阻力观察电流是否上升、转速是否下降。堵住电机转轴确认系统能在短时间内识别堵转并触发蜂鸣器。加热 DS18B20 探头验证过热报警功能。需要注意的是堵转测试时间不能太长否则电机和驱动电路可能因大电流发热而损坏。建议在实验中使用带限流功能的电源并控制测试时间在几秒内。6.3 数据波形分析思路如果使用带波形显示的串口上位机可以把电流数据以曲线形式显示。正常情况下电流曲线应该比较平稳堵转时电流会出现快速上升超载时电流会在较高位置波动。转速曲线同理如果出现频繁跳变则需要检查测速脉冲是否受到干扰。从这些数据中不仅可以判断当前电机状态还能为后续设置更精细的报警阈值提供依据。7. 常见问题与排错清单问题现象常见原因排查与解决ADC 读出的

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价