|
| 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, ¬ification_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 | +} |
0 commit comments