example: update capture example with new driver API

This commit is contained in:
morris
2022-05-28 17:05:09 +08:00
parent b77446b5c8
commit 6751b229f1
3 changed files with 120 additions and 108 deletions

View File

@@ -1,14 +1,15 @@
| Supported Targets | ESP32 | ESP32-S3 | | 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.) (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. 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.
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 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: Typical signals:
@@ -30,8 +31,8 @@ Echo +-----+
### Hardware Required ### Hardware Required
* An ESP development board * An ESP development board that features the MCPWM peripheral
* HC-SR04 module * An HC-SR04 sensor module
Connection : Connection :
@@ -60,21 +61,24 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l
## Example Output ## Example Output
``` ```
I (314) hc-sr04: HC-SR04 example based on capture function from MCPWM I (0) cpu_start: Starting scheduler on APP CPU.
I (324) hc-sr04: Echo pin configured I (304) example: Create capture queue
I (324) gpio: GPIO[19]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 I (304) example: Install capture timer
I (334) hc-sr04: Trig pin configured I (304) example: Install capture channel
I (344) hc-sr04: trig task started I (314) gpio: GPIO[2]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (444) hc-sr04: Pulse width: 419us, Measured distance: 7.22cm I (324) example: Register capture callback
I (544) hc-sr04: Pulse width: 419us, Measured distance: 7.22cm I (324) example: Create a timer to trig HC_SR04 periodically
I (644) hc-sr04: Pulse width: 416us, Measured distance: 7.17cm I (334) example: Configure Trig pin
I (744) hc-sr04: Pulse width: 415us, Measured distance: 7.16cm I (334) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (844) hc-sr04: Pulse width: 415us, Measured distance: 7.16cm I (344) example: Enable and start capture timer
I (944) hc-sr04: Pulse width: 416us, Measured distance: 7.17cm I (434) example: Pulse width: 189.02us, Measured distance: 3.26cm
I (1044) hc-sr04: Pulse width: 391us, Measured distance: 6.74cm 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 ## Troubleshooting

View File

@@ -6,119 +6,111 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_private/esp_clk.h" #include "esp_private/esp_clk.h"
#include "driver/mcpwm.h" #include "driver/mcpwm_cap.h"
#include "driver/gpio.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!"); //////////////////// Please update the following configuration according to your board spec ////////////////////////////
#define HC_SR04_PIN_ECHO GPIO_NUM_18 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define HC_SR04_PIN_TRIG GPIO_NUM_19 #define HC_SR04_TRIG_GPIO 0
#define HC_SR04_ECHO_GPIO 2
#define TRIGGER_THREAD_STACK_SIZE 512 static bool hc_sr04_echo_callback(mcpwm_cap_channel_handle_t cap_chan, const mcpwm_capture_event_data_t *edata, void *user_data)
#define TRIGGER_THREAD_PRIORITY 5 {
static uint32_t cap_val_begin_of_sample = 0;
typedef struct { static uint32_t cap_val_end_of_sample = 0;
uint32_t capture_signal; TaskHandle_t task_to_notify = (TaskHandle_t)user_data;
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.
BaseType_t high_task_wakeup = pdFALSE; 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 // store the timestamp when pos edge is detected
cap_val_begin_of_sample = edata->cap_value; cap_val_begin_of_sample = edata->cap_value;
cap_val_end_of_sample = cap_val_begin_of_sample; cap_val_end_of_sample = cap_val_begin_of_sample;
} else { } else {
cap_val_end_of_sample = edata->cap_value; cap_val_end_of_sample = edata->cap_value;
// following formula refers to: https://www.elecrow.com/download/HC_SR04%20Datasheet.pdf uint32_t tof_ticks = cap_val_end_of_sample - cap_val_begin_of_sample;
uint32_t pulse_count = cap_val_end_of_sample - cap_val_begin_of_sample;
// send measurement back though queue // notify the task to calculate the distance
xQueueSendFromISR(cap_queue, &pulse_count, &high_task_wakeup); xTaskNotifyFromISR(task_to_notify, tof_ticks, eSetValueWithOverwrite, &high_task_wakeup);
} }
return high_task_wakeup == pdTRUE; 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 void app_main(void)
cap_queue = xQueueCreate(1, sizeof(uint32_t)); {
if (cap_queue == NULL) { ESP_LOGI(TAG, "Install capture timer");
ESP_LOGE(TAG, "failed to alloc cap_queue"); mcpwm_cap_timer_handle_t cap_timer = NULL;
return; mcpwm_capture_timer_config_t cap_conf = {
} .clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
.group_id = 0,
/* 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
}; };
ESP_ERROR_CHECK(mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf)); ESP_ERROR_CHECK(mcpwm_new_capture_timer(&cap_conf, &cap_timer));
ESP_LOGI(TAG, "Echo pin configured");
/* 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 = { gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT, .mode = GPIO_MODE_OUTPUT,
.pull_down_en = GPIO_PULLDOWN_DISABLE, .pin_bit_mask = 1ULL << HC_SR04_TRIG_GPIO,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pin_bit_mask = BIT64(HC_SR04_PIN_TRIG),
}; };
ESP_ERROR_CHECK(gpio_config(&io_conf)); ESP_ERROR_CHECK(gpio_config(&io_conf));
ESP_ERROR_CHECK(gpio_set_level(HC_SR04_PIN_TRIG, 0)); // drive low by default // drive low by default
ESP_LOGI(TAG, "Trig pin configured"); ESP_ERROR_CHECK(gpio_set_level(HC_SR04_TRIG_GPIO, 0));
// start generating trig signal ESP_LOGI(TAG, "Enable and start capture timer");
xTaskCreate(gen_trig_output, "gen_trig_output", TRIGGER_THREAD_STACK_SIZE, NULL, TRIGGER_THREAD_PRIORITY, NULL); ESP_ERROR_CHECK(mcpwm_capture_timer_enable(cap_timer));
ESP_LOGI(TAG, "trig task started"); ESP_ERROR_CHECK(mcpwm_capture_timer_start(cap_timer));
// forever loop
while (true) { uint32_t tof_ticks;
uint32_t pulse_count; while (1) {
// block and wait for new measurement // trigger the sensor to start a new sample
xQueueReceive(cap_queue, &pulse_count, portMAX_DELAY); gen_trig_output();
uint32_t pulse_width_us = pulse_count * (1000000.0 / esp_clk_apb_freq()); // wait for echo done signal
// following formula is based on: https://www.elecrow.com/download/HC_SR04%20Datasheet.pdf if (xTaskNotifyWait(0x00, ULONG_MAX, &tof_ticks, pdMS_TO_TICKS(1000)) == pdTRUE) {
if (pulse_width_us > 35000) { float pulse_width_us = tof_ticks * (1000000.0 / esp_clk_apb_freq());
// out of range if (pulse_width_us > 35000) {
continue; // 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; vTaskDelay(pdMS_TO_TICKS(500));
ESP_LOGI(TAG, "Pulse width: %uus, Measured distance: %.2fcm", pulse_width_us, distance);
} }
} }

View File

@@ -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')