diff --git a/internal_filesystem/lib/drivers/fri3d/device.py b/internal_filesystem/lib/drivers/fri3d/device.py new file mode 100644 index 00000000..84f5c76f --- /dev/null +++ b/internal_filesystem/lib/drivers/fri3d/device.py @@ -0,0 +1,26 @@ +import struct + +from micropython import const +from machine import I2C + +# common registers +_REG_VERSION = const(0x00) + + +class Device: + """Fri3d I2C device.""" + + def __init__(self, i2c_bus: I2C, address: int): + """Read from a sensor on the given I2C bus, at the given address.""" + self.i2c = i2c_bus + self.address = address + + def _read(self, format, reg, amount): + return struct.unpack(format, self.i2c.readfrom_mem(self.address, reg, amount)) + + def _write(self, reg, value): + self.i2c.writeto_mem(self.address, reg, value, addrsize=8) + + @property + def version(self) -> tuple[int, int, int]: + return self._read("BBB", _REG_VERSION, 3) diff --git a/internal_filesystem/lib/drivers/fri3d/expander.py b/internal_filesystem/lib/drivers/fri3d/expander.py new file mode 100644 index 00000000..c24cbe6f --- /dev/null +++ b/internal_filesystem/lib/drivers/fri3d/expander.py @@ -0,0 +1,89 @@ +import struct + +from micropython import const +from machine import I2C, Pin + +from .device import Device + +# registers +_EXPANDER_REG_INPUTS = const(0x04) +_EXPANDER_REG_ANALOG = const(0x06) +_EXPANDER_REG_LCD_BRIGHTNESS = const(0x12) +_EXPANDER_REG_DEBUG_LED = const(0x14) +_EXPANDER_REG_CONFIG = const(0x16) + +_EXPANDER_I2CADDR_DEFAULT = const(0x50) + + +class Expander(Device): + """Fri3d Badge 2026 expander MCU.""" + + def __init__( + self, + i2c_bus: I2C, + address: int = _EXPANDER_I2CADDR_DEFAULT, + int_pin: Pin = None, + ): + """Read from a sensor on the given I2C bus, at the given address.""" + Device.__init__(self, i2c_bus, address) + self.use_interrupt = False + if int_pin: + self.use_interrupt = True + self._rx_buf = bytearray(2) + self._rx_mv = memoryview(self._rx_buf) + self.int_pin = int_pin + self.i2c.readfrom_mem_into(self.address, _EXPANDER_REG_INPUTS, self._rx_mv) + self.int_pin.irq(trigger=Pin.IRQ_RISING, handler=self.int_callback) + + def int_callback(self, p): + self.i2c.readfrom_mem_into(self.address, _EXPANDER_REG_INPUTS, self._rx_mv) + + @property + def analog(self) -> tuple[int, int, int, int, int, int]: + """Read the analog inputs: ain1, ain0, battery_monitor, usb_monitor, joystick_y, joystick_x""" + return self._read(" tuple[bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool]: + """Read the digital inputs: usb_plugged, joy_right, joy_left, joy_down, joy_up, button_menu, button_b, button_a, button_y, button_x, charger_standby, charger_charging""" + if self.use_interrupt: + inputs = struct.unpack(" int: + """Read the LCD brightness state (0-100)""" + return self._read("= 0 and value <= 100: + self._write(_EXPANDER_REG_LCD_BRIGHTNESS, struct.pack(" int: + """Read the Debug LED state (0-100)""" + return self._read("= 0 and value <= 100: + self._write(_EXPANDER_REG_DEBUG_LED, struct.pack(" tuple[bool, bool, bool]: + """Read the configuration bits: reboot, lcd_reset, aux_power""" + config = self._read("B", _EXPANDER_REG_CONFIG, 1)[0] + return tuple([bool(int(digit)) for digit in "{:08b}".format(config)[5:]]) + + @config.setter + def config(self, value: int): + """set the configuration byte""" + if value >= 0 and value <= 0xFF: + self._write(_EXPANDER_REG_CONFIG, struct.pack("B", value))