mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Ledc fix (#4338)
This commit is contained in:
		| @@ -8,6 +8,20 @@ | |||||||
| #endif | #endif | ||||||
| #include <driver/ledc.h> | #include <driver/ledc.h> | ||||||
|  |  | ||||||
|  | #define CLOCK_FREQUENCY 80e6f | ||||||
|  |  | ||||||
|  | #ifdef USE_ARDUINO | ||||||
|  | #ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK | ||||||
|  | #undef CLOCK_FREQUENCY | ||||||
|  | // starting with ESP32 Arduino 2.0.2, the 40MHz crystal is used as clock by default if supported | ||||||
|  | #define CLOCK_FREQUENCY 40e6f | ||||||
|  | #endif | ||||||
|  | #else | ||||||
|  | #define DEFAULT_CLK LEDC_USE_APB_CLK | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static const uint8_t SETUP_ATTEMPT_COUNT_MAX = 5; | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace ledc { | namespace ledc { | ||||||
|  |  | ||||||
| @@ -26,11 +40,11 @@ inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; } | |||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return 80e6f / float(1 << bit_depth); } | float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return CLOCK_FREQUENCY / float(1 << bit_depth); } | ||||||
|  |  | ||||||
| float ledc_min_frequency_for_bit_depth(uint8_t bit_depth, bool low_frequency) { | float ledc_min_frequency_for_bit_depth(uint8_t bit_depth, bool low_frequency) { | ||||||
|   const float max_div_num = ((1 << MAX_RES_BITS) - 1) / (low_frequency ? 32.0f : 256.0f); |   const float max_div_num = ((1 << MAX_RES_BITS) - 1) / (low_frequency ? 32.0f : 256.0f); | ||||||
|   return 80e6f / (max_div_num * float(1 << bit_depth)); |   return CLOCK_FREQUENCY / (max_div_num * float(1 << bit_depth)); | ||||||
| } | } | ||||||
|  |  | ||||||
| optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) { | optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) { | ||||||
| @@ -46,6 +60,38 @@ optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) { | |||||||
|   return {}; |   return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP_IDF | ||||||
|  | esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_channel_t chan_num, | ||||||
|  |                                     uint8_t channel, uint8_t &bit_depth, float frequency) { | ||||||
|  |   bit_depth = *ledc_bit_depth_for_frequency(frequency); | ||||||
|  |   if (bit_depth < 1) { | ||||||
|  |     ESP_LOGE(TAG, "Frequency %f can't be achieved with any bit depth", frequency); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ledc_timer_config_t timer_conf{}; | ||||||
|  |   timer_conf.speed_mode = speed_mode; | ||||||
|  |   timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(bit_depth); | ||||||
|  |   timer_conf.timer_num = timer_num; | ||||||
|  |   timer_conf.freq_hz = (uint32_t) frequency; | ||||||
|  |   timer_conf.clk_cfg = DEFAULT_CLK; | ||||||
|  |  | ||||||
|  |   // Configure the time with fallback in case of error | ||||||
|  |   int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX; | ||||||
|  |   esp_err_t init_result = ESP_FAIL; | ||||||
|  |   while (attempt_count_max > 0 && init_result != ESP_OK) { | ||||||
|  |     init_result = ledc_timer_config(&timer_conf); | ||||||
|  |     if (init_result != ESP_OK) { | ||||||
|  |       ESP_LOGW(TAG, "Unable to initialize timer with frequency %.1f and bit depth of %u", frequency, bit_depth); | ||||||
|  |       // try again with a lower bit depth | ||||||
|  |       timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(--bit_depth); | ||||||
|  |     } | ||||||
|  |     attempt_count_max--; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return init_result; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| void LEDCOutput::write_state(float state) { | void LEDCOutput::write_state(float state) { | ||||||
|   if (!initialized_) { |   if (!initialized_) { | ||||||
|     ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); |     ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); | ||||||
| @@ -61,6 +107,7 @@ void LEDCOutput::write_state(float state) { | |||||||
|   auto duty = static_cast<uint32_t>(duty_rounded); |   auto duty = static_cast<uint32_t>(duty_rounded); | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|  |   ESP_LOGV(TAG, "Setting duty: %u on channel %u", duty, this->channel_); | ||||||
|   ledcWrite(this->channel_, duty); |   ledcWrite(this->channel_, duty); | ||||||
| #endif | #endif | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
| @@ -72,6 +119,7 @@ void LEDCOutput::write_state(float state) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void LEDCOutput::setup() { | void LEDCOutput::setup() { | ||||||
|  |   ESP_LOGV(TAG, "Entering setup..."); | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|   this->update_frequency(this->frequency_); |   this->update_frequency(this->frequency_); | ||||||
|   this->turn_off(); |   this->turn_off(); | ||||||
| @@ -83,19 +131,16 @@ void LEDCOutput::setup() { | |||||||
|   auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2); |   auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2); | ||||||
|   auto chan_num = static_cast<ledc_channel_t>(channel_ % 8); |   auto chan_num = static_cast<ledc_channel_t>(channel_ % 8); | ||||||
|  |  | ||||||
|   bit_depth_ = *ledc_bit_depth_for_frequency(frequency_); |   esp_err_t timer_init_result = | ||||||
|   if (bit_depth_ < 1) { |       configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_); | ||||||
|     ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency_); |  | ||||||
|     this->status_set_warning(); |   if (timer_init_result != ESP_OK) { | ||||||
|  |     ESP_LOGE(TAG, "Frequency %f can't be achieved with computed bit depth %u", this->frequency_, this->bit_depth_); | ||||||
|  |     this->status_set_error(); | ||||||
|  |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ledc_timer_config_t timer_conf{}; |   ESP_LOGV(TAG, "Configured frequency %f with a bit depth of %u bits", this->frequency_, this->bit_depth_); | ||||||
|   timer_conf.speed_mode = speed_mode; |  | ||||||
|   timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(bit_depth_); |  | ||||||
|   timer_conf.timer_num = timer_num; |  | ||||||
|   timer_conf.freq_hz = (uint32_t) frequency_; |  | ||||||
|   timer_conf.clk_cfg = LEDC_AUTO_CLK; |  | ||||||
|   ledc_timer_config(&timer_conf); |  | ||||||
|  |  | ||||||
|   ledc_channel_config_t chan_conf{}; |   ledc_channel_config_t chan_conf{}; | ||||||
|   chan_conf.gpio_num = pin_->get_pin(); |   chan_conf.gpio_num = pin_->get_pin(); | ||||||
| @@ -107,6 +152,7 @@ void LEDCOutput::setup() { | |||||||
|   chan_conf.hpoint = 0; |   chan_conf.hpoint = 0; | ||||||
|   ledc_channel_config(&chan_conf); |   ledc_channel_config(&chan_conf); | ||||||
|   initialized_ = true; |   initialized_ = true; | ||||||
|  |   this->status_clear_error(); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -114,36 +160,80 @@ void LEDCOutput::dump_config() { | |||||||
|   ESP_LOGCONFIG(TAG, "LEDC Output:"); |   ESP_LOGCONFIG(TAG, "LEDC Output:"); | ||||||
|   LOG_PIN("  Pin ", this->pin_); |   LOG_PIN("  Pin ", this->pin_); | ||||||
|   ESP_LOGCONFIG(TAG, "  LEDC Channel: %u", this->channel_); |   ESP_LOGCONFIG(TAG, "  LEDC Channel: %u", this->channel_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Frequency: %.1f Hz", this->frequency_); |   ESP_LOGCONFIG(TAG, "  PWM Frequency: %.1f Hz", this->frequency_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Bit depth: %u", this->bit_depth_); | ||||||
|  |   ESP_LOGV(TAG, "  Max frequency for bit depth: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_)); | ||||||
|  |   ESP_LOGV(TAG, "  Min frequency for bit depth: %f", | ||||||
|  |            ledc_min_frequency_for_bit_depth(this->bit_depth_, (this->frequency_ < 100))); | ||||||
|  |   ESP_LOGV(TAG, "  Max frequency for bit depth-1: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_ - 1)); | ||||||
|  |   ESP_LOGV(TAG, "  Min frequency for bit depth-1: %f", | ||||||
|  |            ledc_min_frequency_for_bit_depth(this->bit_depth_ - 1, (this->frequency_ < 100))); | ||||||
|  |   ESP_LOGV(TAG, "  Max frequency for bit depth+1: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_ + 1)); | ||||||
|  |   ESP_LOGV(TAG, "  Min frequency for bit depth+1: %f", | ||||||
|  |            ledc_min_frequency_for_bit_depth(this->bit_depth_ + 1, (this->frequency_ < 100))); | ||||||
|  |   ESP_LOGV(TAG, "  Max res bits: %d", MAX_RES_BITS); | ||||||
|  |   ESP_LOGV(TAG, "  Clock frequency: %f", CLOCK_FREQUENCY); | ||||||
| } | } | ||||||
|  |  | ||||||
| void LEDCOutput::update_frequency(float frequency) { | void LEDCOutput::update_frequency(float frequency) { | ||||||
|   auto bit_depth_opt = ledc_bit_depth_for_frequency(frequency); |   auto bit_depth_opt = ledc_bit_depth_for_frequency(frequency); | ||||||
|   if (!bit_depth_opt.has_value()) { |   if (!bit_depth_opt.has_value()) { | ||||||
|     ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency); |     ESP_LOGE(TAG, "Frequency %f can't be achieved with any bit depth", this->frequency_); | ||||||
|     this->status_set_warning(); |     this->status_set_error(); | ||||||
|   } |   } | ||||||
|   this->bit_depth_ = bit_depth_opt.value_or(8); |   this->bit_depth_ = bit_depth_opt.value_or(8); | ||||||
|   this->frequency_ = frequency; |   this->frequency_ = frequency; | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
|   ledcSetup(this->channel_, frequency, this->bit_depth_); |   ESP_LOGV(TAG, "Using Arduino API - Trying to define channel, frequency and bit depth..."); | ||||||
|  |   u_int32_t configured_frequency = 0; | ||||||
|  |  | ||||||
|  |   // Configure LEDC channel, frequency and bit depth with fallback | ||||||
|  |   int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX; | ||||||
|  |   while (attempt_count_max > 0 && configured_frequency == 0) { | ||||||
|  |     ESP_LOGV(TAG, "Trying initialize channel %u with frequency %.1f and bit depth of %u...", this->channel_, | ||||||
|  |              this->frequency_, this->bit_depth_); | ||||||
|  |     configured_frequency = ledcSetup(this->channel_, frequency, this->bit_depth_); | ||||||
|  |     if (configured_frequency != 0) { | ||||||
|       initialized_ = true; |       initialized_ = true; | ||||||
|  |       this->status_clear_error(); | ||||||
|  |       ESP_LOGV(TAG, "Configured frequency: %u with bit depth: %u", configured_frequency, this->bit_depth_); | ||||||
|  |     } else { | ||||||
|  |       ESP_LOGW(TAG, "Unable to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_, | ||||||
|  |                this->frequency_, this->bit_depth_); | ||||||
|  |       // try again with a lower bit depth | ||||||
|  |       this->bit_depth_--; | ||||||
|  |     } | ||||||
|  |     attempt_count_max--; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (configured_frequency == 0) { | ||||||
|  |     ESP_LOGE(TAG, "Permanently failed to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_, | ||||||
|  |              this->frequency_, this->bit_depth_); | ||||||
|  |     this->status_set_error(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
| #endif  // USE_ARDUINO | #endif  // USE_ARDUINO | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|   if (!initialized_) { |   if (!initialized_) { | ||||||
|     ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); |     ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   auto speed_mode = get_speed_mode(channel_); |   auto speed_mode = get_speed_mode(channel_); | ||||||
|   auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2); |   auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2); | ||||||
|  |   auto chan_num = static_cast<ledc_channel_t>(channel_ % 8); | ||||||
|  |  | ||||||
|   ledc_timer_config_t timer_conf{}; |   esp_err_t timer_init_result = | ||||||
|   timer_conf.speed_mode = speed_mode; |       configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_); | ||||||
|   timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(bit_depth_); |  | ||||||
|   timer_conf.timer_num = timer_num; |   if (timer_init_result != ESP_OK) { | ||||||
|   timer_conf.freq_hz = (uint32_t) frequency_; |     ESP_LOGE(TAG, "Frequency %f can't be achieved with computed bit depth %u", this->frequency_, this->bit_depth_); | ||||||
|   timer_conf.clk_cfg = LEDC_AUTO_CLK; |     this->status_set_error(); | ||||||
|   ledc_timer_config(&timer_conf); |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->status_clear_error(); | ||||||
| #endif | #endif | ||||||
|   // re-apply duty |   // re-apply duty | ||||||
|   this->write_state(this->duty_); |   this->write_state(this->duty_); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user