XL9555 芯片使用

XL9555 是一款常用的 I/O 扩展芯片,通过 I²C 接口与主控芯片通信,可以扩展额外的输入输出引脚。以下是 XL9555 芯片的使用方法和注意事项:


芯片介绍

1. XL9555 芯片概述

  • 功能:提供 16 个可配置的 GPIO 引脚(8 个 Port 0 和 8 个 Port 1)。
  • 通信接口:支持 I²C 通信,默认地址为 0x20(可通过地址引脚配置)。
  • 电源电压:通常为 2.3V 至 5.5V。
  • 应用场景:用于需要扩展 GPIO 的嵌入式系统,如按键检测、LED 控制、传感器接口等。

2. 引脚说明

  • SCL:I²C 时钟线。
  • SDA:I²C 数据线。
  • A0, A1, A2:地址配置引脚,用于设置 I²C 设备地址。
  • P00-P07:Port 0 的 8 个 GPIO 引脚。
  • P10-P17:Port 1 的 8 个 GPIO 引脚。
  • RESET:复位引脚(低电平有效)。
  • INT:中断输出引脚(可选功能)。

3. I²C 地址配置

XL9555 的 I²C 地址由 A0、A1、A2 引脚的电平决定:

  • 默认地址:0x20(A0=A1=A2=0)。
  • 地址计算公式:0x20 + (A2 << 2) + (A1 << 1) + A0
  • 例如:A2=1, A1=0, A0=1,则地址为 0x20 + 4 + 0 + 1 = 0x25

4. 寄存器说明

XL9555 通过寄存器配置 GPIO 的状态和方向:

  • 输入端口寄存器(Input Port)
    • 0x00:Port 0 输入状态。
    • 0x01:Port 1 输入状态。
  • 输出端口寄存器(Output Port)
    • 0x02:Port 0 输出状态。
    • 0x03:Port 1 输出状态。
  • 配置寄存器(Configuration Register)
    • 0x06:Port 0 方向配置(0=输出,1=输入)。
    • 0x07:Port 1 方向配置(0=输出,1=输入)。

5. 使用步骤

硬件连接
  1. 将 XL9555 的 SCL 和 SDA 引脚连接到主控芯片的 I²C 接口。
  2. 配置 A0、A1、A2 引脚设置 I²C 地址。
  3. 连接 RESET 引脚(通常上拉至高电平)。
  4. 将 P00-P07 和 P10-P17 连接到外部设备(如 LED、按键等)。
软件配置
  1. 初始化 I²C 接口

    • 配置主控芯片的 I²C 时钟频率和引脚。
  2. 设置 GPIO 方向

    • 向配置寄存器写入数据,设置引脚为输入或输出。

    • 示例:将 Port 0 设置为输出,Port 1 设置为输入:

      uint8_t config_port0 = 0x00; // Port 0 输出
      uint8_t config_port1 = 0xFF; // Port 1 输入
      i2c_write(XL9555_ADDR, 0x06, &config_port0, 1); // 配置 Port 0
      i2c_write(XL9555_ADDR, 0x07, &config_port1, 1); // 配置 Port 1
      
  3. 控制 GPIO 输出

    • 向输出端口寄存器写入数据,控制引脚电平。

    • 示例:设置 Port 0 的 P00 为高电平:

      uint8_t output_port0 = 0x01; // P00 = 1
      i2c_write(XL9555_ADDR, 0x02, &output_port0, 1);
      
  4. 读取 GPIO 输入

    • 从输入端口寄存器读取数据,获取引脚状态。

    • 示例:读取 Port 1 的状态:

      uint8_t input_port1;
      i2c_read(XL9555_ADDR, 0x01, &input_port1, 1);
      

6. 示例代码

以下是一个简单的 XL9555 使用示例(基于 Arduino):

#include <Wire.h>

#define XL9555_ADDR 0x20 // 默认地址

void setup() {
  Wire.begin();
  Serial.begin(9600);

  // 配置 Port 0 为输出,Port 1 为输入
  i2c_write(XL9555_ADDR, 0x06, 0x00); // Port 0 输出
  i2c_write(XL9555_ADDR, 0x07, 0xFF); // Port 1 输入
}

void loop() {
  // 设置 Port 0 的 P00 为高电平
  i2c_write(XL9555_ADDR, 0x02, 0x01);

  // 读取 Port 1 的状态
  uint8_t port1_state = i2c_read(XL9555_ADDR, 0x01);
  Serial.println(port1_state, BIN);

  delay(1000);
}

void i2c_write(uint8_t addr, uint8_t reg, uint8_t data) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.write(data);
  Wire.endTransmission();
}

uint8_t i2c_read(uint8_t addr, uint8_t reg) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(addr, 1);
  return Wire.read();
}

7. 注意事项

  • I²C 上拉电阻:确保 SCL 和 SDA 引脚接上拉电阻(通常 4.7kΩ)。
  • 电源滤波:在 VCC 和 GND 之间添加滤波电容(如 0.1μF)。
  • 复位信号:上电时确保 RESET 引脚为高电平。
  • 地址冲突:避免 I²C 地址与其他设备冲突。

