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