DS5160 SSG Servo Motor (MCPWM)

Gitprodenv
Posts: 12
Joined: Mon Jun 06, 2022 1:32 pm

DS5160 SSG Servo Motor (MCPWM)

Postby Gitprodenv » Sat May 27, 2023 2:07 pm

Hello,

I want to use the DS5160 Servo Motor to control its angle such as in the esp idf mcpwm servo example: https://github.com/espressif/esp-idf/tr ... vo_control:

I use the identical code from above, where I have only changed some parameters (SERVO_MIN_DEGREE SERVO_MAX_DEGREE), as this servo is a 270 degree servo capable motor.
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/mcpwm_prelude.h"

static const char *TAG = "example";

// 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 -135 // Minimum angle
#define SERVO_MAX_DEGREE 135 // Maximum angle

#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_angle_to_compare(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;
}

void app_main(void)
{
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,
};
ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, &timer));

mcpwm_oper_handle_t oper = 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, &oper));

ESP_LOGI(TAG, "Connect timer and operator");
ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper, 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(oper, &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(oper, &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_action_on_timer_event(generator,
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH)));
// go low on compare threshold
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(generator,
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator, MCPWM_GEN_ACTION_LOW)));

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) {
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;
}
}
The issue is, that with this code the servo rotates too quickly in random directions, totally different than when I test this on the SG90, where on every 500ms it rotates by 1 degree.

The datasheet can be seen here: https://m.media-amazon.com/images/I/81EFGw8qkhL.pdf
Some details of the DS5160 SSG:
Operating frequency: 50 - 330Hz
Pulse width range: 500~2500µsec

I think most likely I have to change the waveform, do I?
// 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()));
The DS5160 requires a waveform of: https://ibb.co/wBHhgh1

Thank you for helping me.

MicroController
Posts: 1738
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: DS5160 SSG Servo Motor (MCPWM)

Postby MicroController » Sat May 27, 2023 10:01 pm

Code should be fine.

But your servo requires 6V+ supply, whereas the SG90 is spec'd @ 4.8V+. Are you feeding it enough?

Gitprodenv
Posts: 12
Joined: Mon Jun 06, 2022 1:32 pm

Re: DS5160 SSG Servo Motor (MCPWM)

Postby Gitprodenv » Sun May 28, 2023 7:00 am

@MicroController Yes, I'm using a power adapter (with the servo motor's recommended voltage of 7.4V). Though, this power adapter supports just an amperage up to 3000mA. Could this be an issue, since according to the datasheet at 6V the servo motor requires:
- Idle current(at stopped) 4mA
- Stall current (at locked) 3.5A

Gitprodenv
Posts: 12
Joined: Mon Jun 06, 2022 1:32 pm

Re: DS5160 SSG Servo Motor (MCPWM)

Postby Gitprodenv » Sun May 28, 2023 7:54 am

Looking at the waveform https://ibb.co/wBHhgh1

Does it mean that up to 0.5ms the signal is low, for 0.5ms high and for 19ms low (pwm cycle of 20ms), for a 0 degree?
Does the code for the SG90 (code below) not state, sinal high for 0.5ms and 19.5ms low (pwm cycle of 20ms), for a 0 degree?

I think then the code above

Code: Select all

// 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()));
should be wrong.

MicroController
Posts: 1738
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: DS5160 SSG Servo Motor (MCPWM)

Postby MicroController » Sun May 28, 2023 10:17 am

The waveform is fine. Remember that the PWM signal is repeated every 20ms, so 0.5ms low + 0.5ms high + 19ms low is exactly the same signal as 0.5ms high + 19.5ms low.

The 3A power supply should be plenty for low/normal load operation of the servo. The normal operation current will be significantly less than the specified stall current.

Gitprodenv
Posts: 12
Joined: Mon Jun 06, 2022 1:32 pm

Re: DS5160 SSG Servo Motor (MCPWM)

Postby Gitprodenv » Mon May 29, 2023 8:07 am

@MicroController Thanks, this makes sense

I have this function:

Code: Select all

