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.
What this cut covers
Section titled “What this cut covers”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.
Register facts
Section titled “Register facts”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 |
Structs
Section titled “Structs”Capture
Section titled “Capture”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
Config
Section titled “Config”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.
Constants
Section titled “Constants”PWM_CLK_HZ
Section titled “PWM_CLK_HZ”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.