第 3 章:PWM 舵机与 ESC 控制
📂 示例代码:03_pwm_servo
3.1 知识要点
- 标准 RC PWM 协议:50 Hz 周期,1000–2000 µs 脉宽
- LEDC 14-bit 分辨率下的占空比计算
- ESC 上电解锁流程(中立位保持 2 秒)
- 双通道同步配置(油门 + 转向)
3.2 课程内容
电调(ESC)和舵机均使用标准 RC PWM 协议控制:50 Hz 重复频率,脉宽 1000 µs 对应最小值,1500 µs 对应中立,2000 µs 对应最大值。OSRCORE 使用 LEDC TIMER0 的两个通道分别驱动 GPIO1(油门)和 GPIO2(转向)。
3.3 基础学习
RC PWM 时序
周期 = 20 ms (50 Hz)
脉宽范围:1000 µs(最小)~ 2000 µs(最大)
中立位:1500 µs14-bit 分辨率下的占空比计算
14-bit 寄存器的最大值为 2¹⁴ − 1 = 16383,对应 100% 占空比(即完整的 20 ms 周期)。正确公式:
duty = pulse_us / 20000.0 * 16383例:1500 µs → 1500/20000 × 16383 ≈ 1228;2000 µs → 2000/20000 × 16383 ≈ 1638。
旧写法 duty = pulse_us × 80 是错误的:1500 × 80 = 120000,远超 14-bit 寄存器上限 16383,会导致寄存器溢出截断,输出脉宽完全错误。
正确实现:
c
/* pulse_us / 20000.0 * (2^14 - 1),结果在 [0, 16383] 范围内 */
static int duty_from_pulse(int pulse_us)
{
if (pulse_us < 1000) pulse_us = 1000;
if (pulse_us > 2000) pulse_us = 2000;
return (int)((float)pulse_us / 20000.0f * ((1 << 14) - 1));
}ESC 解锁流程
大多数 ESC 上电后需要检测到中立位信号(1500 µs)才会解锁,通常需要保持 1–3 秒。未解锁的 ESC 不响应油门指令。
3.4 程序学习
定时器与双通道初始化:
c
ledc_timer_config_t timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = LEDC_TIMER_14_BIT,
.timer_num = LEDC_TIMER_0,
.freq_hz = 50,
.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&timer);
ledc_channel_config_t ch_throttle = {
.gpio_num = 1, .channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0, .duty = duty_from_pulse(1500),
};
ledc_channel_config(&ch_throttle);
ledc_channel_config_t ch_steering = {
.gpio_num = 2, .channel = LEDC_CHANNEL_1,
.timer_sel = LEDC_TIMER_0, .duty = duty_from_pulse(1500),
};
ledc_channel_config(&ch_steering);设置脉宽与 ESC 解锁:
c
static void set_pulse(ledc_channel_t ch, uint32_t pulse_us)
{
ledc_set_duty(LEDC_LOW_SPEED_MODE, ch, duty_from_pulse((int)pulse_us));
ledc_update_duty(LEDC_LOW_SPEED_MODE, ch);
}
// ESC 解锁:中立位保持 2 秒
set_pulse(CH_THROTTLE, 1500);
set_pulse(CH_STEERING, 1500);
vTaskDelay(pdMS_TO_TICKS(2000));3.5 课程总结
本章掌握了 RC PWM 协议的原理和 LEDC 14-bit 精度配置,实现了 ESC 解锁和舵机扫描。duty_from_pulse() 函数将物理脉宽正确映射为 14-bit 寄存器值(公式:pulse_us / 20000.0 * 16383),是后续 PID 控制输出的基础接口。