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))

待续 输入端口 检测。 

### PCA9555 驱动程序使用说明 PCA9555 是一款基于 I2C 总线的 16 位 GPIO 扩展器,广泛应用于嵌入式系统中需要扩展输入输出端口的场景。以下是关于其驱动程序的相关信息、使用方法以及示例代码。 #### 下载 PCA9555 驱动程序 可以访问以下链接下载包含 PCA9555 驱动程序及相关资源的压缩包: - **项目地址**: [https://gitcode.com/open-source-toolkit/1714c](https://gitcode.com/open-source-toolkit/1714c)[^2] 下载完成后,解压 `电子-PCA9555驱动.zip` 文件即可获得驱动源码、示例工程文件及相关文档说明。 --- #### 使用说明 为了成功配置和使用 PCA9555,在开发环境中需完成以下几个方面的工作: 1. **硬件连接** - 确保 PCA9555 的 SDA 和 SCL 引脚分别与主控 MCU 的对应 I2C 接口相连。 - 提供稳定的电源电压(通常为 3.3V 或 5V),并确保电路设计满足数据手册的要求[^1]。 2. **软件准备** - 解压后的目录结构应包含驱动程序源代码、示例工程文件及文档说明。 - 根据所使用的 STM32 型号(F0/F1/F2 系列),选择合适的示例工程作为基础框架。 3. **集成驱动程序** - 将 PCA9555 的驱动源代码复制到项目的指定路径下。 - 修改必要的头文件路径,并确认 HAL 库或其他底层库已正确初始化 I2C 外设功能。 4. **配置参数** - 参考相关文档中的寄存器定义表,通过修改配置项来设定工作模式(如输入/输出方向)、初始状态等[^1]。 --- #### 示例代码 以下是一个简单的 PCA9555 初始化与操作的 C 语言实现案例: ```c #include "stm32f1xx_hal.h" #include "i2c.h" #define PCA9555_I2C_ADDR (0x20 << 1) // 默认设备地址 #define INPUT_PORT 0 // 输入端口号 #define OUTPUT_PORT 1 // 输出端口号 // 函数声明 void pca9555_init(void); uint8_t pca9555_read_pin(uint8_t pin_number); void pca9555_write_pin(uint8_t pin_number, uint8_t state); // 初始化 PCA9555 void pca9555_init() { uint8_t config_data[2]; // 设置所有引脚为输入模式 config_data[0] = 0xFF; // Port A 方向配置 config_data[1] = 0xFF; // Port B 方向配置 HAL_I2C_Master_Transmit(&hi2c1, PCA9555_I2C_ADDR, config_data, sizeof(config_data), HAL_MAX_DELAY); } // 读取某个引脚的状态 uint8_t pca9555_read_pin(uint8_t pin_number) { uint8_t port_reg; uint8_t reg_value; if(pin_number < 8){ port_reg = 0x00; // Input Port A Register }else{ port_reg = 0x01; // Input Port B Register pin_number -= 8; } HAL_I2C_Mem_Read(&hi2c1, PCA9555_I2C_ADDR, port_reg, 1, &reg_value, 1, HAL_MAX_DELAY); return ((reg_value >> pin_number) & 0x01); } // 写入某个引脚的状态 void pca9555_write_pin(uint8_t pin_number, uint8_t state) { uint8_t output_port_reg; uint8_t current_state; if(pin_number < 8){ output_port_reg = 0x12; // Output Port A Register }else{ output_port_reg = 0x13; // Output Port B Register pin_number -= 8; } HAL_I2C_Mem_Read(&hi2c1, PCA9555_I2C_ADDR, output_port_reg, 1, &current_state, 1, HAL_MAX_DELAY); if(state){ current_state |= (1 << pin_number); // Set Pin High }else{ current_state &= ~(1 << pin_number); // Set Pin Low } HAL_I2C_Mem_Write(&hi2c1, PCA9555_I2C_ADDR, output_port_reg, 1, &current_state, 1, HAL_MAX_DELAY); } ``` 上述代码展示了如何初始化 PCA9555 并对其进行基本的操作,包括设置引脚方向、读取引脚状态以及写入特定值[^3]。 --- #### 注意事项 - 如果目标平台并非 STM32,则需要注意调整 GPIO 输入输出设置逻辑以及延时函数精度,以适配具体硬件需求[^4]。 - 开发前建议仔细阅读官方数据手册,了解各寄存器的功能及其默认值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值