1. 舵机控制原理与STM32工程实现

舵机(Servo Motor)并非传统意义上的连续旋转电机,而是一种闭环位置控制系统。其命名源于拉丁语“servus”(奴隶),意指它严格遵循外部指令执行精确角度定位——这种确定性正是航模、机器人关节、云台等应用场景的核心需求。在嵌入式系统中,舵机控制的本质是将目标角度映射为特定占空比的PWM信号,再由舵机内部电路完成角度解算与电机驱动。理解这一映射关系及其底层时序约束,是实现可靠控制的前提。

1.1 舵机电气特性与标准协议

市面主流舵机(如本项目使用的SG90)遵循统一的脉宽调制(PWM)控制协议。该协议定义了三个关键参数:周期、高电平脉宽、角度对应关系。SG90的标准工作周期为20ms(即频率50Hz),其有效控制脉宽范围为500μs至2500μs。这一范围并非随意设定,而是与航模遥控器输出信号完全兼容,构成了事实上的行业标准。

目标角度 对应脉宽 占空比 工程意义
500μs 2.5% 机械零点,舵机内部电位器阻值最小
90° 1500μs 7.5% 中心位置,电位器阻值中点
180° 2500μs 12.5% 最大偏转,电位器阻值最大

需特别注意:占空比计算必须基于 实际周期 。若误将20ms周期当作100%基准,则500μs脉宽对应2.5%占空比;但若使用其他周期(如10ms),相同500μs脉宽将导致25%占空比,舵机将无法识别或进入保护状态。因此,所有PWM配置必须首先锁定周期为20ms,再在此基础上调整脉宽。

1.2 STM32定时器PWM生成机制

在STM32F103系列中,TIM4是APB1总线上的16位通用定时器,其时钟源为PCLK1(默认72MHz)。要生成20ms周期的PWM信号,需通过预分频器(PSC)和自动重装载寄存器(ARR)共同确定计数周期:

  • 计数周期 = (PSC + 1) × (ARR + 1) × T CLK
  • 其中T CLK 为定时器时钟周期(1/72MHz ≈ 13.89ns)

为简化计算并保证精度,选择PSC=719(即720分频),则定时器计数频率为100kHz(72MHz/720)。此时:
- 20ms周期对应计数值:20ms × 100kHz = 2000
- 故设置ARR = 1999(因寄存器从0开始计数)

脉宽由捕获/比较寄存器(CCR)决定。当CCR值为X时,高电平持续时间为X × 10μs(因计数周期为10μs)。因此:
- 0°(500μs)→ CCR = 50
- 90°(1500μs)→ CCR = 150
- 180°(2500μs)→ CCR = 250

此映射关系直接决定了软件中占空比计算的数学模型,任何浮点运算误差都将转化为角度偏差。例如,若因整数除法截断导致CCR计算值偏离1,对应角度误差约为0.9°(180°/200),在精密控制场景中不可忽视。

2. 硬件连接与引脚配置

学习板上的“SOL”接口专为舵机设计,其物理定义如下:
- 棕色线(GND):接地,接J标识端子
- 红色线(VCC):+5V供电,由板载LDO提供
- 橙色线(SIGNAL):PWM信号输入,对应MCU引脚PB8

PB8在STM32F103中具有多重复用功能,需明确配置为TIM4_CH3通道。该配置涉及三个层级的寄存器操作:
1. GPIO模式 :PB8必须配置为复用推挽输出(GPIO_MODE_AF_PP),输出速度设为50MHz以确保信号边沿陡峭
2. AFIO重映射 :TIM4_CH3默认映射至PB8,无需额外重映射,但需使能AFIO时钟(RCC_APB2ENR |= RCC_APB2ENR_AFIOEN)
3. 定时器通道配置 :在TIM4中启用CH3通道,设置为PWM模式1(向上计数时,计数值小于CCR时输出高电平)

硬件连接时需注意电源隔离问题。舵机启动瞬间电流可达500mA以上,若与MCU共用同一LDO,可能导致MCU复位。学习板虽已做基础隔离,但在实际项目中建议为舵机单独配置DC-DC模块或大容量电解电容(≥1000μF)进行储能缓冲。

3. 编码器输入与角度映射设计

本项目采用旋转编码器作为人机交互输入设备,其核心价值在于提供无触点、抗干扰的位置反馈。学习板所配编码器为增量式,每旋转一周产生20个A/B相脉冲。由于采用正交解码,MCU可识别方向并实现4倍频计数——即每圈实际计数值为80(20×4)。但本方案仅需角度相对变化,故采用基础计数模式。

3.1 TIM1编码器模式配置要点

TIM1作为高级定时器,其编码器模式通过以下寄存器组合实现:
- SMCR寄存器 :设置SMS=0b001(编码器模式1,TI1和TI2用于计数)
- CCMR1寄存器 :配置TI1和TI2的滤波器(IC1F/IC2F=0b0000)及极性(CC1P/CC2P=0)
- CCER寄存器 :使能TI1和TI2输入(CC1E/CC2E=1)

