espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
12.7k stars 7.04k forks source link

LEDC PMW on 2 servos impacted by vTaskDelay (IDFGH-12729) #13712

Open combalafra01 opened 1 month ago

combalafra01 commented 1 month ago

Answers checklist.

IDF version.

5.2.1

Espressif SoC revision.

ESP32

Operating System used.

Windows

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

ESP32-DevKitC

Power Supply used.

USB

What is the expected behavior?

2 servos move

What is the actual behavior?

no servo move

Steps to reproduce.

use 2 MOFSET A03000, two servo SG90, one ESP32 on devikitC connect ESP32 PMW GPIO26 to 2 MOFSET source connect ESP32 GPIO27 to first MOFSET grid and ESP32 GPIO14 to a second MOFSET grid connect first servo PWM pin to first MOFSET drain and second servo PWM pin to second MOFSET drain run the code below

include "driver/ledc.h"

include "esp_log.h"

include "freertos/FreeRTOS.h"

include "freertos/task.h"

include <driver/gpio.h>

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 GPIO_NUM_26

define SERVO_DUTY_RESOLUTION LEDC_TIMER_13_BIT

define SERVO_DUTY_MAX 8192

define SERVO_FREQUENCY 50

define SERVO_TIMER LEDC_TIMER_0

define SERVO_CHANNEL LEDC_CHANNEL_0

define SERVO_SPEED_MODE LEDC_LOW_SPEED_MODE

define SERVO1_COMMAND_PIN GPIO_NUM_27

define SERVO2_COMMAND_PIN GPIO_NUM_14

static inline uint32_t angle_to_duty(int angle) { return ((angle - SERVO_MIN_DEGREE) (SERVO_MAX_PULSEWIDTH_US - SERVO_MIN_PULSEWIDTH_US) / (SERVO_MAX_DEGREE - SERVO_MIN_DEGREE) + SERVO_MIN_PULSEWIDTH_US) SERVO_FREQUENCY * SERVO_DUTY_MAX / 1000000; }

static void init_ledc() { ledc_timer_config_t ledc_timer = {}; ledc_timer.speed_mode = SERVO_SPEED_MODE; ledc_timer.timer_num = SERVO_TIMER; ledc_timer.duty_resolution = SERVO_DUTY_RESOLUTION; ledc_timer.freq_hz = SERVO_FREQUENCY; ledc_timer.clk_cfg = LEDC_AUTO_CLK; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

ledc_channel_config_t ledc_channel = {}; ledc_channel.speed_mode = SERVO_SPEED_MODE; ledc_channel.channel = SERVO_CHANNEL; ledc_channel.timer_sel = SERVO_TIMER; ledc_channel.intr_type = LEDC_INTR_DISABLE; ledc_channel.gpio_num = SERVO_PULSE_GPIO; ledc_channel.duty = 0; ledc_channel.hpoint = 0; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); }

