32#include "../SensorBuildOpt.h"
33#if !SENSORLIB_EXCLUDE_IO_EXPANDER_AW9523
39static constexpr uint8_t AW9523_UNKNOWN_ADDRESS = 0xFF;
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;
48static constexpr uint8_t AW9523_DEFAULT_ADDRESS = AW9523_SLAVE_ADDRESS0;
90 const uint8_t lowMask = mask & 0xFF;
91 const uint8_t highMask = (mask >> 8) & 0xFF;
92 bool lowWritten =
false;
93 bool highWritten =
false;
96 lowWritten = updateMaskedRegister(AW9523_REG_OUTPUT0, lowMask, values & 0xFF);
99 highWritten = updateMaskedRegister(AW9523_REG_OUTPUT1, highMask, (values >> 8) & 0xFF);
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))) {
117 const int port0 =
readReg(AW9523_REG_INPUT0);
118 const int port1 =
readReg(AW9523_REG_INPUT1);
119 if (port0 < 0 || port1 < 0) {
123 return (
static_cast<uint16_t
>(port1) << 8) |
static_cast<uint8_t
>(port0);
133 const int control =
readReg(AW9523_REG_GLOBAL_CONTROL);
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;
149 const uint8_t rangeValue =
static_cast<uint8_t
>(range);
155 const int control =
readReg(AW9523_REG_GLOBAL_CONTROL);
159 const uint8_t value = (control & AW9523_GLOBAL_PUSH_PULL) | rangeValue;
160 return writeReg(AW9523_REG_GLOBAL_CONTROL, value) >= 0;
171 if (!isValidPin(pin)) {
174 return setLedModes(
static_cast<uint16_t
>(1UL << pin), enable);
185 const uint8_t lowMask = pinMask & 0xFF;
186 const uint8_t highMask = (pinMask >> 8) & 0xFF;
189 if (enable && !setDirections(pinMask, OUTPUT)) {
192 if (lowMask && !updateMaskedRegister(AW9523_REG_LED_MODE0, lowMask, enable ? 0 : lowMask)) {
195 if (highMask && !updateMaskedRegister(AW9523_REG_LED_MODE1, highMask, enable ? 0 : highMask)) {
209 if (!isValidPin(pin)) {
212 return writeReg(ledCurrentRegister(pin), value) >= 0;
223 if (!isValidPin(pin)) {
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)) {
245 if (highMask && !updateMaskedRegister(AW9523_REG_INTERRUPT1, highMask, enable ? 0 : highMask)) {
257 if (
writeReg(AW9523_REG_SOFTWARE_RESET, 0x00) < 0) {
260 return syncGpioState();
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;
271 if (!updateMaskedRegister(modeReg, 1U << bit, 1U << bit)) {
275 if (!updateMaskedRegister(configReg, 1U << bit, mode == INPUT ? 1U << bit : 0)) {
282 const uint8_t lowMask = pinMask & 0xFF;
283 const uint8_t highMask = (pinMask >> 8) & 0xFF;
286 if (lowMask && !updateMaskedRegister(AW9523_REG_LED_MODE0, lowMask, lowMask)) {
290 if (highMask && !updateMaskedRegister(AW9523_REG_LED_MODE1, highMask, highMask)) {
294 if (!setDirections(pinMask, mode)) {
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)) {
310 const uint8_t reg = pin < 8 ? AW9523_REG_INPUT0 : AW9523_REG_INPUT1;
311 const int value =
readReg(reg);
316 return (value >> (pin % 8)) & 1;
321 if (address == AW9523_UNKNOWN_ADDRESS) {
322 for (uint8_t candidate = AW9523_SLAVE_ADDRESS0; candidate <= AW9523_SLAVE_ADDRESS3; ++candidate) {
324 if (
readReg(AW9523_REG_CHIP_ID) == AW9523_CHIP_ID) {
327 return syncGpioState();
334 if (address < AW9523_SLAVE_ADDRESS0 || address > AW9523_SLAVE_ADDRESS3) {
338 if (
readReg(AW9523_REG_CHIP_ID) != AW9523_CHIP_ID) {
342 return syncGpioState();
346 bool isValidPin(uint8_t pin)
const
348 if (pin >= AW9523_PINS_COUNT) {
349 SENSORLIB_LOG_E(
"Invalid pin number, pin range is 0-%u", AW9523_PINS_COUNT - 1);
355 bool updateMaskedRegister(uint8_t reg, uint8_t mask, uint8_t value)
357 const int current =
readReg(reg);
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;
365 bool setDirections(uint16_t pinMask, uint8_t mode)
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)) {
376 if (highMask && !updateMaskedRegister(AW9523_REG_CONFIG1, highMask, mode == INPUT ? highMask : 0)) {
379 for (uint8_t pin = 0; pin < AW9523_PINS_COUNT; ++pin) {
380 if (pinMask & (1UL << pin)) {
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) {
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;
411 static uint8_t ledCurrentRegister(uint8_t pin)
414 return AW9523_REG_DIM_P0_BASE + pin;
417 return AW9523_REG_DIM_P1_LOW_BASE + pin - 8;
419 return AW9523_REG_DIM_P1_HIGH_BASE + pin - 12;
422 static constexpr uint8_t AW9523_PINS_COUNT = 16;
423 static constexpr uint8_t AW9523_CHIP_ID = 0x23;
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;
442 static constexpr uint8_t AW9523_GLOBAL_CURRENT_MASK = 0x03;
443 static constexpr uint8_t AW9523_GLOBAL_PUSH_PULL = 0x10;
#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)