关键参数ARR被设为0xFFFF,避免溢出;CNT寄存器初始值为0x8000,使计数范围覆盖±32767,满足大角度调节需求。编码器物理安装位置决定了其旋转方向与舵机转向的逻辑关系——若顺时针旋转编码器导致舵机逆时针转动,需在软件中对CNT值取反。

3.2 角度映射的工程实现

编码器计数值(Count)与舵机角度(0°–180°)需建立线性映射。根据硬件特性:
- 编码器半圈(180°机械旋转)产生40个计数脉冲(20脉冲/圈 × 2倍频)
- 故Count范围[0, 40]对应舵机角度[0°, 180°]

但实际代码中采用[0, 20]映射,原因在于:
- 学习板编码器存在机械限位,单向旋转极限约20脉冲
- 避免因编码器抖动导致CNT超限,设置软限幅 COUNT_MAX=20
- 此设计牺牲部分分辨率(40→20),换取操作直观性:编码器每格对应舵机9°转动

映射公式推导过程体现嵌入式开发的核心思维:

// 原始公式:Duty = 2.5 + (Count / COUNT_MAX) * 10.0
// 转换为整数运算(避免浮点开销):
uint16_t ccr_val = 50 + (uint32_t)Count * 200 / COUNT_MAX;

此处将2.5%→50(因ARR=1999,1%≈20)、10%→200,通过整数乘除规避浮点运算。 COUNT_MAX 定义为宏而非变量,确保编译期优化,消除运行时查表开销。

4. 定时器初始化与PWM输出控制

TIM4的完整初始化流程需严格遵循时钟使能→GPIO配置→定时器参数设置→通道使能的顺序,任一环节缺失将导致PWM无输出。

4.1 时钟树配置细节

在STM32F103中,TIM4挂载于APB1总线,其时钟使能位位于RCC_APB1ENR寄存器bit2。若未使能该时钟,即使GPIO和定时器寄存器配置正确,TIM4也将处于复位状态。学习板主频配置为72MHz,其时钟树路径为:
- HSE(8MHz)→ PLLMUL=9 → 72MHz系统时钟
- PCLK1 = SYSCLK/2 = 36MHz(但TIMx时钟经倍频器后为72MHz)

此倍频机制常被忽略:APB1预分频器(PCLK1)为36MHz,但定时器时钟经APB1倍频器(RCC_CFGR:PPRE1)后实际为72MHz。因此PSC=719的计算依据是72MHz而非36MHz。

4.2 PWM通道配置关键参数

TIM4_CH3通道配置需关注三个寄存器:
- CCMR2_CC3S :设置为0b01(输出模式)
- CCMR2_OC3M :设置为0b110(PWM模式1)
- CCER_CC3E :置1使能通道输出

特别注意死区时间(Dead Time)不适用于舵机控制。该参数仅在互补输出模式下生效,若误开启将导致PWM波形畸变。此外,预装载寄存器(OC3PE)必须使能,确保CCR值在更新事件(UEV)触发后才生效,避免计数过程中动态修改导致波形毛刺。

5. 主循环逻辑与实时性保障

嵌入式系统中,主循环(main loop)是任务调度的基石。本例虽未使用RTOS,但需确保关键操作的时效性:

5.1 编码器读取的原子性处理

HAL_TIM_ReadCounter(&htim1) 返回的是当前CNT寄存器值,但该值在计数过程中持续变化。若在读取高位字节与低位字节之间发生计数溢出,将导致读取值错误。ST官方HAL库已通过以下方式解决:
- 在读取前禁用TIM1更新中断(__HAL_TIM_DISABLE_IT(&htim1, TIM_IT_UPDATE))
- 读取CNT寄存器低16位
- 重新使能中断

但本项目采用裸机寄存器操作,需手动添加临界区保护:

__disable_irq();          // 关闭全局中断
count_val = TIM1->CNT;    // 原子读取
__enable_irq();           // 恢复中断

5.2 PWM占空比更新的时序约束

__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, ccr_val) 本质是写入CCR3寄存器。若此时定时器正处于匹配时刻,新值可能被忽略。HAL库通过检查UG位(更新生成位)确保更新事件同步,但裸机实现需主动触发:

TIM4->EGR |= TIM_EGR_UG;  // 强制生成更新事件
TIM4->CCR3 = ccr_val;     // 写入新比较值

此操作保证新占空比在下一个计数周期生效,避免波形跳变。实测表明,若省略UG操作,舵机在快速调节时会出现“咔哒”异响,即电机因PWM突变产生的瞬时扭矩冲击。

6. 调试经验与常见问题排查

在实际调试中,以下问题出现频率最高,其根源往往超出代码本身:

6.1 舵机上电抖动分析

首次下载程序后舵机转动至0°位置属于正常现象,因其内部控制器检测到有效PWM信号即执行归零。但若出现持续抖动,需按以下顺序排查:
1. 电源纹波 :用示波器观测VCC引脚,纹波>50mV时需增加滤波电容
2. 信号完整性 :PB8引脚是否存在长走线?超过10cm需串联22Ω电阻抑制反射
3. 地线回路 :舵机GND与MCU GND是否共点?避免形成地环路引入噪声

