Kaynağa Gözat

example: update stepper motor example with new rmt driver

morris 3 yıl önce
ebeveyn
işleme
0e19bc1463

+ 0 - 92
examples/peripherals/rmt/step_motor/README.md

@@ -1,92 +0,0 @@
-| Supported Targets | ESP32-S2 | ESP32-C3 | ESP32-S3 |
-| ----------------- | -------- | -------- | -------- |
-
-# RMT Transmit Loop Example -- Step Motor controller
-
-(See the README.md file in the upper level 'examples' directory for more information about examples.)
-
-RMT peripheral can send customized RMT items in a loop, which means we can use it to generate a configurable length of periodic signal, with accurate number of pulses.
-
-This example will show how to control an A4988 based step motor driver to step accurately with simple APIs, based on the RMT loop feature. The example also implements a [Smoothstep](https://en.wikipedia.org/wiki/Smoothstep) feature which works out of the box.
-
-## How to Use Example
-
-### Hardware Required
-
-* Recommend running this example on development board with SOC chip that support loop auto-stop feature by hardware (e.g. ESP32-S3)
-* A USB cable for Power supply and programming
-* A 4-wire (A+, A-, B+, B-) step motor
-* An A4988 module
-
-Connection :
-
-```
-+----------------+             +--------------------+      +--------------+
-|                |             |       A4988        |      |    4-wire    |
-|            GND +-------------+ GND                |      |     Step     |
-|                |             |                    |      |     Motor    |
-|             5V +-------------+ VDD             1B +------+ A2           |
-|                |             |                    |      |              |
-|         GPIO18 +------------>+ DIRECTION       1A +------+ A1           |
-|                |             |                    |      |              |
-| ESP     GPIO17 +------------>+ STEP            2A +------+ B1           |
-|                |             |                    |      |              |
-|         GPIO16 +------------>+ SLEEP           2B +------+ B2           |
-|                |             |                    |      +--------------+
-|         GPIO15 +------------>+ RESET         VMOT +-------------------+
-|                |             |                    |                   |
-|          GPIO7 +------------>+ MS3            GND +----------+        |
-|                |             |                    |          |        |
-|          GPIO6 +------------>+ MS2                |          |        |
-|                |             |                    |          |        |
-|          GPIO5 +------------>+ MS1                |      +---+--------+-----+
-|                |             |                    |      |  GND     +12V    |
-|          GPIO4 +------------>+ ENABLE             |      |   POWER SUPPLY   |
-+----------------+             +--------------------+      +------------------+
-
-```
-
-IO mapping on ESP side can be changed in `step_motor_main.c`:
-
-```c
-// GPIO configuration
-#define STEP_MOTOR_DIRECTION_PIN GPIO_NUM_18
-#define STEP_MOTOR_STEP_PIN GPIO_NUM_17
-#define STEP_MOTOR_SLEEP_PIN GPIO_NUM_16
-#define STEP_MOTOR_RESET_PIN GPIO_NUM_15
-#define STEP_MOTOR_MS3_PIN GPIO_NUM_7
-#define STEP_MOTOR_MS2_PIN GPIO_NUM_6
-#define STEP_MOTOR_MS1_PIN GPIO_NUM_5
-#define STEP_MOTOR_ENABLE_PIN GPIO_NUM_4
-```
-
-### 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
-
-```
-I (344) step_motor: init
-I (344) step_motor: set_step
-I (1354) step_motor: step 10 @ 1000/s
-I (2364) step_motor: step 100 @ 1000/s
-I (3464) step_motor: step 1000 @ 1200/s
-I (5294) step_motor: step 5000 @ 1400/s
-I (9864) step_motor: smoothstep start 5000 steps @ 500~1400/s
-I (14454) step_motor: smoothstep finish
-I (15454) step_motor: continuous running for 5s
-I (20454) step_motor: stop
-I (21504) step_motor: deinit
-```
-
-Motor should move as output indicates.
-
-## 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.

+ 0 - 10
examples/peripherals/rmt/step_motor/components/step_motor/CMakeLists.txt

@@ -1,10 +0,0 @@
-set(component_srcs "src/step_motor.c"
-                   "src/step_motor_rmt.c"
-                   "src/step_motor_driver_io_a4988.c"
-)
-
-idf_component_register(SRCS "${component_srcs}"
-                       INCLUDE_DIRS "include"
-                       PRIV_INCLUDE_DIRS ""
-                       PRIV_REQUIRES "driver"
-                       REQUIRES "")

+ 0 - 134
examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor.h

@@ -1,134 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- */
-
-#pragma once
-
-#include <stdbool.h>
-#include "driver/rmt.h"
-#include "hal/rmt_types.h"
-#include "esp_err.h"
-#include "step_motor_driver_io.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @brief Type of step motor interface
- */
-typedef struct step_motor_s step_motor_t;
-
-typedef step_motor_t *step_motor_handle_t;
-
-/**
- * @brief Declaration of step motor interface
- *
- */
-struct step_motor_s {
-    esp_err_t (*init)(step_motor_t *handle);
-    esp_err_t (*deinit)(step_motor_t *handle);
-    esp_err_t (*step)(step_motor_t *handle, uint32_t n, uint32_t speed);
-    esp_err_t (*smooth_step)(step_motor_t *handle, uint32_t n, uint32_t speed_steps, uint32_t speed_min, uint32_t speed_max);
-    esp_err_t (*set_step)(step_motor_t *handle, uint16_t microstep, bool direction);
-
-    // TODO: other API like sleep, enable_output, reset
-};
-
-/**
- * @brief Initialize step motor driver
- *
- * @param handle driver handle
- * @return
- *      - ESP_OK: successfully initialized
- *      - ESP_ERR_INVALID_ARG: wrong parameter
- */
-esp_err_t step_motor_init(step_motor_t *handle);
-
-/**
- * @brief Deinitialize driver
- *
- * @param handle driver handle
- * @return
- *      - ESP_OK: Stop playing successfully
- */
-esp_err_t step_motor_deinit(step_motor_t *handle);
-
-/**
- * @brief Move n small steps.
- *
- * @note Will block until finish if n is finite steps. But will immediately return if n is UINT32_MAX.
- *
- * @param handle driver handle
- * @param n step count, UINT32_MAX for unlimited, 0 to stop
- * @param speed steps per second
- * @return
- *      - ESP_OK: Recycle memory successfully
- */
-esp_err_t step_motor_step(step_motor_t *handle, uint32_t n, uint32_t speed);
-
-/**
- * @brief Move n small steps. Always blocking and take smooth arguments
- *
- *  ^ speed (steps/s)
- *  |                         *********************                       <---- speed_max
- *  |                   *     |                   |     *
- *  |               *         |                   |         *
- *  |             *           |                   |           *
- *  |            *            |                   |            *
- *  |          *     speed    |  n-speed_steps*2  |    speed     *
- *  |      *         steps    |                   |    steps         *    <---- speed_min
- *  |                         |                   |
- *  +-------------------------------------------------------------------> timestamp (s)
- *
- * @param handle driver handle
- * @param n steps
- * @param speed_steps number of sample points during speed smoothing
- * @param speed_min minimal speed, steps per seconds
- * @param speed_max maximum speed, steps per seconds
- * @note may consume lots of ram depending on speed_steps with current implementation (1000 will lead to 8kb of ram usage)
- * @return
- *      - ESP_OK: Recycle memory successfully
- */
-esp_err_t step_motor_smooth_step(step_motor_t *handle, uint32_t n, uint32_t speed_steps, uint32_t speed_min, uint32_t speed_max);
-
-/**
- * @brief Set microstep resolution
- *
- * @param handle driver handle
- * @param step_config microstep resolution
- * @param direction rotating direction
- * @return
- *      - ESP_OK: Recycle memory successfully
- */
-esp_err_t step_motor_set_step(step_motor_t *handle, uint16_t microstep, bool direction);
-
-
-// TODO: move out of this header to rmt one (like step_motor_rmt.h)
-/**
- * @brief Create step motor instance based on RMT driver
- *
- * @param[in] io_driver step motor low part driver
- * @param[out] ret_handle returned handle of step motor instance
- * @return
- *      - ESP_OK: create step motor instance successfully
- *      - ESP_ERR_INVALID_ARG: wrong parameter
- *      - ESP_ERR_NO_MEM: no memory to allocate instance
- */
-esp_err_t step_motor_create_rmt(step_motor_driver_io_t *io_driver, const rmt_config_t *rmt_conf, step_motor_handle_t *ret_handle);
-
-/**
- * @brief Delete step motor instance that previously created
- *
- * @param[in] handle step motor instance to be deleted
- * @return
- *      - ESP_OK: create step motor instance successfully
- *      - ESP_ERR_INVALID_ARG: wrong parameter
- */
-esp_err_t step_motor_delete_rmt(step_motor_handle_t handle);
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 75
examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io.h

@@ -1,75 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- */
-
-#pragma once
-
-#include "esp_err.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct step_motor_driver_io_s step_motor_driver_io_t;
-
-typedef step_motor_driver_io_t *step_motor_driver_io_handle_t;
-
-typedef enum {
-    STEP_MOTOR_DIRECTION_NEGATIVE = 0, STEP_MOTOR_DIRECTION_POSITIVE
-} step_direction;
-
-/**
- * @brief init low part of driver
- * GPIO configuration, Bus initializing...
- */
-typedef esp_err_t (*step_motor_driver_io_init)(step_motor_driver_io_t *handle);
-/**
- * @brief set rotation direction
- */
-typedef esp_err_t (*step_motor_driver_io_set_direction)(step_motor_driver_io_t *handle, step_direction direction);
-/**
- * @brief enable/disable sleep mode if supported
- */
-typedef esp_err_t (*step_motor_driver_io_enable_sleep)(step_motor_driver_io_t *handle, bool enabled);
-/**
- * @brief enable/disable output if supported
- */
-typedef esp_err_t (*step_motor_driver_io_enable_output)(step_motor_driver_io_t *handle, bool enabled);
-/**
- * @brief set microstep configuration if supported.
- * param microstep is treated as denominator. a input of 16 means 1/16 step
- * should return ESP_ERR_NOT_SUPPORTED if not supported
- */
-typedef esp_err_t (*step_motor_driver_io_set_microstep)(step_motor_driver_io_t *handle, uint16_t microstep);
-/**
- * @brief reset low part of driver
- */
-typedef esp_err_t (*step_motor_driver_io_reset)(step_motor_driver_io_t *handle);
-/**
- * @brief deinit low part of driver
- */
-typedef esp_err_t (*step_motor_driver_io_deinit)(step_motor_driver_io_t *handle);
-
-/**
- * @brief Driver IC specified control logic
- *
- * leave callback pointer NULL if action is not supported
- */
-struct step_motor_driver_io_s {
-    step_motor_driver_io_init init;                     /*!< callback to init low part driver */
-    step_motor_driver_io_set_direction set_direction;   /*!< callback to set rotate direction */
-    step_motor_driver_io_enable_sleep enable_sleep;     /*!< callback to enable sleep mode */
-    step_motor_driver_io_enable_output enable_output;   /*!< callback to enable output */
-    step_motor_driver_io_set_microstep set_microstep;   /*!< callback to set microstep configuration */
-    bool step_triggered_edge;                           /*!< true if step is triggered by positive edge, otherwise false */
-    uint32_t pulse_low_period_us;                       /*!< minimum low level pulse width on step pin */
-    uint32_t pulse_high_period_us;                      /*!< minimum high level pulse width on step pin */
-    step_motor_driver_io_reset trigger_reset;           /*!< callback to trigger a reset on low part driver */
-    step_motor_driver_io_deinit deinit;                 /*!< callback to deinit low part driver */
-};
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 49
examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io_a4988.h

@@ -1,49 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- */
-
-#pragma once
-
-#include "esp_err.h"
-#include "step_motor_driver_io.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @brief A4988 configuration
- */
-typedef struct step_motor_io_a4988_conf_s {
-    gpio_num_t direction_pin;
-    gpio_num_t sleep_pin;
-    gpio_num_t reset_pin;
-    gpio_num_t ms3_pin;
-    gpio_num_t ms2_pin;
-    gpio_num_t ms1_pin;
-    gpio_num_t enable_pin;
-} step_motor_io_a4988_conf_t;
-
-/**
- * @brief A4988 low part driver handle
- */
-typedef struct step_motor_driver_io_a4988_s {
-    step_motor_driver_io_t base;
-    step_motor_io_a4988_conf_t conf;
-} step_motor_driver_io_a4988_t;
-
-/**
- * @brief create an A4988 driver handle
- */
-esp_err_t step_motor_new_a4988_io_driver(const step_motor_io_a4988_conf_t *conf, step_motor_driver_io_handle_t *handle);
-
-/**
- * @brief delete an A4988 driver handle
- */
-esp_err_t step_motor_delete_a4988_io_driver(step_motor_driver_io_handle_t handle);
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 32
examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor.c

@@ -1,32 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- */
-
-#include "step_motor.h"
-
-esp_err_t step_motor_init(step_motor_t *handle)
-{
-    return handle->init(handle);
-}
-
-esp_err_t step_motor_deinit(step_motor_t *handle)
-{
-    return handle->deinit(handle);
-}
-
-esp_err_t step_motor_step(step_motor_t *handle, uint32_t n, uint32_t speed)
-{
-    return handle->step(handle, n, speed);
-}
-
-esp_err_t step_motor_smooth_step(step_motor_t *handle, uint32_t n, uint32_t speed_steps, uint32_t speed_min, uint32_t speed_max)
-{
-    return handle->smooth_step(handle, n, speed_steps, speed_min, speed_max);
-}
-
-esp_err_t step_motor_set_step(step_motor_t *handle, uint16_t microstep, bool direction)
-{
-    return handle->set_step(handle, microstep, direction);
-}

+ 0 - 174
examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_driver_io_a4988.c

@@ -1,174 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- */
-
-#include <freertos/FreeRTOS.h>
-#include <freertos/task.h>
-#include <string.h>
-#include "hal/gpio_types.h"
-#include "driver/gpio.h"
-#include "esp_check.h"
-#include "step_motor_driver_io_a4988.h"
-
-static const char *TAG = "A4988_IO";
-
-#define A4988_RESPONSE_DELAY_MS 10
-
-static esp_err_t a4988_init(step_motor_driver_io_t *handle)
-{
-    step_motor_driver_io_a4988_t *a4988_motor = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    gpio_config_t io_conf;
-    io_conf.intr_type = GPIO_INTR_DISABLE;
-    io_conf.mode = GPIO_MODE_OUTPUT;
-    //bit mask of the pins that you want to set,e.g.GPIO18/19
-    io_conf.pin_bit_mask = BIT64(a4988_motor->conf.direction_pin) |
-                           BIT64(a4988_motor->conf.sleep_pin) |
-                           BIT64(a4988_motor->conf.reset_pin) |
-                           BIT64(a4988_motor->conf.ms3_pin) |
-                           BIT64(a4988_motor->conf.ms2_pin) |
-                           BIT64(a4988_motor->conf.ms1_pin) |
-                           BIT64(a4988_motor->conf.enable_pin);
-    io_conf.pull_down_en = 0;
-    io_conf.pull_up_en = 0;
-    ESP_ERROR_CHECK(gpio_config(&io_conf));
-
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.direction_pin, 0));
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.sleep_pin, 0));    // default sleep
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.reset_pin, 0));    // keep reset
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms3_pin, 0));      // 1/1 phase
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms2_pin, 0));
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms1_pin, 0));
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.enable_pin, 1));   // disable by default
-    vTaskDelay(pdMS_TO_TICKS(A4988_RESPONSE_DELAY_MS));
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.reset_pin, 1));
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.sleep_pin, 1));
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.enable_pin, 0));
-    vTaskDelay(pdMS_TO_TICKS(A4988_RESPONSE_DELAY_MS));
-    return ESP_OK;
-}
-
-static esp_err_t a4988_set_direction(step_motor_driver_io_t *handle, step_direction direction)
-{
-    step_motor_driver_io_a4988_t *a4988_motor = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.direction_pin, direction));
-    return ESP_OK;
-}
-
-static esp_err_t a4988_enable_sleep(step_motor_driver_io_t *handle, bool enabled)
-{
-    step_motor_driver_io_a4988_t *a4988_motor = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.sleep_pin, enabled));
-    return ESP_OK;
-}
-
-static esp_err_t a4988_enable_output(step_motor_driver_io_t *handle, bool enabled)
-{
-    step_motor_driver_io_a4988_t *a4988_motor = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.enable_pin, enabled));
-    return ESP_OK;
-}
-
-static esp_err_t a4988_set_microstep(step_motor_driver_io_t *handle, uint16_t microstep)
-{
-    step_motor_driver_io_a4988_t *a4988_motor = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    switch (microstep) {
-    case 1:
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms3_pin, 0));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms2_pin, 0));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms1_pin, 0));
-        break;
-    case 2:
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms3_pin, 0));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms2_pin, 0));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms1_pin, 1));
-        break;
-    case 4:
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms3_pin, 0));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms2_pin, 1));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms1_pin, 0));
-        break;
-    case 8:
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms3_pin, 0));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms2_pin, 1));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms1_pin, 1));
-        break;
-    case 16:
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms3_pin, 1));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms2_pin, 1));
-        ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.ms1_pin, 1));
-        break;
-    default:
-        return ESP_ERR_NOT_SUPPORTED;
-    }
-    return ESP_OK;
-}
-
-static esp_err_t a4988_reset(step_motor_driver_io_t *handle)
-{
-    step_motor_driver_io_a4988_t *a4988_motor = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.reset_pin, 0));
-    vTaskDelay(pdMS_TO_TICKS(A4988_RESPONSE_DELAY_MS));
-    ESP_ERROR_CHECK(gpio_set_level(a4988_motor->conf.reset_pin, 1));
-    return ESP_OK;
-}
-
-static esp_err_t a4988_deinit(step_motor_driver_io_t *handle)
-{
-    step_motor_driver_io_a4988_t *a4988_motor = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    gpio_config_t io_conf;
-    io_conf.intr_type = GPIO_INTR_DISABLE;
-    io_conf.mode = GPIO_MODE_INPUT;
-    io_conf.pin_bit_mask = BIT64(a4988_motor->conf.direction_pin) |
-                           BIT64(a4988_motor->conf.sleep_pin) |
-                           BIT64(a4988_motor->conf.reset_pin) |
-                           BIT64(a4988_motor->conf.ms3_pin) |
-                           BIT64(a4988_motor->conf.ms2_pin) |
-                           BIT64(a4988_motor->conf.ms1_pin) |
-                           BIT64(a4988_motor->conf.enable_pin);
-    io_conf.pull_down_en = 0;
-    io_conf.pull_up_en = 0;
-    ESP_ERROR_CHECK(gpio_config(&io_conf));
-    return ESP_OK;
-}
-
-esp_err_t step_motor_new_a4988_io_driver(const step_motor_io_a4988_conf_t *conf, step_motor_driver_io_handle_t *handle)
-{
-    esp_err_t ret = ESP_OK;
-    step_motor_driver_io_a4988_t *a4988 = NULL;
-    ESP_GOTO_ON_FALSE(conf, ESP_ERR_INVALID_ARG, err, TAG, "configuration can't be null");
-    ESP_GOTO_ON_FALSE(handle, ESP_ERR_INVALID_ARG, err, TAG, "can't assign handle to null");
-
-    a4988 = calloc(1, sizeof(step_motor_driver_io_a4988_t));
-    ESP_GOTO_ON_FALSE(a4988, ESP_ERR_NO_MEM, err, TAG, "allocate context memory failed");
-    memcpy(&a4988->conf, conf, sizeof(step_motor_io_a4988_conf_t));
-
-    a4988->base.init = a4988_init;
-    a4988->base.deinit = a4988_deinit;
-    a4988->base.set_direction = a4988_set_direction;
-    a4988->base.set_microstep = a4988_set_microstep;
-    a4988->base.enable_sleep = a4988_enable_sleep;
-    a4988->base.enable_output = a4988_enable_output;
-    a4988->base.trigger_reset = a4988_reset;
-    a4988->base.step_triggered_edge = 1;
-    a4988->base.pulse_high_period_us = 1;
-    a4988->base.pulse_low_period_us = 1;
-
-    *handle = &(a4988->base);
-    return ESP_OK;
-
-err:
-    if (a4988) {
-        free(a4988);
-    }
-    return ret;
-}
-
-esp_err_t step_motor_delete_a4988_io_driver(step_motor_driver_io_handle_t handle)
-{
-    ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, TAG, "empty handle");
-    step_motor_driver_io_a4988_t *a4988 = __containerof(handle, step_motor_driver_io_a4988_t, base);
-    free(a4988);
-    return ESP_OK;
-}

