/* PWM 的 ESP-IDF 实现(LEDC 低速组)。本项目只有风扇一路。 */ #include "driver/ledc.h" #include "hal_pwm.h" #define PWM_SPEED_MODE LEDC_LOW_SPEED_MODE #define PWM_RES_BITS LEDC_TIMER_10_BIT #define PWM_DUTY_FULL 1023u /* 2^10 - 1 */ static bool s_ready[HAL_PWM_CH_MAX]; static uint16_t s_permille[HAL_PWM_CH_MAX]; hal_err_t hal_pwm_config(int ch, int pin, uint32_t freq_hz) { if (ch < 0 || ch >= HAL_PWM_CH_MAX || pin < 0 || freq_hz == 0) { return HAL_ERR_ARG; } /* 一个通道一个定时器:风扇 25 kHz 与将来别的用途未必同频。 */ ledc_timer_config_t tc = { .speed_mode = PWM_SPEED_MODE, .duty_resolution = PWM_RES_BITS, .timer_num = (ledc_timer_t)ch, .freq_hz = freq_hz, .clk_cfg = LEDC_AUTO_CLK, }; if (ledc_timer_config(&tc) != ESP_OK) { return HAL_ERR_ARG; } ledc_channel_config_t cc = { .gpio_num = pin, .speed_mode = PWM_SPEED_MODE, .channel = (ledc_channel_t)ch, .timer_sel = (ledc_timer_t)ch, .duty = 0, .hpoint = 0, }; if (ledc_channel_config(&cc) != ESP_OK) { return HAL_ERR_ARG; } s_ready[ch] = true; s_permille[ch] = 0; return HAL_OK; } hal_err_t hal_pwm_set_permille(int ch, uint16_t permille) { if (ch < 0 || ch >= HAL_PWM_CH_MAX || !s_ready[ch] || permille > 1000) { return HAL_ERR_ARG; } /* +500 是四舍五入:1000 permille 必须正好是满占空比,别差一个码值。 */ uint32_t duty = ((uint32_t)permille * PWM_DUTY_FULL + 500u) / 1000u; if (ledc_set_duty(PWM_SPEED_MODE, (ledc_channel_t)ch, duty) != ESP_OK || ledc_update_duty(PWM_SPEED_MODE, (ledc_channel_t)ch) != ESP_OK) { return HAL_ERR_BUS; } s_permille[ch] = permille; return HAL_OK; } hal_err_t hal_pwm_get_permille(int ch, uint16_t *permille) { if (ch < 0 || ch >= HAL_PWM_CH_MAX || !s_ready[ch] || permille == NULL) { return HAL_ERR_ARG; } *permille = s_permille[ch]; return HAL_OK; }