告别裸写I2C!用STC12驱动PCF8575读取矩阵键盘的保姆级教程

张开发
2026/5/6 5:31:32 15 分钟阅读
告别裸写I2C!用STC12驱动PCF8575读取矩阵键盘的保姆级教程
STC12驱动PCF8575实现4x4矩阵键盘的完整解决方案在嵌入式系统开发中IO资源紧张是常见问题。当需要连接多个按键时传统的直接IO驱动方式会迅速耗尽单片机有限的引脚资源。本文将详细介绍如何利用STC12单片机通过I2C总线驱动PCF8575扩展芯片构建一个稳定可靠的4x4矩阵键盘输入系统。1. 硬件设计基础1.1 PCF8575芯片特性解析PCF8575是德州仪器推出的I2C接口IO扩展芯片具有以下核心特性16位准双向IO可独立配置为输入或输出模式宽电压工作范围2.5V-5.5V兼容多数单片机系统硬件地址引脚支持最多8个设备级联中断输出功能可配置为输入状态变化触发中断注意实际使用中PCF8575的输出驱动能力有限建议在输出模式下连接1K-10K上拉电阻以保证信号质量。1.2 矩阵键盘电路设计4x4矩阵键盘需要8个IO口4行4列使用PCF8575的16个IO口可按如下方式分配PCF8575引脚键盘连接工作模式P00-P03行线1-4输出P04-P07列线1-4输入P10-P17未使用输入典型连接电路示意图PCF8575 ----- | P00|----[行1]----[按键矩阵] | P01|----[行2]----[按键矩阵] | P02|----[行3]----[按键矩阵] | P03|----[行4]----[按键矩阵] | P04|----[列1]----[按键矩阵] | P05|----[列2]----[按键矩阵] | P06|----[列3]----[按键矩阵] | P07|----[列4]----[按键矩阵] -----2. STC12的I2C驱动实现2.1 硬件I2C与软件模拟对比STC12系列单片机部分型号具备硬件I2C外设但为保持代码通用性本文采用软件模拟实现// I2C引脚定义 sbit SCL P1^0; // I2C时钟线 sbit SDA P1^1; // I2C数据线 // I2C起始信号 void I2C_Start() { SDA 1; SCL 1; Delay_us(2); SDA 0; Delay_us(2); SCL 0; } // I2C停止信号 void I2C_Stop() { SDA 0; SCL 1; Delay_us(2); SDA 1; Delay_us(2); }2.2 PCF8575驱动函数封装针对键盘扫描需求我们封装以下核心函数#define PCF8575_ADDR 0x40 // 默认地址(A2A1A0000) // 写入16位IO状态 void PCF8575_Write(uint16_t data) { I2C_Start(); I2C_SendByte(PCF8575_ADDR); I2C_SendByte(data 0xFF); // 低字节 I2C_SendByte(data 8); // 高字节 I2C_Stop(); } // 读取16位IO状态 uint16_t PCF8575_Read() { uint16_t val 0; I2C_Start(); I2C_SendByte(PCF8575_ADDR | 0x01); val I2C_ReadByte(); // 读取低字节 I2C_SendACK(0); // 发送应答 val | (I2C_ReadByte() 8); // 读取高字节 I2C_SendACK(1); // 发送非应答 I2C_Stop(); return val; }3. 矩阵键盘扫描算法3.1 行列扫描原理矩阵键盘检测采用行扫描、列检测方法将4行依次置低电平其余置高每次行变化后读取列线状态根据行列组合确定按键位置uint8_t KeyScan() { uint8_t row, col; uint16_t port_val; for(row0; row4; row) { // 当前行置0其他行置1 PCF8575_Write(~(1 row) 0x0F); // 读取列线状态(P04-P07) port_val PCF8575_Read(); col (~port_val 4) 0x0F; if(col ! 0) { // 消抖处理 Delay_ms(10); port_val PCF8575_Read(); if((~port_val 4) 0x0F) { return (row 4) | (__builtin_ffs(col)-1); } } } return 0xFF; // 无按键按下 }3.2 键值映射与消抖处理建立键值映射表将行列位置转换为实际键值const char KeyMap[4][4] { {1,2,3,A}, {4,5,6,B}, {7,8,9,C}, {*,0,#,D} }; char GetKeyValue() { uint8_t pos KeyScan(); if(pos ! 0xFF) { return KeyMap[pos4][pos0x0F]; } return 0; }4. 系统集成与优化4.1 中断驱动方案为降低CPU负载可利用PCF8575的中断功能将INT引脚连接到单片机外部中断按键按下时INT产生下降沿中断服务程序中读取键值// 中断初始化 void INT0_Init() { IT0 1; // 下降沿触发 EX0 1; // 使能INT0中断 EA 1; // 开总中断 } // 中断服务程序 void INT0_ISR() interrupt 0 { char key GetKeyValue(); if(key) { // 处理按键事件 ProcessKey(key); } }4.2 实际应用示例智能锁密码输入场景的实现框架#define PASSWORD_LEN 6 char password[PASSWORD_LEN1] 123456; // 预设密码 char input[PASSWORD_LEN1] {0}; uint8_t input_pos 0; void ProcessKey(char key) { if(key #) { // 确认键 if(strcmp(input, password) 0) { UnlockDoor(); } else { ShowError(); } input_pos 0; memset(input, 0, sizeof(input)); } else if(key *) { // 清除键 input_pos 0; memset(input, 0, sizeof(input)); } else if(input_pos PASSWORD_LEN) { input[input_pos] key; ShowStar(input_pos); } }在完成基础功能后建议添加以下增强功能按键音反馈输入超时重置多次错误锁定EEPROM密码存储实际项目中我发现合理利用PCF8575的中断功能可以显著降低系统功耗特别是在电池供电场景下。通过配置PCF8575的输入引脚内部上拉还能进一步简化外围电路设计。

更多文章