+ 0 - 325
examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_rmt.c

@@ -1,325 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- */
-
-/*
- * This file contains an implementation of step motor middleware based on rmt peripheral
- */
-
-#include <math.h>
-#include "freertos/FreeRTOS.h"
-#include "freertos/semphr.h"
-#include "esp_log.h"
-#include "esp_check.h"
-#include "driver/rmt.h"
-#include "step_motor.h"
-
-static const char *TAG = "RMT_STEP_MOTOR";
-
-typedef enum {
-    STOPPED = 0,
-    SMOOTH_SPEED_UP,
-    SMOOTH_KEEP_SPEED,
-    SMOOTH_SLOW_DOWN,
-    UNLIMITED_LOOP,
-    LIMITED_LOOP,
-} rmt_step_motor_running_status;
-
-typedef struct {
-    step_motor_t base;
-    step_motor_driver_io_t *io_driver;
-    rmt_channel_t rmt_ch;
-    rmt_step_motor_running_status status;
-    rmt_item32_t rmt_items_loop;
-    uint32_t rmt_items_loop_count;
-    rmt_item32_t *rmt_items_speedup;
-    rmt_item32_t *rmt_items_speeddown;
-    uint32_t rmt_items_smoothstep_count;
-    SemaphoreHandle_t notify_semphr;
-} rmt_step_motor_t;
-
-static inline float helper_smootherstep_clamp(float x, float lowerlimit, float upperlimit)
-{
-    if (x < lowerlimit) {
-        x = lowerlimit;
-    }
-    if (x > upperlimit) {
-        x = upperlimit;
-    }
-    return x;
-}
-
-// smoothstep formula
-// see https://en.wikipedia.org/wiki/Smoothstep
-static float helper_smootherstep(float edge0, float edge1, float x)
-{
-    // Scale, and clamp x to 0..1 range
-    x = helper_smootherstep_clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
-    // Evaluate polynomial
-    return x * x * x * (x * (x * 6 - 15) + 10) * (edge1 - edge0) + edge0;
-}
-
-static uint16_t helper_speed_to_duration(uint16_t speed)
-{
-    return (uint16_t) round(1.0 * 1000 * 1000 / speed);
-}
-
-static esp_err_t helper_fill_rmt_items(rmt_item32_t *items, uint32_t speed, const step_motor_driver_io_t *io_driver)
-{
-    items->duration1 = io_driver->step_triggered_edge ? io_driver->pulse_high_period_us : io_driver->pulse_low_period_us;
-    items->level1 = io_driver->step_triggered_edge;
-    items->level0 = !io_driver->step_triggered_edge;
-    uint32_t delay_period = helper_speed_to_duration(speed);
-    if (delay_period <= (io_driver->step_triggered_edge ? io_driver->pulse_low_period_us : io_driver->pulse_high_period_us)) {
-        ESP_LOGW(TAG, "maximum rate reached, driver will generate another possible highest rate instead");
-        items->duration0 = io_driver->step_triggered_edge ? io_driver->pulse_low_period_us : io_driver->pulse_high_period_us;
-    } else {
-        items->duration0 = delay_period;
-    }
-    return ESP_OK;
-}
-
-static esp_err_t rmt_step_motor_init(step_motor_t *motor)
-{
-    rmt_step_motor_t *rmt_handle = __containerof(motor, rmt_step_motor_t, base);
-    step_motor_driver_io_t *io_driver = rmt_handle->io_driver;
-    if (io_driver->init) {
-        return io_driver->init(io_driver);
-    }
-    return ESP_OK;
-}
-
-static esp_err_t rmt_step_motor_deinit(step_motor_t *motor)
-{
-    rmt_step_motor_t *rmt_handle = __containerof(motor, rmt_step_motor_t, base);
-    step_motor_driver_io_t *io_driver = rmt_handle->io_driver;
-    if (io_driver->deinit) {
-        return io_driver->deinit(io_driver);
-    }
-    return ESP_OK;
-}
-
-// assume n != 0 and speed is within considerable range
-static esp_err_t rmt_step_motor_step_impl(step_motor_t *motor, uint32_t n, uint32_t speed)
-{
-    rmt_step_motor_t *rmt_handle = __containerof(motor, rmt_step_motor_t, base);
-
-    ESP_ERROR_CHECK(rmt_set_tx_loop_mode(rmt_handle->rmt_ch, true));
-    ESP_ERROR_CHECK(rmt_enable_tx_loop_autostop(rmt_handle->rmt_ch, true));
-
-    rmt_handle->rmt_items_loop_count = n;
-    if ((rmt_handle->rmt_items_loop_count) > 1023) {
-        (rmt_handle->rmt_items_loop_count) -= 1023;
-        ESP_ERROR_CHECK(rmt_set_tx_loop_count(rmt_handle->rmt_ch, 1023));
-    } else {
-        ESP_ERROR_CHECK(rmt_set_tx_loop_count(rmt_handle->rmt_ch, rmt_handle->rmt_items_loop_count));
-        rmt_handle->rmt_items_loop_count = 0;
-    }
-    helper_fill_rmt_items(&rmt_handle->rmt_items_loop, speed, rmt_handle->io_driver);
-
-    rmt_handle->status = LIMITED_LOOP;
-
-    rmt_write_items(rmt_handle->rmt_ch, &rmt_handle->rmt_items_loop, 1, false);
-    xSemaphoreTake(rmt_handle->notify_semphr, portMAX_DELAY);
-    return ESP_OK;
-}
-
-static esp_err_t rmt_step_motor_step(step_motor_t *handle, uint32_t n, uint32_t speed)
-{
-    rmt_step_motor_t *rmt_handle = __containerof(handle, rmt_step_motor_t, base);
-
-    ESP_ERROR_CHECK(rmt_tx_stop(rmt_handle->rmt_ch));
-
-    if (n == UINT32_MAX) {   // forever loop, non-blocking
-        ESP_ERROR_CHECK(rmt_set_tx_loop_count(rmt_handle->rmt_ch, 0));
-        ESP_ERROR_CHECK(rmt_enable_tx_loop_autostop(rmt_handle->rmt_ch, false));
-        ESP_ERROR_CHECK(rmt_set_tx_loop_mode(rmt_handle->rmt_ch, true));
-        helper_fill_rmt_items(&rmt_handle->rmt_items_loop, speed, rmt_handle->io_driver);
-        rmt_handle->status = UNLIMITED_LOOP;
-        ESP_ERROR_CHECK(rmt_write_items(rmt_handle->rmt_ch, &rmt_handle->rmt_items_loop, 1, false));
-        return ESP_OK;
-    } else if (n == 0) {     // break the forever loop
-        rmt_handle->status = STOPPED;
-        ESP_ERROR_CHECK(rmt_tx_stop(rmt_handle->rmt_ch));
-        ESP_ERROR_CHECK(rmt_set_tx_loop_mode(rmt_handle->rmt_ch, false));
-        return ESP_OK;
-    } else {                 // normally move n steps
-        ESP_RETURN_ON_FALSE(helper_speed_to_duration(speed) > 1, ESP_ERR_INVALID_ARG, TAG,
-                            "speed too fast");
-        return rmt_step_motor_step_impl(handle, n, speed);
-    }
-}
-
-static esp_err_t rmt_step_motor_smoothstep(step_motor_t *handle, uint32_t n, uint32_t speed_steps, uint32_t speed_min,
-        uint32_t speed_max)
-{
-    esp_err_t ret = ESP_OK;
-    ESP_RETURN_ON_FALSE(speed_min <= speed_max, ESP_ERR_INVALID_ARG, TAG, "max speed lower than min speed");
-    ESP_RETURN_ON_FALSE(n > speed_steps * 2, ESP_ERR_INVALID_ARG, TAG, "too few steps. consider lower speed_steps");
-    ESP_RETURN_ON_FALSE(helper_speed_to_duration(speed_min) < 1 << 15, ESP_ERR_INVALID_ARG, TAG, "min speed too low");
-    ESP_RETURN_ON_FALSE(helper_speed_to_duration(speed_max) > 1, ESP_ERR_INVALID_ARG, TAG, "max speed too high");
-
-    rmt_step_motor_t *rmt_handle = __containerof(handle, rmt_step_motor_t, base);
-    rmt_handle->rmt_items_speedup = malloc(sizeof(rmt_item32_t) * speed_steps);
-    ESP_RETURN_ON_FALSE(rmt_handle->rmt_items_speedup != NULL, ESP_ERR_NO_MEM, TAG,
-                        "failed to allocate rmt_items_speedup");
-    rmt_handle->rmt_items_speeddown = malloc(sizeof(rmt_item32_t) * speed_steps);
-    ESP_GOTO_ON_FALSE(rmt_handle->rmt_items_speeddown != NULL, ESP_ERR_NO_MEM, err_free_speedup, TAG,
-                      "failed to allocate rmt_items_speeddown");
-    ESP_GOTO_ON_ERROR(rmt_tx_stop(rmt_handle->rmt_ch), err_free_speeddown, TAG, "failed to stop rmt tx");
-
-    // prepare speed tables
-    for (int i = 0; i < speed_steps; ++i) {
-        helper_fill_rmt_items(&rmt_handle->rmt_items_speedup[i],
-                              (uint16_t)helper_smootherstep(
-                                  (float)speed_min,
-                                  (float)speed_max,
-                                  (float)speed_min + ( (float)i / (float)speed_steps) * (float)(speed_max - speed_min))
-                              , rmt_handle->io_driver
-                             );
-    }
-    for (int i = 0; i < speed_steps; ++i) {
-        helper_fill_rmt_items(&rmt_handle->rmt_items_speeddown[i],
-                              speed_max + speed_min - (uint16_t)helper_smootherstep(
-                                  (float)speed_min,
-                                  (float)speed_max,
-                                  (float)speed_min + ((float) i / (float)speed_steps) * (float)(speed_max - speed_min)
-                              )
-                              , rmt_handle->io_driver
-                             );
-    }
-    rmt_handle->rmt_items_smoothstep_count = speed_steps;
-    // prepare continuous phase rmt payload
-    helper_fill_rmt_items(&rmt_handle->rmt_items_loop, speed_max, rmt_handle->io_driver);
-    rmt_handle->rmt_items_loop_count = n - speed_steps * 2;
-    // set status to be checked inside ISR
-    rmt_handle->status = SMOOTH_SPEED_UP;
-    // start transmitting
-    ESP_ERROR_CHECK(rmt_write_items(rmt_handle->rmt_ch, rmt_handle->rmt_items_speedup, speed_steps, false));
-
-    // waiting for transfer done
-    xSemaphoreTake(rmt_handle->notify_semphr, portMAX_DELAY);
-
-err_free_speeddown:
-    free(rmt_handle->rmt_items_speeddown);
-err_free_speedup:
-    free(rmt_handle->rmt_items_speedup);
-    return ret;
-}
-
-static esp_err_t rmt_step_motor_set_step(step_motor_t *handle, uint16_t microstep, bool direction)
-{
-    rmt_step_motor_t *rmt_handle = __containerof(handle, rmt_step_motor_t, base);
-    step_motor_driver_io_t *io_driver = rmt_handle->io_driver;
-    if (io_driver->set_direction) {
-        ESP_ERROR_CHECK(io_driver->set_direction(io_driver, direction));
-    }
-    if (io_driver->set_microstep) {
-        ESP_ERROR_CHECK(io_driver->set_microstep(io_driver, microstep));
-    }
-    // at least 200ns delay as described in datasheet
-    esp_rom_delay_us(1);
-    return ESP_OK;
-}
-
-static IRAM_ATTR void rmt_tx_loop_intr(rmt_channel_t channel, void *args)
-{
-    rmt_step_motor_t *rmt_step_motor = (rmt_step_motor_t *) args;
-
-    // smoothstep speedup stage finished
-    if (rmt_step_motor->status == SMOOTH_SPEED_UP) {
-        rmt_step_motor->status = SMOOTH_KEEP_SPEED;
-        rmt_set_tx_loop_mode(rmt_step_motor->rmt_ch, true);
-        rmt_enable_tx_loop_autostop(rmt_step_motor->rmt_ch, true);
-        rmt_set_tx_intr_en(rmt_step_motor->rmt_ch, 0);
-        // continue and configure loop count
-    }
-
-    if (rmt_step_motor->status == SMOOTH_KEEP_SPEED || rmt_step_motor->status == LIMITED_LOOP) {
-        // loop count not 0, continuing looping
-        if ((rmt_step_motor->rmt_items_loop_count) != 0) {
-            if ((rmt_step_motor->rmt_items_loop_count) > 1023) {
-                (rmt_step_motor->rmt_items_loop_count) -= 1023;
-                rmt_set_tx_loop_count(rmt_step_motor->rmt_ch, 1023);
-            } else {
-                rmt_set_tx_loop_count(rmt_step_motor->rmt_ch, rmt_step_motor->rmt_items_loop_count);
-                rmt_step_motor->rmt_items_loop_count = 0;
-            }
-            rmt_write_items(rmt_step_motor->rmt_ch, &rmt_step_motor->rmt_items_loop, 1, false);
-            return;
-        }
-    }
-
-    // smoothstep keep speed stage finished
-    if (rmt_step_motor->status == SMOOTH_KEEP_SPEED) {
-        rmt_step_motor->status = SMOOTH_SLOW_DOWN;
-        rmt_set_tx_loop_mode(rmt_step_motor->rmt_ch, false);
-        rmt_enable_tx_loop_autostop(rmt_step_motor->rmt_ch, false);
-        rmt_set_tx_intr_en(rmt_step_motor->rmt_ch, 1);
-        rmt_write_items(rmt_step_motor->rmt_ch, rmt_step_motor->rmt_items_speeddown, rmt_step_motor->rmt_items_smoothstep_count, false);
-        return;
-    }
-
-    if (rmt_step_motor->status == LIMITED_LOOP || rmt_step_motor->status == SMOOTH_SLOW_DOWN) {
-        rmt_step_motor->status = STOPPED;
-        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-        xSemaphoreGiveFromISR(rmt_step_motor->notify_semphr, &xHigherPriorityTaskWoken);
-        if (xHigherPriorityTaskWoken == pdTRUE) {
-            portYIELD_FROM_ISR();
-        }
-    }
-}
-
-esp_err_t step_motor_create_rmt(step_motor_driver_io_t *io_driver, const rmt_config_t *rmt_conf, step_motor_handle_t *ret_handle)
-{
-    esp_err_t ret = ESP_OK;
-    rmt_step_motor_t *rmt_step_motor = NULL;
-
-    ESP_RETURN_ON_ERROR(rmt_config(rmt_conf), TAG, "Failed to configure RMT");
-    ESP_RETURN_ON_ERROR(rmt_driver_install(rmt_conf->channel, 0, 0), TAG, "Failed to install RMT driver");
-
-    ESP_GOTO_ON_FALSE(io_driver, ESP_ERR_INVALID_ARG, err, TAG, "configuration can't be null");
-    ESP_GOTO_ON_FALSE(ret_handle, ESP_ERR_INVALID_ARG, err, TAG, "can't assign handle to null");
-
-    rmt_step_motor = calloc(1, sizeof(rmt_step_motor_t));
-    ESP_GOTO_ON_FALSE(rmt_step_motor, ESP_ERR_NO_MEM, err, TAG, "allocate context memory failed");
-    rmt_step_motor->rmt_ch = rmt_conf->channel;
-
-    rmt_step_motor->notify_semphr = xSemaphoreCreateBinary();
-    ESP_GOTO_ON_FALSE(rmt_step_motor, ESP_ERR_NO_MEM, err, TAG, "allocate semaphore memory failed");
-
-    rmt_step_motor->io_driver = io_driver;
-
-    // register tx end callback function, which got invoked when tx loop comes to the end
-    rmt_register_tx_end_callback(rmt_tx_loop_intr, rmt_step_motor);
-
-    rmt_step_motor->base.init = rmt_step_motor_init;
-    rmt_step_motor->base.deinit = rmt_step_motor_deinit;
-    rmt_step_motor->base.step = rmt_step_motor_step;
-    rmt_step_motor->base.set_step = rmt_step_motor_set_step;
-    rmt_step_motor->base.smooth_step = rmt_step_motor_smoothstep;
-
-    *ret_handle = &(rmt_step_motor->base);
-    return ESP_OK;
-
-err:
-    if (rmt_step_motor) {
-        if (rmt_step_motor->notify_semphr) {
-            vSemaphoreDelete(rmt_step_motor->notify_semphr);
-        }
-        free(rmt_step_motor);
-    }
-    return ret;
-}
-
-esp_err_t step_motor_delete_rmt(step_motor_handle_t handle)
-{
-    ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_STATE, TAG, "empty handle");
-    rmt_step_motor_t *rmt_handle = __containerof(handle, rmt_step_motor_t, base);
-    ESP_RETURN_ON_ERROR(rmt_driver_uninstall(rmt_handle->rmt_ch), TAG, "Failed to uninstall RMT driver");
-    vSemaphoreDelete(rmt_handle->notify_semphr);
-    free(rmt_handle);
-    return ESP_OK;
-}

