STM32F103C8T6按键控制SG90舵机角度:从PWM原理到实战(OLED显示)

在嵌入式开发中,将多种外设整合到一个项目中是提升综合能力的重要途径。本文将以STM32F103C8T6为核心,通过按键控制SG90舵机角度,并实时在OLED上显示当前角度值。这个项目不仅涉及PWM信号生成、按键消抖处理,还包括OLED显示驱动,非常适合有一定嵌入式基础的开发者进阶学习。

1. PWM原理与SG90舵机控制

PWM(脉冲宽度调制)是控制舵机的核心技术。SG90舵机的控制信号需要满足以下条件:

  • 周期:20ms(频率50Hz)
  • 脉冲宽度:0.5ms-2.5ms对应0°-180°角度

在STM32中,我们使用定时器来生成PWM信号。关键参数计算如下:

// 系统时钟72MHz,预分频72-1,自动重装载值20000-1
// 实际频率 = 72MHz / (72 * 20000) = 50Hz
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;  // PSC
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;  // ARR

角度到PWM占空比的转换公式:

PulseWidth = (Angle / 180) * 2000 + 500  // 500-2500对应0.5ms-2.5ms

2. 硬件连接与初始化

2.1 硬件连接配置

外设 STM32引脚 说明
SG90信号线 PA6 TIM3_CH1 PWM输出
按键 PB15 上拉输入,按下为低电平
OLED I2C接口 SCL:PB6, SDA:PB7

2.2 外设初始化流程

  1. PWM初始化
    • 开启TIM3和GPIOA时钟
    • 配置PA6为复用推挽输出
    • 设置定时器时基参数
    • 配置PWM模式
void PWM_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
    
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    
    TIM_Cmd(TIM3, ENABLE);
}
  1. 按键初始化

    • 配置PB15为上拉输入
    • 实现按键消抖功能
  2. OLED初始化

    • 通过I2C接口初始化OLED
    • 加载字库显示初始界面

3. 核心功能实现

3.1 角度控制逻辑

按键每按下一次,角度增加30°,超过180°后归零:

void Servo_SetAngle(float Angle) {
    PWM_SetCompare1(Angle / 180 * 2000 + 500);
}

// 在主循环中
if (KeyNum == 1) {
    Angle += 30;
    if (Angle > 180) Angle = 0;
    Servo_SetAngle(Angle);
    OLED_ShowNum(1, 8, Angle, 3);
}

3.2 按键消抖处理

机械按键存在抖动问题,需要通过软件消抖:

uint8_t Key_GetNum(void) {
    if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15)==0) {
        delay_ms(20);  // 首次检测到按下后延时
        while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_15)==0); // 等待释放
        delay_ms(20);  // 释放后延时
        return 1;
    }
    return 0;
}

提示:消抖延时时间需要根据实际按键特性调整,通常10-30ms为宜

3.3 OLED显示优化

为提高显示效果,可以添加更多信息:

OLED_ShowString(1,1,"Angle: ");
OLED_ShowString(2,1,"PWM: ");
OLED_ShowNum(2,6,(uint16_t)(Angle/180*2000+500),4);

4. 项目调试与优化

4.1 常见问题排查

  1. 舵机不转动

    • 检查PWM信号是否正常输出
    • 测量信号线电压(高电平应为3.3V)
    • 确认舵机供电充足(建议单独5V电源)
  2. 角度不准确

    • 校准0°和180°对应的PWM脉宽
    • 检查定时器配置是否正确
  3. 按键响应异常

    • 确认GPIO模式设置为上拉输入
    • 调整消抖延时时间

4.2 性能优化建议

  • 使用中断方式检测按键,减少主循环负担
  • 添加角度渐变效果,避免舵机突然转动
  • 增加多个预设角度位置,通过不同按键切换
// 角度渐变示例
void Servo_SetAngleSmooth(float targetAngle) {
    float currentAngle = ...; // 获取当前角度
    float step = (targetAngle > currentAngle) ? 1.0 : -1.0;
    
    while(fabs(currentAngle - targetAngle) > 1.0) {
        currentAngle += step;
        Servo_SetAngle(currentAngle);
        delay_ms(20);
    }
}

4.3 扩展功能

  1. 多舵机控制

    • 使用TIM3的其他通道(如CH2、CH3、CH4)
    • 每个通道独立控制一个舵机
  2. 无线控制

    • 添加蓝牙或WiFi模块
    • 通过手机APP远程控制角度
  3. 位置反馈

    • 使用电位器或编码器获取实际角度
    • 实现闭环控制

在实际项目中,我发现SG90舵机在负载较大时可能出现角度偏差,这时需要适当增加供电电流或选择扭矩更大的舵机型号。另外,PWM信号线过长也可能导致控制不稳定,建议信号线长度不超过50cm。

Logo

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

更多推荐