TCA9555 PCA9555 micropython ESP32 I2C Driver

网上可查到PCF8575 micropython驱动,但PCF8575没有带LED灯的积木,而9555有带16个LED灯的积木,价格10至11元,没发现MPY驱动,I2C总线,找了一天的资料学习,可以用I2C的内存操作指令i2c.writeto_mem直接测试了,如果控制需求简单,就不需要写驱动,以下供参考。

# I2C总线直接写读PCA9555/TCA9555
from machine import Pin,I2C
import time
# 定义寄存器地址
INPUT_PORT_REG = 0x0
OUTPUT_PORT_REG = 0x2
POLARITY_INVERSION_REG = 0x4
CONFIG_REG = 0x6
 
# I2C构造函数 ESP32引脚可变
i2c=I2C(1,freq=100_000,scl=23,sda=22)
print('I2C ADDRESS',hex(i2c.scan()[0]))
 
# OUTPUT_PORT_REG 必须在 CONFIG_REG 配置OUTPUT前清除记忆,否则有“乱动”危害!!!
print('OUTPUT_PORT_REG 记忆值 ', i2c.readfrom_mem(0x20, 0x02, 2))
print('OUTPUT_PORT_REG 清除了 ', i2c.writeto_mem(0x20, 0x02, b'\xff\xff'))
print('OUTPUT_PORT_REG 记忆值 ', i2c.readfrom_mem(0x20, 0x02, 2))
time.sleep(3)
i2c.writeto_mem(0x20, 0x06, b'\x00\x00') # 写入字节对象
print('CONFIG_REG=', i2c.readfrom_mem(0x20, 0x06, 2)) # 读取配置
print('OUTPUT_PORT_REG=', i2c.readfrom_mem(0x20, 0x02, 2)) # 读取配置
time.sleep(3)

# 简单测试一下点灯
i2c.writeto_mem(0x20, 0x02, b'\xff\xff') # 写入字节对象
time.sleep(1)
i2c.writeto_mem(0x20, 0x02, b'\x00\x00') # 写入字节对象
time.sleep(1)
i2c.writeto_mem(0x20, 0x02, b'\xaa\xaa') # 写入字节对象
time.sleep(1)
i2c.writeto_mem(0x20, 0x02, b'\x55\x55') # 写入字节对象
time.sleep(1)

# 复位包括 CONFIG_REG、OUTPUT_PORT_REG
i2c.writeto_mem(0x20, CONFIG_REG, b'\xff\xff') # 字节对象 = 字节数组 作用相同buffer 只是字节对象不可改变而已
i2c.writeto_mem(0x20, OUTPUT_PORT_REG, b'\xff\xff') # 对于共阳极积木,输出高电平才能保证继电器不动作;共阴极则应输出低电平

熟悉一下bytearray、位操作,仿照PCF8575驱动,使用I2C总线的内存操作命令写9555驱动:

"""
MicroPython PCA9555/TCA9555 16-Bit I2C I/O Expander with Interrupt
LIUCAN 20250104 模仿PCF8575编写9555驱动
LIUCAN 20250308 PCF8575是标准总线操作,9555是内存操作,参考9554驱动
"""
# 定义寄存器地址
INPUT_PORT_REG = 0x0
OUTPUT_PORT_REG = 0x2
POLARITY_INVERSION_REG = 0x4
CONFIG_REG = 0x6
INIT_VALUE = b'\x00\xff' # 初始配置值 P0-P7 输出、P10-P17 输入