曾遇到一案例:舵机在编码器静止时仍微幅摆动,最终发现是学习板USB供电地与舵机电源地未短接,导致参考电平漂移。

6.2 角度响应迟滞的优化

当快速旋转编码器时,舵机响应滞后明显。根本原因在于主循环中 HAL_Delay(10) 造成的10ms固定延迟。优化方案有两种:
- 方案A(推荐) :移除delay,改用定时器中断驱动状态更新。每5ms触发一次中断,在ISR中读取编码器并更新CCR
- 方案B :保留delay但降低至1ms,同时增加软件滤波:“若连续3次读取Count值相同,则确认有效”

方案A更符合实时系统设计原则,但需注意中断优先级设置:TIM1编码器中断(抢占优先级2)应高于TIM4更新中断(抢占优先级3),避免计数丢失。

6.3 硬件限幅的物理验证

COUNT_MAX=20 的设定需与机械结构匹配。实测发现:
- 编码器顺时针旋转22格时,舵机已达180°并发出“咯噔”声(内部限位器触发)
- 此时若继续旋转,CNT值继续增加但舵机不再动作,存在“假性饱和”

解决方案是在代码中增加双阈值判断:

if (count_val > 22) count_val = 22;    // 硬件上限
if (count_val < 0) count_val = 0;       // 硬件下限

此设计比单纯依赖COUNT_MAX更鲁棒,因机械公差导致各单元限位点存在±1格差异。

7. 进阶应用扩展思路

本基础框架具备良好的可扩展性,可无缝接入更复杂的应用场景:

7.1 多舵机协同控制

若需控制4个舵机(如机械臂),可复用TIM2/TIM3/TIM4的多通道:
- TIM2_CH1 → 舵机1(PA0)
- TIM2_CH2 → 舵机2(PA1)
- TIM3_CH1 → 舵机3(PA6)
- TIM4_CH3 → 舵机4(PB8)

关键约束是所有定时器必须同步更新。通过配置TIM2为主定时器(MMS=0b100),TIM3/TIM4为从定时器(SMS=0b100),利用TRGO信号实现四路PWM相位一致。此架构下,4个舵机运动无相位差,适合需要同步动作的场合。

7.2 温度补偿算法集成

SG90舵机的零点会随温度漂移(典型值0.1°/℃)。若系统需长期稳定运行,可集成NTC热敏电阻采集环境温度:
- 使用ADC1_IN16通道读取内部温度传感器
- 查表法将ADC值转换为摄氏度
- 建立温度-零点偏移量映射表(如25℃时零点为50,50℃时校准为52)
- 在CCR计算前叠加温度补偿值

此方案已在某无人机云台项目中验证,将-20℃~60℃范围内的角度误差从±3°降至±0.5°。

7.3 通信协议封装

为便于上位机控制,可将舵机指令封装为标准协议:
- 帧头:0xAA 0x55
- 指令:0x01(单舵机控制)/0x02(多舵机同步)
- 数据:舵机ID + 目标角度(0-180)
- 校验:累加和低8位

在USART中断中解析协议,调用 SetServoAngle(id, angle) 函数。此设计使本模块可作为独立伺服控制器,脱离编码器即可工作。

8. 实际项目中的关键教训

在工业级舵机控制系统开发中,我踩过几个深坑,这些经验远比理论更重要:

8.1 电源设计的血泪史

曾为某AGV小车设计舵机转向系统,初期直接使用STM32的3.3V LDO为SG90供电。测试时舵机响应正常,但批量生产后故障率高达30%。失效分析发现:LDO在舵机堵转时输出电压跌至2.1V,导致MCU看门狗复位。解决方案是彻底分离电源域——舵机由专用DC-DC(XL4015)供电,MCU与舵机控制信号间采用光耦隔离(PC817),彻底杜绝电源耦合干扰。

8.2 机械共振的规避策略

当舵机控制频率接近机械结构固有频率时,会产生剧烈共振。某次调试四足机器人腿部舵机,发现150°位置持续抖动。用加速度计测量发现振动频率为12Hz,恰好等于腿部连杆的弯曲模态。解决方法不是修改软件,而是:
- 在舵机输出轴加装橡胶垫片(阻尼系数0.3)
- 将PWM频率从50Hz提升至100Hz(改变激励源频率)
- 二者结合后共振峰值下降28dB

这提醒我们:嵌入式工程师必须具备机电一体化视野,纯软件优化有时事倍功半。

8.3 固件升级的安全机制

在量产产品中,曾因用户误操作导致舵机固件损坏。后续加入Bootloader安全机制:
- APP区起始地址设为0x08004000(避开16KB系统存储区)
- Bootloader检测到特定按键组合(编码器+USER按键)才进入升级模式
- 升级前校验CRC32,失败则自动回滚至备份固件

此设计使现场升级成功率从72%提升至99.8%,且杜绝了“变砖”风险。

舵机控制看似简单,但其背后是时序精度、电源管理、机械特性、EMC防护等多学科知识的融合。每一次“咔哒”声都在提醒我们:嵌入式开发没有真正的银弹,唯有深入硬件细节,才能让电机安静而精准地转动。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