This repository contains a MicroPython driver for the PCA9698, a 40-bit I/O port expander with interrupt and reset features. The driver allows for easy control and configuration of the PCA9698's input and output ports via I2C communication.
- Set pin and port modes (input/output)
- Read and write individual pins
- Read entire ports
- Toggle all output ports
- Interrupt handling
To use this driver, instantiate the PCA9698
class with an I2C object and the I2C address of the PCA9698 device. Then, use the provided methods to control the I/O ports.
This MicroPython driver is adapted from an Arduino version of the PCA9698 driver provided by Justin-Pl.
PCA9698.cpp
PCA9698.h
from machine import SoftI2C, Pin
import PCA9698
import time
#DRV8833_MOTOR_DRIVER_STBY = Pin(17, Pin.OUT)
#DRV8833_MOTOR_DRIVER_STBY.value(1)
PCA9698_OE_ = Pin(48, Pin.OUT) # active LOW
PCA9698_OE_.value(0) # Set the OE pin to low to enable the output
i2c = SoftI2C(scl=Pin(3), sda=Pin(2), freq=400000) # Initialize I2C with SCL on Pin 3, SDA on Pin 2, and frequency of 400kHz
devices = i2c.scan() # Scan for devices on the I2C bus
print("found devices:", [hex(device) for device in devices]) # Print the addresses of found devices in hexadecimal format
pca_1 = PCA9698.PCA9698(iic=i2c, address=0x20) # Create an instance of PCA9698 with I2C address 0x20
#pca_2 = PCA9698.PCA9698(iic=i2c, address=0x21)
#pca_3 = PCA9698.PCA9698(iic=i2c, address=0x22)
pcas = [pca_1]#[pca_1, pca_2, pca_3]
for pca_num, pca in enumerate(pcas):
print(f"Set all ports to mode 'input'")
pca.set_ports_mode([0,0,0,0,0]) # Set all ports of PCA9698 to output mode
pca.update_all()
for port_num in range(5):
mode_dict = {0x00: "output", 0xFF: "input"}
print(f"pca_{pca_num}:port {port_num} mode: {mode_dict.get(pca.read_port_mode(port_num), 'unknown')}")
print(f"Set all ports to mode 'output'")
pca.set_ports_mode([1,1,1,1,1]) # Set all ports of PCA9698 to output mode
pca.update_all()
for port_num in range(5):
mode_dict = {0x00: "output", 0xFF: "input"}
print(f"pca_{pca_num}:port {port_num} mode: {mode_dict.get(pca.read_port_mode(port_num), 'unknown')}")
#pca_1.set_port_mode(port_num=0, mode=1)
#pca_1.set_port_mode(port_num=1, mode=1)
#pca_1.set_port_mode(port_num=2, mode=1)
#pca_1.set_port_mode(port_num=3, mode=1)
#pca_1.set_port_mode(port_num=4, mode=1)
time.sleep(0.5) # Delay for 0.5 seconds
for pca_num, pca in enumerate(pcas):
print(f"pca_{pca_num}:drive on") # Indicate that driving the pins is starting
for pin_num in range(40):
pca.write_pin(pin_num, 1) # Set each pin to high
print(f"pca_{pca_num}: pin:{pin_num}", "read again:", pca.read_pin(pin_num)) # Print the pin number and read back its status
#new_status = pca.write_pin(pin_num, 1) # on
#print(f"pca_{pca_num}: pin:{pin_num}", "new_status:", new_status, "read again:", pca.read_pin(pin_num))
time.sleep(1) # Delay for 1 second
print(f"pca_{pca_num}:drive off") # Indicate that driving the pins is stopping
for pin_num in range(40):
pca.write_pin(pin_num, 0) # Set each pin to low
print(f"pca_{pca_num}: pin:{pin_num}", "read again:", pca.read_pin(pin_num)) # Print the pin number and read back its status
#new_status = pca.write_pin(pin_num, 0) # off
#print(f"pca_{pca_num}: pin:{pin_num}", "new_status:", new_status, "read again:", pca.read_pin(pin_num))
time.sleep(1)
pca.toggle_all_ports()
print(f"pca_{pca_num}:toggle", [pca.read_pin(pin_num) for pin_num in range(40)])
time.sleep(1)
pca.toggle_all_ports()
print(f"pca_{pca_num}:toggle", [pca.read_pin(pin_num) for pin_num in range(40)])
# Testing interrupt functionality
print(f"pca_{pca_num}:Setting up interrupts for PCA9698")
# Enable interrupts for the first 5 pins of the first PCA device
interrupt_enabled = True
for pin_num in range(5):
pca.set_interrupt(pin=pin_num, enable=1)
#print(f"pca9698_{pca_num}:Interrupt enabled on pin {pin_num}")
# Check and print the interrupt mask status
for pin_num in range(5):
mask_status = pca.read_interrupt_mask(pin_num // 8)
#print(f"pca9698_{pca_num}:Interrupt mask status for port {pin_num // 8}: {bin(mask_status)}")
# Simulate pin state change and check for interrupt response
print(f"pca_{pca_num}:Simulating pin state changes and checking interrupt responses")
for pin_num in range(5):
pca.write_pin(pin_num, 1) # Set pin high
time.sleep(0.1) # Short delay to simulate real conditions
pca.write_pin(pin_num, 0) # Set pin low
# Read pin state to confirm if interrupt was triggered
pin_state = pca.read_pin(pin_num)
if pin_state == 0:
#print(f"pca9698_{pca_num}:Pin {pin_num} state correct after toggle, interrupt functionality OK")
pass
else:
print(f"pca_{pca_num}:Pin {pin_num} state incorrect after toggle, interrupt functionality may have issues")
interrupt_enabled = False
if interrupt_enabled:
print(f"pca_{pca_num}:Interrupt functionality normal for all tested pins")
pass
else:
print(f"pca_{pca_num}:Some pins may have issues with interrupt functionality, please check hardware or configuration")
# Disable all interrupts
print(f"pca_{pca_num}: Disabling interrupts")
for pin_num in range(40):
pca.set_interrupt(pin=pin_num, enable=0)
print(f"pca_{pca_num}:Interrupts disabled for all pins")
print("#############")