esp-idf icon indicating copy to clipboard operation
esp-idf copied to clipboard

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

Open combalafra01 opened this issue 1 year ago • 5 comments

Answers checklist.

  • [X] I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • [X] I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • [X] I have searched the issue tracker for a similar issue and not found a similar issue.

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

combalafra01 avatar Apr 29 '24 11:04 combalafra01

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

suda-morris avatar Apr 30 '24 03:04 suda-morris

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

sdkconfig.txt

combalafra01 avatar Apr 30 '24 06:04 combalafra01

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?

songruo avatar May 06 '24 04:05 songruo

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

combalafra01 avatar May 06 '24 07:05 combalafra01

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.

songruo avatar May 06 '24 08:05 songruo