1. STM32 PWM驱动舵机实战

1.1 舵机控制原理剖析

舵机作为一种常见的角度执行器,其核心控制原理是通过PWM信号的占空比来指定输出轴的角度位置。标准舵机通常接受周期为20ms(50Hz)的PWM信号,其中高电平持续时间在0.5ms到2.5ms之间对应0°到180°的旋转角度。

在STM32中实现这一控制,我们需要精确配置定时器的时基单元:

  • 时钟源:通常使用内部72MHz时钟
  • 预分频器(PSC):设置为71(72-1),将时钟分频至1MHz
  • 自动重装载值(ARR):设置为19999(20000-1),实现20ms周期
  • 捕获比较寄存器(CCR):根据角度计算具体值

注意:不同品牌舵机的脉宽范围可能略有差异,建议在使用前查阅具体型号的数据手册。部分舵机支持更宽的脉宽范围(如0.3ms-2.7ms),可提供更大的转动角度。

1.2 硬件连接与初始化

典型SG90舵机接线方式:

  • 红色线:接5V电源正极
  • 棕色线:接GND
  • 橙色线:接STM32的PWM输出引脚(如PA1)

初始化代码关键点解析:

// GPIO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 定时器基础配置
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;  // ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;  // PSC
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

// PWM通道配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_Pulse = 1500;  // 初始占空比(1.5ms)
TIM_OC2Init(TIM2, &TIM_OCInitStructure);

1.3 角度控制实现

将角度转换为CCR值的计算公式:

CCR = (Angle / 180) * 2000 + 500

其中:

  • 500对应0.5ms(0°位置)
  • 2500对应2.5ms(180°位置)
  • 2000是180°对应的脉宽增量范围(2.5ms-0.5ms=2ms),换算为1MHz时钟下的计数值

实用角度设置函数:

void Servo_SetAngle(float Angle) {
    // 限制角度范围
    Angle = Angle > 180 ? 180 : (Angle < 0 ? 0 : Angle);
    // 转换为CCR值并更新
    PWM_SetCompare2(Angle / 180 * 2000 + 500);
}

1.4 按键控制实现

通过外部按键控制舵机角度递增的典型实现:

float currentAngle = 0;
while(1) {
    if(Key_GetNum() == 1) {  // 检测按键按下
        currentAngle += 30;
        if(currentAngle > 180) currentAngle = 0;
        Servo_SetAngle(currentAngle);
        OLED_ShowNum(1, 7, (uint16_t)currentAngle, 3);
    }
    Delay_ms(20);  // 消抖延时
}

实际应用建议:添加软件消抖和角度渐变效果,避免舵机快速跳动导致机械磨损。可以使用线性插值算法实现平滑的角度过渡。

2. PWM驱动直流电机方案

2.1 直流电机驱动原理

直流电机转速与PWM占空比基本呈线性关系,通过H桥电路可以同时控制电机的转速和转向。STM32产生PWM信号驱动电机控制器(如L298N),典型参数配置:

  • PWM频率:10kHz-20kHz(高于人耳听觉范围,避免啸叫)
  • 占空比:0-100%对应电机转速
  • 方向控制:通过两个GPIO引脚的电平组合决定

2.2 硬件连接方案

典型L298N模块连接方式:

  • ENA:接STM32 PWM输出(PA2)
  • IN1:接方向控制GPIO(PA4)
  • IN2:接方向控制GPIO(PA5)
  • 电机电源:建议与MCU电源隔离

2.3 代码实现详解

PWM初始化(简化参数配置):

// 时基单元配置
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;  // ARR=100便于百分比计算
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; // 72MHz/(36*100)=20kHz
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

// PWM通道3配置
TIM_OCInitStructure.TIM_Pulse = 0;  // 初始占空比0%
TIM_OC3Init(TIM2, &TIM_OCInitStructure);

方向控制与速度设置函数:

void Motor_SetSpeed(int8_t Speed) {
    // 方向控制
    if(Speed >= 0) {  // 正转
        GPIO_SetBits(GPIOA, GPIO_Pin_4);
        GPIO_ResetBits(GPIOA, GPIO_Pin_5);
    } else {  // 反转
        GPIO_ResetBits(GPIOA, GPIO_Pin_4);
        GPIO_SetBits(GPIOA, GPIO_Pin_5);
    }
    // 设置PWM占空比(绝对值)
    PWM_SetCompare3(abs(Speed));
}

