| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- #include "luat_base.h"
- #include "luat_pwm.h"
- #include <math.h>
- #include "driver/ledc.h"
- #include "luat_log.h"
- #define LUAT_LOG_TAG "pwm"
- typedef struct pwm_cont {
- luat_pwm_conf_t pc;
- uint8_t is_opened;
- }pwm_conf_t;
- static pwm_conf_t luat_pwm_idf[LEDC_TIMER_MAX];
- #define LEDC_LL_FRACTIONAL_BITS (8)
- #define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1)
- #define LEDC_TIMER_DIV_NUM_MAX (0x3FFFF)
- #define LEDC_IS_DIV_INVALID(div) ((div) <= LEDC_LL_FRACTIONAL_MAX || (div) > LEDC_TIMER_DIV_NUM_MAX)
- static inline uint32_t ilog2(uint32_t i)
- {
- assert(i > 0);
- uint32_t log = 0;
- while (i >>= 1) {
- ++log;
- }
- return log;
- }
- static inline uint32_t ledc_calculate_divisor(uint32_t src_clk_freq, int freq_hz, uint32_t precision)
- {
- uint64_t fp = freq_hz;
- fp *= precision;
- return ( ( (uint64_t) src_clk_freq << LEDC_LL_FRACTIONAL_BITS ) + (fp / 2 ) ) / fp;
- }
- // https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm
- static uint32_t ledc_find_suitable_duty_resolution2(uint32_t src_clk_freq, uint32_t timer_freq)
- {
- uint32_t div = (src_clk_freq + timer_freq / 2) / timer_freq; // rounded
- uint32_t duty_resolution = ilog2(div);
- if ( duty_resolution >= SOC_LEDC_TIMER_BIT_WIDTH) {
- duty_resolution = SOC_LEDC_TIMER_BIT_WIDTH - 1;
- }
- uint32_t div_param = ledc_calculate_divisor(src_clk_freq, timer_freq, 1 << duty_resolution);
- if (LEDC_IS_DIV_INVALID(div_param)) {
- div = src_clk_freq / timer_freq; // truncated
- duty_resolution = ilog2(div);
- if ( duty_resolution > SOC_LEDC_TIMER_BIT_WIDTH - 1) {
- duty_resolution = SOC_LEDC_TIMER_BIT_WIDTH - 1;
- }
- div_param = ledc_calculate_divisor(src_clk_freq, timer_freq, 1 << duty_resolution);
- if (LEDC_IS_DIV_INVALID(div_param)) {
- duty_resolution = 0;
- }
- }
- return duty_resolution;
- }
- int luat_pwm_setup(luat_pwm_conf_t *conf){
- int duty_resolution = 0;
- int timer = -1;
- int ret = -1;
- if (conf->channel < 0)
- return -1;
- for (size_t i = 0; i < LEDC_TIMER_MAX; i++)
- {
- if (luat_pwm_idf[i].is_opened && luat_pwm_idf[i].pc.channel == conf->channel) {
- timer = i;
- break;
- }
- if (!luat_pwm_idf[i].is_opened) {
- timer = i;
- break;
- }
- }
- if (timer < 0) {
- LLOGE("too many PWM!!! only %d channels supported", LEDC_TIMER_MAX);
- return -1;
- }
- if (conf->pulse > conf->precision) {
- conf->pulse = conf->precision;
- }
- duty_resolution = ledc_find_suitable_duty_resolution2(80*1000*1000, conf->period);
-
- int duty = (conf->pulse * (1 << duty_resolution)) / conf->precision;
- // LLOGD("freq=%d, pulse=%d, precision=%d, resolution=%08X, duty=%08X", (int)conf->period, (int)conf->pulse, (int)conf->precision, (int)(1 << duty_resolution), (int)duty);
- int speed_mode = LEDC_LOW_SPEED_MODE;
-
- // 判断一下是否需要完全重新配置
- if (conf->period != luat_pwm_idf[timer].pc.period || // 频率是否相同
- conf->precision != luat_pwm_idf[timer].pc.precision || // 占空比精度是否相同
- conf->pnum != luat_pwm_idf[timer].pc.pnum // 输出脉冲数是否相同
- ) {
- // LLOGD("need to reconfig channel %d period %d", conf->channel, conf->period);
- ledc_timer_config_t ledc_timer = {
- .speed_mode = speed_mode,
- .timer_num = timer,
- .freq_hz = conf->period,
- .clk_cfg = LEDC_AUTO_CLK,
- .duty_resolution = duty_resolution
- };
- // LLOGD("duty_resolution %d %04X", duty_resolution, 1 << duty_resolution);
- ret = ledc_timer_config(&ledc_timer);
- if (ret) {
- return -1;
- }
- ledc_channel_config_t ledc_channel = {
- .speed_mode = speed_mode,
- .channel = timer,
- .timer_sel = timer,
- .intr_type = LEDC_INTR_DISABLE,
- .gpio_num = conf->channel,
- .duty = duty,
- .hpoint = 0,
- };
- ledc_channel_config(&ledc_channel);
- }
- ledc_set_duty(speed_mode, timer, duty);
- ledc_update_duty(speed_mode, timer);
- memcpy( &luat_pwm_idf[timer].pc, conf, sizeof(luat_pwm_conf_t));
- luat_pwm_idf[timer].is_opened = 1;
- return 0;
- }
- int luat_pwm_close(int channel){
- int timer = -1;
- if (channel < 0)
- return -1;
- for (size_t i = 0; i < LEDC_TIMER_MAX; i++){
- if (luat_pwm_idf[i].is_opened && luat_pwm_idf[i].pc.channel == channel){
- timer = i;
- break;
- }
- }
- if (timer < 0) {
- return -1;
- }
- int ret = ledc_stop(LEDC_LOW_SPEED_MODE, timer, 0);
- if (ret) {
- return -1;
- }
- gpio_reset_pin(channel);
- luat_pwm_idf[timer].is_opened = 0;
- memset(&luat_pwm_idf[timer].pc, 0, sizeof(luat_pwm_conf_t) );
- return 0;
- }
- int luat_pwm_capture(int channel, int freq){
- return -1;
- }
|