diff --git a/CHANGELOG.md b/CHANGELOG.md index 21dc9df14..4defcfc7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Support deep sleep (standby) for VL53L0X (#22441) - Support for MS5837 pressure and temperature sensor (#22376) - Berry add I2C read16/write16 supporting Little Endian (#22448) +- Berry drivers for PCA9535 (generic and in SenseCAP D1) ### Breaking Changed diff --git a/lib/libesp32/berry_tasmota/src/be_i2c_axp192_axp202_lib.c b/lib/libesp32/berry_tasmota/src/be_i2c_axp192_axp202_lib.c index aec926077..f121b1708 100644 --- a/lib/libesp32/berry_tasmota/src/be_i2c_axp192_axp202_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_i2c_axp192_axp202_lib.c @@ -1,5 +1,5 @@ /******************************************************************** - * Tasmota LVGL lv_signal_bars widget + * Drivers for AXP192 and AXP202 I2C Solidified *******************************************************************/ #include "solidify/solidified_i2c_axp192.h" #include "solidify/solidified_i2c_axp202.h" diff --git a/tasmota/berry/drivers/PCA9535.be b/tasmota/berry/drivers/PCA9535.be new file mode 100644 index 000000000..e5328caa4 --- /dev/null +++ b/tasmota/berry/drivers/PCA9535.be @@ -0,0 +1,82 @@ +################################################################################# +# Generic driver for PCA9535 - solidified +# +# I2C IO Expander, similar to PCA9557 +# Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA9535_PCA9535C.pdf +# +# This expander is used in SeedStudio SenseCAP D1 +################################################################################# + +#@ solidify:PCA9535 +class PCA9535 : I2C_Driver + var last_read # time when last read (avoid reading too often) + var input_port # shadow of registers 0+1 with state of input registers + var output_port # shadow of registers 2+3 with known outputs + var inversion_port # shadow of registers 4+5 with input inversion + var config_port # shadow of registers 6+7 with Input/Output configurations + + def init(address) + if (address == nil) address = 0x20 end # default address is 0x20 + super(self).init("PCA9535", address) + self.last_read = 0 + if self.wire + # if detected + self.read_all() + end + end + + def read_all() + var now = tasmota.millis() + if (now - self.last_read > 10) # if last read was more than 10 ms in the past + self.input_port = self.read16LE(0x00) + self.output_port = self.read16LE(0x02) + self.inversion_port = self.read16LE(0x04) + self.config_port = self.read16LE(0x06) + self.last_read = tasmota.millis() + if tasmota.loglevel(4) + log(f"I2C: PCA9535 read input(0x{self.input_port:04X}) output(0x{self.output_port:04X}) inversion(0x{self.inversion_port:04X}) config(0x{self.config_port:04X})") + end + end + end + + def config_all(v) + self.config_port = int(v) + self.write16LE(0x06, self.config_port) + end + # port: 0..15 + # mode: 0=output 1=input + def config_gpio(port, mode) + self.read_all() + if (mode != 0 && mode != 1) raise "value_error", f"mode muste be 0 or 1" end + var config_new = self._bit_set_to(self.config_port, port, mode) + # write only if value changed + if config_new != self.config_port + self.config_all(config_new) + end + end + + def read_gpio(port) + self.read_all() + return (self.input_port & (1 << port)) ? 1 : 0 + end + + def write_gpio(port, v) + self.read_all() + v = (v ? 1 : 0) # ensure we have only 0/1 as values + var output_new = self._bit_set_to(self.output_port, port, v) + if output_new != self.output_port + self.output_port = output_new + self.write16LE(0x02, self.output_port) + end + end + + # inspired from https://stackoverflow.com/questions/47981/how-to-set-clear-and-toggle-a-single-bit + # num: int value + # n: bit to change + # x: value 0/1 + static def _bit_set_to(num, n, x) + return (num & ~(1 << n)) | (x << n) + end +end + +return PCA9535 diff --git a/tasmota/berry/drivers/PCA9535_SenseCAP_D1.be b/tasmota/berry/drivers/PCA9535_SenseCAP_D1.be new file mode 100644 index 000000000..7afd9d928 --- /dev/null +++ b/tasmota/berry/drivers/PCA9535_SenseCAP_D1.be @@ -0,0 +1,42 @@ +################################################################################# +# Specialized driver for SeedStudio SenseCAP D1 display +################################################################################# + +import PCA9535 + +class PCA9535_SenseCAP_D1 : PCA9535 + def init() + super(self).init(0x20) + + if self.wire + self.write_gpio(0x05, 1) # set IO0.5 (LCD_RESET) high + self.write_gpio(0x07, 1) # set IO0.7 (TOUCH_RESET) high + self.write_gpio(0x08, 1) # set IO1.0 (RP2040_RESET) high + self.config_gpio(0x05, 0) # configure IO0.5 (LCD_RESET) as output + self.config_gpio(0x07, 0) # configure IO0.7 (TOUCH_RESET) as output + self.config_gpio(0x08, 0) # configure IO1.0 (RP2040_RESET) as output + # reset display at boot + self.reset_display() + end + end + + # hardware reset for the MCU RP2040 + def reset_RP2040() + self.write_gpio(0x08, 0) # drive RESET Low + tasmota.delay(2) # the recommended delay is 1ms, we take some margin + self.write_gpio(0x08, 1) # drive RESET High + end + + # reset both display and touch screen + def reset_display() + self.write_gpio(0x05, 0) + self.write_gpio(0x07, 0) + tasmota.delay(2) + self.write_gpio(0x05, 1) + self.write_gpio(0x07, 1) + tasmota.delay(50) + end + +end + +return PCA9535_SenseCAP_D1()