example: update servo example with new driver API

This commit is contained in:
morris
2022-05-28 17:05:36 +08:00
parent 6751b229f1
commit 1557a533fe
4 changed files with 115 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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