Skip to content

Commit 9e6edc7

Browse files
authored
Merge pull request #25 from YanKE01/feat/add_clac_pwm_freq_duty
feat: add mcpwm capture pwm info example
2 parents 99c32b4 + ef1db0d commit 9e6edc7

6 files changed

Lines changed: 159 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following five lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(pwm_info_clac)

examples/peripherals/pwm/pwm_info_clac/README.md

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
idf_component_register(SRCS "pwm_info_clac.c"
2+
INCLUDE_DIRS ".")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
menu "Example Configuration"
2+
3+
config PWM_CAPTURE_IO
4+
int "Pwm Capture Io"
5+
default 2
6+
help
7+
Example used gpio for capture pwm.
8+
9+
endmenu
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Unlicense OR CC0-1.0
5+
*/
6+
7+
#include "freertos/FreeRTOS.h"
8+
#include "freertos/task.h"
9+
#include "esp_log.h"
10+
#include "esp_private/esp_clk.h"
11+
#include "driver/mcpwm_cap.h"
12+
#include "driver/gpio.h"
13+
14+
const static char *TAG = "pwm_measure";
15+
16+
// PWM measurement data structure - only raw tick values in ISR
17+
typedef struct {
18+
uint32_t period_ticks;
19+
uint32_t high_time_ticks;
20+
} pwm_raw_data_t;
21+
22+
static bool pwm_measure_callback(mcpwm_cap_channel_handle_t cap_chan, const mcpwm_capture_event_data_t *edata, void *user_data)
23+
{
24+
static uint32_t last_pos_edge = 0;
25+
static uint32_t last_neg_edge = 0;
26+
static uint32_t period_start = 0;
27+
static bool first_pos_edge = true;
28+
29+
TaskHandle_t task_to_notify = (TaskHandle_t)user_data;
30+
BaseType_t high_task_wakeup = pdFALSE;
31+
static pwm_raw_data_t raw_data = {0};
32+
33+
if (edata->cap_edge == MCPWM_CAP_EDGE_POS) {
34+
if (first_pos_edge) {
35+
// First positive edge - just record the timestamp
36+
period_start = edata->cap_value;
37+
last_pos_edge = edata->cap_value;
38+
first_pos_edge = false;
39+
} else {
40+
// Calculate period (time between two positive edges) - only integer arithmetic in ISR
41+
raw_data.period_ticks = edata->cap_value - period_start;
42+
raw_data.high_time_ticks = last_neg_edge - last_pos_edge;
43+
44+
// Update for next cycle
45+
period_start = edata->cap_value;
46+
last_pos_edge = edata->cap_value;
47+
48+
// Notify the task with raw tick data - no floating point in ISR
49+
xTaskNotifyFromISR(task_to_notify, (uint32_t)&raw_data, eSetValueWithOverwrite, &high_task_wakeup);
50+
}
51+
} else {
52+
// Negative edge - record the timestamp
53+
last_neg_edge = edata->cap_value;
54+
}
55+
56+
return high_task_wakeup == pdTRUE;
57+
}
58+
59+
60+
void app_main(void)
61+
{
62+
ESP_LOGI(TAG, "PWM Frequency and Duty Cycle Measurement Example");
63+
ESP_LOGI(TAG, "Connect PWM signal to GPIO %d", CONFIG_PWM_CAPTURE_IO);
64+
65+
ESP_LOGI(TAG, "Install capture timer");
66+
mcpwm_cap_timer_handle_t cap_timer = NULL;
67+
mcpwm_capture_timer_config_t cap_conf = {
68+
.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
69+
.group_id = 0,
70+
};
71+
ESP_ERROR_CHECK(mcpwm_new_capture_timer(&cap_conf, &cap_timer));
72+
73+
ESP_LOGI(TAG, "Install capture channel");
74+
mcpwm_cap_channel_handle_t cap_chan = NULL;
75+
mcpwm_capture_channel_config_t cap_ch_conf = {
76+
.gpio_num = CONFIG_PWM_CAPTURE_IO,
77+
.prescale = 1,
78+
// capture on both edges to measure period and duty cycle
79+
.flags.neg_edge = true,
80+
.flags.pos_edge = true,
81+
};
82+
ESP_ERROR_CHECK(mcpwm_new_capture_channel(cap_timer, &cap_ch_conf, &cap_chan));
83+
// pull up the GPIO internally
84+
ESP_ERROR_CHECK(gpio_set_pull_mode(CONFIG_PWM_CAPTURE_IO, GPIO_PULLUP_ONLY));
85+
86+
ESP_LOGI(TAG, "Register capture callback");
87+
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
88+
mcpwm_capture_event_callbacks_t cbs = {
89+
.on_cap = pwm_measure_callback,
90+
};
91+
ESP_ERROR_CHECK(mcpwm_capture_channel_register_event_callbacks(cap_chan, &cbs, cur_task));
92+
93+
ESP_LOGI(TAG, "Enable capture channel");
94+
ESP_ERROR_CHECK(mcpwm_capture_channel_enable(cap_chan));
95+
96+
ESP_LOGI(TAG, "Enable and start capture timer");
97+
ESP_ERROR_CHECK(mcpwm_capture_timer_enable(cap_timer));
98+
ESP_ERROR_CHECK(mcpwm_capture_timer_start(cap_timer));
99+
100+
ESP_LOGI(TAG, "Starting PWM measurement...");
101+
ESP_LOGI(TAG, "Waiting for PWM signal on GPIO %d", CONFIG_PWM_CAPTURE_IO);
102+
103+
uint32_t notification_value;
104+
while (1) {
105+
// wait for PWM measurement data
106+
if (xTaskNotifyWait(0x00, ULONG_MAX, &notification_value, pdMS_TO_TICKS(2000)) == pdTRUE) {
107+
pwm_raw_data_t *raw_data = (pwm_raw_data_t *)notification_value;
108+
109+
// Check for valid measurement (period > 0)
110+
if (raw_data->period_ticks > 0) {
111+
// Calculate frequency and duty cycle in main task (floating point operations)
112+
float period_us = raw_data->period_ticks * (1000000.0 / esp_clk_apb_freq());
113+
float frequency_hz = 1000000.0 / period_us;
114+
float duty_cycle_percent = (float)raw_data->high_time_ticks / raw_data->period_ticks * 100.0;
115+
float high_time_us = raw_data->high_time_ticks * (1000000.0 / esp_clk_apb_freq());
116+
117+
// Check for reasonable frequency range
118+
if (frequency_hz > 0 && frequency_hz < 1000000) {
119+
ESP_LOGI(TAG, "========================================");
120+
ESP_LOGI(TAG, "PWM Measurement:");
121+
ESP_LOGI(TAG, " Frequency: %.2f Hz", frequency_hz);
122+
ESP_LOGI(TAG, " Duty Cycle: %.2f%%", duty_cycle_percent);
123+
ESP_LOGI(TAG, " Period: %.2f us", period_us);
124+
ESP_LOGI(TAG, " High Time: %.2f us", high_time_us);
125+
ESP_LOGI(TAG, "========================================");
126+
} else {
127+
ESP_LOGW(TAG, "Invalid frequency calculated: %.2f Hz", frequency_hz);
128+
}
129+
} else {
130+
ESP_LOGW(TAG, "Invalid PWM measurement received (period_ticks = %u)", raw_data->period_ticks);
131+
}
132+
} else {
133+
ESP_LOGW(TAG, "No PWM signal detected on GPIO %d", CONFIG_PWM_CAPTURE_IO);
134+
}
135+
vTaskDelay(pdMS_TO_TICKS(1000));
136+
}
137+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# This file was generated using idf.py save-defconfig. It can be edited manually.
2+
# Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Minimal Configuration
3+
#
4+
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
5+
CONFIG_FREERTOS_HZ=1000

0 commit comments

Comments
 (0)