通过以上步骤和代码示例,可以快速上手使用 XL9555 扩展 GPIO,满足项目需求。

在 XL9555 中,中断引脚(INT)可以用于检测输入引脚的状态变化,从而减少主控芯片的轮询开销。以下是基于 ESP32-IDF 的示例代码,增加了对 XL9555 中断引脚的使用。


1. 硬件连接

  • ESP32XL9555 的连接:
    • ESP32 GPIO 引脚 -> XL9555 引脚
    • SCL (默认 GPIO 22) -> SCL
    • SDA (默认 GPIO 21) -> SDA
    • GND -> GND
    • 3.3V -> VCC
    • XL9555 的 INT 引脚 -> ESP32 的 GPIO 引脚(例如 GPIO 4)。
  • XL9555 的 A0、A1、A2 引脚接地(默认地址 0x20)。

2. 代码实现

以下代码演示了如何使用 XL9555 的中断功能来检测输入引脚的状态变化。

#include <stdio.h>
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#define XL9555_ADDR 0x20 // XL9555 默认地址
#define I2C_MASTER_SCL_IO 22 // I2C 时钟引脚
#define I2C_MASTER_SDA_IO 21 // I2C 数据引脚
#define I2C_MASTER_FREQ_HZ 100000 // I2C 时钟频率
#define I2C_MASTER_PORT I2C_NUM_0 // I2C 端口
#define XL9555_INT_GPIO 4 // XL9555 中断引脚连接的 GPIO

static const char *TAG = "XL9555_INTERRUPT_EXAMPLE";
static QueueHandle_t gpio_evt_queue = NULL;

// I2C 初始化
static void i2c_master_init() {
    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };
    i2c_param_config(I2C_MASTER_PORT, &conf);
    i2c_driver_install(I2C_MASTER_PORT, conf.mode, 0, 0, 0);
}

// I2C 写数据
static esp_err_t i2c_write(uint8_t reg_addr, uint8_t data) {
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (XL9555_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);
    i2c_master_write_byte(cmd, data, true);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

// I2C 读数据
static esp_err_t i2c_read(uint8_t reg_addr, uint8_t *data) {
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (XL9555_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (XL9555_ADDR << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, data, I2C_MASTER_NACK);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);
    return ret;
}

// GPIO 中断处理函数
static void IRAM_ATTR gpio_isr_handler(void *arg) {
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

// 初始化 GPIO 中断
static void gpio_interrupt_init() {
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << XL9555_INT_GPIO),
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_NEGEDGE, // 下降沿触发
    };
    gpio_config(&io_conf);

    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    gpio_install_isr_service(0);
    gpio_isr_handler_add(XL9555_INT_GPIO, gpio_isr_handler, (void *) XL9555_INT_GPIO);
}

void app_main() {
    // 初始化 I2C
    i2c_master_init();
    ESP_LOGI(TAG, "I2C initialized");

    // 配置 Port 0 为输出,Port 1 为输入
    i2c_write(0x06, 0x00); // Port 0 输出
    i2c_write(0x07, 0xFF); // Port 1 输入
    ESP_LOGI(TAG, "Port 0 set as output, Port 1 set as input");

    // 设置 Port 0 的 P00 为高电平
    i2c_write(0x02, 0x01); // P00 = 1
    ESP_LOGI(TAG, "Set P00 to HIGH");

    // 初始化 GPIO 中断
    gpio_interrupt_init();
    ESP_LOGI(TAG, "GPIO interrupt initialized");

    uint32_t io_num;
    while (1) {
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            ESP_LOGI(TAG, "Interrupt detected on GPIO %d", io_num);

            // 读取 Port 1 的状态
            uint8_t port1_state;
            i2c_read(0x01, &port1_state);
            ESP_LOGI(TAG, "Port 1 state: 0x%02X", port1_state);
        }
    }
}

3. 代码说明

  1. GPIO 中断初始化
    • 使用 gpio_config 配置中断引脚为输入模式,并设置为下降沿触发。
    • 使用 gpio_install_isr_servicegpio_isr_handler_add 注册中断服务函数。
  2. 中断处理函数
    • 在中断发生时,将 GPIO 编号发送到队列中。
  3. 主循环
    • 通过队列接收中断事件,并读取 XL9555 的输入状态。

5. 输出结果

  • 当 XL9555 的输入引脚状态发生变化时,会触发中断。
  • 程序会读取 Port 1 的状态并通过串口输出。

6. 注意事项

  • 中断引脚配置:确保 XL9555 的 INT 引脚正确连接到 ESP32 的 GPIO。
  • 中断触发方式:根据需求选择上升沿、下降沿或电平触发。
  • 去抖动处理:如果输入信号有抖动,可以在硬件或软件中添加去抖动逻辑。

通过以上代码,可以实现 XL9555 的中断功能,减少主控芯片的轮询开销,提高系统效率。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