SensorLib 0.5.1
Multi-platform sensor driver library for Arduino, PlatformIO, and ESP-IDF
Loading...
Searching...
No Matches
IoExpanderAW9523.hpp
Go to the documentation of this file.
1
30#pragma once
31
32#include "../SensorBuildOpt.h"
33#if !SENSORLIB_EXCLUDE_IO_EXPANDER_AW9523
34
36#include "IoExpanderBase.hpp"
37
39static constexpr uint8_t AW9523_UNKNOWN_ADDRESS = 0xFF;
40
42static constexpr uint8_t AW9523_SLAVE_ADDRESS0 = 0x58;
43static constexpr uint8_t AW9523_SLAVE_ADDRESS1 = 0x59;
44static constexpr uint8_t AW9523_SLAVE_ADDRESS2 = 0x5A;
45static constexpr uint8_t AW9523_SLAVE_ADDRESS3 = 0x5B;
46
48static constexpr uint8_t AW9523_DEFAULT_ADDRESS = AW9523_SLAVE_ADDRESS0;
49
59{
60public:
61 enum Port : uint16_t {
62 PORT0 = 0x00FF,
63 PORT1 = 0xFF00,
64 PORT_ALL = 0xFFFF
65 };
66
68 enum class LedCurrentRange : uint8_t {
69 FULL = 0,
71 HALF = 2,
72 QUARTER = 3
73 };
74
75 IoExpanderAW9523() : IoExpanderBase(AW9523_PINS_COUNT) {}
76 ~IoExpanderAW9523() = default;
77
83 void digitalWritePort(uint16_t mask, uint16_t values) override
84 {
85 if (!_outputStates) {
86 SENSORLIB_LOG_E("Output states array not initialized");
87 return;
88 }
89
90 const uint8_t lowMask = mask & 0xFF;
91 const uint8_t highMask = (mask >> 8) & 0xFF;
92 bool lowWritten = false;
93 bool highWritten = false;
94
95 if (lowMask) {
96 lowWritten = updateMaskedRegister(AW9523_REG_OUTPUT0, lowMask, values & 0xFF);
97 }
98 if (highMask) {
99 highWritten = updateMaskedRegister(AW9523_REG_OUTPUT1, highMask, (values >> 8) & 0xFF);
100 }
101
102 for (uint8_t pin = 0; pin < AW9523_PINS_COUNT; ++pin) {
103 const bool portWritten = pin < 8 ? lowWritten : highWritten;
104 if (portWritten && (mask & (1UL << pin))) {
105 _outputStates[pin] = (values >> pin) & 1;
106 }
107 }
108 }
109
115 uint16_t digitalReadPort() override
116 {
117 const int port0 = readReg(AW9523_REG_INPUT0);
118 const int port1 = readReg(AW9523_REG_INPUT1);
119 if (port0 < 0 || port1 < 0) {
120 SENSORLIB_LOG_E("Failed to read AW9523 input ports");
121 return 0;
122 }
123 return (static_cast<uint16_t>(port1) << 8) | static_cast<uint8_t>(port0);
124 }
125
131 bool setPort0PushPull(bool pushPull)
132 {
133 const int control = readReg(AW9523_REG_GLOBAL_CONTROL);
134 if (control < 0) {
135 return false;
136 }
137 const uint8_t value = (control & AW9523_GLOBAL_CURRENT_MASK) |
138 (pushPull ? AW9523_GLOBAL_PUSH_PULL : 0);
139 return writeReg(AW9523_REG_GLOBAL_CONTROL, value) >= 0;
140 }
141
148 {
149 const uint8_t rangeValue = static_cast<uint8_t>(range);
150 if (rangeValue > static_cast<uint8_t>(LedCurrentRange::QUARTER)) {
151 SENSORLIB_LOG_E("Invalid AW9523 LED current range");
152 return false;
153 }
154
155 const int control = readReg(AW9523_REG_GLOBAL_CONTROL);
156 if (control < 0) {
157 return false;
158 }
159 const uint8_t value = (control & AW9523_GLOBAL_PUSH_PULL) | rangeValue;
160 return writeReg(AW9523_REG_GLOBAL_CONTROL, value) >= 0;
161 }
162
169 bool setLedMode(uint8_t pin, bool enable = true)
170 {
171 if (!isValidPin(pin)) {
172 return false;
173 }
174 return setLedModes(static_cast<uint16_t>(1UL << pin), enable);
175 }
176
183 bool setLedModes(uint16_t pinMask, bool enable = true)
184 {
185 const uint8_t lowMask = pinMask & 0xFF;
186 const uint8_t highMask = (pinMask >> 8) & 0xFF;
187
188 // LED channels are outputs. Configure direction before changing mode.
189 if (enable && !setDirections(pinMask, OUTPUT)) {
190 return false;
191 }
192 if (lowMask && !updateMaskedRegister(AW9523_REG_LED_MODE0, lowMask, enable ? 0 : lowMask)) {
193 return false;
194 }
195 if (highMask && !updateMaskedRegister(AW9523_REG_LED_MODE1, highMask, enable ? 0 : highMask)) {
196 return false;
197 }
198 return true;
199 }
200
207 bool analogWrite(uint8_t pin, uint8_t value)
208 {
209 if (!isValidPin(pin)) {
210 return false;
211 }
212 return writeReg(ledCurrentRegister(pin), value) >= 0;
213 }
214
221 bool enableInterrupt(uint8_t pin, bool enable = true)
222 {
223 if (!isValidPin(pin)) {
224 return false;
225 }
226 return enableInterrupts(static_cast<uint16_t>(1UL << pin), enable);
227 }
228
238 bool enableInterrupts(uint16_t pinMask, bool enable = true)
239 {
240 const uint8_t lowMask = pinMask & 0xFF;
241 const uint8_t highMask = (pinMask >> 8) & 0xFF;
242 if (lowMask && !updateMaskedRegister(AW9523_REG_INTERRUPT0, lowMask, enable ? 0 : lowMask)) {
243 return false;
244 }
245 if (highMask && !updateMaskedRegister(AW9523_REG_INTERRUPT1, highMask, enable ? 0 : highMask)) {
246 return false;
247 }
248 return true;
249 }
250
256 {
257 if (writeReg(AW9523_REG_SOFTWARE_RESET, 0x00) < 0) {
258 return false;
259 }
260 return syncGpioState();
261 }
262
263protected:
264 void pinModeImpl(uint8_t pin, uint8_t mode) override
265 {
266 const uint8_t bit = pin % 8;
267 const uint8_t modeReg = pin < 8 ? AW9523_REG_LED_MODE0 : AW9523_REG_LED_MODE1;
268 const uint8_t configReg = pin < 8 ? AW9523_REG_CONFIG0 : AW9523_REG_CONFIG1;
269
270 // A digital pin operation always selects GPIO mode.
271 if (!updateMaskedRegister(modeReg, 1U << bit, 1U << bit)) {
272 SENSORLIB_LOG_E("Failed to select AW9523 GPIO mode for pin %u", pin);
273 return;
274 }
275 if (!updateMaskedRegister(configReg, 1U << bit, mode == INPUT ? 1U << bit : 0)) {
276 SENSORLIB_LOG_E("Failed to configure AW9523 pin %u", pin);
277 }
278 }
279
280 void configPinsImpl(uint16_t pinMask, uint8_t mode) override
281 {
282 const uint8_t lowMask = pinMask & 0xFF;
283 const uint8_t highMask = (pinMask >> 8) & 0xFF;
284
285 // A digital pin operation always selects GPIO mode.
286 if (lowMask && !updateMaskedRegister(AW9523_REG_LED_MODE0, lowMask, lowMask)) {
287 SENSORLIB_LOG_E("Failed to select AW9523 P0 GPIO mode");
288 return;
289 }
290 if (highMask && !updateMaskedRegister(AW9523_REG_LED_MODE1, highMask, highMask)) {
291 SENSORLIB_LOG_E("Failed to select AW9523 P1 GPIO mode");
292 return;
293 }
294 if (!setDirections(pinMask, mode)) {
295 SENSORLIB_LOG_E("Failed to configure AW9523 pin directions");
296 }
297 }
298
299 void digitalWriteImpl(uint8_t pin, bool value) override
300 {
301 const uint8_t reg = pin < 8 ? AW9523_REG_OUTPUT0 : AW9523_REG_OUTPUT1;
302 const uint8_t bitMask = 1U << (pin % 8);
303 if (!updateMaskedRegister(reg, bitMask, value ? bitMask : 0)) {
304 SENSORLIB_LOG_E("Failed to write AW9523 pin %u", pin);
305 }
306 }
307
308 bool digitalReadImpl(uint8_t pin) override
309 {
310 const uint8_t reg = pin < 8 ? AW9523_REG_INPUT0 : AW9523_REG_INPUT1;
311 const int value = readReg(reg);
312 if (value < 0) {
313 SENSORLIB_LOG_E("Failed to read AW9523 pin %u", pin);
314 return false;
315 }
316 return (value >> (pin % 8)) & 1;
317 }
318
319 bool initImpl(uint8_t address) override
320 {
321 if (address == AW9523_UNKNOWN_ADDRESS) {
322 for (uint8_t candidate = AW9523_SLAVE_ADDRESS0; candidate <= AW9523_SLAVE_ADDRESS3; ++candidate) {
323 setAddress(candidate);
324 if (readReg(AW9523_REG_CHIP_ID) == AW9523_CHIP_ID) {
325 _addr = candidate;
326 SENSORLIB_LOG_D("Found AW9523 at address 0x%" PRIx8, candidate);
327 return syncGpioState();
328 }
329 }
330 SENSORLIB_LOG_E("Failed to find AW9523");
331 return false;
332 }
333
334 if (address < AW9523_SLAVE_ADDRESS0 || address > AW9523_SLAVE_ADDRESS3) {
335 SENSORLIB_LOG_E("Invalid AW9523 address 0x%" PRIx8, address);
336 return false;
337 }
338 if (readReg(AW9523_REG_CHIP_ID) != AW9523_CHIP_ID) {
339 SENSORLIB_LOG_E("AW9523 chip ID mismatch");
340 return false;
341 }
342 return syncGpioState();
343 }
344
345private:
346 bool isValidPin(uint8_t pin) const
347 {
348 if (pin >= AW9523_PINS_COUNT) {
349 SENSORLIB_LOG_E("Invalid pin number, pin range is 0-%u", AW9523_PINS_COUNT - 1);
350 return false;
351 }
352 return true;
353 }
354
355 bool updateMaskedRegister(uint8_t reg, uint8_t mask, uint8_t value)
356 {
357 const int current = readReg(reg);
358 if (current < 0) {
359 return false;
360 }
361 const uint8_t updated = (static_cast<uint8_t>(current) & ~mask) | (value & mask);
362 return updated == static_cast<uint8_t>(current) || writeReg(reg, updated) >= 0;
363 }
364
365 bool setDirections(uint16_t pinMask, uint8_t mode)
366 {
367 if (!_pinModes) {
368 SENSORLIB_LOG_E("Pin modes array not initialized");
369 return false;
370 }
371 const uint8_t lowMask = pinMask & 0xFF;
372 const uint8_t highMask = (pinMask >> 8) & 0xFF;
373 if (lowMask && !updateMaskedRegister(AW9523_REG_CONFIG0, lowMask, mode == INPUT ? lowMask : 0)) {
374 return false;
375 }
376 if (highMask && !updateMaskedRegister(AW9523_REG_CONFIG1, highMask, mode == INPUT ? highMask : 0)) {
377 return false;
378 }
379 for (uint8_t pin = 0; pin < AW9523_PINS_COUNT; ++pin) {
380 if (pinMask & (1UL << pin)) {
381 _pinModes[pin] = mode;
382 }
383 }
384 return true;
385 }
386
387 bool syncGpioState()
388 {
389 if (!_pinModes || !_outputStates) {
390 SENSORLIB_LOG_E("AW9523 state arrays not initialized");
391 return false;
392 }
393 const int config0 = readReg(AW9523_REG_CONFIG0);
394 const int config1 = readReg(AW9523_REG_CONFIG1);
395 const int output0 = readReg(AW9523_REG_OUTPUT0);
396 const int output1 = readReg(AW9523_REG_OUTPUT1);
397 if (config0 < 0 || config1 < 0 || output0 < 0 || output1 < 0) {
398 SENSORLIB_LOG_E("Failed to synchronize AW9523 GPIO state");
399 return false;
400 }
401
402 const uint16_t configs = (static_cast<uint16_t>(config1) << 8) | static_cast<uint8_t>(config0);
403 const uint16_t outputs = (static_cast<uint16_t>(output1) << 8) | static_cast<uint8_t>(output0);
404 for (uint8_t pin = 0; pin < AW9523_PINS_COUNT; ++pin) {
405 _pinModes[pin] = (configs & (1UL << pin)) ? INPUT : OUTPUT;
406 _outputStates[pin] = (outputs >> pin) & 1;
407 }
408 return true;
409 }
410
411 static uint8_t ledCurrentRegister(uint8_t pin)
412 {
413 if (pin < 8) {
414 return AW9523_REG_DIM_P0_BASE + pin;
415 }
416 if (pin < 12) {
417 return AW9523_REG_DIM_P1_LOW_BASE + pin - 8;
418 }
419 return AW9523_REG_DIM_P1_HIGH_BASE + pin - 12;
420 }
421
422 static constexpr uint8_t AW9523_PINS_COUNT = 16;
423 static constexpr uint8_t AW9523_CHIP_ID = 0x23;
424
425 static constexpr uint8_t AW9523_REG_INPUT0 = 0x00;
426 static constexpr uint8_t AW9523_REG_INPUT1 = 0x01;
427 static constexpr uint8_t AW9523_REG_OUTPUT0 = 0x02;
428 static constexpr uint8_t AW9523_REG_OUTPUT1 = 0x03;
429 static constexpr uint8_t AW9523_REG_CONFIG0 = 0x04;
430 static constexpr uint8_t AW9523_REG_CONFIG1 = 0x05;
431 static constexpr uint8_t AW9523_REG_INTERRUPT0 = 0x06;
432 static constexpr uint8_t AW9523_REG_INTERRUPT1 = 0x07;
433 static constexpr uint8_t AW9523_REG_CHIP_ID = 0x10;
434 static constexpr uint8_t AW9523_REG_GLOBAL_CONTROL = 0x11;
435 static constexpr uint8_t AW9523_REG_LED_MODE0 = 0x12;
436 static constexpr uint8_t AW9523_REG_LED_MODE1 = 0x13;
437 static constexpr uint8_t AW9523_REG_DIM_P1_LOW_BASE = 0x20;
438 static constexpr uint8_t AW9523_REG_DIM_P0_BASE = 0x24;
439 static constexpr uint8_t AW9523_REG_DIM_P1_HIGH_BASE = 0x2C;
440 static constexpr uint8_t AW9523_REG_SOFTWARE_RESET = 0x7F;
441
442 static constexpr uint8_t AW9523_GLOBAL_CURRENT_MASK = 0x03;
443 static constexpr uint8_t AW9523_GLOBAL_PUSH_PULL = 0x10;
444};
445
446#endif
@license MIT License
@license MIT License
#define SENSORLIB_LOG_E(...)
#define SENSORLIB_LOG_D(...)
Driver for the AW9523/AW9523B 16-channel GPIO and LED controller.
~IoExpanderAW9523()=default
LedCurrentRange
Global maximum LED current as a fraction of the typical 37 mA IMAX.
bool initImpl(uint8_t address) override
Chip‑specific initialization.
void digitalWriteImpl(uint8_t pin, bool value) override
Hardware‑specific implementation of digitalWrite().
bool enableInterrupts(uint16_t pinMask, bool enable=true)
Enable or disable input-change interrupts for selected pins.
bool digitalReadImpl(uint8_t pin) override
Hardware‑specific implementation of digitalRead().
bool setLedCurrentRange(LedCurrentRange range)
Set the global maximum current range for all LED channels.
bool setLedMode(uint8_t pin, bool enable=true)
Enable or disable constant-current LED mode on one pin.
bool setPort0PushPull(bool pushPull)
Select the P0 GPIO output driver type.
void configPinsImpl(uint16_t pinMask, uint8_t mode) override
Hardware‑specific implementation of batch pin mode configuration.
bool setLedModes(uint16_t pinMask, bool enable=true)
Enable or disable constant-current LED mode on selected pins.
uint16_t digitalReadPort() override
Read both GPIO input ports.
bool softwareReset()
Restore all registers to their hardware defaults over I2C.
void pinModeImpl(uint8_t pin, uint8_t mode) override
Hardware‑specific implementation of pinMode().
bool analogWrite(uint8_t pin, uint8_t value)
Set one constant-current LED channel's brightness.
bool enableInterrupt(uint8_t pin, bool enable=true)
Enable or disable input-change interrupt generation for one pin.
void digitalWritePort(uint16_t mask, uint16_t values) override
Write selected GPIO outputs while preserving unselected pins.
Abstract base class for GPIO expander devices.
std::unique_ptr< bool[]> _outputStates
Cached output states (true = HIGH)
std::unique_ptr< uint8_t[]> _pinModes
Cached pin modes (INPUT/OUTPUT)
int readReg(uint8_t reg) const
int writeReg(uint8_t reg, uint8_t val)
void setAddress(uint8_t address)