+ 0 - 2
examples/peripherals/rmt/step_motor/main/CMakeLists.txt

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

+ 0 - 92
examples/peripherals/rmt/step_motor/main/step_motor_main.c

@@ -1,92 +0,0 @@
-/* RMT example -- step motor */
-
-/*
- * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: CC0-1.0
- */
-
-#include "sdkconfig.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "esp_log.h"
-#include "driver/rmt.h"
-#include "step_motor.h"
-#include "step_motor_driver_io_a4988.h"
-
-// GPIO configuration
-#define STEP_MOTOR_DIRECTION_PIN GPIO_NUM_18
-#define STEP_MOTOR_STEP_PIN GPIO_NUM_17
-#define STEP_MOTOR_SLEEP_PIN GPIO_NUM_16
-#define STEP_MOTOR_RESET_PIN GPIO_NUM_15
-#define STEP_MOTOR_MS3_PIN GPIO_NUM_7
-#define STEP_MOTOR_MS2_PIN GPIO_NUM_6
-#define STEP_MOTOR_MS1_PIN GPIO_NUM_5
-#define STEP_MOTOR_ENABLE_PIN GPIO_NUM_4
-
-#define RMT_TX_CHANNEL RMT_CHANNEL_0
-
-static const char *TAG = "step_motor";
-
-void app_main(void)
-{
-    // Apply default RMT configuration
-    rmt_config_t dev_config = RMT_DEFAULT_CONFIG_TX(STEP_MOTOR_STEP_PIN, RMT_TX_CHANNEL);
-
-    step_motor_io_a4988_conf_t a4988_conf = {
-        .direction_pin = STEP_MOTOR_DIRECTION_PIN,
-        .sleep_pin = STEP_MOTOR_SLEEP_PIN,
-        .reset_pin = STEP_MOTOR_RESET_PIN,
-        .ms3_pin = STEP_MOTOR_MS3_PIN,
-        .ms2_pin = STEP_MOTOR_MS2_PIN,
-        .ms1_pin = STEP_MOTOR_MS1_PIN,
-        .enable_pin = STEP_MOTOR_ENABLE_PIN,
-    };
-
-    // Install low part driver
-    step_motor_driver_io_t *a4988_io;
-    ESP_ERROR_CHECK(step_motor_new_a4988_io_driver(&a4988_conf, &a4988_io));
-
-    // Install rmt driver
-    step_motor_t *motor = NULL;
-    ESP_ERROR_CHECK(step_motor_create_rmt(a4988_io, &dev_config, &motor));
-
-    step_motor_init(motor);
-    ESP_LOGI(TAG, "init");
-
-    ESP_LOGI(TAG, "set_step");
-    // configure Microstep to Full Step
-    step_motor_set_step(motor, 1, STEP_MOTOR_DIRECTION_POSITIVE);
-    vTaskDelay(pdMS_TO_TICKS(1000));
-
-    ESP_LOGI(TAG, "step 10 @ 1000/s");
-    step_motor_step(motor, 10, 1000);
-    vTaskDelay(pdMS_TO_TICKS(1000));
-    ESP_LOGI(TAG, "step 100 @ 1000/s");
-    step_motor_step(motor, 100, 1000);
-    vTaskDelay(pdMS_TO_TICKS(1000));
-    ESP_LOGI(TAG, "step 1000 @ 1200/s");
-    step_motor_step(motor, 1000, 1200);
-    vTaskDelay(pdMS_TO_TICKS(1000));
-    ESP_LOGI(TAG, "step 5000 @ 1400/s");
-    step_motor_step(motor, 5000, 1400);
-    vTaskDelay(pdMS_TO_TICKS(1000));
-
-    ESP_LOGI(TAG, "smoothstep start 5000 steps @ 500~1400/s");
-    step_motor_smooth_step(motor, 5000, 1000, 500, 1400);
-    ESP_LOGI(TAG, "smoothstep finish");
-    vTaskDelay(pdMS_TO_TICKS(1000));
-
-    ESP_LOGI(TAG, "continuous running for 5s");
-    step_motor_step(motor, UINT32_MAX, 1000);
-    vTaskDelay(pdMS_TO_TICKS(5000));
-    ESP_LOGI(TAG, "stop");
-    step_motor_step(motor, 0, 1000);
-
-    vTaskDelay(pdMS_TO_TICKS(1000));
-    step_motor_deinit(motor);
-    ESP_LOGI(TAG, "deinit");
-    ESP_ERROR_CHECK(step_motor_delete_rmt(motor));
-
-    ESP_ERROR_CHECK(step_motor_delete_a4988_io_driver(a4988_io));
-}

