ESPHome  2024.12.2
bp5758d.cpp
Go to the documentation of this file.
1 #include "bp5758d.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace bp5758d {
6 
7 static const char *const TAG = "bp5758d";
8 
9 static const uint8_t BP5758D_MODEL_ID = 0b10000000;
10 static const uint8_t BP5758D_ADDR_STANDBY = 0b00000000;
11 // Note, channel start address seems ambiguous and mis-translated.
12 // Documentation states all are "invalid sleep"
13 // All 3 values appear to activate all 5 channels. Using the mapping
14 // from BP1658CJ ordering since it won't break anything.
15 static const uint8_t BP5758D_ADDR_START_3CH = 0b00010000;
16 static const uint8_t BP5758D_ADDR_START_2CH = 0b00100000;
17 static const uint8_t BP5758D_ADDR_START_5CH = 0b00110000;
18 static const uint8_t BP5758D_ALL_DATA_CHANNEL_ENABLEMENT = 0b00011111;
19 
20 static const uint8_t BP5758D_DELAY = 2;
21 
23  ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component...");
24  this->data_pin_->setup();
25  this->data_pin_->digital_write(false);
26  delayMicroseconds(BP5758D_DELAY);
27  this->clock_pin_->setup();
28  this->clock_pin_->digital_write(false);
29  delayMicroseconds(BP5758D_DELAY);
30  this->channel_current_.resize(5, 0);
31  this->pwm_amounts_.resize(5, 0);
32 }
34  ESP_LOGCONFIG(TAG, "BP5758D:");
35  LOG_PIN(" Data Pin: ", this->data_pin_);
36  LOG_PIN(" Clock Pin: ", this->clock_pin_);
37 }
38 
39 void BP5758D::loop() {
40  if (!this->update_)
41  return;
42 
43  uint8_t data[17];
44  if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
45  this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
46  for (int i = 1; i < 17; i++)
47  data[i] = 0;
48 
49  // First turn all channels off
50  data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_5CH;
51  this->write_buffer_(data, 17);
52  // Then sleep
53  data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY;
54  this->write_buffer_(data, 17);
55  } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
56  (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) {
57  // Only data on white channels
58  data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_2CH;
59  data[1] = BP5758D_ALL_DATA_CHANNEL_ENABLEMENT;
60  data[2] = 0;
61  data[3] = 0;
62  data[4] = 0;
63  data[5] = this->pwm_amounts_[3] > 0 ? correct_current_level_bits_(this->channel_current_[3]) : 0;
64  data[6] = this->pwm_amounts_[4] > 0 ? correct_current_level_bits_(this->channel_current_[4]) : 0;
65  for (int i = 7, j = 0; i <= 15; i += 2, j++) {
66  data[i] = this->pwm_amounts_[j] & 0x1F;
67  data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
68  }
69  this->write_buffer_(data, 17);
70  } else if ((this->pwm_amounts_[0] > 0 || this->pwm_amounts_[1] > 0 || this->pwm_amounts_[2] > 0) &&
71  this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
72  // Only data on RGB channels
73  data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_3CH;
74  data[1] = BP5758D_ALL_DATA_CHANNEL_ENABLEMENT;
75  data[2] = this->pwm_amounts_[0] > 0 ? correct_current_level_bits_(this->channel_current_[0]) : 0;
76  data[3] = this->pwm_amounts_[1] > 0 ? correct_current_level_bits_(this->channel_current_[1]) : 0;
77  data[4] = this->pwm_amounts_[2] > 0 ? correct_current_level_bits_(this->channel_current_[2]) : 0;
78  data[5] = 0;
79  data[6] = 0;
80  for (int i = 7, j = 0; i <= 15; i += 2, j++) {
81  data[i] = this->pwm_amounts_[j] & 0x1F;
82  data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
83  }
84  this->write_buffer_(data, 17);
85  } else {
86  // All channels
87  data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_5CH;
88  data[1] = BP5758D_ALL_DATA_CHANNEL_ENABLEMENT;
89  data[2] = this->pwm_amounts_[0] > 0 ? correct_current_level_bits_(this->channel_current_[0]) : 0;
90  data[3] = this->pwm_amounts_[1] > 0 ? correct_current_level_bits_(this->channel_current_[1]) : 0;
91  data[4] = this->pwm_amounts_[2] > 0 ? correct_current_level_bits_(this->channel_current_[2]) : 0;
92  data[5] = this->pwm_amounts_[3] > 0 ? correct_current_level_bits_(this->channel_current_[3]) : 0;
93  data[6] = this->pwm_amounts_[4] > 0 ? correct_current_level_bits_(this->channel_current_[4]) : 0;
94  for (int i = 7, j = 0; i <= 15; i += 2, j++) {
95  data[i] = this->pwm_amounts_[j] & 0x1F;
96  data[i + 1] = (this->pwm_amounts_[j] >> 5) & 0x1F;
97  }
98  this->write_buffer_(data, 17);
99  }
100 
101  this->update_ = false;
102 }
103 
104 uint8_t BP5758D::correct_current_level_bits_(uint8_t current) {
105  // Anything below 64 uses normal bitmapping.
106  if (current < 64) {
107  return current;
108  }
109 
110  // Anything above 63 needs to be offset by +34 because the driver remaps bit 7 (normally 64) to 30.
111  // (no idea why(!) but it is documented)
112  // Example:
113  // integer 64 would normally put out 0b01000000 but here 0b01000000 = 30 whereas everything lower
114  // is normal, so we add 34 to the integer where
115  // integer 98 = 0b01100010 which is 30 (7th bit adjusted) + 34 (1st-6th bits).
116  return current + 34;
117 }
118 
119 void BP5758D::set_channel_value_(uint8_t channel, uint16_t value) {
120  if (this->pwm_amounts_[channel] != value) {
121  this->update_ = true;
122  this->update_channel_ = channel;
123  }
124  this->pwm_amounts_[channel] = value;
125 }
126 
127 void BP5758D::set_channel_current_(uint8_t channel, uint8_t current) { this->channel_current_[channel] = current; }
128 
129 void BP5758D::write_bit_(bool value) {
130  this->data_pin_->digital_write(value);
131  delayMicroseconds(BP5758D_DELAY);
132  this->clock_pin_->digital_write(true);
133  delayMicroseconds(BP5758D_DELAY);
134  this->clock_pin_->digital_write(false);
135  delayMicroseconds(BP5758D_DELAY);
136 }
137 
138 void BP5758D::write_byte_(uint8_t data) {
139  for (uint8_t mask = 0x80; mask; mask >>= 1) {
140  this->write_bit_(data & mask);
141  }
142 
143  // ack bit
145  this->clock_pin_->digital_write(true);
146  delayMicroseconds(BP5758D_DELAY);
147  this->clock_pin_->digital_write(false);
148  delayMicroseconds(BP5758D_DELAY);
150 }
151 
152 void BP5758D::write_buffer_(uint8_t *buffer, uint8_t size) {
153  this->data_pin_->digital_write(false);
154  delayMicroseconds(BP5758D_DELAY);
155  this->clock_pin_->digital_write(false);
156  delayMicroseconds(BP5758D_DELAY);
157 
158  for (uint32_t i = 0; i < size; i++) {
159  this->write_byte_(buffer[i]);
160  }
161 
162  this->clock_pin_->digital_write(true);
163  delayMicroseconds(BP5758D_DELAY);
164  this->data_pin_->digital_write(true);
165  delayMicroseconds(BP5758D_DELAY);
166 }
167 
168 } // namespace bp5758d
169 } // namespace esphome
virtual void digital_write(bool value)=0
void set_channel_current_(uint8_t channel, uint8_t current)
Definition: bp5758d.cpp:127
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
void dump_config() override
Definition: bp5758d.cpp:33
std::vector< uint16_t > pwm_amounts_
Definition: bp5758d.h:59
void setup() override
Definition: bp5758d.cpp:22
void write_buffer_(uint8_t *buffer, uint8_t size)
Definition: bp5758d.cpp:152
void set_channel_value_(uint8_t channel, uint16_t value)
Definition: bp5758d.cpp:119
void loop() override
Send new values if they were updated.
Definition: bp5758d.cpp:39
std::vector< uint8_t > channel_current_
Definition: bp5758d.h:58
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
void write_bit_(bool value)
Definition: bp5758d.cpp:129
void write_byte_(uint8_t data)
Definition: bp5758d.cpp:138
uint8_t correct_current_level_bits_(uint8_t current)
Definition: bp5758d.cpp:104