feat: add mcpwm capture pwm info example#25
Conversation
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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
| 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; |
There was a problem hiding this comment.
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
Summary by CodeRabbit
New Features
Chores