STM32F103C8T6实战:用电位器精准控制SG90舵机角度(附完整代码)
·
STM32F103C8T6实战:用电位器精准控制SG90舵机角度(附完整代码)
在嵌入式开发领域,精确控制舵机角度是一个经典且实用的项目。本文将带你从零开始,使用STM32F103C8T6微控制器和电位器实现对SG90舵机的精准角度控制。这个项目不仅适合初学者理解PWM和ADC的基本原理,也能帮助有一定经验的开发者优化控制精度。
1. 硬件准备与连接
要实现电位器控制舵机,我们需要以下硬件组件:
- STM32F103C8T6开发板:作为控制核心
- SG90舵机:9g微型舵机,工作电压4.8-6V
- 10KΩ电位器:用于角度调节
- 面包板和连接线:用于电路搭建
- 外部电源(可选):当舵机功率需求较大时使用
硬件连接示意图:
| 组件 | STM32引脚 | 备注 |
|---|---|---|
| 电位器中间引脚 | PA0 | ADC输入 |
| 电位器两端 | 3.3V和GND | 供电 |
| 舵机信号线 | PB0 | TIM3_CH3 PWM输出 |
| 舵机电源 | 5V | 建议外部供电 |
注意:SG90舵机工作电流可能达到500mA,直接使用STM32的5V输出可能导致电压不稳,建议使用外部电源供电。
2. PWM信号配置与舵机控制原理
2.1 舵机控制基础
SG90舵机通过PWM信号控制角度,其关键参数如下:
- 信号周期:20ms(50Hz)
- 脉冲宽度范围:0.5ms-2.5ms
- 对应角度范围:0°-180°
PWM占空比与角度关系:
| 脉冲宽度(ms) | 对应角度 |
|---|---|
| 0.5 | 0° |
| 1.0 | 45° |
| 1.5 | 90° |
| 2.0 | 135° |
| 2.5 | 180° |
2.2 STM32定时器配置
我们使用TIM3的通道3(PB0)生成PWM信号。以下是关键配置参数计算:
- 系统时钟:72MHz
- 目标PWM频率:50Hz(周期20ms)
- ARR(自动重装载值):18000-1
- PSC(预分频器):80-1
计算验证:
PWM频率 = 系统时钟 / ( (ARR+1) × (PSC+1) )
= 72,000,000 / (18,000 × 80)
= 50Hz
PWM初始化代码:
void PWM_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置PB0为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 定时器基础配置
TIM_TimeBaseStructure.TIM_Period = 18000 - 1; // ARR
TIM_TimeBaseStructure.TIM_Prescaler = 80 - 1; // PSC
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
// 使能定时器
TIM_Cmd(TIM3, ENABLE);
}
3. ADC配置与电位器读数
3.1 ADC初始化
我们使用PA0(ADC1通道0)读取电位器电压值:
void ADC1_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
// 配置PA0为模拟输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// ADC配置
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 12MHz
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 配置规则通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// 校准ADC
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
3.2 读取ADC值
uint16_t Get_ADC_Value(uint8_t ch) {
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
return ADC_GetConversionValue(ADC1);
}
4. 系统集成与角度映射
4.1 角度控制函数
将ADC值(0-4095)映射到舵机角度(0-180°):
void Set_Servo_Angle(uint16_t angle) {
// 限制角度范围
if(angle > 180) angle = 180;
// 计算对应的CCR值
// 0.5ms = 450, 2.5ms = 2250 (基于ARR=18000-1)
uint16_t pulse = 450 + angle * 10;
TIM_SetCompare3(TIM3, pulse);
}
4.2 主程序逻辑
int main(void) {
uint16_t adc_value, angle;
// 初始化各模块
PWM_Init();
ADC1_Init();
while(1) {
// 读取电位器值
adc_value = Get_ADC_Value(ADC_Channel_0);
// 映射到角度 (4095/180 ≈ 22.75)
angle = adc_value / 22.75;
// 设置舵机角度
Set_Servo_Angle(angle);
// 适当延时
Delay_ms(20);
}
}
5. 优化与调试技巧
5.1 提高控制精度
- ADC采样平均:多次采样取平均减少噪声影响
uint16_t Get_ADC_Average(uint8_t ch, uint8_t times) {
uint32_t sum = 0;
for(uint8_t i=0; i<times; i++) {
sum += Get_ADC_Value(ch);
Delay_ms(5);
}
return sum/times;
}
- 非线性补偿:针对电位器非线性特性进行补偿
// 使用查表法补偿非线性
const uint16_t angle_compensation[181] = { /* 补偿值表 */ };
angle = angle_compensation[adc_value / 22.75];
5.2 常见问题排查
-
舵机不转动:
- 检查电源是否足够
- 确认PWM信号线连接正确
- 测量PWM信号是否符合规范
-
角度控制不准确:
- 检查ARR和PSC配置
- 验证ADC读数是否稳定
- 调整角度映射公式
-
系统不稳定:
- 增加电源滤波电容
- 缩短信号线长度
- 检查接地是否良好
在实际项目中,我发现使用钽电容对舵机电源进行滤波能显著提高系统稳定性。另外,给STM32和舵机使用独立的电源,通过共地连接,可以避免很多电源干扰问题。
更多推荐



所有评论(0)