1. PWM基础与电机控制原理
PWM(脉冲宽度调制)是控制电机速度最常用的方法之一。简单来说,PWM就是通过快速开关电源来控制平均电压。想象一下水龙头,如果快速开关水龙头,就能控制水流的大小。PWM也是类似的原理,只不过控制的是电流。
在STM32中,定时器可以生成精确的PWM信号。以STM32F103C8T6为例,它有4个通用定时器(TIM2-TIM5),每个定时器有4个独立的通道,都可以输出PWM信号。PWM信号有三个关键参数:
- 频率:决定PWM信号开关的快慢
- 占空比:决定高电平所占的比例
- 分辨率:决定占空比调节的精细程度
对于电机控制来说,PWM频率通常在1kHz-20kHz之间。频率太低会导致电机发出噪音,太高则可能超出驱动电路的响应能力。占空比直接决定了电机的转速,0%占空比电机停止,100%占空比全速运转。
2. 定时器PWM输出配置
2.1 硬件准备与时钟配置
首先需要开启定时器的时钟。STM32F103C8T6的定时器2-4挂在APB1总线上,时钟频率为72MHz(系统时钟经过分频后)。配置步骤如下:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);接下来配置时基单元,这决定了PWM的基本频率:
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);这个配置会产生一个1kHz的PWM信号(72MHz/(71+1)/(999+1)=1kHz)。
2.2 PWM通道配置
配置PWM输出模式,以通道1为例:
TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; // 初始占空比50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCInitStructure);最后别忘了使能定时器和PWM输出:
TIM_Cmd(TIM3, ENABLE); TIM_CtrlPWMOutputs(TIM3, ENABLE);3. 电机驱动电路设计
3.1 H桥驱动原理
要控制直流电机的方向和速度,通常使用H桥电路。H桥由四个开关管组成,可以控制电流方向。常见的H桥驱动芯片有L298N、TB6612等。
以L298N为例,它的基本连接方式:
- 两个PWM输入控制速度
- 两个方向控制引脚控制转向
- 需要外接电源(注意与逻辑电源隔离)
3.2 保护电路设计
电机是感性负载,会产生反向电动势,必须设计保护电路:
- 续流二极管:在每个MOSFET上并联快速恢复二极管
- 滤波电容:在电源端加装大容量电解电容(100uF以上)
- 光耦隔离:防止电机干扰MCU
实际接线时,PWM信号线要尽量短,避免干扰。如果驱动大功率电机,建议使用独立的电源为电机供电。
4. 完整代码实现与调试
4.1 初始化代码整合
将前面的配置整合成一个初始化函数:
void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 开启时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // TIM3_CH1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 定时器基础配置 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 999; TIM_TimeBaseStructure.TIM_Prescaler = 71; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // PWM通道配置 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比0% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCInitStructure); // 使能 TIM_Cmd(TIM3, ENABLE); TIM_CtrlPWMOutputs(TIM3, ENABLE); }4.2 速度控制函数
添加一个设置占空比的函数:
void Set_Motor_Speed(uint16_t speed) { // 限制速度范围 if(speed > 999) speed = 999; TIM_SetCompare1(TIM3, speed); }4.3 实际调试技巧
调试时常见问题及解决方法:
- 电机不转:检查电源是否接通,PWM信号是否输出
- 电机抖动:可能是PWM频率不合适,尝试调整频率
- 干扰MCU:检查地线连接,增加滤波电容
- 发热严重:检查H桥是否短路,PWM频率是否过高
建议调试步骤:
- 先用示波器确认PWM信号正常
- 接上电机前先测量输出电压
- 从小占空比开始逐步增加
- 监测电流防止过载
5. 进阶应用与优化
5.1 闭环速度控制
开环控制容易受负载影响,可以加入编码器实现闭环控制。常见方案:
- 光电编码器:通过中断测量脉冲数
- 霍尔传感器:成本低但精度较差
- 电流检测:通过采样电阻检测电机电流
以编码器为例,STM32的定时器可以直接读取编码器信号:
void Encoder_Init(void) { // 编码器接口模式配置 TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_SetCounter(TIM2, 0); TIM_Cmd(TIM2, ENABLE); }5.2 PID算法实现
PID控制器可以显著改善电机控制性能。简单PID实现:
typedef struct { float Kp, Ki, Kd; float error, lastError, integral; } PID_Controller; float PID_Update(PID_Controller* pid, float setpoint, float measurement) { pid->error = setpoint - measurement; pid->integral += pid->error; float derivative = pid->error - pid->lastError; pid->lastError = pid->error; return pid->Kp * pid->error + pid->Ki * pid->integral + pid->Kd * derivative; }使用时需要根据实际电机特性调整PID参数,这是一个需要耐心调试的过程。
5.3 多电机同步控制
对于需要协调控制的场合(如机器人),可以使用多个定时器同步触发:
// 配置主从定时器 TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0); // TIM2作为从定时器 TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Trigger); // 触发模式 TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); // TIM3作为主定时器 TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); // 更新事件触发这样TIM3的更新事件会同步触发TIM2,确保多个电机的PWM信号同步。