#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000 // 1MHz, 1us per tick

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,
};
Could it be that the resolution for this servo motor is just wrong (#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000)?

Furthermore, I changed my code, since the DS5160 differs to the ESP IDF demo code, in that way that degree 0 is not 1500us like the SG90, but instead it is 500us.

Code: Select all

// degree 0:    500us
// degree 135: 1500us
// degree 270: 2500us
static inline uint32_t example_angle_to_compare(int degree)
{
  return (500 + (float)degree/270 * 2000);
}

And my updated while loop: (Counting up from 0 to 270 degree at a stepsize of 1, see code explanation below)

Code: Select all

int angle = 0;
int step = 1;
int pwmUS = 0; // the pwm active in micro seconds based on its angle
while (1)
{
   pwmUS = example_degree_to_compare(angle);
   ESP_LOGI(TAG, "Angle of rotation: %d. Micro seconds active (PWM): %d", angle, pwmUS);
   ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, pwmUS));
   
   vTaskDelay(pdMS_TO_TICKS(500));
   if ((((angle + step) > 270) && (step > 0)) || ((angle + step) < 0 && (step < 0)))
   {
      step *= -1;
   }
   angle += step;
}

My full code so far

Code: Select all

// Operating Voltage Range: 6-8.4V
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/mcpwm_prelude.h"

static const char *TAG = "example";

#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_degree_to_compare(int degree)
{
  return (500 + (float)degree/270 * 2000);
}

void app_main(void)
{
  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,
  };
  ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, &timer));

  mcpwm_oper_handle_t oper = 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, &oper));

  ESP_LOGI(TAG, "Connect timer and operator");
  ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper, 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(oper, &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(oper, &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_degree_to_compare(135)));

  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 = 1;
int pwmUS = 0; // the pwm active in micro seconds based on its angle
while (1)
{
   pwmUS = example_degree_to_compare(angle);
   ESP_LOGI(TAG, "Angle of rotation: %d. Micro seconds active (PWM): %d", angle, pwmUS);
   ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparator, pwmUS));
  
   vTaskDelay(pdMS_TO_TICKS(500));
   if ((((angle + step) > 270) && (step > 0)) || ((angle + step) < 0 && (step < 0)))
   {
      step *= -1;
    }
    angle += step;
}    
}
It counts the degree up from 0 to 270 and down again to 0 at a step size of 1. And creates a pwm signal between 500us to 2500us based on the currently used angle. The servo motor should increment its rotation angle by 1 degree for every 500ms. Unfortunately the servo still rotates in a random way.

MicroController
Posts: 1738
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: DS5160 SSG Servo Motor (MCPWM)

Postby MicroController » Mon May 29, 2023 1:29 pm

As I said, the code and PWM output should be correct. There may be some hardware issue preventing the PWM signal to be correctly interpreted by the servo (connection? voltage level? interference?). (#define SERVO_PULSE_GPIO 0 - The servo is actually connected to GPIO 0, right? - Have you tried another GPIO pin?)
Could it be that the resolution for this servo motor is just wrong (#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000)?
No. This is just the "resolution" of the timer, i.e. the minimum step size for the PWM duty cycle. At 1MHz, you can set the PWM "on" time in steps of 1 micro-second, i.e. 1/2000th of the full range.

However, you can try to increase the PWM frequency, as 50 Hz is the minimum allowed. If you change SERVO_TIMEBASE_PERIOD to 10000 you'll get 100 Hz which is still way below the maximum the servo is spec'd for.

MicroController
Posts: 1738
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: DS5160 SSG Servo Motor (MCPWM)

Postby MicroController » Mon May 29, 2023 1:42 pm

Oh, btw, which version of IDF are you using?
It seems that up to v4.4 there was a function mcpwm_gpio_init(...) which had to be called, cf. old example.

Gitprodenv
Posts: 12
Joined: Mon Jun 06, 2022 1:32 pm

Re: DS5160 SSG Servo Motor (MCPWM)

Postby Gitprodenv » Mon May 29, 2023 6:16 pm

I'm using the ESP IDF version 5.0.1.

I have also used another gpio pin (GPIO_NUM_23), but it doesn't make any difference.
I also tried to power the ESP32 with an external 5V power supply instead of the USB port.

Furthermore in this process I also switched back to the ESP Idf demo code version. You were right. Since the neutral position of this servo is 1500us, it's exactly the same code as e.g. for the SG90.

I think most likely there is a problem with interference, tomorrow I will inspect the demo code again. Thank's for your help so far :)

MicroController
Posts: 1738
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: DS5160 SSG Servo Motor (MCPWM)

Postby MicroController » Mon May 29, 2023 6:57 pm

I also tried to power the ESP32 with an external 5V power supply instead of the USB port.
That reminds me: Are the servo's and the ESP's GND connected together?

Who is online

Users browsing this forum: No registered users and 91 guests