+ 0 - 0
examples/peripherals/rmt/step_motor/CMakeLists.txt → examples/peripherals/rmt/stepper_motor/CMakeLists.txt


+ 79 - 0
examples/peripherals/rmt/stepper_motor/README.md

@@ -0,0 +1,79 @@
+| Supported Targets | ESP32-S3 |
+| ----------------- | -------- |
+
+# RMT Based Stepper Motor Smooth Controller
+
+(See the README.md file in the upper level 'examples' directory for more information about examples.)
+
+One RMT TX channel can use different encoders in sequence, which is useful to generate waveforms that have obvious multiple stages.
+
+This example shows how to drive a stepper motor with a **STEP/DIR** interfaced controller (e.g. [DRV8825](https://www.ti.com/lit/ds/symlink/drv8825.pdf)) in a [smooth](https://en.wikipedia.org/wiki/Smoothstep) way. To smoothly drive a stepper motor, there're three phases: **Acceleration**, **Uniform** and **Deceleration**. Accordingly, this example implements two encoders so that RMT channel can generate the waveforms with different characteristics:
+
+* `curve_encoder` is to encode the **Acceleration** and **Deceleration** phase
+* `uniform_encoder` is to encode the ***Uniform** phase
+
+## How to Use Example
+
+### Hardware Required
+
+* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
+* A USB cable for Power supply and programming
+* A two-phase four-wire stepper motor
+* A DRV8825 stepper motor controller
+
+Connection :
+
+```
++---------------------------+             +--------------------+      +--------------+
+|          ESP Board        |             |       DRV8825      |      |    4-wire    |
+|                       GND +-------------+ GND                |      |     Step     |
+|                           |             |                    |      |     Motor    |
+|                       3V3 +-------------+ VDD             A+ +------+ A+           |
+|                           |             |                    |      |              |
+|       STEP_MOTOR_GPIO_DIR +------------>+ DIR             A- +------+ A-           |
+|                           |             |                    |      |              |
+|      STEP_MOTOR_GPIO_STEP +------------>+ STEP            B- +------+ B-           |
+|                           |             |                    |      |              |
+|                           |      3V3----+ nSLEEP          B+ +------+ B+           |
+|                           |             |                    |      +--------------+
+|                           |      3V3----+ nRST            VM +-------------------+
+|                           |             |                    |                   |
+|                           |  3V3|GND----+ M2             GND +----------+        |
+|                           |             |                    |          |        |
+|                           |  3V3|GND----+ M1                 |          |        |
+|                           |             |                    |          |        |
+|                           |  3V3|GND----+ M0                 |      +---+--------+-----+
+|                           |             |                    |      |  GND     +12V    |
+|        STEP_MOTOR_GPIO_EN +------------>+ nEN                |      |   POWER SUPPLY   |
++---------------------------+             +--------------------+      +------------------+
+```
+
+The GPIO number used in this example can be changed according to your board, by the macro `STEP_MOTOR_GPIO_EN`, `STEP_MOTOR_GPIO_DIR` and `STEP_MOTOR_GPIO_STEP` defined in the [source file](main/stepper_motor_example_main.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
+
+```
+I (0) cpu_start: Starting scheduler on APP CPU.
+I (325) example: Initialize EN + DIR GPIO
+I (325) gpio: GPIO[16]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
+I (335) gpio: GPIO[17]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
+I (345) example: Create RMT TX channel
+I (365) example: Set spin direction
+I (365) example: Enable step motor
+I (375) example: Create motor encoders
+I (405) example: Start RMT channel
+I (405) example: Spin motor for 6000 steps: 500 accel + 5000 uniform + 500 decel
+```
+
+## 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.

+ 2 - 0
examples/peripherals/rmt/stepper_motor/main/CMakeLists.txt

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

+ 181 - 0
examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.c

@@ -0,0 +1,181 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_check.h"
+#include "stepper_motor_encoder.h"
+
+static const char *TAG = "stepper_motor_encoder";
+
+static float convert_to_smooth_freq(uint32_t freq1, uint32_t freq2, uint32_t freqx)
+{
+    float normalize_x = ((float)(freqx - freq1)) / (freq2 - freq1);
+    // third-order "smoothstep" function: https://en.wikipedia.org/wiki/Smoothstep
+    float smooth_x = normalize_x * normalize_x * (3 - 2 * normalize_x);
+    return smooth_x * (freq2 - freq1) + freq1;
+}
+
+typedef struct {
+    rmt_encoder_t base;
+    rmt_encoder_handle_t copy_encoder;
+    uint32_t sample_points;
+    struct {
+        uint32_t is_accel_curve: 1;
+    } flags;
+    rmt_symbol_word_t curve_table[];
+} rmt_stepper_curve_encoder_t;
+
+static size_t rmt_encode_stepper_motor_curve(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
+{
+    rmt_stepper_curve_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_curve_encoder_t, base);
+    rmt_encoder_handle_t copy_encoder = motor_encoder->copy_encoder;
+    rmt_encode_state_t session_state = 0;
+    uint32_t points_num = *(uint32_t *)primary_data;
+    size_t encoded_symbols = 0;
+    if (motor_encoder->flags.is_accel_curve) {
+        encoded_symbols = copy_encoder->encode(copy_encoder, channel, &motor_encoder->curve_table[0],
+                                               points_num * sizeof(rmt_symbol_word_t), &session_state);
+    } else {
+        encoded_symbols = copy_encoder->encode(copy_encoder, channel, &motor_encoder->curve_table[0] + motor_encoder->sample_points - points_num,
+                                               points_num * sizeof(rmt_symbol_word_t), &session_state);
+    }
+    *ret_state = session_state;
+    return encoded_symbols;
+}
+
+static esp_err_t rmt_del_stepper_motor_curve_encoder(rmt_encoder_t *encoder)
+{
+    rmt_stepper_curve_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_curve_encoder_t, base);
+    rmt_del_encoder(motor_encoder->copy_encoder);
+    free(motor_encoder);
+    return ESP_OK;
+}
+
+static esp_err_t rmt_reset_stepper_motor_curve_encoder(rmt_encoder_t *encoder)
+{
+    rmt_stepper_curve_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_curve_encoder_t, base);
+    rmt_encoder_reset(motor_encoder->copy_encoder);
+    return ESP_OK;
+}
+
+esp_err_t rmt_new_stepper_motor_curve_encoder(const stepper_motor_curve_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
+{
+    esp_err_t ret = ESP_OK;
+    rmt_stepper_curve_encoder_t *step_encoder = NULL;
+    float smooth_freq;
+    uint32_t symbol_duration;
+    ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid arguments");
+    ESP_GOTO_ON_FALSE(config->sample_points, ESP_ERR_INVALID_ARG, err, TAG, "sample points number can't be zero");
+    ESP_GOTO_ON_FALSE(config->start_freq_hz != config->end_freq_hz, ESP_ERR_INVALID_ARG, err, TAG, "start freq can't equal to end freq");
+    step_encoder = calloc(1, sizeof(rmt_stepper_curve_encoder_t) + config->sample_points * sizeof(rmt_symbol_word_t));
+    ESP_GOTO_ON_FALSE(step_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for stepper curve encoder");
+    rmt_copy_encoder_config_t copy_encoder_config = {};
+    ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &step_encoder->copy_encoder), err, TAG, "create copy encoder failed");
+    bool is_accel_curve = config->start_freq_hz < config->end_freq_hz;
+
+    // prepare the curve table, in RMT symbol format
+    if (is_accel_curve) {
+        uint32_t curve_step = (config->end_freq_hz - config->start_freq_hz) / (config->sample_points - 1);
+        for (uint32_t i = 0; i < config->sample_points; i++) {
+            smooth_freq = convert_to_smooth_freq(config->start_freq_hz, config->end_freq_hz, config->start_freq_hz + curve_step * i);
+            symbol_duration = config->resolution / smooth_freq / 2;
+            step_encoder->curve_table[i].level0 = 0;
+            step_encoder->curve_table[i].duration0 = symbol_duration;
+            step_encoder->curve_table[i].level1 = 1;
+            step_encoder->curve_table[i].duration1 = symbol_duration;
+        }
+    } else {
+        uint32_t curve_step = (config->start_freq_hz - config->end_freq_hz) / (config->sample_points - 1);
+        for (uint32_t i = 0; i < config->sample_points; i++) {
+            smooth_freq = convert_to_smooth_freq(config->end_freq_hz, config->start_freq_hz, config->end_freq_hz + curve_step * i);
+            symbol_duration = config->resolution / smooth_freq / 2;
+            step_encoder->curve_table[config->sample_points - i - 1].level0 = 0;
+            step_encoder->curve_table[config->sample_points - i - 1].duration0 = symbol_duration;
+            step_encoder->curve_table[config->sample_points - i - 1].level1 = 1;
+            step_encoder->curve_table[config->sample_points - i - 1].duration1 = symbol_duration;
+        }
+    }
+
+    step_encoder->sample_points = config->sample_points;
+    step_encoder->flags.is_accel_curve = is_accel_curve;
+    step_encoder->base.del = rmt_del_stepper_motor_curve_encoder;
+    step_encoder->base.encode = rmt_encode_stepper_motor_curve;
+    step_encoder->base.reset = rmt_reset_stepper_motor_curve_encoder;
+    *ret_encoder = &(step_encoder->base);
+    return ESP_OK;
+err:
+    if (step_encoder) {
+        if (step_encoder->copy_encoder) {
+            rmt_del_encoder(step_encoder->copy_encoder);
+        }
+        free(step_encoder);
+    }
+    return ret;
+}
+
+typedef struct {
+    rmt_encoder_t base;
+    rmt_encoder_handle_t copy_encoder;
+    uint32_t resolution;
+} rmt_stepper_uniform_encoder_t;
+
+static size_t rmt_encode_stepper_motor_uniform(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
+{
+    rmt_stepper_uniform_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_uniform_encoder_t, base);
+    rmt_encoder_handle_t copy_encoder = motor_encoder->copy_encoder;
+    rmt_encode_state_t session_state = 0;
+    uint32_t target_freq_hz = *(uint32_t *)primary_data;
+    uint32_t symbol_duration = motor_encoder->resolution / target_freq_hz / 2;
+    rmt_symbol_word_t freq_sample = {
+        .level0 = 0,
+        .duration0 = symbol_duration,
+        .level1 = 1,
+        .duration1 = symbol_duration,
+    };
+    size_t encoded_symbols = copy_encoder->encode(copy_encoder, channel, &freq_sample, sizeof(freq_sample), &session_state);
+    *ret_state = session_state;
+    return encoded_symbols;
+}
+
+static esp_err_t rmt_del_stepper_motor_uniform_encoder(rmt_encoder_t *encoder)
+{
+    rmt_stepper_uniform_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_uniform_encoder_t, base);
+    rmt_del_encoder(motor_encoder->copy_encoder);
+    free(motor_encoder);
+    return ESP_OK;
+}
+
+static esp_err_t rmt_reset_stepper_motor_uniform(rmt_encoder_t *encoder)
+{
+    rmt_stepper_uniform_encoder_t *motor_encoder = __containerof(encoder, rmt_stepper_uniform_encoder_t, base);
+    rmt_encoder_reset(motor_encoder->copy_encoder);
+    return ESP_OK;
+}
+
+esp_err_t rmt_new_stepper_motor_uniform_encoder(const stepper_motor_uniform_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
+{
+    esp_err_t ret = ESP_OK;
+    rmt_stepper_uniform_encoder_t *step_encoder = NULL;
+    ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid arguments");
+    step_encoder = calloc(1, sizeof(rmt_stepper_uniform_encoder_t));
+    ESP_GOTO_ON_FALSE(step_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for stepper uniform encoder");
+    rmt_copy_encoder_config_t copy_encoder_config = {};
+    ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &step_encoder->copy_encoder), err, TAG, "create copy encoder failed");
+
+    step_encoder->resolution = config->resolution;
+    step_encoder->base.del = rmt_del_stepper_motor_uniform_encoder;
+    step_encoder->base.encode = rmt_encode_stepper_motor_uniform;
+    step_encoder->base.reset = rmt_reset_stepper_motor_uniform;
+    *ret_encoder = &(step_encoder->base);
+    return ESP_OK;
+err:
+    if (step_encoder) {
+        if (step_encoder->copy_encoder) {
+            rmt_del_encoder(step_encoder->copy_encoder);
+        }
+        free(step_encoder);
+    }
+    return ret;
+}

