ESPHome 2026.6.0-dev
Loading...
Searching...
No Matches
pca9685_output.cpp
Go to the documentation of this file.
1#include "pca9685_output.h"
2#include "esphome/core/hal.h"
4#include "esphome/core/log.h"
5
6namespace esphome::pca9685 {
7
8static const char *const TAG = "pca9685";
9
10// PCA9685 mode constants are now inline constexpr in pca9685_output.h
11
12static const uint8_t PCA9685_REGISTER_SOFTWARE_RESET = 0x06;
13static const uint8_t PCA9685_REGISTER_MODE1 = 0x00;
14static const uint8_t PCA9685_REGISTER_MODE2 = 0x01;
15static const uint8_t PCA9685_REGISTER_LED0 = 0x06;
16static const uint8_t PCA9685_REGISTER_PRE_SCALE = 0xFE;
17
18static const uint8_t PCA9685_MODE1_RESTART = 0b10000000;
19static const uint8_t PCA9685_MODE1_EXTCLK = 0b01000000;
20static const uint8_t PCA9685_MODE1_AUTOINC = 0b00100000;
21static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000;
22
24 ESP_LOGV(TAG, " Resetting devices");
25 if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) {
26 this->mark_failed();
27 return;
28 }
29
30 if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
31 this->mark_failed();
32 return;
33 }
34 if (!this->write_byte(PCA9685_REGISTER_MODE2, this->mode_)) {
35 this->mark_failed();
36 return;
37 }
38
39 uint8_t mode1;
40 if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) {
41 this->mark_failed();
42 return;
43 }
44 mode1 = (mode1 & ~PCA9685_MODE1_RESTART) | PCA9685_MODE1_SLEEP;
45 if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
46 this->mark_failed();
47 return;
48 }
49
50 int pre_scaler = 3;
51 if (this->extclk_) {
52 mode1 = mode1 | PCA9685_MODE1_EXTCLK;
53 if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
54 this->mark_failed();
55 return;
56 }
57 } else {
58 pre_scaler = static_cast<int>((25000000 / (4096 * this->frequency_)) - 1);
59 pre_scaler = clamp(pre_scaler, 3, 255);
60
61 ESP_LOGV(TAG, " -> Prescaler: %d", pre_scaler);
62 }
63 if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) {
64 this->mark_failed();
65 return;
66 }
67
68 mode1 = (mode1 & ~PCA9685_MODE1_SLEEP) | PCA9685_MODE1_RESTART;
69 if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
70 this->mark_failed();
71 return;
72 }
74
75 this->loop();
76}
77
79 ESP_LOGCONFIG(TAG,
80 "PCA9685:\n"
81 " Mode: 0x%02X",
82 this->mode_);
83 if (this->extclk_) {
84 ESP_LOGCONFIG(TAG, " EXTCLK: enabled");
85 } else {
86 ESP_LOGCONFIG(TAG,
87 " EXTCLK: disabled\n"
88 " Frequency: %.0f Hz",
89 this->frequency_);
90 }
91 if (this->is_failed()) {
92 ESP_LOGE(TAG, "Setting up PCA9685 failed!");
93 }
94}
95
97 if (this->min_channel_ == 0xFF || !this->update_)
98 return;
99
100 const uint16_t num_channels = this->max_channel_ - this->min_channel_ + 1;
101 const uint16_t phase_delta_begin = 4096 / num_channels;
102 for (uint8_t channel = this->min_channel_; channel <= this->max_channel_; channel++) {
103 uint16_t phase_begin;
104 switch (this->balancer_) {
106 phase_begin = 0;
107 break;
109 phase_begin = (channel - this->min_channel_) * phase_delta_begin;
110 break;
111 default:
112 ESP_LOGE(TAG, "Unknown phase balancer %d", static_cast<int>(this->balancer_));
113 return;
114 }
115 uint16_t phase_end;
116 uint16_t amount = this->pwm_amounts_[channel];
117 if (amount == 0) {
118 phase_end = 4096;
119 } else if (amount >= 4096) {
120 phase_begin = 4096;
121 phase_end = 0;
122 } else {
123 phase_end = phase_begin + amount;
124 if (phase_end >= 4096)
125 phase_end -= 4096;
126 }
127
128 ESP_LOGVV(TAG, "Channel %02u: amount=%04u phase_begin=%04u phase_end=%04u", channel, amount, phase_begin,
129 phase_end);
130
131 uint8_t data[4];
132 data[0] = phase_begin & 0xFF;
133 data[1] = (phase_begin >> 8) & 0xFF;
134 data[2] = phase_end & 0xFF;
135 data[3] = (phase_end >> 8) & 0xFF;
136
137 uint8_t reg = PCA9685_REGISTER_LED0 + 4 * channel;
138 if (!this->write_bytes(reg, data, 4)) {
139 this->status_set_warning();
140 return;
141 }
142 }
143
144 this->status_clear_warning();
145 this->update_ = false;
146}
147
149 auto c = channel->channel_;
150 this->min_channel_ = std::min(this->min_channel_, c);
151 this->max_channel_ = std::max(this->max_channel_, c);
152 channel->set_parent(this);
153}
154
156 const uint16_t max_duty = 4096;
157 const float duty_rounded = roundf(state * max_duty);
158 auto duty = static_cast<uint16_t>(duty_rounded);
159 this->parent_->set_channel_value_(this->channel_, duty);
160}
161
162} // namespace esphome::pca9685
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:272
void status_clear_warning()
Definition component.h:289
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:152
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const
Definition i2c.h:251
void write_state(float state) override
void set_parent(PCA9685Output *parent)
void set_channel_value_(uint8_t channel, uint16_t value)
void register_channel(PCA9685Channel *channel)
bool state
Definition fan.h:2
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48