diff --git a/examples/peripherals/mcpwm/mcpwm_servo_control/README.md b/examples/peripherals/mcpwm/mcpwm_servo_control/README.md index cc174b1b1f..c25d811e51 100644 --- a/examples/peripherals/mcpwm/mcpwm_servo_control/README.md +++ b/examples/peripherals/mcpwm/mcpwm_servo_control/README.md @@ -4,7 +4,7 @@ (See the README.md file in the upper level 'examples' directory for more information about examples.) -This example illustrates how to drive a typical [RC Servo](https://en.wikipedia.org/wiki/Servo_%28radio_control%29) by sending a PWM signal using the MCPWM driver. The PWM pulse has a frequency of 50Hz (period of 20ms), and the active-high time (which controls the rotation) ranges from 1ms to 2ms with 1.5ms always being center of range. +This example illustrates how to drive a typical [RC Servo](https://en.wikipedia.org/wiki/Servo_%28radio_control%29) by sending a PWM signal using the MCPWM driver. The PWM pulse has a frequency of 50Hz (period of 20ms), and the active-high time (which controls the rotation) ranges from 0.5s to 2.5ms with 1.5ms always being center of range. ## How to Use Example @@ -17,15 +17,16 @@ This example illustrates how to drive a typical [RC Servo](https://en.wikipedia. Connection : ``` -+-------+ +-----------------+ -| | | | -| +-+ GPIO18++ PWM(Orange) +----------+ | -| ESP |---5V------+ Vcc(Red) +--------------| Servo Motor | -| +---------+ GND(Brown) +----------+ | -| | | | -+-------+ +-----------------+ + ESP Board Servo Motor 5V ++-------------------+ +---------------+ ^ +| SERVO_PULSE_GPIO +-----+PWM VCC +----+ +| | | | +| GND +-----+GND | ++-------------------+ +---------------+ ``` +Note that, some kind of servo might need a higher current supply than the development board usually can provide. It's recommended to power the servo separately. + ### Build and Flash Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. @@ -42,11 +43,22 @@ Run the example, you will see the following output log: ``` ... I (0) cpu_start: Starting scheduler on APP CPU. -I (349) example: Angle of rotation: -90 -I (449) example: Angle of rotation: -89 -I (549) example: Angle of rotation: -88 -I (649) example: Angle of rotation: -87 -I (749) example: Angle of rotation: -86 +I (305) example: Create timer and operator +I (305) example: Connect timer and operator +I (305) example: Create comparator and generator from the operator +I (315) gpio: GPIO[44]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (325) example: Set generator action on timer and compare event +I (335) example: Enable and start timer +I (335) example: Angle of rotation: 0 +I (1345) example: Angle of rotation: 2 +I (2345) example: Angle of rotation: 4 +I (3345) example: Angle of rotation: 6 +I (4345) example: Angle of rotation: 8 +I (5345) example: Angle of rotation: 10 +I (6345) example: Angle of rotation: 12 +I (7345) example: Angle of rotation: 14 +I (8345) example: Angle of rotation: 16 +I (9345) example: Angle of rotation: 18 ... ``` @@ -54,6 +66,4 @@ The servo will rotate from -90 degree to 90 degree, and then turn back again. ## Troubleshooting -Note that, some kind of servo might need a higher current supply than the development board usually can provide. It's recommended to power the servo separately. - 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/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c b/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c index 0c11a6db84..c905eeafc8 100644 --- a/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c +++ b/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c @@ -1,47 +1,93 @@ -/* Servo Motor control example +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" -#include "driver/mcpwm.h" +#include "driver/mcpwm_prelude.h" static const char *TAG = "example"; -// You can get these value from the datasheet of servo you use, in general pulse width varies between 1000 to 2000 mocrosecond -#define SERVO_MIN_PULSEWIDTH_US (1000) // Minimum pulse width in microsecond -#define SERVO_MAX_PULSEWIDTH_US (2000) // Maximum pulse width in microsecond -#define SERVO_MAX_DEGREE (90) // Maximum angle in degree upto which servo can rotate +// Please consult the datasheet of your servo before changing the following parameters +#define SERVO_MIN_PULSEWIDTH_US 500 // Minimum pulse width in microsecond +#define SERVO_MAX_PULSEWIDTH_US 2500 // Maximum pulse width in microsecond +#define SERVO_MIN_DEGREE -90 // Minimum angle +#define SERVO_MAX_DEGREE 90 // Maximum angle -#define SERVO_PULSE_GPIO (18) // GPIO connects to the PWM signal line +#define SERVO_PULSE_GPIO 0 // GPIO connects to the PWM signal line +#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000 // 1MHz, 1us per tick +#define SERVO_TIMEBASE_PERIOD 20000 // 20000 ticks, 20ms -static inline uint32_t example_convert_servo_angle_to_duty_us(int angle) +static inline uint32_t example_angle_to_compare(int angle) { - return (angle + SERVO_MAX_DEGREE) * (SERVO_MAX_PULSEWIDTH_US - SERVO_MIN_PULSEWIDTH_US) / (2 * SERVO_MAX_DEGREE) + SERVO_MIN_PULSEWIDTH_US; + return (angle - SERVO_MIN_DEGREE) * (SERVO_MAX_PULSEWIDTH_US - SERVO_MIN_PULSEWIDTH_US) / (SERVO_MAX_DEGREE - SERVO_MIN_DEGREE) + SERVO_MIN_PULSEWIDTH_US; } void app_main(void) { - mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, SERVO_PULSE_GPIO); // To drive a RC servo, one MCPWM generator is enough - - mcpwm_config_t pwm_config = { - .frequency = 50, // frequency = 50Hz, i.e. for every servo motor time period should be 20ms - .cmpr_a = 0, // duty cycle of PWMxA = 0 - .counter_mode = MCPWM_UP_COUNTER, - .duty_mode = MCPWM_DUTY_MODE_0, + ESP_LOGI(TAG, "Create timer and operator"); + mcpwm_timer_handle_t timer = NULL; + mcpwm_timer_config_t timer_config = { + .group_id = 0, + .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT, + .resolution_hz = SERVO_TIMEBASE_RESOLUTION_HZ, + .period_ticks = SERVO_TIMEBASE_PERIOD, + .count_mode = MCPWM_TIMER_COUNT_MODE_UP, }; - mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); + ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, &timer)); + mcpwm_oper_handle_t operator = NULL; + mcpwm_operator_config_t operator_config = { + .group_id = 0, // operator must be in the same group to the timer + }; + ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config, &operator)); + + ESP_LOGI(TAG, "Connect timer and operator"); + ESP_ERROR_CHECK(mcpwm_operator_connect_timer(operator, timer)); + + ESP_LOGI(TAG, "Create comparator and generator from the operator"); + mcpwm_cmpr_handle_t comparator = NULL; + mcpwm_comparator_config_t comparator_config = { + .flags.update_cmp_on_tez = true, + }; + ESP_ERROR_CHECK(mcpwm_new_comparator(operator, &comparator_config, &comparator)); + + mcpwm_gen_handle_t generator = NULL; + mcpwm_generator_config_t generator_config = { + .gen_gpio_num = SERVO_PULSE_GPIO, + }; + ESP_ERROR_CHECK(mcpwm_new_generator(operator, &generator_config, &generator)); + + // set the initial compare value, so that the servo will spin to the center position + ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, example_angle_to_compare(0))); + + ESP_LOGI(TAG, "Set generator action on timer and compare event"); + // go high on counter empty + ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_timer_event(generator, + MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH), + MCPWM_GEN_TIMER_EVENT_ACTION_END())); + // go low on compare threshold + ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(generator, + MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator, MCPWM_GEN_ACTION_LOW), + MCPWM_GEN_COMPARE_EVENT_ACTION_END())); + + ESP_LOGI(TAG, "Enable and start timer"); + ESP_ERROR_CHECK(mcpwm_timer_enable(timer)); + ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP)); + + int angle = 0; + int step = 2; while (1) { - for (int angle = -SERVO_MAX_DEGREE; angle < SERVO_MAX_DEGREE; angle++) { - ESP_LOGI(TAG, "Angle of rotation: %d", angle); - ESP_ERROR_CHECK(mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, example_convert_servo_angle_to_duty_us(angle))); - vTaskDelay(pdMS_TO_TICKS(100)); //Add delay, since it takes time for servo to rotate, generally 100ms/60degree rotation under 5V power supply + ESP_LOGI(TAG, "Angle of rotation: %d", angle); + ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, example_angle_to_compare(angle))); + //Add delay, since it takes time for servo to rotate, usually 200ms/60degree rotation under 5V power supply + vTaskDelay(pdMS_TO_TICKS(500)); + if ((angle + step) > 60 || (angle + step) < -60) { + step *= -1; } + angle += step; } } diff --git a/examples/peripherals/mcpwm/mcpwm_servo_control/pytest_servo_mg996r.py b/examples/peripherals/mcpwm/mcpwm_servo_control/pytest_servo_mg996r.py new file mode 100644 index 0000000000..d22aa363d5 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_servo_control/pytest_servo_mg996r.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.esp32 +@pytest.mark.esp32s3 +@pytest.mark.generic +def test_servo_mg996r_example(dut: Dut) -> None: + dut.expect_exact('example: Create timer and operator') + dut.expect_exact('example: Connect timer and operator') + dut.expect_exact('example: Create comparator and generator from the operator') + dut.expect_exact('example: Set generator action on timer and compare event') + dut.expect_exact('example: Enable and start timer') + dut.expect_exact('example: Angle of rotation: 0') diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index e13723ab90..40f6d52873 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1773,7 +1773,6 @@ examples/peripherals/i2c/i2c_tools/main/cmd_i2ctools.h examples/peripherals/i2c/i2c_tools/main/i2ctools_example_main.c examples/peripherals/ledc/ledc_basic/main/ledc_basic_example_main.c examples/peripherals/ledc/ledc_fade/main/ledc_fade_example_main.c -examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c examples/peripherals/sdio/host/main/app_main.c examples/peripherals/sdio/sdio_test.py examples/peripherals/sdio/slave/main/app_main.c