void app_main(void) {

gpio_reset_pin(SERVO1_COMMAND_PIN); gpio_set_direction(SERVO1_COMMAND_PIN, GPIO_MODE_OUTPUT); gpio_reset_pin(SERVO2_COMMAND_PIN); gpio_set_direction(SERVO2_COMMAND_PIN, GPIO_MODE_OUTPUT);

init_ledc();

gpio_set_level(SERVO1_COMMAND_PIN, 1); gpio_set_level(SERVO2_COMMAND_PIN, 1); ESP_ERROR_CHECK(ledc_set_duty(SERVO_SPEED_MODE, SERVO_CHANNEL, angle_to_duty(0))); ESP_ERROR_CHECK(ledc_update_duty(SERVO_SPEED_MODE, SERVO_CHANNEL)); vTaskDelay(pdMS_TO_TICKS(1000)); gpio_set_level(SERVO1_COMMAND_PIN, 0); gpio_set_level(SERVO2_COMMAND_PIN, 0);

int angle1 = 0; int angle2 = 0; int step = 2; int timeToWait = 20;

while (angle1 < 90) { gpio_set_level(SERVO1_COMMAND_PIN, 1); ESP_ERROR_CHECK(ledc_set_duty(SERVO_SPEED_MODE, SERVO_CHANNEL, angle_to_duty(angle1))); ESP_ERROR_CHECK(ledc_update_duty(SERVO_SPEED_MODE, SERVO_CHANNEL)); vTaskDelay(pdMS_TO_TICKS(timeToWait)); gpio_set_level(SERVO1_COMMAND_PIN, 0);

gpio_set_level(SERVO2_COMMAND_PIN, 1);
ESP_ERROR_CHECK(ledc_set_duty(SERVO_SPEED_MODE, SERVO_CHANNEL, angle_to_duty(angle2)));
ESP_ERROR_CHECK(ledc_update_duty(SERVO_SPEED_MODE, SERVO_CHANNEL));
vTaskDelay(pdMS_TO_TICKS(timeToWait));
gpio_set_level(SERVO2_COMMAND_PIN, 0);

angle1 += step;
angle2 -= step;

}

vTaskDelay(pdMS_TO_TICKS(5000));

while (angle1 > 0) { gpio_set_level(SERVO1_COMMAND_PIN, 1); ESP_ERROR_CHECK(ledc_set_duty(SERVO_SPEED_MODE, SERVO_CHANNEL, angle_to_duty(angle1))); ESP_ERROR_CHECK(ledc_update_duty(SERVO_SPEED_MODE, SERVO_CHANNEL)); vTaskDelay(pdMS_TO_TICKS(timeToWait)); gpio_set_level(SERVO1_COMMAND_PIN, 0);

gpio_set_level(SERVO2_COMMAND_PIN, 1);
ESP_ERROR_CHECK(ledc_set_duty(SERVO_SPEED_MODE, SERVO_CHANNEL, angle_to_duty(angle2)));
ESP_ERROR_CHECK(ledc_update_duty(SERVO_SPEED_MODE, SERVO_CHANNEL));
vTaskDelay(pdMS_TO_TICKS(timeToWait));
gpio_set_level(SERVO2_COMMAND_PIN, 0);

angle1 -= step;
angle2 += step;

} }

the first while loop works perfectly, the 2 servos move the second while loop does nothing, no servo move if I remove the line vTaskDelay(pdMS_TO_TICKS(5000)); the second while loop work perfectly, the 2 servos move. if I reduce the delay duration from 5000ms to 200ms the second while loop work perfectly, the 2 servos move. if I control only one servo there is no problem, the servo moves after the 5000ms delay

Debug Logs.

No response

More Information.

No response

suda-morris commented 1 month ago

Have you enabled CONFIG_PM_ENABLE? Please also attach your sdkconfig.h

combalafra01 commented 1 month ago

Hello, CONFIG_PM_ENABLE is not activated, I do not use Power Management. I have attached a TXT file of my sdkconfig

sdkconfig.txt

songruo commented 1 month ago

Hi @combalafra01,

I checked the PWM IO (without any load) using logic analyzer, it outputs the signal fine.

I think your usage of PWM to the servos is strange. You are using SERVOx_COMMAND_PIN to control the PWM signal into the servo. You set the PWM frequency to be 50Hz, however, to the servo, the PWM frequency becomes 25 Hz (when the other servo is receiving the PWM, this servo is always receiving low level input), and the duty cycle is not what you desired.

I don't see the point why you would need to use MOSFET? If you don't need 5V PWM to the servos, why not directly connect two different LEDC PWM signals to the two servos?

combalafra01 commented 1 month ago

I want to control 8 servo using a 74HC165 in order to use only 3 gpio and I want them to work at the same time (at least for the eye) one for PWM signal sent to all servos, two to control 74HC165 so the idea was to send the PWM signal to all the MOSFET source and control its grid by the 74HC165 i guess this is not the best idea since I kind of see you point concerning the duty cycle for the moment I have decided to use 4 PWM gpio to control only 4 servos ... if you have a good solution for what I want to do I would be please to hear it otherwise I think we can close this issue.

thank you

songruo commented 1 month ago

For your initial code with two servos, try to set SERVO_FREQUENCY to 100Hz (so that the frequency to the servo is 50Hz), and adjust the duty cycle accordingly to give it a try. If it works, then add two more servos and test again. However, for 8 servos, the maximum duty cycle you can give to each servo is only 12.5%, which I don't think it meets your angle requirement?

Overall, it seems to me that the degree of freedom is not enough, maybe at least 4 IOs in total, you can do more stuffs.