Chapter 3: PWM Servo and ESC Control
3.1 Key Points
- Standard RC PWM: 50 Hz period and 1000–2000 µs pulse width
- Duty calculation with LEDC 14-bit resolution
- ESC arming sequence: hold neutral for about two seconds
- Dual-channel configuration for throttle and steering
3.2 Course Content
Both the ESC and steering servo use standard RC PWM. A 1000 µs pulse represents the minimum command, 1500 µs is neutral, and 2000 µs is the maximum command. OSRCORE uses LEDC TIMER0 with two channels: GPIO1 for throttle and GPIO2 for steering.
3.3 Basic Learning
RC PWM Timing
text
Period: 20 ms (50 Hz)
Pulse range: 1000 µs to 2000 µs
Neutral: 1500 µsDuty Calculation
With an 80 MHz clock and 14-bit LEDC resolution:
text
period_ticks = 80_000_000 / 50 = 1_600_000
1 µs = 80 ticks
duty = pulse_us × 80Examples:
text
1500 µs -> 120000
2000 µs -> 160000A convenient macro is:
c
#define US_TO_DUTY(us) ((us) * 80)ESC Arming
Many ESCs require a neutral input signal after power-up before accepting throttle commands. Holding 1500 µs for one to three seconds is a common arming sequence.
3.4 Program Study
Configure the timer and two PWM channels:
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 = US_TO_DUTY(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 = US_TO_DUTY(1500),
};
ledc_channel_config(&ch_steering);Set pulse width and arm the ESC:
c
static void set_pulse(ledc_channel_t ch, uint32_t pulse_us)
{
ledc_set_duty(LEDC_LOW_SPEED_MODE, ch, US_TO_DUTY(pulse_us));
ledc_update_duty(LEDC_LOW_SPEED_MODE, ch);
}
set_pulse(CH_THROTTLE, 1500);
set_pulse(CH_STEERING, 1500);
vTaskDelay(pdMS_TO_TICKS(2000));3.5 Summary
This chapter covered RC PWM timing, LEDC 14-bit duty calculation, and ESC arming. The US_TO_DUTY() macro maps physical pulse width directly to the LEDC duty value used by later control loops.