Skip to content

feat: add mcpwm capture pwm info example#25

Merged
YanKE01 merged 1 commit intomasterfrom
feat/add_clac_pwm_freq_duty
Oct 14, 2025
Merged

feat: add mcpwm capture pwm info example#25
YanKE01 merged 1 commit intomasterfrom
feat/add_clac_pwm_freq_duty

Conversation

@YanKE01
Copy link
Owner

@YanKE01 YanKE01 commented Oct 9, 2025

Summary by CodeRabbit

  • New Features

    • Added a PWM measurement example that reports frequency, period, and duty cycle in real time.
    • Input GPIO is configurable via the project configuration menu.
    • Improved logging for invalid or missing signals.
  • Chores

    • Introduced project build configuration and defaults for easier setup, including 240 MHz CPU frequency and 1 kHz RTOS tick settings.

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Walkthrough

Adds a new ESP-IDF example project pwm_info_clac. Includes project/component CMake files, Kconfig for GPIO selection, sdkconfig.defaults, and a new app_main implementing PWM frequency and duty cycle measurement via MCPWM capture with an ISR-style callback notifying a FreeRTOS task for computation and logging.

Changes

Cohort / File(s) Summary
Project setup (CMake)
examples/.../pwm_info_clac/CMakeLists.txt, examples/.../pwm_info_clac/main/CMakeLists.txt
Introduces top-level project CMake with ESP-IDF include and project() call; registers component with source pwm_info_clac.c and local include dir.
Runtime configuration (Kconfig)
examples/.../pwm_info_clac/main/Kconfig.projbuild
Adds “Example Configuration” menu with integer option PWM_CAPTURE_IO (default 2) to select the GPIO for PWM capture.
Application logic (MCPWM capture)
examples/.../pwm_info_clac/main/pwm_info_clac.c
New application implementing MCPWM capture: configures capture timer and channel, edge-triggered capture on selected GPIO, ISR-like callback to accumulate ticks, task notification to app_main for computing frequency/period/duty, logging, and validation.
Default build config
examples/.../pwm_info_clac/sdkconfig.defaults
Adds defaults generated via idf.py save-defconfig: sets CPU 240 MHz and FreeRTOS tick rate 1000 Hz with reference comments.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as User/Board
  participant App as app_main (Task)
  participant Kcfg as Kconfig
  participant MCPWM as MCPWM Capture Driver
  participant Timer as Capture Timer
  participant GPIO as PWM Signal (GPIO)

  Note over App,Kcfg: Startup & Configuration
  App->>Kcfg: Read PWM_CAPTURE_IO
  App->>MCPWM: install_timer(), install_channel(GPIO)
  App->>MCPWM: config edges (pos/neg), set callback
  App->>Timer: start()

  Note over MCPWM,GPIO: Measurement Loop
  loop On each edge
    GPIO-->>MCPWM: Capture event (tick, level)
    MCPWM->>App: xTaskNotifyFromISR(data)
  end

  Note over App: Processing
  App->>App: Wait for notify with timeout
  alt data valid
    App->>App: Compute period, freq, duty using apb_freq
    App-->>User: Print results
  else timeout/invalid
    App-->>User: Log warning/idle
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

In the hum of clocks and timer ticks,
I thump my paw at rising edges’ tricks.
Duty whispers, frequency sings—
MCPWM counts with fluttering wings.
From GPIO burrows, numbers flow;
I nibble logs that neatly show
the pulse of fields where gadgets grow. 🐇⏱️

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title accurately reflects the primary change in this pull request, which is the addition of a new MCPWM capture-based PWM information example, and it does so concisely and specifically with no extraneous details.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add_clac_pwm_freq_duty

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99c32b4 and ef1db0d.

📒 Files selected for processing (5)
  • examples/peripherals/pwm/pwm_info_clac/CMakeLists.txt (1 hunks)
  • examples/peripherals/pwm/pwm_info_clac/main/CMakeLists.txt (1 hunks)
  • examples/peripherals/pwm/pwm_info_clac/main/Kconfig.projbuild (1 hunks)
  • examples/peripherals/pwm/pwm_info_clac/main/pwm_info_clac.c (1 hunks)
  • examples/peripherals/pwm/pwm_info_clac/sdkconfig.defaults (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Comment on lines +31 to +53
static pwm_raw_data_t raw_data = {0};

if (edata->cap_edge == MCPWM_CAP_EDGE_POS) {
if (first_pos_edge) {
// First positive edge - just record the timestamp
period_start = edata->cap_value;
last_pos_edge = edata->cap_value;
first_pos_edge = false;
} else {
// Calculate period (time between two positive edges) - only integer arithmetic in ISR
raw_data.period_ticks = edata->cap_value - period_start;
raw_data.high_time_ticks = last_neg_edge - last_pos_edge;

// Update for next cycle
period_start = edata->cap_value;
last_pos_edge = edata->cap_value;

// Notify the task with raw tick data - no floating point in ISR
xTaskNotifyFromISR(task_to_notify, (uint32_t)&raw_data, eSetValueWithOverwrite, &high_task_wakeup);
}
} else {
// Negative edge - record the timestamp
last_neg_edge = edata->cap_value;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the ISR/task data race on captured ticks

raw_data lives in the ISR and you hand its address to the task. The moment the task starts reading period_ticks / high_time_ticks, the capture ISR can pre-empt and overwrite one of the fields, so you end up mixing data from different PWM cycles (period from sample A, high time from sample B). This produces bogus frequency/duty numbers and is a classic ISR/task race. Guard the struct while it is being written/read, or pass a copy via a queue.

Example fix using a spinlock to protect the shared struct:

@@
-const static char *TAG = "pwm_measure";
+const static char *TAG = "pwm_measure";
+static portMUX_TYPE raw_data_spinlock = portMUX_INITIALIZER_UNLOCKED;
@@
-    static pwm_raw_data_t raw_data = {0};
+    static pwm_raw_data_t raw_data = {0};
@@
-            raw_data.period_ticks = edata->cap_value - period_start;
-            raw_data.high_time_ticks = last_neg_edge - last_pos_edge;
+            portENTER_CRITICAL_ISR(&raw_data_spinlock);
+            raw_data.period_ticks = edata->cap_value - period_start;
+            raw_data.high_time_ticks = last_neg_edge - last_pos_edge;
+            portEXIT_CRITICAL_ISR(&raw_data_spinlock);
@@
-            pwm_raw_data_t *raw_data = (pwm_raw_data_t *)notification_value;
+            const pwm_raw_data_t *raw_data = (const pwm_raw_data_t *)notification_value;
+            pwm_raw_data_t snapshot;
+            portENTER_CRITICAL(&raw_data_spinlock);
+            snapshot = *raw_data;
+            portEXIT_CRITICAL(&raw_data_spinlock);

Then use snapshot for the calculations below (frequency, duty, etc.) so the task always works on a coherent measurement.

Also applies to: 104-125

@YanKE01 YanKE01 merged commit 9e6edc7 into master Oct 14, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant