From 0e19bc146314be8e8d674bf7b5a770b0336d4cbb Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 7 Apr 2022 13:14:01 +0800 Subject: [PATCH] example: update stepper motor example with new rmt driver --- examples/peripherals/rmt/step_motor/README.md | 92 ----- .../components/step_motor/CMakeLists.txt | 10 - .../step_motor/include/step_motor.h | 134 -------- .../step_motor/include/step_motor_driver_io.h | 75 ---- .../include/step_motor_driver_io_a4988.h | 49 --- .../components/step_motor/src/step_motor.c | 32 -- .../src/step_motor_driver_io_a4988.c | 174 ---------- .../step_motor/src/step_motor_rmt.c | 325 ------------------ .../rmt/step_motor/main/CMakeLists.txt | 2 - .../rmt/step_motor/main/step_motor_main.c | 92 ----- .../CMakeLists.txt | 0 .../peripherals/rmt/stepper_motor/README.md | 79 +++++ .../rmt/stepper_motor/main/CMakeLists.txt | 2 + .../main/stepper_motor_encoder.c | 181 ++++++++++ .../main/stepper_motor_encoder.h | 58 ++++ .../main/stepper_motor_example_main.c | 106 ++++++ .../rmt/stepper_motor/pytest_stepper_motor.py | 17 + 17 files changed, 443 insertions(+), 985 deletions(-) delete mode 100644 examples/peripherals/rmt/step_motor/README.md delete mode 100644 examples/peripherals/rmt/step_motor/components/step_motor/CMakeLists.txt delete mode 100644 examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor.h delete mode 100644 examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io.h delete mode 100644 examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io_a4988.h delete mode 100644 examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor.c delete mode 100644 examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_driver_io_a4988.c delete mode 100644 examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_rmt.c delete mode 100644 examples/peripherals/rmt/step_motor/main/CMakeLists.txt delete mode 100644 examples/peripherals/rmt/step_motor/main/step_motor_main.c rename examples/peripherals/rmt/{step_motor => stepper_motor}/CMakeLists.txt (100%) create mode 100644 examples/peripherals/rmt/stepper_motor/README.md create mode 100644 examples/peripherals/rmt/stepper_motor/main/CMakeLists.txt create mode 100644 examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.c create mode 100644 examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.h create mode 100644 examples/peripherals/rmt/stepper_motor/main/stepper_motor_example_main.c create mode 100644 examples/peripherals/rmt/stepper_motor/pytest_stepper_motor.py diff --git a/examples/peripherals/rmt/step_motor/README.md b/examples/peripherals/rmt/step_motor/README.md deleted file mode 100644 index e5c98ffccd..0000000000 --- a/examples/peripherals/rmt/step_motor/README.md +++ /dev/null @@ -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. diff --git a/examples/peripherals/rmt/step_motor/components/step_motor/CMakeLists.txt b/examples/peripherals/rmt/step_motor/components/step_motor/CMakeLists.txt deleted file mode 100644 index 5e28a285f3..0000000000 --- a/examples/peripherals/rmt/step_motor/components/step_motor/CMakeLists.txt +++ /dev/null @@ -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 "") diff --git a/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor.h b/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor.h deleted file mode 100644 index d4149b45b9..0000000000 --- a/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#pragma once - -#include -#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 diff --git a/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io.h b/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io.h deleted file mode 100644 index 800029f644..0000000000 --- a/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io.h +++ /dev/null @@ -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 diff --git a/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io_a4988.h b/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io_a4988.h deleted file mode 100644 index e61cad187d..0000000000 --- a/examples/peripherals/rmt/step_motor/components/step_motor/include/step_motor_driver_io_a4988.h +++ /dev/null @@ -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 diff --git a/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor.c b/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor.c deleted file mode 100644 index 8e9e8e7826..0000000000 --- a/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor.c +++ /dev/null @@ -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); -} diff --git a/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_driver_io_a4988.c b/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_driver_io_a4988.c deleted file mode 100644 index e106671f1a..0000000000 --- a/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_driver_io_a4988.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -#include -#include -#include -#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; -} diff --git a/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_rmt.c b/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_rmt.c deleted file mode 100644 index b78a98a6ab..0000000000 --- a/examples/peripherals/rmt/step_motor/components/step_motor/src/step_motor_rmt.c +++ /dev/null @@ -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 -#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; -} diff --git a/examples/peripherals/rmt/step_motor/main/CMakeLists.txt b/examples/peripherals/rmt/step_motor/main/CMakeLists.txt deleted file mode 100644 index 1cb079a34f..0000000000 --- a/examples/peripherals/rmt/step_motor/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "step_motor_main.c" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/rmt/step_motor/main/step_motor_main.c b/examples/peripherals/rmt/step_motor/main/step_motor_main.c deleted file mode 100644 index 2c18716529..0000000000 --- a/examples/peripherals/rmt/step_motor/main/step_motor_main.c +++ /dev/null @@ -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)); -} diff --git a/examples/peripherals/rmt/step_motor/CMakeLists.txt b/examples/peripherals/rmt/stepper_motor/CMakeLists.txt similarity index 100% rename from examples/peripherals/rmt/step_motor/CMakeLists.txt rename to examples/peripherals/rmt/stepper_motor/CMakeLists.txt diff --git a/examples/peripherals/rmt/stepper_motor/README.md b/examples/peripherals/rmt/stepper_motor/README.md new file mode 100644 index 0000000000..cc66f2500a --- /dev/null +++ b/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. diff --git a/examples/peripherals/rmt/stepper_motor/main/CMakeLists.txt b/examples/peripherals/rmt/stepper_motor/main/CMakeLists.txt new file mode 100644 index 0000000000..ca5fa8da19 --- /dev/null +++ b/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 ".") diff --git a/examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.c b/examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.c new file mode 100644 index 0000000000..b125b59a31 --- /dev/null +++ b/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(©_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(©_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; +} diff --git a/examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.h b/examples/peripherals/rmt/stepper_motor/main/stepper_motor_encoder.h new file mode 100644 index 0000000000..915d6b5e6f --- /dev/null +++ b/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 +#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 diff --git a/examples/peripherals/rmt/stepper_motor/main/stepper_motor_example_main.c b/examples/peripherals/rmt/stepper_motor/main/stepper_motor_example_main.c new file mode 100644 index 0000000000..f25051ba2e --- /dev/null +++ b/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)); + } +} diff --git a/examples/peripherals/rmt/stepper_motor/pytest_stepper_motor.py b/examples/peripherals/rmt/stepper_motor/pytest_stepper_motor.py new file mode 100644 index 0000000000..d290fb17b9 --- /dev/null +++ b/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')