-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updated MCPWM Implementation for servo control to support ESP-IDF 5.0 (#90) #94
base: main
Are you sure you want to change the base?
Changes from 6 commits
e668d21
5505b86
47fee7a
ad77332
70fa202
98e2a57
c15ace8
4dbfa51
3606958
994cef4
a3144e5
cc7b4ea
a763759
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a lot of repetitive logic in this code. Can’t we just create an object of the servo for three different GPIOs? Hardcoding this seems unnecessary. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,58 +26,161 @@ | |
|
||
static const char *TAG_SERVO = "servo"; | ||
static int enabled_servo_flag = 0; | ||
#define STR(A) #A | ||
|
||
#define SERVO_TIMEBASE_RESOLUTION_HZ 1000000 // 1MHz, 1us per tick | ||
#define SERVO_TIMEBASE_PERIOD 20000 // 20000 ticks, 20ms | ||
|
||
mcpwm_cmpr_handle_t comparator_0= NULL; | ||
mcpwm_cmpr_handle_t comparator_1= NULL; | ||
|
||
esp_err_t enable_servo() | ||
{ | ||
esp_err_t err; | ||
CHECK_LOGE(err, mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, SERVO_A), TAG_SERVO, "error: servo A: %s", esp_err_to_name(err)); | ||
CHECK_LOGE(err, mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, SERVO_B), TAG_SERVO, "error: servo B: %s", esp_err_to_name(err)); | ||
CHECK_LOGE(err, mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, SERVO_C), TAG_SERVO, "error: servo C: %s", esp_err_to_name(err)); | ||
CHECK_LOGE(err, mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, SERVO_D), TAG_SERVO, "error: servo D: %s", esp_err_to_name(err)); | ||
|
||
mcpwm_config_t pwm_config; | ||
// sets the pwm frequency = 50 | ||
pwm_config.frequency = 50; | ||
// sets the initial duty cycle of PWMxA = 0 | ||
pwm_config.cmpr_a = 0; | ||
// sets the initial duty cycle of PWMxB = 0 | ||
pwm_config.cmpr_b = 0; | ||
// sets the pwm counter mode | ||
pwm_config.counter_mode = MCPWM_UP_COUNTER; | ||
// sets the pwm duty mode | ||
pwm_config.duty_mode = MCPWM_DUTY_MODE_0; | ||
|
||
// init pwm 0a, 1a, 2a with the above settings | ||
|
||
esp_err_t err_A = mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); | ||
esp_err_t err_B = mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); | ||
|
||
if (err_A == ESP_OK && err_B == ESP_OK) | ||
{ | ||
enabled_servo_flag = 1; | ||
ESP_LOGI(TAG_SERVO, "enabled servos"); | ||
ESP_LOGI(TAG_SERVO, "Create timer and operator for servos A and B"); | ||
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)); | ||
|
||
return ESP_OK; | ||
} | ||
else | ||
{ | ||
enabled_servo_flag = 0; | ||
return ESP_FAIL; | ||
} | ||
mcpwm_oper_handle_t oper = NULL; | ||
mcpwm_operator_config_t operator_config = { | ||
.group_id = 0, // operator must be in the same group as the timer | ||
}; | ||
ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config, &oper)); | ||
|
||
ESP_LOGI(TAG_SERVO, "Connect timer and operator for servos A and B"); | ||
ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper, timer)); | ||
|
||
ESP_LOGI(TAG_SERVO, "Create comparator_0 and generator from the operator for servos A and B"); | ||
|
||
mcpwm_comparator_config_t comparator_config = { | ||
.flags.update_cmp_on_tez = true, | ||
}; | ||
ESP_ERROR_CHECK(mcpwm_new_comparator(oper, &comparator_config, &comparator_0)); | ||
|
||
mcpwm_gen_handle_t generator_a = NULL; | ||
mcpwm_generator_config_t generator_config_a = { | ||
.gen_gpio_num = SERVO_A, | ||
}; | ||
ESP_ERROR_CHECK(mcpwm_new_generator(oper, &generator_config_a, &generator_a)); | ||
|
||
// Similarly, create generator B for SERVO_B | ||
mcpwm_gen_handle_t generator_b = NULL; | ||
mcpwm_generator_config_t generator_config_b = { | ||
.gen_gpio_num = SERVO_B, | ||
}; | ||
ESP_ERROR_CHECK(mcpwm_new_generator(oper, &generator_config_b, &generator_b)); | ||
|
||
// Set actions for generator A and B | ||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(generator_a, | ||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH))); | ||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(generator_a, | ||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator_0, MCPWM_GEN_ACTION_LOW))); | ||
|
||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(generator_b, | ||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH))); | ||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(generator_b, | ||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator_0, MCPWM_GEN_ACTION_LOW))); | ||
|
||
|
||
// Now, create a new timer, operator, and generators for servos C and D | ||
ESP_LOGI(TAG_SERVO, "Create timer and operator for servos C and D"); | ||
mcpwm_timer_handle_t timer_1 = NULL; | ||
mcpwm_timer_config_t timer_config_1 = { | ||
.group_id = 1, | ||
.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_1, &timer_1)); | ||
|
||
mcpwm_oper_handle_t oper_1 = NULL; | ||
mcpwm_operator_config_t operator_config_1 = { | ||
.group_id = 1, // operator must be in the same group as the timer | ||
}; | ||
ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config_1, &oper_1)); | ||
|
||
ESP_LOGI(TAG_SERVO, "Connect timer and operator for servos C and D"); | ||
ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper_1, timer_1)); | ||
|
||
ESP_LOGI(TAG_SERVO, "Create comparator and generator from the operator for servos C and D"); | ||
|
||
mcpwm_comparator_config_t comparator_config_1 = { | ||
.flags.update_cmp_on_tez = true, | ||
}; | ||
ESP_ERROR_CHECK(mcpwm_new_comparator(oper_1, &comparator_config_1, &comparator_1)); | ||
|
||
|
||
// Similarly, create generator C for SERVO_C | ||
mcpwm_gen_handle_t generator_c = NULL; | ||
mcpwm_generator_config_t generator_config_c = { | ||
.gen_gpio_num = SERVO_C, | ||
}; | ||
|
||
ESP_ERROR_CHECK(mcpwm_new_generator(oper_1, &generator_config_c, &generator_c)); | ||
|
||
// Similarly, create generator D for SERVO_D | ||
mcpwm_gen_handle_t generator_d = NULL; | ||
mcpwm_generator_config_t generator_config_d = { | ||
.gen_gpio_num = SERVO_D, | ||
}; | ||
ESP_ERROR_CHECK(mcpwm_new_generator(oper_1, &generator_config_d, &generator_d)); | ||
|
||
// Set actions for generator C and D | ||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(generator_c, | ||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH))); | ||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(generator_c, | ||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator_1, MCPWM_GEN_ACTION_LOW))); | ||
|
||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(generator_d, | ||
MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH))); | ||
ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(generator_d, | ||
MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator_1, MCPWM_GEN_ACTION_LOW))); | ||
|
||
|
||
// Enable and start both timers | ||
ESP_LOGI(TAG_SERVO, "Enable and start timers"); | ||
ESP_ERROR_CHECK(mcpwm_timer_enable(timer)); | ||
ESP_ERROR_CHECK(mcpwm_timer_enable(timer_1)); | ||
|
||
ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP)); | ||
ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer_1, MCPWM_TIMER_START_NO_STOP)); | ||
|
||
// Set the flag to indicate that servos are enabled | ||
enabled_servo_flag = 1; | ||
|
||
return ESP_OK; | ||
} | ||
|
||
static esp_err_t set_angle_servo_helper(int servo_pin, int servo_max, int servo_min_pulsewidth, int servo_max_pulsewidth, unsigned int degree_of_rotation, mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_generator_t gen) | ||
static esp_err_t set_angle_servo_helper(int servo_pin, int servo_max, int servo_min_pulsewidth, int servo_max_pulsewidth, int cmp_num, unsigned int degree_of_rotation) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cmp_num is used and just 0 or 1 is the value, then how will it identify which out of 4 motors are going to turn? |
||
{ | ||
degree_of_rotation = degree_of_rotation > servo_max ? servo_max : degree_of_rotation; | ||
|
||
uint32_t cal_pulsewidth = 0; | ||
cal_pulsewidth = (servo_min_pulsewidth + ((servo_max_pulsewidth - servo_min_pulsewidth) * (degree_of_rotation)) / (servo_max)); | ||
|
||
esp_err_t err = mcpwm_set_duty_in_us(mcpwm_num, timer_num, gen, cal_pulsewidth); | ||
|
||
|
||
esp_err_t err; | ||
if (cmp_num == 0) | ||
purviyeshi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
err = mcpwm_comparator_set_compare_value(comparator_0, cal_pulsewidth); | ||
} | ||
else | ||
{ | ||
err = mcpwm_comparator_set_compare_value(comparator_1, cal_pulsewidth); | ||
} | ||
|
||
|
||
|
||
if (err == ESP_OK) | ||
{ | ||
ESP_LOGI(TAG_SERVO, "set servo at pin %d: %ud", servo_pin, degree_of_rotation); | ||
ESP_LOGI(TAG_SERVO, "set servo at pin %d: %d", servo_pin, degree_of_rotation); | ||
} | ||
else | ||
{ | ||
|
@@ -94,7 +197,7 @@ esp_err_t set_angle_servo(servo_config *config, unsigned int degree_of_rotation) | |
if (config->servo_pin) | ||
{ | ||
config->angle = degree_of_rotation; | ||
return set_angle_servo_helper(config->servo_pin, config->max_degree, config->min_pulse_width, config->max_pulse_width, degree_of_rotation, config->mcpwm_num, config->timer_num, config->gen); | ||
return set_angle_servo_helper(config->servo_pin, config->max_degree, config->min_pulse_width, config->max_pulse_width, config->cmp_num, degree_of_rotation); | ||
} | ||
else | ||
{ | ||
|
@@ -108,8 +211,3 @@ esp_err_t set_angle_servo(servo_config *config, unsigned int degree_of_rotation) | |
return ESP_FAIL; | ||
} | ||
} | ||
|
||
int read_servo(servo_config *config) | ||
{ | ||
return config->angle; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix these types of newline diffs. They are unnecessary.