ESP32S3使用xl9555扩展IO

1.XL9555介绍

 XL9555 是一款 24 引脚的 CMOS 器件,支持 IIC 总线或 SMBus 接口进行驱动。XL9555 器
件是一个 16 位通用并行输入/输出(GPIO)扩展器,可用其 GPIO 连接按键、LED、传感器等,解
决需要额外的 I/O 的需求。 
XL9555有如下特性:
 ⚫ IIC 总线至 16 GPIO 扩展器
⚫ 工作电源电压范围为 2.3 V 至 5.5 V
⚫ 低待机电流消耗
⚫ 5 V 容错 I/O 端口
⚫ 400 kHz 快速模式 IIC 总线时钟频率
⚫ SCL/SDA 输入上的噪声滤波器
⚫ 内部通电复位
⚫ 器件地址由 3 个硬件地址引脚决定,最多可在总线上挂载 8 个器件
⚫ 中断脚为开漏输出模式(低电平有效)
⚫ 16 I/O 引脚,默认为 16 个输入 
  XL引脚图如下:
        
void i2c_init_num0(void)
{
    i2c_config_t i2c_config_struct = {0};
    uint8_t init_flag = 0;

    i2c_config_struct.mode = I2C_MODE_MASTER;                                           //配置I2C模式为master
    i2c_config_struct.sda_io_num = IIC0_SDA_GPIO_PIN;                                   //配置I2C的SDA引脚号
    i2c_config_struct.scl_io_num = IIC0_SCL_GPIO_PIN;                                   //配置I2C的SCL引脚号
    i2c_config_struct.sda_pullup_en = GPIO_PULLUP_ENABLE;                               //配置SDA引脚上拉使能
    i2c_config_struct.scl_pullup_en = GPIO_PULLUP_ENABLE;                               //配置SCL引脚上拉使能
    i2c_config_struct.master.clk_speed = IIC_FREQ;                                      //设置I2C通信速率
    i2c_param_config(I2C_NUM_0, &i2c_config_struct);                                    //设置I2C初始化参数
    init_flag = i2c_driver_install(I2C_NUM_0,                                           //端口号
                                   i2c_config_struct.mode,                              //主机模式
                                   I2C_MASTER_RX_BUF_DISABLE,                           //从机模式下接收缓存大小(主机模式下不使用)
                                   I2C_MASTER_TX_BUF_DISABLE,                           //从机模式下发送缓存大小(主机模式下不使用)
                                   0);                                                  //用于分配中断的标志(通常从机模式使用)
    if(init_flag != ESP_OK)
    {
        while (1)
        {
            printf("%s,ret: %d",__func__, init_flag);
            vTaskDelay(1000);
        }
        
    }
}

        
XL9555 上电进行复位,16 个 I/O 口默认为输入模式,当输入模式的 IO 口状态发生变化时,即发生从高电平变低电平或者从 低电平变高电平,中断脚会拉低。当中断有效后,必须对 XL9555 进行一次读取/写入操作,复位中断,才可以输出下一次中断,否则中断将一直保持。

 2.xl9555寄存器介绍

1.XL9555寻址

        XL9555器件地址是7位,如下图

Fixed bits:代表固定的位为"0100",

Programmable bits:可编程的位 "A2 A1 A0”,在硬件上,若把这三个引脚都接地处理,则这三位为“000”。最终得到XL9555器件地址为“0100000”7位。若读操作为0x41;写操作为0x40。

2.XL9555寄存器

 因XL9555只有8个寄存器是对16个GPIO进行配置的,所以控制寄存器只有低三位有效如图Table5

主要分为四种:

输入查询: Input Port 0 (0x00)  Input Port 1 (0x01) 

输出设置: Output Port 0(0x02)  Output Port 1(0x03)

极性翻转: Polarity Inversion Port 0(0x04)  Polarity Inversion Port 1(0x05)

端口配置:Configuration Port 0(0x06)    Configuration Port 1(0x07)

3.软件配置

1.初始化配置ESP32S3模组的I2C外设和XL9555初始化。代码如下

