蓝桥杯单片机I2C实战:手把手教你用PCF8591和AT24C02做个简易数据采集器
蓝桥杯单片机I2C实战手把手教你用PCF8591和AT24C02做个简易数据采集器在蓝桥杯单片机竞赛中I2C总线技术是必须掌握的核心技能之一。本文将带你从零开始利用PCF8591模数转换器和AT24C02 EEPROM存储器构建一个功能完整的简易数据采集系统。这个系统不仅能实时采集环境数据还能将关键信息存储起来即使断电也不会丢失。1. 项目需求分析与硬件准备我们的目标是打造一个具备以下功能的数据采集器实时采集光敏电阻和电位计的模拟信号通过按键切换采集模式将采集到的关键数据存储到EEPROM中通过LED和数码管显示当前状态和采集值设置阈值触发LED报警功能所需硬件组件组件功能说明连接方式PCF85918位AD/DA转换芯片I2C总线AT24C02256字节EEPROMI2C总线光敏电阻环境光强检测PCF8591通道1电位器可调电压输入PCF8591通道3独立按键模式切换/数据存储GPIO口LED灯状态指示GPIO口数码管数据显示GPIO口在蓝桥杯官方开发板上这些组件已经预先布局好我们只需要关注软件实现即可。特别提醒I2C总线的SCL和SDA分别连接在P2.0和P2.1引脚上。2. I2C总线驱动实现I2C总线通信需要严格的时序控制我们先实现底层驱动函数#include stc15.h #include intrins.h sbit sda P2^1; sbit scl P2^0; #define I2C_DELAY() _nop_();_nop_();_nop_();_nop_();_nop_() void I2C_Start() { sda 1; I2C_DELAY(); scl 1; I2C_DELAY(); sda 0; I2C_DELAY(); scl 0; I2C_DELAY(); } void I2C_Stop() { sda 0; I2C_DELAY(); scl 1; I2C_DELAY(); sda 1; I2C_DELAY(); } bit I2C_WaitAck() { scl 1; I2C_DELAY(); if(sda) { scl 0; return 1; } scl 0; I2C_DELAY(); return 0; } void I2C_SendByte(unsigned char dat) { unsigned char i; for(i0; i8; i) { scl 0; sda (dat 0x80) ? 1 : 0; I2C_DELAY(); scl 1; dat 1; I2C_DELAY(); } scl 0; } unsigned char I2C_ReceiveByte() { unsigned char i, dat 0; for(i0; i8; i) { scl 1; I2C_DELAY(); dat 1; if(sda) dat | 0x01; scl 0; I2C_DELAY(); } return dat; }注意实际应用中可能需要根据单片机主频调整延时时间确保I2C时序满足器件要求。3. PCF8591数据采集实现PCF8591是一款集成了4通道模拟输入和1路模拟输出的8位AD/DA转换器。在蓝桥杯开发板上我们主要使用它的两个功能通道1AIN1连接光敏电阻检测环境光强通道3AIN3连接电位器检测手动调节的电压值PCF8591关键操作函数#define PCF8591_WRITE 0x90 #define PCF8591_READ 0x91 // 读取PCF8591指定通道的AD值 unsigned char PCF8591_ReadADC(unsigned char channel) { unsigned char val; I2C_Start(); I2C_SendByte(PCF8591_WRITE); I2C_WaitAck(); I2C_SendByte(channel); // 通道选择 I2C_WaitAck(); I2C_Stop(); I2C_Start(); I2C_SendByte(PCF8591_READ); I2C_WaitAck(); val I2C_ReceiveByte(); I2C_WaitAck(); I2C_Stop(); return val; } // 设置PCF8591的DA输出 void PCF8591_WriteDAC(unsigned char value) { I2C_Start(); I2C_SendByte(PCF8591_WRITE); I2C_WaitAck(); I2C_SendByte(0x40); // 使能DA输出 I2C_WaitAck(); I2C_SendByte(value); I2C_WaitAck(); I2C_Stop(); }通道选择对照表控制字对应功能0x01读取光敏电阻(AIN1)0x03读取电位器(AIN3)0x40使能DA输出在实际应用中我们通过定时扫描这两个通道就能实时获取环境参数的变化情况。4. AT24C02数据存储实现AT24C02是一款256字节的EEPROM存储器具有掉电不丢失的特性非常适合存储系统配置和采集的关键数据。AT24C02操作要点写操作后需要5-10ms的写入周期避免频繁写入同一地址延长芯片寿命单次写入不超过一页(8字节)实现代码如下#define AT24C02_WRITE 0xA0 #define AT24C02_READ 0xA1 // 向指定地址写入数据 void AT24C02_WriteByte(unsigned char addr, unsigned char dat) { I2C_Start(); I2C_SendByte(AT24C02_WRITE); I2C_WaitAck(); I2C_SendByte(addr); I2C_WaitAck(); I2C_SendByte(dat); I2C_WaitAck(); I2C_Stop(); Delay10ms(); // 等待写入完成 } // 从指定地址读取数据 unsigned char AT24C02_ReadByte(unsigned char addr) { unsigned char dat; I2C_Start(); I2C_SendByte(AT24C02_WRITE); I2C_WaitAck(); I2C_SendByte(addr); I2C_WaitAck(); I2C_Stop(); I2C_Start(); I2C_SendByte(AT24C02_READ); I2C_WaitAck(); dat I2C_ReceiveByte(); I2C_WaitAck(); I2C_Stop(); return dat; }提示在实际项目中可以将阈值参数、校准数据等关键信息存储在AT24C02中系统启动时读取这些配置。5. 系统整合与功能实现现在我们将各个模块整合起来实现完整的采集系统功能。以下是主程序框架#include stc15.h #include intrins.h #include i2c.h // 全局变量定义 unsigned char g_AD_Value 0; // 当前AD值 unsigned char g_Threshold 60; // 报警阈值 bit g_LightMode 0; // 0-光敏模式 1-电位器模式 unsigned char g_StoredValue 0; // EEPROM中存储的值 // 数码管显示函数 void Display_Value(unsigned char value) { // 实现数码管显示逻辑 } // 按键处理函数 void Key_Process() { // 实现按键扫描和处理逻辑 // S11 - 切换采集模式 // S6 - 存储当前值到EEPROM } // LED状态更新 void Update_LEDs() { if(g_AD_Value g_Threshold) { // 点亮报警LED } else { // 熄灭报警LED } // 更新模式指示LED } void main() { // 初始化 P2 0xA0; // 初始化IO口 Delay100ms(); // 从EEPROM读取存储值 g_StoredValue AT24C02_ReadByte(0x50); while(1) { // 采集数据 if(g_LightMode) { g_AD_Value PCF8591_ReadADC(0x03); // 电位器模式 } else { g_AD_Value PCF8591_ReadADC(0x01); // 光敏模式 } // 处理按键 Key_Process(); // 更新显示 Display_Value(g_AD_Value); // 更新LED状态 Update_LEDs(); Delay100ms(); } }功能扩展建议数据记录功能定期将采集数据存储到EEPROM的不同地址形成历史记录阈值自适应根据环境变化自动调整报警阈值校准功能增加校准模式消除硬件差异带来的误差通信接口通过串口将数据上传到上位机6. 调试技巧与常见问题在实际开发过程中你可能会遇到以下典型问题I2C通信失败排查步骤检查硬件连接确认SCL和SDA线连接正确用示波器或逻辑分析仪观察时序波形确认器件地址正确PCF8591为0x90/0x91AT24C02为0xA0/0xA1检查上拉电阻是否合适通常4.7kΩ调整I2C时钟频率确保不超过器件限制EEPROM写入失败的可能原因写入后立即读取未等待足够时间写入地址超出范围AT24C02只有0x00-0xFF同一地址频繁写入导致寿命耗尽电源电压不稳定导致写入失败PCF8591采集值不准确的解决方法检查参考电压是否稳定增加软件滤波算法如滑动平均进行校准建立实际物理量与AD值的对应关系检查模拟输入信号是否在0-Vref范围内在项目开发中我习惯先用逻辑分析仪抓取I2C总线数据确认通信协议正确后再进行功能开发这样可以节省大量调试时间。另外对于关键数据存储建议实现校验机制如CRC校验确保数据可靠性。