Skip to content

esp32_mcpwm

MCPWM: motor-control PWM — one unit (MCPWM0), timer 0, operator 0.

This is the peripheral LEDC is not: a complementary output pair with hardware dead-time insertion, a fault input that forces the outputs low in silicon, and a capture unit for timing an input edge. Dead time is why MCPWM, not LEDC, drives an H-bridge — it stops both transistors of a leg conducting at once, a short that destroys the bridge, and it has to be enforced by the hardware because software cannot be trusted to be that prompt.

Timer 0 up-counting at a configured frequency; operator 0 generating an active-high pulse on PWM0A whose width is a compare value; the dead-time module deriving PWM0B as A’s complement with configurable rising- and falling-edge delays; fault F0 as a latched (one-shot) trip forcing both outputs low; and capture channel 0 timestamping edges. One unit and one operator — MCPWM1 and operators 1/2 are the same registers at a fixed stride and are a mechanical extension, not new hardware understanding.

DR_REG_MCPWM0_BASE = 0x3FF5E000 (esp-idf soc/soc.h, DR_REG_PWM0_BASE). Fields from soc/mcpwm_reg.h / mcpwm_struct.h; sequences from hal/esp32/include/hal/mcpwm_ll.h and driver/mcpwm.c. Clock/reset is DPORT bit 17 ([dport::ClockBit::PWM0]).

Register Offset Fields
CLK_CFG 0x00 CLK_PRESCALE [7:0]
TIMER0_CFG0 0x04 PRESCALE [7:0], PERIOD [23:8]
TIMER0_CFG1 0x08 START [2:0], MOD [4:3]
OPERATOR_TIMERSEL 0x38 OP0_SEL [1:0]
GEN0_TSTMP_A 0x40 compare A [15:0]
GEN0_A 0x50 UTEZ [1:0], UTEA [5:4] (up: zero/compareA actions)
DT0_CFG 0x58 dead-time mode bits (active-high complementary)
DT0_FED_CFG/DT0_RED_CFG 0x5C/0x60 falling/rising edge delay [15:0]
FH0_CFG0 0x68 F0_OST 7, A/B one-shot forced-low actions
FH0_CFG1 0x6C CLR_OST 0 (rising edge re-arms)
FH0_STATUS 0x70 OST_ON 1
FAULT_DETECT 0xE4 F0_EN 0, F0_POLE 3
CAP_TIMER_CFG 0xE8 TIMER_EN 0
CAP_CH0_CFG 0xF0 EN 0, MODE [2:1], PRESCALE [10:3]
CAP_CH0 0xFC 32-bit timestamp
CAP_STATUS 0x108 CAP0_EDGE 0 (1 = negedge)
INT_RAW/INT_CLR 0x114/0x11C CAP0 bit 27
struct Capture {
pub timestamp: u32,
pub falling: bool,
}

A captured edge: the capture-timer timestamp and whether it was a falling edge.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

struct Config {
pub clk_prescale: u16,
pub timer_prescale: u16,
pub period: u16,
pub compare_a: u16,
pub dead_red: u16,
pub dead_fed: u16,
}

One up-counting complementary configuration.

Output frequency = PWM_CLK_HZ / (clk_prescale+1) / (timer_prescale+1) / (period+1). A is high for compare_a timer ticks each period, so the duty is compare_a / (period+1) before dead time. dead_red/dead_fed are in PWM-clock cycles (PWM_CLK_HZ / (clk_prescale+1)): dead_red delays A’s rising edge, dead_fed delays B’s.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

struct Mcpwm { /* private fields */ }

The MCPWM0 controller.

const PWM_CLK_HZ: u32;

The PWM clock is 160 MHz divided by (clk_prescale + 1). Both the timer (through its own prescaler) and the dead-time/capture blocks derive from it.