void i2c_init_num0(void)
{
    i2c_config_t i2c_config_struct = {0};
    uint8_t init_flag = 0;

    i2c_config_struct.mode = I2C_MODE_MASTER;                                           //配置I2C模式为master
    i2c_config_struct.sda_io_num = IIC0_SDA_GPIO_PIN;                                   //配置I2C的SDA引脚号
    i2c_config_struct.scl_io_num = IIC0_SCL_GPIO_PIN;                                   //配置I2C的SCL引脚号
    i2c_config_struct.sda_pullup_en = GPIO_PULLUP_ENABLE;                               //配置SDA引脚上拉使能
    i2c_config_struct.scl_pullup_en = GPIO_PULLUP_ENABLE;                               //配置SCL引脚上拉使能
    i2c_config_struct.master.clk_speed = IIC_FREQ;                                      //设置I2C通信速率
    i2c_param_config(I2C_NUM_0, &i2c_config_struct);                                    //设置I2C初始化参数
    init_flag = i2c_driver_install(I2C_NUM_0,                                           //端口号
                                   i2c_config_struct.mode,                              //主机模式
                                   I2C_MASTER_RX_BUF_DISABLE,                           //从机模式下接收缓存大小(主机模式下不使用)
                                   I2C_MASTER_TX_BUF_DISABLE,                           //从机模式下发送缓存大小(主机模式下不使用)
                                   0);                                                  //用于分配中断的标志(通常从机模式使用)
    if(init_flag != ESP_OK)
    {
        while (1)
        {
            printf("%s,ret: %d",__func__, init_flag);
            vTaskDelay(1000);
        }
        
    }
}
void xl9555_init(void)
{
    gpio_config_t gpio_init_struct = {0};
    uint16_t reg_data;
    i2c_init_num0();                                                            //初始化i2c_num_0          
    
    gpio_init_struct.intr_type = GPIO_INTR_DISABLE;
    gpio_init_struct.mode = GPIO_MODE_INPUT;
    gpio_init_struct.pin_bit_mask = (1ull << XL9555_INT_IO);
    gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;
    gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;
    gpio_config(&gpio_init_struct);                                             /* 配置XL_INT引脚 */
    xl9555_read_Input_Value(&reg_data, XL9555_GPIO_Port0 | XL9555_GPIO_Port1);  //读一次所有的GPIO口,清除中断
    // printf("xl9555_init InputReg_data:%d\n",reg_data);
    xl9555_ioconfig(0xF003);                                                    //配置相应引脚的IO模式(输入(0)、输出(1))

}

3.XL9555读写时序

1.写寄存器时序

上图中是主机将单字节写入到寄存器的时序,主机在 IIC总线发送第 1个字节的数据
XL9555 的写操作地址 0x40(设备地址 0x20 << 1 | 0),用于寻找总线上找到 XL9555,在获得 XL9555 的应答信号之后,继续发送第 2 个字节数据,该字节数据是 XL9555 的寄存器地址,再
等到 XL9555 的应答信号,主机继续发送第 3 字节数据,这里的数据即是写入在第 2 字节寄存器
地址的数据。主机完成写操作后,可以发出停止信号,终止数据传输。
代码实现如下图
/**
 * @brief       设置XL9555的16位IO值
 * @param       data:控制IO的变量
 * @param       GPIO_Port: 要控制IO所属的端口
 * @retval      ESP_OK:读取成功;其他:读取失败
 */
esp_err_t xl9555_write_Output_Value (uint16_t data, uint8_t GPIO_Port)
{
    esp_err_t ret = ESP_FAIL;
    uint8_t tmp[2] = {0};
    if((GPIO_Port & XL9555_GPIO_Port0) == 0x1)
    {
        tmp[0] = XL9555_OUTPUT_PORT0_REG;
        tmp[1] = (uint8_t)(data & 0x00FF);
        ret = i2c_num0_write_byte(XL9555_ADDR, tmp, 2);
    }
    if((GPIO_Port & XL9555_GPIO_Port1) == 0x2)
    {
        tmp[0] = XL9555_OUTPUT_PORT1_REG;
        tmp[1] = (uint8_t)((data & 0xFF00) >> 8);
        ret = i2c_num0_write_byte(XL9555_ADDR, tmp, 2);
    }
    return ret;
}
esp_err_t i2c_num0_write_byte(uint8_t addr, uint8_t *byte, uint16_t len)
{
    esp_err_t ret = ESP_FAIL;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();                           //用于创建一个I2C命令链接,在发送或接收I2C数据时,需要先建立一个命令链,然后添加相应的命令
    i2c_master_start(cmd);                                                  //向I2C命令链中添加开始信号
    i2c_master_write_byte(cmd, (addr << 1)|I2C_FLAG_WRITE, ACK_CHECK_EN);   //向命令链中添加一个写设备地址和写命令的字节。
    i2c_master_write(cmd, byte, len, ACK_CHECK_EN);                         //向命令链中添加写设备寄存器与寄存器数据命令的字节
    i2c_master_stop(cmd);                                                   //向命令链中添加停止信号命令
    ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 100 * (1+len)/portTICK_PERIOD_MS);   //开始发送命令
    i2c_cmd_link_delete(cmd);                                                       //释放命令链接存储空间
    return ret;        
}

