ESPHome  2023.3.2
pca9685_output.cpp
Go to the documentation of this file.
1 #include "pca9685_output.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 #include "esphome/core/hal.h"
5 
6 namespace esphome {
7 namespace pca9685 {
8 
9 static const char *const TAG = "pca9685";
10 
11 const uint8_t PCA9685_MODE_INVERTED = 0x10;
12 const uint8_t PCA9685_MODE_OUTPUT_ONACK = 0x08;
13 const uint8_t PCA9685_MODE_OUTPUT_TOTEM_POLE = 0x04;
14 const uint8_t PCA9685_MODE_OUTNE_HIGHZ = 0x02;
15 const uint8_t PCA9685_MODE_OUTNE_LOW = 0x01;
16 
17 static const uint8_t PCA9685_REGISTER_SOFTWARE_RESET = 0x06;
18 static const uint8_t PCA9685_REGISTER_MODE1 = 0x00;
19 static const uint8_t PCA9685_REGISTER_MODE2 = 0x01;
20 static const uint8_t PCA9685_REGISTER_LED0 = 0x06;
21 static const uint8_t PCA9685_REGISTER_PRE_SCALE = 0xFE;
22 
23 static const uint8_t PCA9685_MODE1_RESTART = 0b10000000;
24 static const uint8_t PCA9685_MODE1_EXTCLK = 0b01000000;
25 static const uint8_t PCA9685_MODE1_AUTOINC = 0b00100000;
26 static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000;
27 
29  ESP_LOGCONFIG(TAG, "Setting up PCA9685OutputComponent...");
30 
31  ESP_LOGV(TAG, " Resetting devices...");
32  uint8_t address_tmp = this->address_;
33  this->set_i2c_address(0x00);
34  if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) {
35  this->mark_failed();
36  return;
37  }
38  this->set_i2c_address(address_tmp);
39 
40  if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) {
41  this->mark_failed();
42  return;
43  }
44  if (!this->write_byte(PCA9685_REGISTER_MODE2, this->mode_)) {
45  this->mark_failed();
46  return;
47  }
48 
49  uint8_t mode1;
50  if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) {
51  this->mark_failed();
52  return;
53  }
54  mode1 = (mode1 & ~PCA9685_MODE1_RESTART) | PCA9685_MODE1_SLEEP;
55  if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
56  this->mark_failed();
57  return;
58  }
59 
60  int pre_scaler = 3;
61  if (this->extclk_) {
62  mode1 = mode1 | PCA9685_MODE1_EXTCLK;
63  if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
64  this->mark_failed();
65  return;
66  }
67  } else {
68  pre_scaler = static_cast<int>((25000000 / (4096 * this->frequency_)) - 1);
69  pre_scaler = clamp(pre_scaler, 3, 255);
70 
71  ESP_LOGV(TAG, " -> Prescaler: %d", pre_scaler);
72  }
73  if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) {
74  this->mark_failed();
75  return;
76  }
77 
78  mode1 = (mode1 & ~PCA9685_MODE1_SLEEP) | PCA9685_MODE1_RESTART;
79  if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) {
80  this->mark_failed();
81  return;
82  }
83  delayMicroseconds(500);
84 
85  this->loop();
86 }
87 
89  ESP_LOGCONFIG(TAG, "PCA9685:");
90  ESP_LOGCONFIG(TAG, " Mode: 0x%02X", this->mode_);
91  if (this->extclk_) {
92  ESP_LOGCONFIG(TAG, " EXTCLK: enabled");
93  } else {
94  ESP_LOGCONFIG(TAG, " EXTCLK: disabled");
95  ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
96  }
97  if (this->is_failed()) {
98  ESP_LOGE(TAG, "Setting up PCA9685 failed!");
99  }
100 }
101 
103  if (this->min_channel_ == 0xFF || !this->update_)
104  return;
105 
106  const uint16_t num_channels = this->max_channel_ - this->min_channel_ + 1;
107  for (uint8_t channel = this->min_channel_; channel <= this->max_channel_; channel++) {
108  uint16_t phase_begin = uint16_t(channel - this->min_channel_) / num_channels * 4096;
109  uint16_t phase_end;
110  uint16_t amount = this->pwm_amounts_[channel];
111  if (amount == 0) {
112  phase_end = 4096;
113  } else if (amount >= 4096) {
114  phase_begin = 4096;
115  phase_end = 0;
116  } else {
117  phase_end = phase_begin + amount;
118  if (phase_end >= 4096)
119  phase_end -= 4096;
120  }
121 
122  ESP_LOGVV(TAG, "Channel %02u: amount=%04u phase_begin=%04u phase_end=%04u", channel, amount, phase_begin,
123  phase_end);
124 
125  uint8_t data[4];
126  data[0] = phase_begin & 0xFF;
127  data[1] = (phase_begin >> 8) & 0xFF;
128  data[2] = phase_end & 0xFF;
129  data[3] = (phase_end >> 8) & 0xFF;
130 
131  uint8_t reg = PCA9685_REGISTER_LED0 + 4 * channel;
132  if (!this->write_bytes(reg, data, 4)) {
133  this->status_set_warning();
134  return;
135  }
136  }
137 
138  this->status_clear_warning();
139  this->update_ = false;
140 }
141 
143  auto c = channel->channel_;
144  this->min_channel_ = std::min(this->min_channel_, c);
145  this->max_channel_ = std::max(this->max_channel_, c);
146  channel->set_parent(this);
147 }
148 
150  const uint16_t max_duty = 4096;
151  const float duty_rounded = roundf(state * max_duty);
152  auto duty = static_cast<uint16_t>(duty_rounded);
153  this->parent_->set_channel_value_(this->channel_, duty);
154 }
155 
156 } // namespace pca9685
157 } // namespace esphome
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition: i2c.h:96
const uint8_t PCA9685_MODE_OUTPUT_ONACK
Channel update happens upon ACK (post-set) rather than on STOP (endTransmission)
I2CRegister reg(uint8_t a_register)
Definition: i2c.h:46
const uint8_t PCA9685_MODE_INVERTED
Inverts polarity of channel output signal.
void set_parent(PCA9685Output *parent)
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition: helpers.h:89
void register_channel(PCA9685Channel *channel)
void status_clear_warning()
Definition: component.cpp:153
void status_set_warning()
Definition: component.cpp:145
void write_state(float state) override
uint8_t address_
Definition: i2c.h:130
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:123
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:112
Definition: a4988.cpp:4
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:29
const uint8_t PCA9685_MODE_OUTPUT_TOTEM_POLE
Use a totem-pole (push-pull) style output rather than an open-drain structure.
const uint8_t PCA9685_MODE_OUTNE_HIGHZ
For active low output enable, sets channel output to high-impedance state.
void set_i2c_address(uint8_t address)
Definition: i2c.h:43
bool state
Definition: fan.h:34
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len, bool stop=true)
Definition: i2c.h:109
const uint8_t PCA9685_MODE_OUTNE_LOW
Similarly, sets channel output to high if in totem-pole mode, otherwise.