这段代码是一个基于ESP32的舵机控制示例,通过MCPWM模块配置定时器、操作符、比较器和发生器,生成特定脉冲宽度的PWM信号,控制舵机在 -60度到60度之间以2度为步长往复转动。
1. 源码部分
#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 -90 // Minimum angle
#define SERVO_MAX_DEGREE 90 // Maximum angle
#define SERVO_PULSE_GPIO 1 // 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
// 这行代码的主要功能是设置MCPWM(Motor Control PWM)发生器在特定定时器事件发生时的动作。
// 具体来说,当定时器向上计数到零(即定时器计数器重置为零)时,发生器的输出电平将被设置为高电平。
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
// 这两行代码的功能是设置MCPWM发生器,当定时器向上计数达到比较器的比较值时,发生器的输出电平将变为低电平。
// 在舵机控制的场景中,这有助于生成特定脉冲宽度的PWM信号,从而精确控制舵机的转动角度。
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)); // 能MCPWM定时器。使能定时器后,定时器会开始按照之前配置的参数进行计数。
// 来控制定时器的启动与停止。这里使用 MCPWM_TIMER_START_NO_STOP 作为参数,意味着定时器启动后不会自动停止,会持续运行。
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;
}
}
以下是对 文件中代码的详细解释:
2. 头文件包含
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/mcpwm_prelude.h"
freertos/FreeRTOS.h
和freertos/task.h
:引入FreeRTOS实时操作系统的相关头文件,用于任务管理和调度。esp_log.h
:引入ESP-IDF的日志功能头文件,用于输出调试信息。driver/mcpwm_prelude.h
:引入MCPWM(Motor Control PWM)驱动的头文件,用于控制舵机所需的PWM信号。
3. 日志标签定义
static const char *TAG = "example";
定义一个日志标签 TAG
,用于在日志输出中标识该代码模块。
4. 舵机参数宏定义
// 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 1 // 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
SERVO_MIN_PULSEWIDTH_US
和SERVO_MAX_PULSEWIDTH_US
:定义舵机控制所需的最小和最大脉冲宽度(单位:微秒)。SERVO_MIN_DEGREE
和SERVO_MAX_DEGREE
:定义舵机的最小和最大转动角度。SERVO_PULSE_GPIO
:定义连接到PWM信号线路的GPIO引脚编号。SERVO_TIMEBASE_RESOLUTION_HZ
:定义PWM定时器的分辨率(1MHz,即每个时钟周期为1微秒)。SERVO_TIMEBASE_PERIOD
:定义PWM定时器的周期(20000个时钟周期,即20ms)。
5. 角度转换函数
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;
}
这是一个内联函数,用于将输入的角度值转换为对应的PWM比较值。通过线性映射的方式,将角度范围 [SERVO_MIN_DEGREE, SERVO_MAX_DEGREE]
映射到脉冲宽度范围 [SERVO_MIN_PULSEWIDTH_US, SERVO_MAX_PULSEWIDTH_US]
。
6. 主函数 app_main
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
// 这行代码的主要功能是设置MCPWM(Motor Control PWM)发生器在特定定时器事件发生时的动作。
// 具体来说,当定时器向上计数到零(即定时器计数器重置为零)时,发生器的输出电平将被设置为高电平。
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
// 这两行代码的功能是设置MCPWM发生器,当定时器向上计数达到比较器的比较值时,发生器的输出电平将变为低电平。
// 在舵机控制的场景中,这有助于生成特定脉冲宽度的PWM信号,从而精确控制舵机的转动角度。
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)); // 使能MCPWM定时器。使能定时器后,定时器会开始按照之前配置的参数进行计数。
// 来控制定时器的启动与停止。这里使用 MCPWM_TIMER_START_NO_STOP 作为参数,意味着定时器启动后不会自动停止,会持续运行。
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;
}
}
6.1 定时器和操作符创建
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)); // 创建操作符,负责管理定时器、比较器和发生器之间的交互
- 创建一个MCPWM定时器,并配置其参数,包括组ID、时钟源、分辨率、周期和计数模式。
- 创建一个MCPWM操作符,并将其与定时器连接到同一个组中。
6.2 连接定时器和操作符
ESP_LOGI(TAG, "Connect timer and operator");
ESP_ERROR_CHECK(mcpwm_operator_connect_timer(oper, timer)); // 将操作符连接到定时器上
将操作符与定时器连接起来,以便操作符可以管理定时器的运行。
6.3 比较器和发生器创建
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)); // 创建发生器
- 创建一个MCPWM比较器,并配置其参数。
- 创建一个MCPWM发生器,并指定其输出GPIO引脚。
6.4 设置比较器初始值
// 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))); // 设置比较器的初始值,这样舵机会旋转到中位位置
将比较器的初始值设置为对应角度为0度的脉冲宽度,使舵机旋转到中位位置。
6.5 设置发生器动作
ESP_LOGI(TAG, "Set generator action on timer and compare event");
// go high on counter empty
// 这行代码的主要功能是设置MCPWM(Motor Control PWM)发生器在特定定时器事件发生时的动作。
// 具体来说,当定时器向上计数到零(即定时器计数器重置为零)时,发生器的输出电平将被设置为高电平。
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
// 这两行代码的功能是设置MCPWM发生器,当定时器向上计数达到比较器的比较值时,发生器的输出电平将变为低电平。
// 在舵机控制的场景中,这有助于生成特定脉冲宽度的PWM信号,从而精确控制舵机的转动角度。
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))); // 设置发生器在比较器阈值时的动作
- 设置发生器在定时器计数到零时输出高电平。
- 设置发生器在定时器计数达到比较器的比较值时输出低电平,从而生成特定脉冲宽度的PWM信号。
6.6 启动定时器
ESP_LOGI(TAG, "Enable and start timer");
ESP_ERROR_CHECK(mcpwm_timer_enable(timer)); // 使能MCPWM定时器。使能定时器后,定时器会开始按照之前配置的参数进行计数。
// 来控制定时器的启动与停止。这里使用 MCPWM_TIMER_START_NO_STOP 作为参数,意味着定时器启动后不会自动停止,会持续运行。
ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
使能并启动MCPWM定时器,使其开始计数。
6.7 循环控制舵机角度
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;
}
- 初始化舵机角度为0度,每次增加2度。
- 在循环中不断更新比较器的比较值,从而改变PWM信号的脉冲宽度,控制舵机的转动角度。
- 每次更新角度后,添加500ms的延迟,以确保舵机有足够的时间转动到指定角度。
- 当角度超过60度或小于 -60度时,改变角度增加的方向。
综上所述,这段代码的主要功能是通过MCPWM模块生成PWM信号,控制舵机在 -60度到60度之间来回转动。