1. 项目概述:电机控制与转速监测系统
这个项目实现了一个完整的闭环电机控制系统,核心功能包括STM32微控制器生成PWM信号、L298N驱动模块控制电机转速、板载显示屏实时反馈转速数据,以及通过LabVIEW上位机实现远程监控。我在工业自动化项目中多次使用类似架构,特别适合需要精确控制直流电机转速的场景,比如生产线传送带调速、实验设备转速调节等。
系统工作流程是这样的:STM32通过定时器产生可调占空比的PWM波→L298N将控制信号转换为电机驱动电流→编码器检测实际转速并反馈给STM32→OLED屏幕显示实时转速→串口将数据发送到LabVIEW界面。这种设计既保证了本地操作的实时性,又提供了友好的远程监控界面,实际测试中转速控制精度能达到±2 RPM(转/分钟)。
2. 硬件设计与核心器件选型
2.1 STM32主控方案选择
我推荐使用STM32F103C8T6作为主控芯片(即常见的"蓝莓板"),原因有三:首先它自带多达4个高级定时器(TIM1/TIM2/TIM3/TIM4),每个定时器都支持6路PWM输出,完全满足多电机控制需求;其次72MHz主频能流畅处理编码器信号和PID运算;最后它的USART接口可以方便地与LabVIEW通信。实际项目中我用TIM3的CH1通道生成PWM,配置为向上计数模式,预分频值设为72-1(即1MHz计数频率),自动重装载值ARR设为1000-1,这样产生的PWM频率就是1MHz/1000=1kHz,这个频率既能保证控制精度又不会让L298N过度发热。
注意:PWM频率不宜超过5kHz,否则L298N的MOSFET会因开关损耗严重发热。但也不能低于500Hz,否则电机运转会有明显抖动。
2.2 L298N驱动电路详解
L298N作为经典的双H桥驱动芯片,最大可驱动2A电流的直流电机。我的接法是这样的:
- 将STM32的PWM输出接L298N的ENA使能端(控制电机启停)
- IN1/IN2接普通IO口(控制转向)
- OUT1/OUT2接电机两极
- 供电采用12V/2A开关电源(注意一定要与STM32共地)
关键技巧:在电机两端并联一个1N5819肖特基二极管(阴极接电源正极),能有效抑制关断时产生的反电动势。实测不加保护二极管时,L298N的发热量会增加约40%。
2.3 转速检测方案对比
我测试过三种转速检测方式:
- 光电编码器:最精准但成本高(如1000线的编码器每转产生1000个脉冲)
- 霍尔传感器:性价比之选(如常用的3144霍尔模块,配合磁铁使用)
- 反电动势检测:无需额外传感器但精度低
最终选择方案2,在电机转轴粘贴4个等距磁铁,霍尔传感器输出接STM32的TIM2编码器接口。配置TIM2为编码器模式,每检测到上升沿计数器加1。通过定时读取CNT值并清零,转速计算公式为:
code复制转速(RPM) = (脉冲数 × 60) / (磁铁数量 × 采样周期(秒))
例如1秒内检测到300个脉冲,则转速=(300×60)/4=4500RPM。
3. 软件实现与PID控制
3.1 PWM生成与电机驱动
在STM32CubeMX中配置TIM3如下:
c复制htim3.Instance = TIM3;
htim3.Init.Prescaler = 71; // 72MHz/(71+1)=1MHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 999; // 1MHz/1000=1kHz PWM
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
// 启动PWM通道
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
// 设置占空比(0-1000对应0%-100%)
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 500); // 50%占空比
电机转向控制代码示例:
c复制// 正转
HAL_GPIO_WritePin(GPIOB, IN1_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, IN2_Pin, GPIO_PIN_RESET);
// 反转
HAL_GPIO_WritePin(GPIOB, IN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, IN2_Pin, GPIO_PIN_SET);
3.2 增量式PID算法实现
在motor_control.c中实现PID控制器:
c复制typedef struct {
float Kp, Ki, Kd; // PID系数
float integral; // 积分项
float prev_error; // 上次误差
} PID_Controller;
float PID_Update(PID_Controller* pid, float setpoint, float actual) {
float error = setpoint - actual;
float p_term = pid->Kp * error;
pid->integral += error;
float i_term = pid->Ki * pid->integral;
float d_term = pid->Kd * (error - pid->prev_error);
pid->prev_error = error;
return p_term + i_term + d_term;
}
// 初始化PID参数
PID_Controller pid = {0.8, 0.05, 0.1, 0, 0};
调试技巧:先用纯P控制(Ki=0,Kd=0),逐渐增大Kp直到系统开始振荡,然后取该值的60%作为最终Kp。Ki和Kd用类似方法确定。
3.3 OLED显示屏驱动
使用SSD1306 OLED显示转速和PWM占空比:
c复制void Show_RPM(uint16_t rpm) {
char buffer[20];
sprintf(buffer, "Speed:%4d RPM", rpm);
SSD1306_GotoXY(0, 2);
SSD1306_Puts(buffer, &Font_7x10, SSD1306_COLOR_WHITE);
SSD1306_UpdateScreen();
}
4. LabVIEW上位机设计
4.1 串口通信协议
定义简单的ASCII协议:
code复制$RPM,1234,# // 转速数据
$PWM,500,# // PWM占空比
STM32发送代码:
c复制void USART_Send_RPM(uint16_t rpm) {
char msg[20];
sprintf(msg, "$RPM,%d,#\r\n", rpm);
HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 100);
}
4.2 LabVIEW程序框图关键节点
-
VISA串口配置:
- 波特率:115200
- 数据位:8
- 停止位:1
- 无校验
-
数据解析:
- 使用"Match Pattern"函数匹配"$RPM,"和"#"
- 用"Fractional String To Number"转换数值
-
波形图表:
- X轴显示时间(默认自动)
- Y轴显示转速值
- 添加游标显示当前值
4.3 前面板设计要点
- 转速表盘控件:量程设为电机最大转速(如3000RPM)
- 历史曲线图:缓冲区保留1000个数据点
- 紧急停止按钮:通过发送"$STOP,#"指令控制电机
5. 系统调试与性能优化
5.1 常见问题排查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 电机不转 | L298N使能端未接通 | 检查ENA跳线帽是否插上 |
| 转速波动大 | PID参数不合适 | 重新整定PID系数 |
| LabVIEW无数据 | 串口配置错误 | 确认波特率、停止位设置 |
| OLED显示乱码 | I2C地址错误 | 尝试0x3C或0x3D地址 |
5.2 抗干扰设计经验
- 电源去耦:在L298N的VCC和GND间并联100uF电解电容+0.1uF陶瓷电容
- 信号隔离:PWM信号线使用双绞线,长度超过15cm时加74HC245缓冲器
- 软件滤波:对转速采样值进行移动平均滤波(窗口大小5-10)
5.3 动态性能测试数据
在不同PWM占空比下的实测转速:
| 占空比(%) | 空载转速(RPM) | 带载(500g)转速(RPM) |
|---|---|---|
| 20 | 850 | 720 |
| 50 | 2100 | 1850 |
| 80 | 3400 | 2900 |
从数据可以看出,随着负载增加,相同PWM下的转速会下降约12-15%,这正是需要闭环控制的原因。加入PID控制后,带载情况下的转速波动从±8%降低到±2%以内。