From 6751b229f11da471bc531951d7b6670f1949f420 Mon Sep 17 00:00:00 2001 From: morris Date: Sat, 28 May 2022 17:05:09 +0800 Subject: [PATCH] example: update capture example with new driver API --- .../mcpwm/mcpwm_capture_hc_sr04/README.md | 42 +++-- .../main/mcpwm_capture_hc_sr04.c | 170 +++++++++--------- .../mcpwm_capture_hc_sr04/pytest_hc_sr04.py | 16 ++ 3 files changed, 120 insertions(+), 108 deletions(-) create mode 100644 examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/pytest_hc_sr04.py diff --git a/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/README.md b/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/README.md index 4df1e96735..941d25a886 100644 --- a/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/README.md +++ b/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/README.md @@ -1,14 +1,15 @@ | Supported Targets | ESP32 | ESP32-S3 | | ----------------- | ----- | -------- | -# HC-SR04 Example +# HC-SR04 Example based on MCPWM Capture (See the README.md file in the upper level 'examples' directory for more information about examples.) -The capture module in MCPWM peripheral is designed to accurately log the time stamp on the hardware side when an event happens (compared to GPIO ISR which requires a software-based logging method). Each capture unit has three channels, which can be used together to capture IO events parallelly. -This example shows how to make use of the HW features to decode the pulse width signals generated from a common HC-SR04 sonar range finder -- [HC-SR04](https://www.sparkfun.com/products/15569). +The capture module in MCPWM peripheral is designed to accurately log the time stamp on the hardware side when an event happens (compared to GPIO ISR which requires a software-based logging method). Each capture unit has three channels, which can be used together to capture IO events in parallel. -The signal that HC-SR04 produces (and what can be handled by this example) is a simple pulse whose width indicates the measured distance. A pulse is required to send to HC-SR04 on `Trig` pin to begin a new measurement. Then the pulse described above will be sent back on `Echo` pin for decoding. +This example shows how to make use of the hardware features to decode the pulse width signals generated from a common HC-SR04 sonar sensor -- [HC-SR04](https://www.sparkfun.com/products/15569). + +The signal that HC-SR04 produces (and what can be handled by this example) is a simple pulse whose width indicates the measured distance. An excitation pulse is required to send to HC-SR04 on `Trig` pin to begin a new measurement. Then the pulse described above will appear on the `Echo` pin after a while. Typical signals: @@ -30,8 +31,8 @@ Echo +-----+ ### Hardware Required -* An ESP development board -* HC-SR04 module +* An ESP development board that features the MCPWM peripheral +* An HC-SR04 sensor module Connection : @@ -60,21 +61,24 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l ## Example Output ``` -I (314) hc-sr04: HC-SR04 example based on capture function from MCPWM -I (324) hc-sr04: Echo pin configured -I (324) gpio: GPIO[19]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 -I (334) hc-sr04: Trig pin configured -I (344) hc-sr04: trig task started -I (444) hc-sr04: Pulse width: 419us, Measured distance: 7.22cm -I (544) hc-sr04: Pulse width: 419us, Measured distance: 7.22cm -I (644) hc-sr04: Pulse width: 416us, Measured distance: 7.17cm -I (744) hc-sr04: Pulse width: 415us, Measured distance: 7.16cm -I (844) hc-sr04: Pulse width: 415us, Measured distance: 7.16cm -I (944) hc-sr04: Pulse width: 416us, Measured distance: 7.17cm -I (1044) hc-sr04: Pulse width: 391us, Measured distance: 6.74cm +I (0) cpu_start: Starting scheduler on APP CPU. +I (304) example: Create capture queue +I (304) example: Install capture timer +I (304) example: Install capture channel +I (314) gpio: GPIO[2]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (324) example: Register capture callback +I (324) example: Create a timer to trig HC_SR04 periodically +I (334) example: Configure Trig pin +I (334) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (344) example: Enable and start capture timer +I (434) example: Pulse width: 189.02us, Measured distance: 3.26cm +I (534) example: Pulse width: 189.02us, Measured distance: 3.26cm +I (634) example: Pulse width: 189.01us, Measured distance: 3.26cm +I (734) example: Pulse width: 188.98us, Measured distance: 3.26cm +I (834) example: Pulse width: 188.99us, Measured distance: 3.26cm ``` -This example runs at 10Hz sampling rate. out of range data is dropped and only valid measurement is printed. +This example runs at 10Hz sampling rate. Measure data that out of the range is dropped and only valid measurement is printed out. ## Troubleshooting diff --git a/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c b/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c index e7fefeb6d2..b18fbfe81e 100644 --- a/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c +++ b/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c @@ -6,119 +6,111 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "freertos/queue.h" #include "esp_log.h" #include "esp_private/esp_clk.h" -#include "driver/mcpwm.h" +#include "driver/mcpwm_cap.h" #include "driver/gpio.h" -const static char *TAG = "hc-sr04"; +const static char *TAG = "example"; -#define HC_SR04_SAMPLE_PERIOD_MS 100 -_Static_assert(HC_SR04_SAMPLE_PERIOD_MS > 50, "Sample period too short!"); -#define HC_SR04_PIN_ECHO GPIO_NUM_18 -#define HC_SR04_PIN_TRIG GPIO_NUM_19 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// Please update the following configuration according to your board spec //////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define HC_SR04_TRIG_GPIO 0 +#define HC_SR04_ECHO_GPIO 2 -#define TRIGGER_THREAD_STACK_SIZE 512 -#define TRIGGER_THREAD_PRIORITY 5 - -typedef struct { - uint32_t capture_signal; - mcpwm_capture_signal_t sel_cap_signal; -} capture; - -static uint32_t cap_val_begin_of_sample = 0; -static uint32_t cap_val_end_of_sample = 0; - -static QueueHandle_t cap_queue; - -/** - * @brief generate single pulse on Trig pin to activate a new sample - */ -static void gen_trig_output(void *arg) { - TickType_t xLastWakeTime = xTaskGetTickCount(); - while (true) { - vTaskDelayUntil(&xLastWakeTime, HC_SR04_SAMPLE_PERIOD_MS / portTICK_PERIOD_MS); - ESP_ERROR_CHECK(gpio_set_level(HC_SR04_PIN_TRIG, 1)); // set high - esp_rom_delay_us(10); - ESP_ERROR_CHECK(gpio_set_level(HC_SR04_PIN_TRIG, 0)); // set low - } -} - -/** - * @brief this is an ISR callback, we take action according to the captured edge - */ -static bool sr04_echo_isr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, - void *arg) { - //calculate the interval in the ISR, - //so that the interval will be always correct even when cap_queue is not handled in time and overflow. +static bool hc_sr04_echo_callback(mcpwm_cap_channel_handle_t cap_chan, const mcpwm_capture_event_data_t *edata, void *user_data) +{ + static uint32_t cap_val_begin_of_sample = 0; + static uint32_t cap_val_end_of_sample = 0; + TaskHandle_t task_to_notify = (TaskHandle_t)user_data; BaseType_t high_task_wakeup = pdFALSE; - if (edata->cap_edge == MCPWM_POS_EDGE) { + + //calculate the interval in the ISR, + //so that the interval will be always correct even when capture_queue is not handled in time and overflow. + if (edata->cap_edge == MCPWM_CAP_EDGE_POS) { // store the timestamp when pos edge is detected cap_val_begin_of_sample = edata->cap_value; cap_val_end_of_sample = cap_val_begin_of_sample; } else { cap_val_end_of_sample = edata->cap_value; - // following formula refers to: https://www.elecrow.com/download/HC_SR04%20Datasheet.pdf - uint32_t pulse_count = cap_val_end_of_sample - cap_val_begin_of_sample; - // send measurement back though queue - xQueueSendFromISR(cap_queue, &pulse_count, &high_task_wakeup); + uint32_t tof_ticks = cap_val_end_of_sample - cap_val_begin_of_sample; + + // notify the task to calculate the distance + xTaskNotifyFromISR(task_to_notify, tof_ticks, eSetValueWithOverwrite, &high_task_wakeup); } + return high_task_wakeup == pdTRUE; } -void app_main(void) { - ESP_LOGI(TAG, "HC-SR04 example based on capture function from MCPWM"); +/** + * @brief generate single pulse on Trig pin to start a new sample + */ +static void gen_trig_output(void) +{ + gpio_set_level(HC_SR04_TRIG_GPIO, 1); // set high + esp_rom_delay_us(10); + gpio_set_level(HC_SR04_TRIG_GPIO, 0); // set low +} - // the queue where we read data - cap_queue = xQueueCreate(1, sizeof(uint32_t)); - if (cap_queue == NULL) { - ESP_LOGE(TAG, "failed to alloc cap_queue"); - return; - } - - /* configure Echo pin */ - // set CAP_0 on GPIO - ESP_ERROR_CHECK(mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, HC_SR04_PIN_ECHO)); - // enable pull down CAP0, to reduce noise - ESP_ERROR_CHECK(gpio_pulldown_en(HC_SR04_PIN_ECHO)); - // enable both edge capture on CAP0 - mcpwm_capture_config_t conf = { - .cap_edge = MCPWM_BOTH_EDGE, - .cap_prescale = 1, - .capture_cb = sr04_echo_isr_handler, - .user_data = NULL +void app_main(void) +{ + ESP_LOGI(TAG, "Install capture timer"); + mcpwm_cap_timer_handle_t cap_timer = NULL; + mcpwm_capture_timer_config_t cap_conf = { + .clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT, + .group_id = 0, }; - ESP_ERROR_CHECK(mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf)); - ESP_LOGI(TAG, "Echo pin configured"); + ESP_ERROR_CHECK(mcpwm_new_capture_timer(&cap_conf, &cap_timer)); - /* configure Trig pin */ + ESP_LOGI(TAG, "Install capture channel"); + mcpwm_cap_channel_handle_t cap_chan = NULL; + mcpwm_capture_channel_config_t cap_ch_conf = { + .gpio_num = HC_SR04_ECHO_GPIO, + .prescale = 1, + // capture on both edge + .flags.neg_edge = true, + .flags.pos_edge = true, + // pull up internally + .flags.pull_up = true, + }; + ESP_ERROR_CHECK(mcpwm_new_capture_channel(cap_timer, &cap_ch_conf, &cap_chan)); + + ESP_LOGI(TAG, "Register capture callback"); + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + mcpwm_capture_event_callbacks_t cbs = { + .on_cap = hc_sr04_echo_callback, + }; + ESP_ERROR_CHECK(mcpwm_capture_channel_register_event_callbacks(cap_chan, &cbs, cur_task)); + + ESP_LOGI(TAG, "Configure Trig pin"); gpio_config_t io_conf = { - .intr_type = GPIO_INTR_DISABLE, .mode = GPIO_MODE_OUTPUT, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pin_bit_mask = BIT64(HC_SR04_PIN_TRIG), + .pin_bit_mask = 1ULL << HC_SR04_TRIG_GPIO, }; ESP_ERROR_CHECK(gpio_config(&io_conf)); - ESP_ERROR_CHECK(gpio_set_level(HC_SR04_PIN_TRIG, 0)); // drive low by default - ESP_LOGI(TAG, "Trig pin configured"); + // drive low by default + ESP_ERROR_CHECK(gpio_set_level(HC_SR04_TRIG_GPIO, 0)); - // start generating trig signal - xTaskCreate(gen_trig_output, "gen_trig_output", TRIGGER_THREAD_STACK_SIZE, NULL, TRIGGER_THREAD_PRIORITY, NULL); - ESP_LOGI(TAG, "trig task started"); - // forever loop - while (true) { - uint32_t pulse_count; - // block and wait for new measurement - xQueueReceive(cap_queue, &pulse_count, portMAX_DELAY); - uint32_t pulse_width_us = pulse_count * (1000000.0 / esp_clk_apb_freq()); - // following formula is based on: https://www.elecrow.com/download/HC_SR04%20Datasheet.pdf - if (pulse_width_us > 35000) { - // out of range - continue; + ESP_LOGI(TAG, "Enable and start capture timer"); + ESP_ERROR_CHECK(mcpwm_capture_timer_enable(cap_timer)); + ESP_ERROR_CHECK(mcpwm_capture_timer_start(cap_timer)); + + uint32_t tof_ticks; + while (1) { + // trigger the sensor to start a new sample + gen_trig_output(); + // wait for echo done signal + if (xTaskNotifyWait(0x00, ULONG_MAX, &tof_ticks, pdMS_TO_TICKS(1000)) == pdTRUE) { + float pulse_width_us = tof_ticks * (1000000.0 / esp_clk_apb_freq()); + if (pulse_width_us > 35000) { + // out of range + continue; + } + // convert the pulse width into measure distance + float distance = (float) pulse_width_us / 58; + ESP_LOGI(TAG, "Measured distance: %.2fcm", distance); } - float distance = (float) pulse_width_us / 58; - ESP_LOGI(TAG, "Pulse width: %uus, Measured distance: %.2fcm", pulse_width_us, distance); + vTaskDelay(pdMS_TO_TICKS(500)); } } diff --git a/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/pytest_hc_sr04.py b/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/pytest_hc_sr04.py new file mode 100644 index 0000000000..6e519dff82 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/pytest_hc_sr04.py @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32s3 +@pytest.mark.generic +def test_hc_sr04_example(dut: Dut) -> None: + dut.expect_exact('example: Install capture timer') + dut.expect_exact('example: Install capture channel') + dut.expect_exact('example: Register capture callback') + dut.expect_exact('example: Configure Trig pin') + dut.expect_exact('example: Enable and start capture timer')