在《XL9555 数据手册》P16 中,还提供有对寄存器组写时序图,如下图

2.读寄存器时序

上图是主机从寄存器中读取一个字节数据的时序图。
XL9555 读取数据的过程是一 个复合的时序,其中包含写时序和读时序。
1.先产生起始信号,master发送XL9555的写操作地址 0x40(设备地址 0x20<< 1 | 0),等待获取Slave应答信号。
2.发送需要读取的寄存器地址,获取Slave应答信号后。Master发送起始信号,在发XL9555 的读操作地址 0x41(设备地址 0x20 << 1 | 1),等待获取Slave应答信号。
3.Master接收Slave的寄存器数据后发送非应答信号,在发送一个停止信号。结束通信。
代码实现如下图:
/**
 * @brief       读取XL9555的16位IO值
 * @param       data:读取数据的存储区
 * @retval      ESP_OK:读取成功;其他:读取失败
 */
esp_err_t xl9555_read_Input_Value (uint16_t *data, uint8_t GPIO_Port)
{
    esp_err_t ret = ESP_FAIL;
    uint8_t tmp;
    if((GPIO_Port & XL9555_GPIO_Port0) == 0x1)
    {
        ret = i2c_num0_read_byte(XL9555_ADDR, XL9555_INPUT_PORT0_REG, &tmp);
        *data = tmp;
    }
    if((GPIO_Port & XL9555_GPIO_Port1) == 0x2)
    {
        ret = i2c_num0_read_byte(XL9555_ADDR, XL9555_INPUT_PORT1_REG, &tmp);
        *data |= ((((uint16_t)tmp) << 8)& 0xFF00);
    }
    return ret;
}

