Merge branch 'fix/mcpwm_cap_prescale' into 'master'

fix(mcpwm): correct the wrong capture prescale

Closes IDFGH-18010

See merge request espressif/esp-idf!51691
This commit is contained in:
morris
2026-09-03 23:28:31 +08:00
17 changed files with 551 additions and 42 deletions

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 440" font-family="'DejaVu Sans Mono', 'Consolas', 'Liberation Mono', monospace">
<defs>
<marker id="arr" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0,0 8,3 0,6" fill="#555"/>
</marker>
</defs>
<rect width="700" height="440" fill="#ffffff" rx="8"/>
<text x="350" y="24" text-anchor="middle" font-size="13" fill="#333" font-weight="700">Both edges</text>
<text x="350" y="42" text-anchor="middle" font-size="10" fill="#666">API prescale = same-edge ratio</text>
<text x="20" y="68" font-size="12" fill="#333" font-weight="700">A. prescale = 1 (bypass)</text>
<g transform="translate(80, 78)">
<text x="-10" y="36" text-anchor="end" font-size="11" fill="#1e88e5" font-weight="600">input</text>
<polyline fill="none" stroke="#1e88e5" stroke-width="2"
points="0,40 0,12 35,12 35,40 70,40 70,12 105,12 105,40 140,40 140,12 175,12 175,40 210,40"/>
<g fill="#e53935"><circle cx="0" cy="12" r="3.5"/><circle cx="70" cy="12" r="3.5"/><circle cx="140" cy="12" r="3.5"/></g>
<g fill="#fb8c00"><rect x="31" y="8" width="8" height="8"/><rect x="101" y="8" width="8" height="8"/><rect x="171" y="8" width="8" height="8"/></g>
<text x="-10" y="100" text-anchor="end" font-size="11" fill="#333" font-weight="600">capture</text>
<line x1="0" y1="92" x2="210" y2="92" stroke="#999" stroke-width="1.5"/>
<line x1="0" y1="12" x2="0" y2="92" stroke="#e53935" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="35" y1="40" x2="35" y2="92" stroke="#fb8c00" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="70" y1="12" x2="70" y2="92" stroke="#e53935" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="105" y1="40" x2="105" y2="92" stroke="#fb8c00" stroke-width="1" stroke-dasharray="3,3"/>
<polyline points="0,92 0,72 12,72 12,92" fill="none" stroke="#e53935" stroke-width="2"/>
<polyline points="35,92 35,72 47,72 47,92" fill="none" stroke="#fb8c00" stroke-width="2"/>
<polyline points="70,92 70,72 82,72 82,92" fill="none" stroke="#e53935" stroke-width="2"/>
<polyline points="105,92 105,72 117,72 117,92" fill="none" stroke="#fb8c00" stroke-width="2"/>
<text x="6" y="66" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="41" y="66" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="76" y="66" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="111" y="66" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="250" y="36" font-size="10" fill="#e53935" font-weight="600">R = GPIO rising</text>
<text x="250" y="54" font-size="10" fill="#fb8c00" font-weight="600">F = GPIO falling</text>
</g>
<text x="20" y="228" font-size="12" fill="#333" font-weight="700">B. prescale = 4</text>
<g transform="translate(80, 250)">
<text x="-10" y="36" text-anchor="end" font-size="11" fill="#1e88e5" font-weight="600">input</text>
<polyline fill="none" stroke="#1e88e5" stroke-width="2"
points="0,40 0,12 35,12 35,40 70,40 70,12 105,12 105,40 140,40 140,12 175,12 175,40 210,40 210,12 245,12 245,40 280,40 280,12 315,12 315,40 350,40 350,12 385,12 385,40 420,40 420,12 455,12 455,40 490,40 490,12"/>
<!-- first at cycle 1, then every 2: 1(R),3(F),5(R),7(F) -->
<g fill="#1e88e5"><circle cx="70" cy="12" r="3.5"/><circle cx="210" cy="12" r="3.5"/><circle cx="350" cy="12" r="3.5"/><circle cx="490" cy="12" r="3.5"/></g>
<g fill="#bbb"><circle cx="0" cy="12" r="3"/><circle cx="140" cy="12" r="3"/><circle cx="280" cy="12" r="3"/><circle cx="420" cy="12" r="3"/></g>
<g fill="#ccc"><rect x="31" y="8" width="8" height="8"/><rect x="101" y="8" width="8" height="8"/><rect x="171" y="8" width="8" height="8"/><rect x="241" y="8" width="8" height="8"/><rect x="311" y="8" width="8" height="8"/><rect x="381" y="8" width="8" height="8"/><rect x="451" y="8" width="8" height="8"/></g>
<text x="70" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 1</text>
<text x="210" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 3</text>
<text x="350" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 5</text>
<text x="490" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 7</text>
<text x="-10" y="100" text-anchor="end" font-size="11" fill="#333" font-weight="600">capture</text>
<line x1="0" y1="92" x2="490" y2="92" stroke="#999" stroke-width="1.5"/>
<line x1="70" y1="12" x2="70" y2="92" stroke="#e53935" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="210" y1="12" x2="210" y2="92" stroke="#fb8c00" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="350" y1="12" x2="350" y2="92" stroke="#e53935" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="490" y1="12" x2="490" y2="92" stroke="#fb8c00" stroke-width="1" stroke-dasharray="3,3"/>
<polyline points="70,92 70,70 82,70 82,92" fill="none" stroke="#e53935" stroke-width="2"/>
<polyline points="210,92 210,70 222,70 222,92" fill="none" stroke="#fb8c00" stroke-width="2"/>
<polyline points="350,92 350,70 362,70 362,92" fill="none" stroke="#e53935" stroke-width="2"/>
<polyline points="490,92 490,70 502,70 502,92" fill="none" stroke="#fb8c00" stroke-width="2"/>
<text x="76" y="64" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="216" y="64" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="356" y="64" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="496" y="64" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<line x1="70" y1="110" x2="70" y2="120" stroke="#666"/><line x1="210" y1="110" x2="210" y2="120" stroke="#666"/>
<line x1="70" y1="115" x2="210" y2="115" stroke="#666" stroke-width="1.5" marker-end="url(#arr)"/>
<text x="140" y="134" text-anchor="middle" font-size="10" fill="#666" font-weight="600">adj = 2 x T = (prescale/2) x T</text>
<line x1="70" y1="144" x2="70" y2="154" stroke="#e53935"/><line x1="350" y1="144" x2="350" y2="154" stroke="#e53935"/>
<line x1="70" y1="149" x2="350" y2="149" stroke="#e53935" stroke-width="1.5" marker-end="url(#arr)"/>
<text x="210" y="168" text-anchor="middle" font-size="11" fill="#e53935" font-weight="600">R-to-R = 4 x T = prescale x T</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 420" font-family="'DejaVu Sans Mono', 'Consolas', 'Liberation Mono', monospace">
<defs>
<marker id="arr" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0,0 8,3 0,6" fill="#555"/>
</marker>
</defs>
<rect width="700" height="420" fill="#ffffff" rx="8"/>
<text x="350" y="24" text-anchor="middle" font-size="13" fill="#333" font-weight="700">Falling-edge only</text>
<text x="350" y="42" text-anchor="middle" font-size="10" fill="#666">API prescale = same-edge ratio</text>
<text x="20" y="68" font-size="12" fill="#333" font-weight="700">A. prescale = 1 (bypass)</text>
<g transform="translate(80, 78)">
<text x="-10" y="36" text-anchor="end" font-size="11" fill="#1e88e5" font-weight="600">input</text>
<polyline fill="none" stroke="#1e88e5" stroke-width="2"
points="0,40 0,12 35,12 35,40 70,40 70,12 105,12 105,40 140,40 140,12 175,12 175,40 210,40"/>
<g fill="#ccc"><circle cx="0" cy="12" r="3"/><circle cx="70" cy="12" r="3"/><circle cx="140" cy="12" r="3"/></g>
<g fill="#fb8c00"><rect x="31" y="8" width="8" height="8"/><rect x="101" y="8" width="8" height="8"/><rect x="171" y="8" width="8" height="8"/></g>
<text x="-10" y="100" text-anchor="end" font-size="11" fill="#fb8c00" font-weight="600">capture</text>
<line x1="0" y1="92" x2="210" y2="92" stroke="#fb8c00" stroke-width="1.5"/>
<line x1="35" y1="40" x2="35" y2="92" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="105" y1="40" x2="105" y2="92" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="175" y1="40" x2="175" y2="92" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<polyline points="35,92 35,72 47,72 47,92" fill="none" stroke="#fb8c00" stroke-width="2"/>
<polyline points="105,92 105,72 117,72 117,92" fill="none" stroke="#fb8c00" stroke-width="2"/>
<polyline points="175,92 175,72 187,72 187,92" fill="none" stroke="#fb8c00" stroke-width="2"/>
<text x="41" y="66" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="111" y="66" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="181" y="66" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="250" y="40" font-size="10" fill="#555">Every falling edge -&gt; F</text>
<text x="250" y="58" font-size="10" fill="#888">Rising edges ignored</text>
</g>
<text x="20" y="228" font-size="12" fill="#333" font-weight="700">B. prescale = 4</text>
<g transform="translate(80, 250)">
<text x="-10" y="36" text-anchor="end" font-size="11" fill="#1e88e5" font-weight="600">input</text>
<polyline fill="none" stroke="#1e88e5" stroke-width="2"
points="0,40 0,12 35,12 35,40 70,40 70,12 105,12 105,40 140,40 140,12 175,12 175,40 210,40 210,12 245,12 245,40 280,40 280,12 315,12 315,40 350,40 350,12 385,12 385,40 420,40 420,12 455,12 455,40 490,40 490,12"/>
<!-- first at cycle 3, then every 4: cycle 3, 7 -->
<g fill="#1e88e5"><circle cx="210" cy="12" r="3.5"/><circle cx="490" cy="12" r="3.5"/></g>
<g fill="#bbb"><circle cx="0" cy="12" r="3"/><circle cx="70" cy="12" r="3"/><circle cx="140" cy="12" r="3"/><circle cx="280" cy="12" r="3"/><circle cx="350" cy="12" r="3"/><circle cx="420" cy="12" r="3"/></g>
<g fill="#ccc"><rect x="31" y="8" width="8" height="8"/><rect x="101" y="8" width="8" height="8"/><rect x="171" y="8" width="8" height="8"/><rect x="241" y="8" width="8" height="8"/><rect x="311" y="8" width="8" height="8"/><rect x="381" y="8" width="8" height="8"/><rect x="451" y="8" width="8" height="8"/></g>
<text x="210" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 3</text>
<text x="490" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 7</text>
<line x1="0" y1="52" x2="70" y2="52" stroke="#bbb" stroke-width="1"/>
<line x1="0" y1="48" x2="0" y2="56" stroke="#bbb"/><line x1="70" y1="48" x2="70" y2="56" stroke="#bbb"/>
<text x="35" y="68" text-anchor="middle" font-size="10" fill="#888">T</text>
<text x="-10" y="108" text-anchor="end" font-size="11" fill="#fb8c00" font-weight="600">capture</text>
<line x1="0" y1="100" x2="490" y2="100" stroke="#fb8c00" stroke-width="1.5"/>
<line x1="210" y1="12" x2="210" y2="100" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="490" y1="12" x2="490" y2="100" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<polyline points="210,100 210,80 222,80 222,100" fill="none" stroke="#fb8c00" stroke-width="2"/>
<polyline points="490,100 490,80 502,80 502,100" fill="none" stroke="#fb8c00" stroke-width="2"/>
<text x="216" y="74" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="496" y="74" text-anchor="middle" font-size="10" fill="#fb8c00" font-weight="700">F</text>
<text x="350" y="90" text-anchor="middle" font-size="10" fill="#555">first = prescale - 1, then every 4</text>
<line x1="210" y1="118" x2="210" y2="130" stroke="#333"/><line x1="490" y1="118" x2="490" y2="130" stroke="#333"/>
<line x1="210" y1="124" x2="490" y2="124" stroke="#333" stroke-width="1.5" marker-end="url(#arr)"/>
<text x="350" y="146" text-anchor="middle" font-size="11" fill="#333" font-weight="600">same-edge = 4 x T = prescale x T</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 400" font-family="'DejaVu Sans Mono', 'Consolas', 'Liberation Mono', monospace">
<defs>
<marker id="arr" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0,0 8,3 0,6" fill="#555"/>
</marker>
</defs>
<rect width="700" height="400" fill="#ffffff" rx="8"/>
<text x="350" y="24" text-anchor="middle" font-size="13" fill="#333" font-weight="700">Rising-edge only</text>
<text x="350" y="42" text-anchor="middle" font-size="10" fill="#666">API prescale = same-edge ratio</text>
<text x="20" y="68" font-size="12" fill="#333" font-weight="700">A. prescale = 1 (bypass)</text>
<g transform="translate(80, 78)">
<text x="-10" y="36" text-anchor="end" font-size="11" fill="#1e88e5" font-weight="600">input</text>
<polyline fill="none" stroke="#1e88e5" stroke-width="2"
points="0,40 0,12 35,12 35,40 70,40 70,12 105,12 105,40 140,40 140,12 175,12 175,40 210,40"/>
<g fill="#1e88e5"><circle cx="0" cy="12" r="3.5"/><circle cx="70" cy="12" r="3.5"/><circle cx="140" cy="12" r="3.5"/></g>
<g fill="#ccc"><rect x="31" y="8" width="8" height="8"/><rect x="101" y="8" width="8" height="8"/><rect x="171" y="8" width="8" height="8"/></g>
<text x="-10" y="100" text-anchor="end" font-size="11" fill="#e53935" font-weight="600">capture</text>
<line x1="0" y1="92" x2="210" y2="92" stroke="#e53935" stroke-width="1.5"/>
<line x1="0" y1="12" x2="0" y2="92" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="70" y1="12" x2="70" y2="92" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="140" y1="12" x2="140" y2="92" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<polyline points="0,92 0,72 12,72 12,92" fill="none" stroke="#e53935" stroke-width="2"/>
<polyline points="70,92 70,72 82,72 82,92" fill="none" stroke="#e53935" stroke-width="2"/>
<polyline points="140,92 140,72 152,72 152,92" fill="none" stroke="#e53935" stroke-width="2"/>
<text x="6" y="66" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="76" y="66" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="146" y="66" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="250" y="40" font-size="10" fill="#555">Every rising edge -&gt; R</text>
<text x="250" y="58" font-size="10" fill="#888">Falling edges ignored</text>
</g>
<text x="20" y="228" font-size="12" fill="#333" font-weight="700">B. prescale = 4</text>
<g transform="translate(80, 250)">
<text x="-10" y="36" text-anchor="end" font-size="11" fill="#1e88e5" font-weight="600">input</text>
<polyline fill="none" stroke="#1e88e5" stroke-width="2"
points="0,40 0,12 35,12 35,40 70,40 70,12 105,12 105,40 140,40 140,12 175,12 175,40 210,40 210,12 245,12 245,40 280,40 280,12 315,12 315,40 350,40 350,12 385,12 385,40 420,40 420,12"/>
<!-- first at cycle 1, then every 4: cycle 1, 5 -->
<g fill="#bbb"><circle cx="0" cy="12" r="3"/><circle cx="140" cy="12" r="3"/><circle cx="210" cy="12" r="3"/><circle cx="280" cy="12" r="3"/><circle cx="420" cy="12" r="3"/></g>
<g fill="#1e88e5"><circle cx="70" cy="12" r="3.5"/><circle cx="350" cy="12" r="3.5"/></g>
<text x="70" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 1</text>
<text x="350" y="-4" text-anchor="middle" font-size="9" fill="#1e88e5" font-weight="700">cycle 5</text>
<line x1="0" y1="52" x2="70" y2="52" stroke="#bbb" stroke-width="1"/>
<line x1="0" y1="48" x2="0" y2="56" stroke="#bbb"/><line x1="70" y1="48" x2="70" y2="56" stroke="#bbb"/>
<text x="35" y="68" text-anchor="middle" font-size="10" fill="#888">T</text>
<text x="-10" y="108" text-anchor="end" font-size="11" fill="#e53935" font-weight="600">capture</text>
<line x1="0" y1="100" x2="420" y2="100" stroke="#e53935" stroke-width="1.5"/>
<line x1="70" y1="12" x2="70" y2="100" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="350" y1="12" x2="350" y2="100" stroke="#ccc" stroke-width="1" stroke-dasharray="3,3"/>
<polyline points="70,100 70,80 82,80 82,100" fill="none" stroke="#e53935" stroke-width="2"/>
<polyline points="350,100 350,80 362,80 362,100" fill="none" stroke="#e53935" stroke-width="2"/>
<text x="76" y="74" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="356" y="74" text-anchor="middle" font-size="10" fill="#e53935" font-weight="700">R</text>
<text x="210" y="90" text-anchor="middle" font-size="10" fill="#555">first = prescale/2 - 1, then every 4</text>
<line x1="70" y1="118" x2="70" y2="130" stroke="#333"/><line x1="350" y1="118" x2="350" y2="130" stroke="#333"/>
<line x1="70" y1="124" x2="350" y2="124" stroke="#333" stroke-width="1.5" marker-end="url(#arr)"/>
<text x="210" y="146" text-anchor="middle" font-size="11" fill="#333" font-weight="600">same-edge = 4 x T = prescale x T</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -68,15 +68,56 @@ Capture channel configuration
.. list::
- :cpp:member:`gpio_num <mcpwm_capture_channel_config_t::gpio_num>` — the GPIO carrying the input signal.
- :cpp:member:`prescale <mcpwm_capture_channel_config_t::prescale>`divides the input signal before capture; the effective input frequency is the capture clock divided by ``prescale``. Raise it to extend the measurable period range, at the cost of time resolution.
- :cpp:member:`gpio_num <mcpwm_capture_channel_config_t::gpio_num>` — the GPIO carrying the input signal. The driver configures it as an input but does not enable any pull-up or pull-down; if the signal is not actively driven to both levels, call :cpp:func:`gpio_set_pull_mode()` so the pin idles at the level you expect.
- :cpp:member:`prescale <mcpwm_capture_channel_config_t::prescale>`input prescale ratio. Same-edge captures (same reported edge type) are spaced about ``prescale`` input periods apart (capture rate ≈ ``input_rate / prescale``). ``0`` or ``1`` means no prescaling (bypass); any other value must be even. Leaving the field at ``0`` (the C default) is therefore bypass. Raise it to extend the measurable period range, at the cost of time resolution. See :ref:`mcpwm-cap-input-prescale` for pipeline order and edge-mode behavior.
- :cpp:member:`pos_edge <mcpwm_capture_channel_config_t::flags::pos_edge>` and :cpp:member:`neg_edge <mcpwm_capture_channel_config_t::flags::neg_edge>` — which edges are captured. The example captures both, which is what a pulse-width measurement needs.
- :cpp:member:`invert_cap_signal <mcpwm_capture_channel_config_t::flags::invert_cap_signal>` — inverts the input signal before capture, so a logical ``1`` on the pin is seen as ``0`` by the capture peripheral and vice versa.
- :cpp:member:`intr_priority <mcpwm_capture_channel_config_t::intr_priority>` — the interrupt priority used by the capture callbacks. Not setting it (``0``) lets the driver choose a low priority.
.. _mcpwm-cap-input-prescale:
Input prescale
==============
The capture channel processes the input in a **fixed, serial** order; the two stages cannot be swapped:
1. **First** divide the GPIO waveform by ``prescale`` (``0``/``1`` means bypass);
2. **Then** use ``pos_edge``/``neg_edge`` to decide which post-prescale events are reported to software.
In other words, hardware does **not** “pick GPIO edges by the configured polarity first, then divide those edges.” With ``prescale > 1``:
- The :cpp:member:`cap_edge <mcpwm_capture_event_data_t::cap_edge>` in the callback may not match the physical GPIO edge;
- Adjacent opposite-edge gaps are **not pulse width**, and **the true duty cycle cannot be recovered**.
Always compute period or frequency from timestamps of the same reported edge type. See the figures below for per-mode timing.
.. note::
The capture driver configures the GPIO as an input but does not set any pull-up or pull-down resistor. If the input signal is not actively driven to both levels, call :cpp:func:`gpio_set_pull_mode()` to select the pull direction that keeps the pin at the level you expect when the line is idle.
Keep ``prescale = 1`` (bypass) and capture both edges when measuring pulse width or duty cycle. Prefer raising ``prescale`` only for very fast inputs when you only need frequency or period, and capture a single edge type only.
The figures below show how ``prescale`` affects capture timing for rising-only, falling-only, and both-edge modes.
.. figure:: /../_static/mcpwm/capture_prescale_rising.svg
:align: center
:alt: Rising-edge only capture with prescale bypass and prescale 4.
Rising-edge only. With ``prescale = 1`` (bypass), every rising edge captures as ``R``. With ``prescale > 1``, the first capture is at cycle ``prescale / 2 - 1``, then every ``prescale``-th rising edge (the figure shows ``prescale = 4``: cycle 1, 5, …). Same-edge spacing is ``prescale`` input periods.
.. figure:: /../_static/mcpwm/capture_prescale_falling.svg
:align: center
:alt: Falling-edge only capture with prescale bypass and prescale 4.
Falling-edge only. With ``prescale = 1`` (bypass), every falling edge captures as ``F``. With ``prescale > 1``, GPIO falling edges do not produce captures; **events land on rising steps**. The first capture is at cycle ``prescale - 1``, then every ``prescale`` rising steps, while ``cap_edge`` still reports ``F`` (the figure shows ``prescale = 4``: cycle 3, 7, …).
.. figure:: /../_static/mcpwm/capture_prescale_both.svg
:align: center
:alt: Both-edge capture with prescale bypass and prescale 4.
Both edges. With ``prescale = 1`` (bypass), ``R``/``F`` match the physical pin edges. With ``prescale > 1``, captures fire on rising pin steps only (every ``prescale / 2`` periods). The first capture matches rising-only (cycle ``prescale / 2 - 1``) and reports ``R``, then ``R``/``F`` alternate; adjacent ``R``/``F`` spacing is not pulse width. Same-edge gaps remain about ``prescale`` periods. Always compute period or frequency from timestamps of the same reported edge type.
.. warning::
With ``prescale > 1``, edge polarity can disappear: reported ``R``/``F`` describe the post-prescale event type, not the physical GPIO edge. Falling-edge-only capture in particular can fire on rising pin steps while still reporting ``F``. Do not infer the pin transition from ``cap_edge``, and do not treat adjacent opposite-edge gaps as pulse width.
Capture event callbacks
=======================

