example: update capture example with new driver API

pull/9491/head
morris 2022-05-28 17:05:09 +08:00
rodzic b77446b5c8
commit 6751b229f1
3 zmienionych plików z 120 dodań i 108 usunięć

Wyświetl plik

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

Wyświetl plik

@ -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));
}
}

Wyświetl plik

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