esp_err_t i2c_num0_read_byte(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data)
{
    esp_err_t ret = ESP_FAIL;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();                               //用于创建一个I2C命令链接,在发送或接收I2C数据时,需要先建立一个命令链,然后添加相应的命令
    i2c_master_start(cmd);                                                      //向I2C命令链中添加开始信号
    i2c_master_write_byte(cmd, (dev_addr << 1)|I2C_FLAG_WRITE, ACK_CHECK_EN);   //向命令链中添加一个写设备地址和写命令的字节。
    i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);                         //向命令链中添加写设备寄存器地址命令字符。
    i2c_master_start(cmd);                                                      //向命令链发送起始信号
    i2c_master_write_byte(cmd, (dev_addr << 1)|I2C_FLAG_READ, ACK_CHECK_EN);    //向命令链发送设备地址和读命令的字符
    i2c_master_read_byte(cmd, data, I2C_MASTER_LAST_NACK);                      //读取从机数据
    i2c_master_stop(cmd);                                                       //发送停止信号

    ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 400/portTICK_PERIOD_MS);         //开始发送命令
    i2c_cmd_link_delete(cmd);                                                   //释放命令链接存储空间
    return ret;  
} 
XL9555 数据手册》 中支持从多个寄存器中读取多个字节的时序,如下图
<think>我们正在讨论ESP32-S3的串口引脚配置问题。根据用户的问题:“我想了解esp32s3串口是否可以任意配置io口 请问esp32s3串口能否任意配置io口”,我们需要明确ESP32-S3的串口引脚配置能力。 参考引用内容: [^1]中提到ESP32S3的BOOT程序使用UART0进行程序下载和LOG输出,而UART1和UART2没有外接USB转串口芯片,需要使用USB转TTL设备验证。 [^2]中展示了使用串口0作为LOG输出的代码示例。 根据ESP32-S3的技术特性: 1. ESP32-S3具有多个UART控制器(通常至少3个:UART0, UART1, UART2)。 2. 每个UART控制器可以映射到不同的GPIO引脚,但不是所有GPIO都可以任意使用,因为某些引脚有特殊功能限制(如外部Flash/PSRAM使用的引脚)。 具体回答: - ESP32-S3的串口具有引脚重映射功能,每个UART的TX和RX信号可以通过IO MUX或GPIO矩阵路由到大多数GPIO引脚。 - 但是,并非所有GPIO都是完全自由的,需要注意: a) 某些引脚在启动时具有特定功能(例如GPIO26-32通常用于连接外部Flash/PSRAM,不能作为普通GPIO使用)。 b) 建议避开这些限制引脚(strap pins)以及设计中已用于其他功能的引脚。 因此,答案是:ESP32-S3的串口可以配置到大多数GPIO引脚上,但需要避开受限引脚(如GPIO26-32)[^1][^3]。 下面我们将详细说明配置方法和注意事项。 </think>### ESP32-S3 串口引脚任意配置指南 #### ✅ 核心结论 **ESP32-S3 的串口支持任意配置IO引脚**,但需遵循硬件限制规则[^1]。通过GPIO矩阵和IO MUX功能,可将UART信号路由到大多数GPIO引脚(除特定限制引脚外)。 --- ### 一、配置原理与限制 1. **引脚重映射机制** ESP32-S3 提供两种信号路由方式: - **IO MUX**(默认路径):低延时直连,但仅限固定引脚 ```mermaid graph LR UART0 -->|固定连接| GPIO1(TX)/GPIO3(RX) ``` - **GPIO矩阵**:任意引脚映射,增加1个时钟周期延时 ```mermaid graph LR UART0 -->|可配置| 任意GPIO[GPIO矩阵] -->|自由路由| 用户指定引脚 ``` 2. **引脚限制表** | 引脚类型 | 是否可用 | 典型引脚范围 | |-------------------|----------|--------------------| | 普通GPIO | ✅ | 0-21, 35-48 | | Strapping引脚 | ️启动后可用 | GPIO45(GPIO0)等 | | 默认PSRAM/FLASH引脚 | ❌ | GPIO26-32 | --- ### 二、配置步骤(代码实现) #### 方法1:初始化时指定引脚 ```c #include "driver/uart.h" void app_main() { uart_config_t uart_cfg = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1 }; // 自定义任意引脚(示例:GPIO15为TX,GPIO16为RX) uart_param_config(UART_NUM_1, &uart_cfg); uart_set_pin(UART_NUM_1, 15, 16, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); // 启用UART驱动 uart_driver_install(UART_NUM_1, 1024, 0, 0, NULL, 0); } ``` #### 方法2:menuconfig动态配置 1. 执行 `idf.py menuconfig` 2. 导航路径: ``` Component config → ESP System Settings → [*] Use custom UART pins (15) TX GPIO number (16) RX GPIO number ``` --- ### 三、关键注意事项 1. **UART0的特殊限制** BOOT阶段固定使用GPIO43/44(USB串口)或GPIO1/3(传统串口)[^1] ```c // 启动后仍可重定向LOG输出到其他串口 esp_log_set_vprintf(&custom_uart_vprintf); ``` 2. **GPIO冲突规避** 避免使用以下受限引脚: - **绝对禁用**:GPIO26-32(默认FLASH/PSRAM接口) - **谨慎使用**:GPIO45-46(启动配置引脚,上电后可用) 3. **性能影响** 使用GPIO矩阵时: - 最大波特率降至 ≤ 5Mbps - 引入约100ns延时(IO MUX无此问题) --- ### 四、验证配置(示例代码) ```c // 发送测试数据到自定义串口 uint8_t test_data[] = "GPIO矩阵测试\n"; uart_write_bytes(UART_NUM_1, test_data, sizeof(test_data)); // 接收数据(使用自定义引脚) uint8_t buffer[128]; int len = uart_read_bytes(UART_NUM_1, buffer, sizeof(buffer), 20 / portTICK_PERIOD_MS); ESP_LOGI("UART", "收到数据: %.*s", len, buffer); ``` > 📌 **实测建议**:优先选择 GPIO4-21 和 35-48 作为自定义串口引脚,这些引脚无特殊功能冲突[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sky丶日暮途远

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值