View File

@@ -68,15 +68,56 @@ MCPWM 捕获:测量输入脉冲
.. list::
- :cpp:member:`gpio_num <mcpwm_capture_channel_config_t::gpio_num>` — 承载输入信号的 GPIO。
- :cpp:member:`prescale <mcpwm_capture_channel_config_t::prescale>`捕获前对输入信号分频,有效输入频率为捕获时钟除以 ``prescale``。提高它可扩展可测周期范围,但会降低时间分辨率
- :cpp:member:`gpio_num <mcpwm_capture_channel_config_t::gpio_num>` — 承载输入信号的 GPIO。驱动会把它配成输入,但不会设置上拉或下拉;若信号并非主动驱动到两个电平,请调用 :cpp:func:`gpio_set_pull_mode()`,让引脚空闲时保持在期望电平。
- :cpp:member:`prescale <mcpwm_capture_channel_config_t::prescale>`输入预分频比。同沿(相同上报边沿类型)两次捕获的间隔约为 ``prescale`` 个输入周期(捕获速率 ≈ ``输入速率 / prescale``)。``0````1`` 表示不分频bypass其它值必须为偶数。结构体里不写该字段时默认为 ``0``,即 bypass。提高它可扩展可测周期范围但会降低时间分辨率。流水线顺序与各边沿模式下的行为见 :ref:`mcpwm-cap-input-prescale`
- :cpp:member:`pos_edge <mcpwm_capture_channel_config_t::flags::pos_edge>`:cpp:member:`neg_edge <mcpwm_capture_channel_config_t::flags::neg_edge>` — 捕获哪些边沿。示例同时捕获两个边沿,这正是脉宽测量所需的。
- :cpp:member:`invert_cap_signal <mcpwm_capture_channel_config_t::flags::invert_cap_signal>` — 捕获前反相输入信号,让引脚上的逻辑 ``1`` 在捕获外设看来是 ``0``,反之亦然。
- :cpp:member:`intr_priority <mcpwm_capture_channel_config_t::intr_priority>` — 捕获回调使用的中断优先级。不设置(``0``)时由驱动选择较低优先级。
.. _mcpwm-cap-input-prescale:
输入预分频
==========
捕获通道内部按**固定顺序**串行处理输入,两级不能对调:
1. 先用 ``prescale`` 对 GPIO 波形做预分频(``0``/``1`` 为 bypass
2. 再根据 ``pos_edge``/``neg_edge`` 决定哪些分频后的事件上报给软件。
也就是说,硬件不是“先按设定的边沿挑选 GPIO再对选中的边沿分频”。因此 ``prescale > 1`` 时:
- 回调里的 :cpp:member:`cap_edge <mcpwm_capture_event_data_t::cap_edge>` 不一定等于 GPIO 的物理边沿;
- 相邻异沿间隔**不是真实脉宽****无法还原真实占空比**
请用相同上报类型(同沿)的时间戳计算周期或频率。各边沿模式下的具体时序见下方示意图。
.. note::
捕获驱动会把 GPIO 配置为输入,但不会设置任何上拉或下拉电阻。如果输入信号并非主动驱动到两个电平,请调用 :cpp:func:`gpio_set_pull_mode()` 选择上拉或下拉方向,让引脚空闲时保持在你期望的电平。
测脉宽或占空比时保持 ``prescale = 1`` bypass并捕获双边沿。建议仅在输入过快、只需测量频率/周期时再提高 ``prescale``,且只捕获单边沿。
下图说明 ``prescale`` 在仅上升沿、仅下降沿和双边沿模式下如何影响捕获时机。
.. figure:: /../_static/mcpwm/capture_prescale_rising.svg
:align: center
:alt: 仅上升沿捕获prescale bypass 与 prescale 4。
仅上升沿。``prescale = 1`` bypass时每个上升沿捕获为 ``R````prescale > 1`` 时,首次捕获在 cycle ``prescale / 2 - 1``,之后每隔 ``prescale`` 个上升沿一次(图中 ``prescale = 4``cycle 1、5、…。同沿间隔为 ``prescale`` 个输入周期。
.. figure:: /../_static/mcpwm/capture_prescale_falling.svg
:align: center
:alt: 仅下降沿捕获prescale bypass 与 prescale 4。
仅下降沿。``prescale = 1`` bypass时每个下降沿捕获为 ``F````prescale > 1`` 时 GPIO 下降沿不产生捕获;**事件落在上升沿步骤上**。首次捕获在 cycle ``prescale - 1``,之后每隔 ``prescale`` 个上升沿一次,``cap_edge`` 仍报 ``F`` (图中 ``prescale = 4``cycle 3、7、…
.. figure:: /../_static/mcpwm/capture_prescale_both.svg
:align: center
:alt: 双边沿捕获prescale bypass 与 prescale 4。
双边沿。``prescale = 1`` bypass``R``/``F`` 对应真实引脚边沿。``prescale > 1`` 时只在上升沿步骤触发(每隔 ``prescale / 2`` 个周期。首次与仅上升沿相同cycle ``prescale / 2 - 1``)且报 ``R``,随后 ``R``/``F`` 交替;相邻 ``R``/``F`` 间隔不是脉宽。同沿间隔仍约为 ``prescale`` 个周期。请用相同上报类型(同沿)的时间戳计算周期或频率。
.. warning::
``prescale > 1`` 时边沿极性可能“消失”:上报的 ``R``/``F`` 描述的是分频之后的事件类型,不是 GPIO 物理边沿。尤其是仅下降沿路径会出现“上升沿步骤却报 ``F``”——不要用它推断引脚真实跳变,也不要用相邻异沿去算脉宽。
捕获事件回调
============