重要提示:电机启动时应避免突然施加高占空比,建议实现加速度控制。可通过逐步增加PWM占空比的方式实现软启动,延长电机寿命。

2.4 按键调速实现

带速度回显的按键控制示例:

int8_t motorSpeed = 0;
OLED_ShowString(1, 1, "Speed:");

while(1) {
    if(Key_GetNum() == 1) {
        motorSpeed += 20;
        if(motorSpeed > 100) motorSpeed = -100;
        Motor_SetSpeed(motorSpeed);
        OLED_ShowSignedNum(1, 7, motorSpeed, 3);
    }
}

3. PWM频率与占空比测量技术

3.1 测量原理分析

输入捕获模式通过记录信号边沿的定时器计数值来计算信号参数:

  • 频率测量:捕获连续两个上升沿的时间间隔
  • 占空比测量:捕获一个周期内高电平持续时间

PWMI模式(PWM输入模式)是STM32提供的特殊测量模式,可自动配置两个通道分别捕获上升沿和下降沿。

3.2 硬件连接方式

测量信号连接:

  • 待测PWM信号 → PA6(TIM3_CH1)
  • 共地连接确保参考电平一致

3.3 代码配置详解

输入捕获初始化关键步骤:

// GPIO配置为上拉输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 定时器时基配置(1MHz计数频率)
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

// PWMI模式配置
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;  // 强滤波
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);

// 从模式配置
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);

测量函数实现:

uint32_t IC_GetFreq(void) {
    return 1000000 / (TIM_GetCapture1(TIM3) + 1);  // 1MHz时钟
}

uint32_t IC_GetDuty(void) {
    return (TIM_GetCapture2(TIM3) + 1) * 100 / 
           (TIM_GetCapture1(TIM3) + 1);
}

3.4 测量误差分析与优化

常见误差来源及解决方案:

  1. 高频信号测量:当信号周期接近定时器分辨率时,误差增大。可降低定时器时钟分频提高分辨率
  2. 信号抖动:适当增加数字滤波器值(TIM_ICFilter)
  3. 中断延迟:对于高频测量,建议使用DMA传输捕获值

实测技巧:对于低频PWM信号(<1kHz),可多次测量取平均值提高精度。高频信号建议使用更高主频的定时器(如TIM2/TIM5为32位计数器)。

4. 旋转编码器测速实现

4.1 编码器工作原理

增量式旋转编码器通过两路正交信号(A/B相)输出旋转信息:

  • 旋转方向:A/B相信号的相位差决定
  • 旋转速度:单位时间的脉冲数决定
  • 旋转角度:累计脉冲数决定

STM32的编码器接口模式可自动处理正交信号,实现四倍频计数。

4.2 硬件连接方式

典型EC11编码器连接:

  • CLK(A相)→ PA6(TIM3_CH1)
  • DT(B相)→ PA7(TIM3_CH2)
  • SW(按键)→ 单独GPIO(未使用可接地)

4.3 代码配置详解

编码器模式初始化:

// GPIO配置为上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 定时器时基配置
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;  // 16位最大值
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

// 编码器接口配置
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, 
                          TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

速度读取函数:

int16_t Encoder_Get(void) {
    int16_t count = TIM_GetCounter(TIM3);
    TIM_SetCounter(TIM3, 0);  // 读取后清零
    return count;
}

4.4 速度计算与显示

定时中断中读取编码器计数:

void TIM2_IRQHandler(void) {
    if(TIM_GetITStatus(TIM2, TIM_IT_Update)) {
        Speed = Encoder_Get();  // 获取单位时间脉冲数
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

速度换算公式:

实际转速(RPM) = (Speed * 60) / (PPR * 采样周期)

其中:

  • PPR:编码器每转脉冲数(EC11通常为20)
  • 采样周期:定时中断间隔(秒)

工程实践建议:对于高速旋转测量,应缩短采样周期或使用更高PPR的编码器。低速测量时可适当延长采样周期并启用定时器溢出中断处理。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