STM32F103C8T6按键控制SG90舵机角度:从PWM原理到实战(OLED显示)
·
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 外设初始化流程
- 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);
}
-
按键初始化:
- 配置PB15为上拉输入
- 实现按键消抖功能
-
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 常见问题排查
-
舵机不转动:
- 检查PWM信号是否正常输出
- 测量信号线电压(高电平应为3.3V)
- 确认舵机供电充足(建议单独5V电源)
-
角度不准确:
- 校准0°和180°对应的PWM脉宽
- 检查定时器配置是否正确
-
按键响应异常:
- 确认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 扩展功能
-
多舵机控制:
- 使用TIM3的其他通道(如CH2、CH3、CH4)
- 每个通道独立控制一个舵机
-
无线控制:
- 添加蓝牙或WiFi模块
- 通过手机APP远程控制角度
-
位置反馈:
- 使用电位器或编码器获取实际角度
- 实现闭环控制
在实际项目中,我发现SG90舵机在负载较大时可能出现角度偏差,这时需要适当增加供电电流或选择扭矩更大的舵机型号。另外,PWM信号线过长也可能导致控制不稳定,建议信号线长度不超过50cm。
更多推荐
所有评论(0)