+ 58 - 0
examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.h

@@ -0,0 +1,58 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include <stdint.h>
+#include "driver/rmt_encoder.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Stepper motor curve encoder configuration
+ */
+typedef struct {
+    uint32_t resolution;    // Encoder resolution, in Hz
+    uint32_t sample_points; // Sample points used for deceleration phase
+    uint32_t start_freq_hz; // Start frequency on the curve, in Hz
+    uint32_t end_freq_hz;   // End frequency on the curve, in Hz
+} stepper_motor_curve_encoder_config_t;
+
+/**
+ * @brief Stepper motor uniform encoder configuration
+ */
+typedef struct {
+    uint32_t resolution; // Encoder resolution, in Hz
+} stepper_motor_uniform_encoder_config_t;
+
+/**
+ * @brief Create stepper motor curve encoder
+ *
+ * @param[in] config Encoder configuration
+ * @param[out] ret_encoder Returned encoder handle
+ * @return
+ *      - ESP_ERR_INVALID_ARG for any invalid arguments
+ *      - ESP_ERR_NO_MEM out of memory when creating step motor encoder
+ *      - ESP_OK if creating encoder successfully
+ */
+esp_err_t rmt_new_stepper_motor_curve_encoder(const stepper_motor_curve_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
+
+/**
+ * @brief Create RMT encoder for encoding step motor uniform phase into RMT symbols
+ *
+ * @param[in] config Encoder configuration
+ * @param[out] ret_encoder Returned encoder handle
+ * @return
+ *      - ESP_ERR_INVALID_ARG for any invalid arguments
+ *      - ESP_ERR_NO_MEM out of memory when creating step motor encoder
+ *      - ESP_OK if creating encoder successfully
+ */
+esp_err_t rmt_new_stepper_motor_uniform_encoder(const stepper_motor_uniform_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
+
+#ifdef __cplusplus
+}
+#endif

+ 106 - 0
examples/peripherals/rmt/stepper_motor/main/stepper_motor_example_main.c

@@ -0,0 +1,106 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/rmt_tx.h"
+#include "driver/gpio.h"
+#include "esp_log.h"
+#include "stepper_motor_encoder.h"
+
+///////////////////////////////Change the following configurations according to your board//////////////////////////////
+#define STEP_MOTOR_GPIO_EN       16
+#define STEP_MOTOR_GPIO_DIR      17
+#define STEP_MOTOR_GPIO_STEP     18
+#define STEP_MOTOR_ENABLE_LEVEL  0 // DRV8825 is enabled on low level
+#define STEP_MOTOR_SPIN_DIR_CLOCKWISE 0
+#define STEP_MOTOR_SPIN_DIR_COUNTERCLOCKWISE !STEP_MOTOR_SPIN_DIR_CLOCKWISE
+
+#define STEP_MOTOR_RESOLUTION_HZ 1000000 // 1MHz resolution
+
+static const char *TAG = "example";
+
+void app_main(void)
+{
+    ESP_LOGI(TAG, "Initialize EN + DIR GPIO");
+    gpio_config_t en_dir_gpio_config = {
+        .mode = GPIO_MODE_OUTPUT,
+        .intr_type = GPIO_INTR_DISABLE,
+        .pin_bit_mask = 1ULL << STEP_MOTOR_GPIO_DIR | 1ULL << STEP_MOTOR_GPIO_EN,
+    };
+    ESP_ERROR_CHECK(gpio_config(&en_dir_gpio_config));
+
+    ESP_LOGI(TAG, "Create RMT TX channel");
+    rmt_channel_handle_t motor_chan = NULL;
+    rmt_tx_channel_config_t tx_chan_config = {
+        .clk_src = RMT_CLK_SRC_DEFAULT, // select clock source
+        .gpio_num = STEP_MOTOR_GPIO_STEP,
+        .mem_block_symbols = 64,
+        .resolution_hz = STEP_MOTOR_RESOLUTION_HZ,
+        .trans_queue_depth = 10, // set the number of transactions that can be pending in the background
+    };
+    ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &motor_chan));
+
+    ESP_LOGI(TAG, "Set spin direction");
+    gpio_set_level(STEP_MOTOR_GPIO_DIR, STEP_MOTOR_SPIN_DIR_CLOCKWISE);
+    ESP_LOGI(TAG, "Enable step motor");
+    gpio_set_level(STEP_MOTOR_GPIO_EN, STEP_MOTOR_ENABLE_LEVEL);
+
+    ESP_LOGI(TAG, "Create motor encoders");
+    stepper_motor_curve_encoder_config_t accel_encoder_config = {
+        .resolution = STEP_MOTOR_RESOLUTION_HZ,
+        .sample_points = 500,
+        .start_freq_hz = 500,
+        .end_freq_hz = 1500,
+    };
+    rmt_encoder_handle_t accel_motor_encoder = NULL;
+    ESP_ERROR_CHECK(rmt_new_stepper_motor_curve_encoder(&accel_encoder_config, &accel_motor_encoder));
+
+    stepper_motor_uniform_encoder_config_t uniform_encoder_config = {
+        .resolution = STEP_MOTOR_RESOLUTION_HZ,
+    };
+    rmt_encoder_handle_t uniform_motor_encoder = NULL;
+    ESP_ERROR_CHECK(rmt_new_stepper_motor_uniform_encoder(&uniform_encoder_config, &uniform_motor_encoder));
+
+    stepper_motor_curve_encoder_config_t decel_encoder_config = {
+        .resolution = STEP_MOTOR_RESOLUTION_HZ,
+        .sample_points = 500,
+        .start_freq_hz = 1500,
+        .end_freq_hz = 500,
+    };
+    rmt_encoder_handle_t decel_motor_encoder = NULL;
+    ESP_ERROR_CHECK(rmt_new_stepper_motor_curve_encoder(&decel_encoder_config, &decel_motor_encoder));
+
+    ESP_LOGI(TAG, "Enable RMT channel");
+    ESP_ERROR_CHECK(rmt_enable(motor_chan));
+
+    ESP_LOGI(TAG, "Spin motor for 6000 steps: 500 accel + 5000 uniform + 500 decel");
+    rmt_transmit_config_t tx_config = {
+        .loop_count = 0,
+    };
+
+    const static uint32_t accel_samples = 500;
+    const static uint32_t uniform_speed_hz = 1500;
+    const static uint32_t decel_samples = 500;
+
+    while (1) {
+        // acceleration phase
+        tx_config.loop_count = 0;
+        ESP_ERROR_CHECK(rmt_transmit(motor_chan, accel_motor_encoder, &accel_samples, sizeof(accel_samples), &tx_config));
+
+        // uniform phase
+        tx_config.loop_count = 5000;
+        ESP_ERROR_CHECK(rmt_transmit(motor_chan, uniform_motor_encoder, &uniform_speed_hz, sizeof(uniform_speed_hz), &tx_config));
+
+        // deceleration phase
+        tx_config.loop_count = 0;
+        ESP_ERROR_CHECK(rmt_transmit(motor_chan, decel_motor_encoder, &decel_samples, sizeof(decel_samples), &tx_config));
+        // wait all transactions finished
+        ESP_ERROR_CHECK(rmt_tx_wait_all_done(motor_chan, -1));
+
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+}

+ 17 - 0
examples/peripherals/rmt/stepper_motor/pytest_stepper_motor.py

@@ -0,0 +1,17 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: CC0-1.0
+
+import pytest
+from pytest_embedded import Dut
+
+
+@pytest.mark.esp32s3
+@pytest.mark.generic
+def test_ir_nec_example(dut: Dut) -> None:
+    dut.expect_exact('example: Initialize EN + DIR GPIO')
+    dut.expect_exact('example: Create RMT TX channel')
+    dut.expect_exact('example: Set spin direction')
+    dut.expect_exact('example: Enable step motor')
+    dut.expect_exact('example: Create motor encoders')
+    dut.expect_exact('example: Enable RMT channel')
+    dut.expect_exact('example: Spin motor for 6000 steps: 500 accel + 5000 uniform + 500 decel')