/** * @file servo.c * @brief 180° PWM 舵机控制实现 (LEDC, 50Hz, 0.5~2.5ms) */ #include "servo.h" #include "driver/ledc.h" #include "esp_log.h" #define SERVO_FREQ_HZ 50 #define SERVO_TIMER LEDC_TIMER_0 #define SERVO_MODE LEDC_LOW_SPEED_MODE #define SERVO_DUTY_RES LEDC_TIMER_16_BIT /* 0~65535 */ #define SERVO_MAX_DUTY 65535 /* 脉宽范围 (微秒) */ #define PULSE_MIN_US 500 #define PULSE_MAX_US 2500 #define PERIOD_US 20000 /* 50Hz = 20ms */ static const char *TAG = "SERVO"; static ledc_channel_t s_channel; static float s_limit_up = 180.0f; static float s_limit_down = 0.0f; /* 角度 → 占空比 */ static uint32_t angle_to_duty(float angle) { if (angle < 0.0f) angle = 0.0f; if (angle > 180.0f) angle = 180.0f; float pulse_us = PULSE_MIN_US + (angle / 180.0f) * (PULSE_MAX_US - PULSE_MIN_US); return (uint32_t)((pulse_us / PERIOD_US) * SERVO_MAX_DUTY); } void servo_init(int gpio) { /* 分配 LEDC 通道 */ static int chan_idx = 0; s_channel = (chan_idx++ == 0) ? LEDC_CHANNEL_0 : LEDC_CHANNEL_1; ledc_timer_config_t timer_cfg = { .speed_mode = SERVO_MODE, .duty_resolution = SERVO_DUTY_RES, .timer_num = SERVO_TIMER, .freq_hz = SERVO_FREQ_HZ, .clk_cfg = LEDC_AUTO_CLK, }; ESP_ERROR_CHECK(ledc_timer_config(&timer_cfg)); ledc_channel_config_t chan_cfg = { .gpio_num = gpio, .speed_mode = SERVO_MODE, .channel = s_channel, .intr_type = LEDC_INTR_DISABLE, .timer_sel = SERVO_TIMER, .duty = angle_to_duty(0), /* 初始: 0° */ .hpoint = 0, .flags.output_invert = 0, }; ESP_ERROR_CHECK(ledc_channel_config(&chan_cfg)); ESP_LOGI(TAG, "初始化完成 (GPIO%d, 50Hz, 通道%d)", gpio, s_channel); } void servo_set_angle(float angle) { uint32_t duty = angle_to_duty(angle); ESP_ERROR_CHECK(ledc_set_duty(SERVO_MODE, s_channel, duty)); ESP_ERROR_CHECK(ledc_update_duty(SERVO_MODE, s_channel)); ESP_LOGD(TAG, "角度: %.1f° (duty=%lu)", angle, duty); } void servo_set_up(void) { servo_set_angle(s_limit_up); } void servo_set_down(void) { servo_set_angle(s_limit_down); } void servo_set_limits(float up, float down) { s_limit_up = up; s_limit_down = down; ESP_LOGI(TAG, "限位: 上=%.0f° 下=%.0f°", up, down); }