gptimer: added etm example

This commit is contained in:
morris
2022-09-01 16:01:53 +08:00
parent 356c6bb528
commit 4186bd041c
7 changed files with 268 additions and 0 deletions

View File

@@ -193,6 +193,10 @@ examples/peripherals/temp_sensor:
disable:
- if: SOC_TEMP_SENSOR_SUPPORTED != 1
examples/peripherals/timer_group/gptimer_capture_hc_sr04:
disable:
- if: SOC_TIMER_SUPPORT_ETM != 1
examples/peripherals/touch_sensor:
disable:
- if: SOC_TOUCH_SENSOR_SUPPORTED != 1

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(gptimer_etm_capture)

View File

@@ -0,0 +1,86 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |
# HC-SR04 Example based on GPTimer Capture and ETM
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The general purpose timer can capture internal timer count value into dedicated registers. This feature can be used to measure the time between two events. With the help of ETM (Event Task Matrix) peripheral, we can route different events to trigger the GPTimer's capture task. In this example, we configure one GPIO to detect any edge signals and then notify the GPTimer to capture the timestamp at once, all done by hardware.
This example also shows how 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:
```text
Trig +-----+
| |
| |
-----+ +-----------------------
Echo +-----+
| |
| |
-----------------+ +-----------
+--------------------------------------->
Timeline
```
## How to Use Example
### Hardware Required
* A development board with ESP SOC chip that supports ETM peripheral
* An HC-SR04 sensor module
Connection :
```text
+------+ +--------------------------------------+
+-------+ | | |
| | VCC +--------------+ 5V |
+-------+ | | |
+ Echo +----=====>----+ HC_SR04_ECHO_GPIO (internal pull up) |
| | | |
+ Trig +----<=====----+ HC_SR04_TRIG_GPIO |
+-------| | | |
| | GND +--------------+ GND |
+-------| | | |
+------+ +--------------------------------------+
```
You can change the GPIO number according to your board, by `HC_SR04_TRIG_GPIO` and `HC_SR04_ECHO_GPIO` in the [source code](main/gptimer_capture_hc_sr04.c).
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```text
I (339) cpu_start: Starting scheduler.
I (344) example: Configure trig gpio
I (344) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (354) example: Configure echo gpio
I (354) gpio: GPIO[1]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:3
I (364) example: Create etm event handle for echo gpio
I (374) example: Create gptimer handle
I (384) example: Get gptimer etm task handle (capture)
I (384) example: Create ETM channel then connect gpio event and gptimer task
I (394) example: Install GPIO edge interrupt
I (404) example: Enable etm channel and gptimer
I (404) example: Start gptimer
I (424) example: Measured distance: 93.40cm
I (934) example: Measured distance: 103.93cm
I (1444) example: Measured distance: 102.09cm
I (1954) example: Measured distance: 122.47cm
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "gptimer_capture_hc_sr04.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,151 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gptimer.h"
#include "driver/gpio.h"
#include "driver/gpio_etm.h"
#include "esp_log.h"
static const char *TAG = "example";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your board spec ////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define HC_SR04_TRIG_GPIO 0
#define HC_SR04_ECHO_GPIO 1
#define EXAMPLE_GPTIMER_RESOLUTION_HZ 1000000 // 1MHz, 1 tick = 1us
/**
* @brief User defined context, to be passed to GPIO ISR callback function.
*/
typedef struct {
gptimer_handle_t gptimer;
TaskHandle_t task_to_notify;
gpio_num_t echo_gpio;
} gpio_callback_user_data_t;
/**
* @brief GPIO ISR callback function, called when there's any edge detected on the GPIO.
*/
static void hc_sr04_echo_callback(void *user_data)
{
static uint64_t cap_val_end_of_sample = 0;
static uint64_t cap_val_begin_of_sample = 0;
gpio_callback_user_data_t *callback_user_data = (gpio_callback_user_data_t *)user_data;
gpio_num_t echo_gpio = callback_user_data->echo_gpio;
TaskHandle_t task_to_notify = callback_user_data->task_to_notify;
gptimer_handle_t gptimer = callback_user_data->gptimer;
if (gpio_get_level(echo_gpio) == 1) {
// store the timestamp when pos edge is detected
gptimer_get_captured_count(gptimer, &cap_val_begin_of_sample);
cap_val_end_of_sample = cap_val_begin_of_sample;
} else {
gptimer_get_captured_count(gptimer, &cap_val_end_of_sample);
uint32_t tof_ticks = (uint32_t)(cap_val_end_of_sample - cap_val_begin_of_sample);
// notify the task to calculate the distance
xTaskNotifyFromISR(task_to_notify, tof_ticks, eSetValueWithOverwrite, NULL);
}
}
/**
* @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
}
void app_main(void)
{
ESP_LOGI(TAG, "Configure trig gpio");
gpio_config_t trig_io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << HC_SR04_TRIG_GPIO,
};
ESP_ERROR_CHECK(gpio_config(&trig_io_conf));
// drive low by default
ESP_ERROR_CHECK(gpio_set_level(HC_SR04_TRIG_GPIO, 0));
ESP_LOGI(TAG, "Configure echo gpio");
gpio_config_t echo_io_conf = {
.mode = GPIO_MODE_INPUT,
.intr_type = GPIO_INTR_ANYEDGE, // capture signal on both edge
.pull_up_en = true, // pull up internally
.pin_bit_mask = 1ULL << HC_SR04_ECHO_GPIO,
};
ESP_ERROR_CHECK(gpio_config(&echo_io_conf));
ESP_LOGI(TAG, "Create etm event handle for echo gpio");
esp_etm_event_handle_t gpio_event = NULL;
gpio_etm_event_config_t gpio_event_config = {
.edge = GPIO_ETM_EVENT_EDGE_ANY, // capture signal on both edge
};
ESP_ERROR_CHECK(gpio_new_etm_event(&gpio_event_config, &gpio_event));
ESP_ERROR_CHECK(gpio_etm_event_bind_gpio(gpio_event, HC_SR04_ECHO_GPIO));
ESP_LOGI(TAG, "Create gptimer handle");
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = EXAMPLE_GPTIMER_RESOLUTION_HZ,
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
ESP_LOGI(TAG, "Get gptimer etm task handle (capture)");
esp_etm_task_handle_t gptimer_capture_task = NULL;
ESP_ERROR_CHECK(gptimer_new_etm_task(gptimer, GPTIMER_ETM_TASK_CAPTURE, &gptimer_capture_task));
ESP_LOGI(TAG, "Create ETM channel then connect gpio event and gptimer task");
esp_etm_channel_handle_t etm_chan = NULL;
esp_etm_channel_config_t etm_chan_config = {};
ESP_ERROR_CHECK(esp_etm_new_channel(&etm_chan_config, &etm_chan));
// GPIO any edge ==> ETM channel ==> GPTimer capture task
ESP_ERROR_CHECK(esp_etm_channel_connect(etm_chan, gpio_event, gptimer_capture_task));
ESP_LOGI(TAG, "Install GPIO edge interrupt");
ESP_ERROR_CHECK(gpio_install_isr_service(0));
gpio_callback_user_data_t callback_user_data = {
.gptimer = gptimer,
.echo_gpio = HC_SR04_ECHO_GPIO,
.task_to_notify = xTaskGetCurrentTaskHandle(),
};
ESP_ERROR_CHECK(gpio_isr_handler_add(HC_SR04_ECHO_GPIO, hc_sr04_echo_callback, &callback_user_data));
ESP_LOGI(TAG, "Enable etm channel and gptimer");
ESP_ERROR_CHECK(esp_etm_channel_enable(etm_chan));
ESP_ERROR_CHECK(gptimer_enable(gptimer));
// Print the ETM channel usage
ESP_ERROR_CHECK(esp_etm_dump(stdout));
ESP_LOGI(TAG, "Start gptimer");
ESP_ERROR_CHECK(gptimer_start(gptimer));
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) {
if (tof_ticks > 35000) {
// out of range
continue;
}
// convert the pulse width into measure distance
float distance = (float) tof_ticks / 58.0f;
ESP_LOGI(TAG, "Measured distance: %.2fcm", distance);
}
vTaskDelay(pdMS_TO_TICKS(500));
}
}

View File

@@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32c6
@pytest.mark.generic
def test_gptimer_capture(dut: Dut) -> None:
dut.expect_exact('Configure trig gpio')
dut.expect_exact('Configure echo gpio')
dut.expect_exact('Create etm event handle for echo gpio')
dut.expect_exact('Create gptimer handle')
dut.expect_exact('Get gptimer etm task handle (capture)')
dut.expect_exact('Create ETM channel then connect gpio event and gptimer task')
dut.expect_exact('Install GPIO edge interrupt')
dut.expect_exact('Enable etm channel and gptimer')
dut.expect_exact('Start gptimer')