I2C GPIO 扩展器在嵌入式系统中的实战应用与寄存器配置
2026/7/14 18:46:35 网站建设 项目流程

1. I2C GPIO扩展器是什么?

当你用单片机做项目时,最常遇到的瓶颈就是GPIO口不够用。比如要控制16个LED,但你的STM32只剩8个GPIO,这时候PCA9555这类I2C GPIO扩展器就像给你的开发板插上了"外挂"——通过两根I2C线(SCL/SDA)就能扩展出16个可编程IO口。

我去年做智能家居中控时就遇到过这个问题。主控板需要同时读取多个温湿度传感器、控制继电器阵列,还要驱动OLED屏,GPIO根本不够分配。后来用了两颗PCA9555,通过I2C级联,硬是把8个GPIO扩展成了32个,完美解决问题。

这类芯片本质上是个"智能开关阵列",内部结构可以理解为:

  • 输入/输出寄存器:保存当前IO口电平状态
  • 配置寄存器:决定每个引脚是输入还是输出模式
  • 极性反转寄存器:可以翻转输入信号的电平逻辑

2. 关键寄存器详解

以PCA9555为例,它的寄存器就像控制面板上的8个按钮:

#define INPUT_PORT0 0x00 // Port0输入状态 #define INPUT_PORT1 0x01 // Port1输入状态 #define OUTPUT_PORT0 0x02 // Port0输出锁存 #define OUTPUT_PORT1 0x03 // Port1输出锁存 #define POLARITY_PORT0 0x04 // Port0极性反转 #define POLARITY_PORT1 0x05 // Port1极性反转 #define CONFIG_PORT0 0x06 // Port0输入/输出配置 #define CONFIG_PORT1 0x07 // Port1输入/输出配置

配置寄存器最常用,它的每个bit对应一个引脚:

  • 1:对应引脚设为输入模式(高阻抗状态)
  • 0:对应引脚设为输出模式

比如要让Port0的0-3引脚作为输入,4-7引脚作为输出:

uint8_t config = 0x0F; // 00001111 HAL_I2C_Mem_Write(&hi2c1, 0x40, CONFIG_PORT0, 1, &config, 1, 100);

实际项目中我发现,上电时配置寄存器默认全为1(输入模式)。如果不先初始化就直接写输出寄存器,会发现电平拉不低,这时候要检查是否漏了配置模式。

3. STM32实战代码解析

最近用STM32F103驱动PCA9555的完整流程,分享几个关键函数:

3.1 初始化配置

void PCA9555_Init(I2C_HandleTypeDef *hi2c, uint8_t devAddr) { uint8_t config[2] = {0x00, 0x00}; // 全部设为输出模式 HAL_I2C_Mem_Write(hi2c, devAddr<<1, CONFIG_PORT0, 1, config, 2, 100); }

这里有个坑点:STM32的HAL库要求7位设备地址左移1位。比如PCA9555地址是0x40,实际要传入0x80。

3.2 输出控制

控制LED阵列的典型写法:

void PCA9555_SetOutput(I2C_HandleTypeDef *hi2c, uint8_t devAddr, uint16_t pins) { uint8_t output[2] = { (uint8_t)(pins & 0xFF), // Port0 (uint8_t)(pins >> 8) // Port1 }; HAL_I2C_Mem_Write(hi2c, devAddr<<1, OUTPUT_PORT0, 1, output, 2, 100); }

调用示例(点亮P0.1和P1.3):

PCA9555_SetOutput(&hi2c1, 0x40, (1<<1) | (1<<11));

3.3 输入读取

读取按键状态的代码:

uint16_t PCA9555_GetInput(I2C_HandleTypeDef *hi2c, uint8_t devAddr) { uint8_t input[2]; HAL_I2C_Mem_Read(hi2c, devAddr<<1, INPUT_PORT0, 1, input, 2, 100); return (input[1] << 8) | input[0]; }

4. 典型应用场景

4.1 矩阵键盘扫描

用8个GPIO实现4x4矩阵键盘:

  • 4个引脚配置为输出(行扫描)
  • 4个引脚配置为输入(列检测)
// 扫描第row行 uint8_t ScanKeyRow(uint8_t row) { PCA9555_SetOutput(0xFF ^ (1<<row)); // 当前行拉低 return PCA9555_GetInput() >> 4; // 读取高4位列状态 }

4.2 多设备级联

通过改变A0-A2地址引脚,可以挂载8个PCA9555。我在智能灯带项目中这样级联:

#define DEV_ADDR_BASE 0x40 for(int i=0; i<8; i++) { PCA9555_Init(DEV_ADDR_BASE | i); PCA9555_SetOutput(DEV_ADDR_BASE | i, ledPattern[i]); }

5. 避坑指南

  1. 上电瞬态问题:某些型号上电时输出会短暂振荡,敏感电路要加RC延时
  2. I2C布线:长距离传输要加1kΩ上拉电阻,实测超过50cm时要用屏蔽线
  3. 中断功能:PCA9555的INT引脚可以配置为输入变化中断,但要注意消抖
  4. 热插拔风险:带电插拔可能损坏I2C接口,建议增加TVS二极管保护

最近调试时还发现一个隐蔽问题:当多个设备共用I2C总线时,如果某个PCA9555电源不稳,会导致整个总线挂死。后来在每条分支I2C线上串了100Ω电阻隔离,问题解决。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询