class TCA9555:
    def __init__(self, i2c, address=0x20):
        self._i2c = i2c
        self._address = address
        self._port = bytearray(2) # 字节数组可变,字节对象不可改变
        self._i2c.writeto_mem(self._address, CONFIG_REG, INIT_VALUE) # IO端口配置初始化

    def check(self):
        if self._i2c.scan().count(self._address) == 0:
            raise OSError(f"TCA9555 not found at I2C address {self._address:#x}")
        return True

    @property
    def port(self):
        self._read() # 读入字节数组
        return self._port[0] | (self._port[1] << 8)

    @port.setter
    def port(self, value):
        self._port[0] = value & 0xFF
        self._port[1] = (value >> 8) & 0xFF
        self._write() # 写入字节数组

    def pin(self, pin, value=None):
        pin = self._validate_pin(pin)
        if value is None:
            self._read()
            return (self._port[pin // 8] >> (pin % 8)) & 1
        if value:
            self._port[pin // 8] |= 1 << (pin % 8)
        else:
            self._port[pin // 8] &= ~(1 << (pin % 8))
        self._write()

    def toggle(self, pin):
        pin = self._validate_pin(pin)
        self._port[pin // 8] ^= 1 << (pin % 8)
        self._write()

    def _validate_pin(self, pin):
        # pin valid range 0..7 and 10-17 (shifted to 8-15)
        # first digit: port (0-1)
        # second digit: io (0-7)
        if not 0 <= pin <= 7 and not 10 <= pin <= 17:
            raise ValueError(f"Invalid pin {pin}. Use 0-7 or 10-17.")
        if pin >= 10:
            pin -= 2
        return pin

    def pin_set_mode(self, pin: int, mode: bool) -> int:
        """
        设置引脚模式 (INPUT/OUTPUT)
        :param pin: 引脚号 (0-7/10-17)
        :param mode: 模式 (True 为 INPUT, False 为 OUTPUT)
        """
        # bytes字节对象是不可变的,先将字节对象转换为字节数组bytearray再处理
        # readfrom_mem 快递不包邮、readfrom_mem_into 包邮更香
        self._port = bytearray(self._i2c.readfrom_mem(self._address, CONFIG_REG, 2))
        print(self._port, self._port[0], self._port[1])
        if mode:  # INPUT
            self._port[pin // 8] |= (1 << (pin % 8))
        else:  # OUTPUT
            self._port[pin // 8] &= ~(1 << (pin % 8))
        print(self._port, self._port[0], self._port[1])
        self._i2c.writeto_mem(self._address, CONFIG_REG, self._port)
        
    def _read(self): # 读取的字节数是self._port的长度 而readfrom_mem是读指定的字节数
        self._i2c.readfrom_mem_into(self._address, INPUT_PORT_REG, self._port)

    def _write(self): # 将字节数组内容写入从站
        self._i2c.writeto_mem(self._address, OUTPUT_PORT_REG, self._port)
        
if __name__=='__main__':
    from machine import Pin, I2C
    import time
    print('class TCA9555 file tca9555')
    # I2C构造函数 ESP32引脚可变
    i2c = I2C(1, freq=100_000, scl=23, sda=22)
    print("I2C ADDRESS", hex(i2c.scan()[0]))
    # TCA9555默认地址0x20
    tca = TCA9555(i2c)
    print(tca.port)
#     for i in range(8):
#         tca.pin(i,0)
#         time.sleep(1)
#     print(tca.port)
    tca.port = 255 # 装饰器@property将方法变属性 @port.setter设置属性值
    tca.toggle(0)
    time.sleep(1)
    tca.toggle(0)
    
    # 临时设置引脚IO模式 发现仅设置为输出模式就自动点灯,这可不行哦!
    tca.pin_set_mode(8,0)
    time.sleep(1)
    tca.pin_set_mode(8,1)
    # 用总线内存操作验证上面的情况属实,临时配置P10-P17为输出端口,结果灯全亮了
    i2c.writeto_mem(0x20, 0x07, b'\x00')
    # 再想想亮灯是因为输出端口=0,TCA9555带16LED的积木设计是共阳极,0=点灯,这就对了
    
#     常见错误
#     tca.port = b'\xff\xff' # TypeError: unsupported types for __and__: 'bytes', 'int'
#     tca.port(255) # TypeError: 'int' object isn't callable

如何引用9555.py参考:

from machine import Pin, I2C
import pca9555
import time

# I2C构造函数 ESP32引脚可变
i2c = I2C(1, freq=100_000, scl=22, sda=21)
print("I2C ADDRESS", hex(i2c.scan()[0]))

# PCA9555默认地址0x20
pca = pca9555.PCA9555(i2c)

pca.port = 0xFFFF # 清场 全部配置为输出 高电平熄灯
print(pca.pin(1), pca.pin(10)) # 1 1 读
time.sleep_ms(3000)
pca.pin(1, 0), pca.pin(10, 0)  # 0 0 写 输出低电平亮灯
time.sleep_ms(3000)
pca.toggle(1), pca.toggle(10)  # 1 1 翻转熄灯
time.sleep_ms(3000)

pca.port = 0x0000  # 设置 CONFIG_REG 0x06 0x07 全亮
print(i2c.readfrom_mem(0x20, 0x02, 2)) # OUTPUT_PORT_REG = 0x2 没作用

# attach an IRQ to any mcu pin that can be pulled high.
# INT is open drain, so the mcu pin needs a pull-up
# when the INT pin activates, it will go LOW
p32 = Pin(32, Pin.IN, Pin.PULL_UP)
# 中断处理函数
def _handler(p):
    print(f"INT: {p.value()}, PORT: {pca.port}")
# 上升沿或下降沿触发中断
p32.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=_handler)

# 实现流水灯
while True:
    for i in range(0, 8, 1):
        pca.pin(i, not pca.pin(i))
        time.sleep_ms(1000)
    print(i2c.readfrom_mem(0x20, 0x02, 2))
    for i in range(10, 18, 1):
        pca.pin(i, not pca.pin(i))
        time.sleep_ms(1000)
    print(i2c.readfrom_mem(0x20, 0x02, 2))

待续 输入端口 检测。 

ESP32单片机中,你可以通过I2C通信来控制滑动变阻器的电压,进而调整LED灯的亮度。假设你的滑动变阻器是一个模拟信号输出型的,例如AD5245或PCA9685等,可以利用其连接到ESP32的GPIO口。以下是基本的代码框架,用于读取滑动变阻器的值并设置LED的亮度: ```c++ #include <Adafruit_TCA9548A.h> // 如果你的滑块变阻器是通过I2C总线驱动 #include <Adafruit_AD5245.h> // 或者其他对应库 #define LED_PIN D2 // LED接在GPIO2 #define SLIDER_I2C_ADDR 0x70 // 滑动变阻器的I2C地址 #define SLIDER_CHANNEL 0 // 使用的第一个通道 Adafruit_AD5245 slider; // 创建AD5245实例 TCA9548A i2cMultiplexer; // 如果使用的是TCA9548A作为I2C多路开关 void setup() { // 初始化I2C if (i2cMultiplexer.begin(SLIDER_I2C_ADDR)) { slider.begin(&i2cMultiplexer); // 如果有多路I2C,传入对应的I2C实例 } else { Serial.println("Failed to initialize I2C"); } pinMode(LED_PIN, OUTPUT); } void loop() { int sliderValue = slider.readChannel(SLIDER_CHANNEL); // 读取滑动变阻器当前的值 float voltage = sliderValue / 1023.0 * 3.3; // 将值转换为0-3.3V范围内的电压 analogWrite(LED_PIN, map(voltage, 0, 3.3, 0, 255)); // 利用analogWrite设置LED的亮度 } ``` 在这个代码中,我们首先初始化了I2C和LED,并在循环里持续读取滑动变阻器的电压值,然后使用`map()`函数将电压映射到0-255的LED亮度范围。请注意,你需要根据具体的滑动变阻器型号和LED的工作电压进行适当的电压计算和映射。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值