ESPHome  2024.6.6
sx1509.cpp
Go to the documentation of this file.
1 #include "sx1509.h"
2 #include "esphome/core/helpers.h"
3 #include "esphome/core/log.h"
4 
5 namespace esphome {
6 namespace sx1509 {
7 
8 static const char *const TAG = "sx1509";
9 
11  ESP_LOGCONFIG(TAG, "Setting up SX1509Component...");
12 
13  ESP_LOGV(TAG, " Resetting devices...");
14  if (!this->write_byte(REG_RESET, 0x12)) {
15  this->mark_failed();
16  return;
17  }
18  this->write_byte(REG_RESET, 0x34);
19 
20  uint16_t data;
21  if (!this->read_byte_16(REG_INTERRUPT_MASK_A, &data)) {
22  this->mark_failed();
23  return;
24  }
25  if (data != 0xFF00) {
26  this->mark_failed();
27  return;
28  }
30  delayMicroseconds(500);
31  if (this->has_keypad_)
32  this->setup_keypad_();
33 }
34 
36  ESP_LOGCONFIG(TAG, "SX1509:");
37  if (this->is_failed()) {
38  ESP_LOGE(TAG, "Setting up SX1509 failed!");
39  }
40  LOG_I2C_DEVICE(this);
41 }
42 
44  if (this->has_keypad_) {
46  return;
47  this->last_loop_timestamp_ = millis();
48  uint16_t key_data = this->read_key_data();
49  for (auto *binary_sensor : this->keypad_binary_sensors_)
50  binary_sensor->process(key_data);
51  }
52 }
53 
54 bool SX1509Component::digital_read(uint8_t pin) {
55  if (this->ddr_mask_ & (1 << pin)) {
56  uint16_t temp_reg_data;
57  if (!this->read_byte_16(REG_DATA_B, &temp_reg_data))
58  return false;
59  if (temp_reg_data & (1 << pin))
60  return true;
61  }
62  return false;
63 }
64 
65 void SX1509Component::digital_write(uint8_t pin, bool bit_value) {
66  if ((~this->ddr_mask_) & (1 << pin)) {
67  // If the pin is an output, write high/low
68  uint16_t temp_reg_data = 0;
69  this->read_byte_16(REG_DATA_B, &temp_reg_data);
70  if (bit_value) {
71  output_state_ |= (1 << pin); // set bit in shadow register
72  } else {
73  output_state_ &= ~(1 << pin); // reset bit shadow register
74  }
75  for (uint16_t b = 0x8000; b; b >>= 1) {
76  if ((~ddr_mask_) & b) { // transfer bits of outputs, but don't mess with inputs
77  if (output_state_ & b) {
78  temp_reg_data |= b;
79  } else {
80  temp_reg_data &= ~b;
81  }
82  }
83  }
84  this->write_byte_16(REG_DATA_B, temp_reg_data);
85  }
86 }
87 
89  ESP_LOGI(TAG, "Configuring pin %u with flags %x", pin, flags);
90 
91  uint16_t temp_word = 0;
92 
93  this->read_byte_16(REG_DIR_B, &this->ddr_mask_);
94  if (flags & gpio::FLAG_OUTPUT) {
95  // Always disable input buffer
96  this->read_byte_16(REG_INPUT_DISABLE_B, &temp_word);
97  temp_word |= (1 << pin);
98  this->write_byte_16(REG_INPUT_DISABLE_B, temp_word);
99 
100  if (flags & gpio::FLAG_OPEN_DRAIN) {
101  // Pullup must be disabled for open drain mode
102  this->read_byte_16(REG_PULL_UP_B, &temp_word);
103  temp_word &= ~(1 << pin);
104  this->write_byte_16(REG_PULL_UP_B, temp_word);
105  this->read_byte_16(REG_OPEN_DRAIN_B, &temp_word);
106  temp_word |= (1 << pin);
107  this->write_byte_16(REG_OPEN_DRAIN_B, temp_word);
108  ESP_LOGD(TAG, "Open drain output mode set for %u", pin);
109  } else {
110  ESP_LOGD(TAG, "Output Mode for %u", pin);
111  }
112 
113  // Set direction to output
114  this->ddr_mask_ &= ~(1 << pin);
115  this->write_byte_16(REG_DIR_B, this->ddr_mask_);
116  } else {
117  ESP_LOGD(TAG, "Input Mode for %u", pin);
118 
119  // Always enable input buffer
120  this->read_byte_16(REG_INPUT_DISABLE_B, &temp_word);
121  temp_word &= ~(1 << pin);
122  this->write_byte_16(REG_INPUT_DISABLE_B, temp_word);
123 
124  // Pullup
125  this->read_byte_16(REG_PULL_UP_B, &temp_word);
126  if (flags & gpio::FLAG_PULLUP) {
127  temp_word |= (1 << pin);
128  } else {
129  temp_word &= ~(1 << pin);
130  }
131  this->write_byte_16(REG_PULL_UP_B, temp_word);
132 
133  // Pulldown
134  this->read_byte_16(REG_PULL_DOWN_B, &temp_word);
135  if (flags & gpio::FLAG_PULLDOWN) {
136  temp_word |= (1 << pin);
137  } else {
138  temp_word &= ~(1 << pin);
139  }
140  this->write_byte_16(REG_PULL_DOWN_B, temp_word);
141 
142  // Set direction to input
143  this->ddr_mask_ |= (1 << pin);
144  this->write_byte_16(REG_DIR_B, this->ddr_mask_);
145  }
146 }
147 
149  uint16_t temp_word = 0;
150  uint8_t temp_byte = 0;
151 
152  this->read_byte_16(REG_INPUT_DISABLE_B, &temp_word);
153  temp_word |= (1 << pin);
154  this->write_byte_16(REG_INPUT_DISABLE_B, temp_word);
155 
156  this->ddr_mask_ &= ~(1 << pin); // 0=output
157  this->write_byte_16(REG_DIR_B, this->ddr_mask_);
158 
159  this->read_byte(REG_CLOCK, &temp_byte);
160  temp_byte |= (1 << 6); // Internal 2MHz oscillator part 1 (set bit 6)
161  temp_byte &= ~(1 << 5); // Internal 2MHz oscillator part 2 (clear bit 5)
162  this->write_byte(REG_CLOCK, temp_byte);
163 
164  this->read_byte(REG_MISC, &temp_byte);
165  temp_byte &= ~(1 << 7); // set linear mode bank B
166  temp_byte &= ~(1 << 3); // set linear mode bank A
167  temp_byte |= 0x70; // Frequency of the LED Driver clock ClkX of all IOs:
168  this->write_byte(REG_MISC, temp_byte);
169 
170  this->read_byte_16(REG_LED_DRIVER_ENABLE_B, &temp_word);
171  temp_word |= (1 << pin);
172  this->write_byte_16(REG_LED_DRIVER_ENABLE_B, temp_word);
173 
174  this->read_byte_16(REG_DATA_B, &temp_word);
175  temp_word &= ~(1 << pin);
176  output_state_ &= ~(1 << pin);
177  this->write_byte_16(REG_DATA_B, temp_word);
178 }
179 
180 void SX1509Component::clock_(uint8_t osc_source, uint8_t osc_pin_function, uint8_t osc_freq_out, uint8_t osc_divider) {
181  osc_source = (osc_source & 0b11) << 5; // 2-bit value, bits 6:5
182  osc_pin_function = (osc_pin_function & 1) << 4; // 1-bit value bit 4
183  osc_freq_out = (osc_freq_out & 0b1111); // 4-bit value, bits 3:0
184  uint8_t reg_clock = osc_source | osc_pin_function | osc_freq_out;
185  this->write_byte(REG_CLOCK, reg_clock);
186 
187  osc_divider = clamp<uint8_t>(osc_divider, 1, 7u);
188  this->clk_x_ = 2000000;
189  osc_divider = (osc_divider & 0b111) << 4; // 3-bit value, bits 6:4
190 
191  uint8_t reg_misc = 0;
192  this->read_byte(REG_MISC, &reg_misc);
193  reg_misc &= ~(0b111 << 4);
194  reg_misc |= osc_divider;
195  this->write_byte(REG_MISC, reg_misc);
196 }
197 
199  uint8_t temp_byte = 0;
200 
201  // setup row/col pins for INPUT OUTPUT
202  this->read_byte_16(REG_DIR_B, &this->ddr_mask_);
203  for (int i = 0; i < this->rows_; i++)
204  this->ddr_mask_ &= ~(1 << i);
205  for (int i = 8; i < (this->cols_ * 2); i++)
206  this->ddr_mask_ |= (1 << i);
207  this->write_byte_16(REG_DIR_B, this->ddr_mask_);
208 
209  this->read_byte(REG_OPEN_DRAIN_A, &temp_byte);
210  for (int i = 0; i < this->rows_; i++)
211  temp_byte |= (1 << i);
212  this->write_byte(REG_OPEN_DRAIN_A, temp_byte);
213 
214  this->read_byte(REG_PULL_UP_B, &temp_byte);
215  for (int i = 0; i < this->cols_; i++)
216  temp_byte |= (1 << i);
217  this->write_byte(REG_PULL_UP_B, temp_byte);
218 
219  if (debounce_time_ >= scan_time_) {
220  debounce_time_ = scan_time_ >> 1; // Force debounce_time to be less than scan_time
221  }
222  set_debounce_keypad_(debounce_time_, rows_, cols_);
223  uint8_t scan_time_bits = 0;
224  for (uint8_t i = 7; i > 0; i--) {
225  if (scan_time_ & (1 << i)) {
226  scan_time_bits = i;
227  break;
228  }
229  }
230  scan_time_bits &= 0b111; // Scan time is bits 2:0
231  temp_byte = sleep_time_ | scan_time_bits;
232  this->write_byte(REG_KEY_CONFIG_1, temp_byte);
233  rows_ = (rows_ - 1) & 0b111; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc.
234  cols_ = (cols_ - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc.
235  this->write_byte(REG_KEY_CONFIG_2, (rows_ << 3) | cols_);
236 }
237 
239  uint16_t key_data = 0;
240  this->read_byte_16(REG_KEY_DATA_1, &key_data);
241  return (0xFFFF ^ key_data);
242 }
243 
244 void SX1509Component::set_debounce_config_(uint8_t config_value) {
245  // First make sure clock is configured
246  uint8_t temp_byte = 0;
247  this->read_byte(REG_MISC, &temp_byte);
248  temp_byte |= (1 << 4); // Just default to no divider if not set
249  this->write_byte(REG_MISC, temp_byte);
250  this->read_byte(REG_CLOCK, &temp_byte);
251  temp_byte |= (1 << 6); // default to internal osc.
252  this->write_byte(REG_CLOCK, temp_byte);
253 
254  config_value &= 0b111; // 3-bit value
255  this->write_byte(REG_DEBOUNCE_CONFIG, config_value);
256 }
257 
259  uint8_t config_value = 0;
260 
261  for (int i = 7; i >= 0; i--) {
262  if (time & (1 << i)) {
263  config_value = i + 1;
264  break;
265  }
266  }
267  config_value = clamp<uint8_t>(config_value, 0, 7);
268 
269  set_debounce_config_(config_value);
270 }
271 
273  uint16_t debounce_enable = 0;
274  this->read_byte_16(REG_DEBOUNCE_ENABLE_B, &debounce_enable);
275  debounce_enable |= (1 << pin);
276  this->write_byte_16(REG_DEBOUNCE_ENABLE_B, debounce_enable);
277 }
278 
280 
281 void SX1509Component::set_debounce_keypad_(uint8_t time, uint8_t num_rows, uint8_t num_cols) {
282  set_debounce_time_(time);
283  for (uint16_t i = 0; i < num_rows; i++)
285  for (uint16_t i = 0; i < (8 + num_cols); i++)
287 }
288 
289 } // namespace sx1509
290 } // namespace esphome
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition: i2c.h:235
bool read_byte_16(uint8_t a_register, uint16_t *data)
Definition: i2c.h:246
void set_debounce_config_(uint8_t config_value)
Definition: sx1509.cpp:244
const uint8_t REG_RESET
const uint8_t REG_KEY_DATA_1
const uint8_t REG_MISC
bool is_failed() const
Definition: component.cpp:143
const uint8_t REG_DATA_B
const uint8_t REG_LED_DRIVER_ENABLE_B
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
const uint8_t REG_DEBOUNCE_ENABLE_B
const uint8_t INTERNAL_CLOCK_2MHZ
Definition: sx1509.h:15
const char *const TAG
Definition: spi.cpp:8
void pin_mode(uint8_t pin, gpio::Flags flags)
Definition: sx1509.cpp:88
const uint8_t REG_INPUT_DISABLE_B
const uint8_t REG_DIR_B
void clock_(uint8_t osc_source=2, uint8_t osc_pin_function=1, uint8_t osc_freq_out=0, uint8_t osc_divider=0)
Definition: sx1509.cpp:180
const uint8_t REG_KEY_CONFIG_1
const uint32_t min_loop_period_
Definition: sx1509.h:74
std::vector< SX1509Processor * > keypad_binary_sensors_
Definition: sx1509.h:71
const uint8_t REG_INTERRUPT_MASK_A
void set_debounce_enable_(uint8_t pin)
Definition: sx1509.cpp:272
const uint32_t flags
Definition: stm32flash.h:85
void set_debounce_time_(uint8_t time)
Definition: sx1509.cpp:258
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:262
void setup_led_driver(uint8_t pin)
Definition: sx1509.cpp:148
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
void set_debounce_pin_(uint8_t pin)
Definition: sx1509.cpp:279
void dump_config() override
Definition: sx1509.cpp:35
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
void digital_write(uint8_t pin, bool bit_value)
Definition: sx1509.cpp:65
void set_debounce_keypad_(uint8_t time, uint8_t num_rows, uint8_t num_cols)
Definition: sx1509.cpp:281
const uint8_t REG_PULL_UP_B
const uint8_t REG_CLOCK
bool write_byte_16(uint8_t a_register, uint16_t data)
Definition: i2c.h:266
const uint8_t REG_KEY_CONFIG_2
const uint8_t REG_OPEN_DRAIN_A
bool digital_read(uint8_t pin)
Definition: sx1509.cpp:54
const uint8_t REG_OPEN_DRAIN_B
const uint8_t REG_PULL_DOWN_B
const uint8_t REG_DEBOUNCE_CONFIG