尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
用N76E003模拟AT24C02:I2C从机状态机设计全解析
简介针对N76E003单片机实现I2C从机模拟AT24C02的完整工程代码面向嵌入式开发、电子设计以及外设扩展应用场景尤其适合需要低成本替代EEPROM芯片或深入理解I2C从机协议的项目参考。程序涵盖I2C引脚初始化、从机地址0xA0识别、主从通信起始与停止条件检测、读写命令解析、256字节存储区模拟、应答信号控制以及超时/数据冲突等异常处理几乎覆盖AT24C02核心功能源码采用模块化组织注释清晰已在实际板卡上跑通全部读写流程。压缩包共16个文件包括头文件、C源文件、启动文件、HEX固件、Keil工程及调试辅助文件整体仅56KB结构紧凑便于学习其中HEX可直接烧录省去重新编译的麻烦也可直接基于Keil工程二次开发。已有1439人学习下载对掌握N76E003的I2C从模式开发、模拟标准EEPROM行为以及I2C时序调试均有直接帮助。 前阵子帮人做一块老设备的升级板主控板上原本挂了一片AT24C02用来存配置参数。改硬件容易但改主控程序要动产线、要重新烧录麻烦得很。后来我想了个办法干脆用N76E003单片机模拟一片AT24C02挂到I2C总线上让主控完全无感照常读写EEPROM实际上数据全落在我这块N76E003里。项目做完以后读写都测试通过主控端从头到尾不知道“EEPROM”已经换掉了。这篇文章就把这个方案的完整思路、协议细节、代码框架和测试过程整理出来。适合手里有N76E003、想搞懂I2C从机怎么写的朋友也适合正在纠结“如何不改主控程序就换掉一颗EEPROM”的工程师。我会把AT24C02的四种读写时序、从机状态机的设计逻辑、以及实测中容易踩的坑都过一遍尽量做到看完就能自己动手。1. 为什么非要用单片机去“冒充”EEPROM先说清楚应用场景。如果你只是想在原有电路板上换一颗容量更大的EEPROM或者改造一下存储内容为什么不直接换芯片或者改主控程序原因很现实老设备的主控程序往往没有源码或者固件被加密保护你根本动不了。但系统里要存的参数又确实需要变化比如设备编号、校准系数、不同客户定制的配置字。这时候把I2C总线上的AT24C02“偷梁换柱”成一颗价格差不多的N76E003既不动主控代码又能让存储内容变成“活的”是最省事的方案。1.1 这类方案通常用在什么产品里我实际接触到的需求大概有这么几类给老设备增加序列号和校准信息不重新烧录主控固件需要根据外部条件动态生成配置比如设备每天切换工作模式在调试阶段做数据注入让主控读到指定的测试值而不用反复擦写EEPROM想给设备加一层数据保护主机读写的内容经过N76E003做校验或解密。N76E003选它的理由也很直接便宜一片几块钱自带硬件I2C外设不需要完全靠GPIO模拟时序512字节SRAM足够放一块256字节的模拟存储区。最关键的是它和AT24C02一样都是I2C从机总线行为可以用状态机精确匹配。1.2 “模拟”到底模拟的是什么有人以为模拟就是把I2C引脚接上去、收到数据存起来就行。其实远不止。主机读AT24C02时依赖的是一套固定的“行为习惯”主机发送设备地址0xA0写/ 0xA1读后片子必须在第九个时钟周期拉低SDA作为应答主机写入寄存器地址后地址指针要记住连续读时每读一个字节地址要自动加1读到0xFF再加1要回卷到0x00页写时写到本页最后一个字节后地址要回卷到本页开头不能串到下一页主机认为读够了会在最后一个字节后回一个NACK从机就要立刻释放总线。这些行为如果有一个对不上主控程序就会卡死或者读到错误数据。所以模拟AT24C02的本质是把这颗芯片的“协议状态机”搬到N76E003里。2. 硬件准备与I2C物理层三个容易出错的地方硬件接线不算复杂N76E003的SDA和SCL接主机对应引脚共地、供电然后在两条线上各接一只上拉电阻到电源。但就这么简单的电路我在实际项目里还是踩过几个坑。2.1 N76E003的I2C引脚和电气特性N76E003是增强型8051内核I2C外设默认映射在P1组引脚上具体是P1.3、P1.4有些封装可以通过配置位切换。我在Keil C51环境下开发I2C寄存器直接操作I2CON、I2STAT、I2DAT这套。建议开始写代码前先把手头数据手册里I2C章节翻出来确认引脚映射和中断向量号不同批次/封装略有差异。供电方面N76E003工作电压范围比较宽通常在2.4V到5.5V之间。如果你的主控I2C总线是3.3V就给N76E003供3.3V如果是5V直接供5V。总之要保证两边IO电平一致否则会出现“主机以为发了1从机读到0”的怪问题。2.2 上拉电阻到底选多大I2C总线是开漏结构SCL和SDA必须靠外部上拉电阻拉高。阻值选择看通信速率和总线电容标准模式100kHz上拉电阻可以用10kΩ快速模式400kHz建议用4.7kΩ如果总线上挂的设备多、走线长再降到2.2kΩ。我通常先用4.7kΩ起步再用示波器看边沿。上拉太小低电平被拉不到0.3VDD以下从机会误判上拉太大上升沿太缓高速通信会出错。特别是在面包板上验证时面包板的寄生电容比PCB大10kΩ就会有明显的圆角波形。2.3 最容易忽略的硬件冲突画板或者飞线之前一定要确认目标总线上没有另外一颗同地址的AT24C02还挂在上面。AT24C02地址是7位0x508位写地址0xA0如果板上原来那颗芯片还在且地址没改N76E003一上去就是地址冲突两个从机同时应答数据直接乱掉。替换测试时把原芯片摘了或者把它的A0/A1/A2地址引脚挑到别的地址。另外N76E003的I2C引脚建议配置成准双向模式。不要设成强推挽输出否则和主机之间可能出现电平“打架”。这一点在官方例程的引脚初始化里一般都有但很多人漏看。3. AT24C02的协议拆解不搞懂这4种时序就写不对程序AT24C02容量2Kbit也就是256字节挂在I2C总线上7位地址是0x50。它支持字节写、页写、当前地址读、随机读、顺序读。模拟器的核心就是把这五种时序用状态机翻译成N76E003的行为。3.1 设备和寄存器地址怎么算AT24C02的设备寻址字节格式是这样的高四位固定1010接着是A2、A1、A0三个引脚电平最后一位是读写方向位。当A2A1A00时写方向设备地址 0b10100000 0xA0读方向设备地址 0b10100001 0xA1很多人用Linux的i2cdetect扫描时看到的0x50是7位地址读0xA1、写0xA0是8位地址两者相差一位左移关系。调试时别搞混。存储器地址就是0x00到0xFF正好一字节对应AT24C02的256字节容量。3.2 五种时序的差别我把主机侧的操作整理成表格后面写状态机就是照着这个表来操作类型时序过程字节写START → 0xA0 → 寄存器地址 → 1字节数据 → STOP页写START → 0xA0 → 寄存器地址 → 2~8字节数据 → STOP当前地址读START → 0xA1 → 读1字节 → 主机NACK → STOP随机读START → 0xA0 → 寄存器地址 → 重复START → 0xA1 → 读1字节 → 主机NACK → STOP顺序读任意读方式开始后主机每ACK一次从机继续发下一字节直到主机NACK注意随机读里面有重复起始条件不是简单的“写完再读”。很多第一次写模拟器的人在这里翻车因为他没有处理重复START。3.3 页写回卷和地址指针自增页写是AT24C02最容易模拟错的地方。AT24C02的页大小是8字节主机连续写入时如果跨越页边界地址会“回卷”到本页开头而不是自动进入下一页。举个例子寄存器地址为0x07连续写入5字节真实芯片上这5字节会依次落在0x07、0x00、0x01、0x02、0x03而不是0x07、0x08、0x09。读操作的地址指针规则又不一样读多字节时地址在每个字节后自动加1超过0xFF后回卷到0x00。所以写地址回卷用“页内掩码”读地址回卷用“全局递增”。4. 从机状态机与关键代码含“先写数据再清SI”的技巧搞懂协议之后代码设计就有方向了。我见过有人用顺序查状态的方式写从机也就是收到一个字节就if判断一次。这在主机时序简单时能用但一旦遇到重复起始、主机NACK、页写回卷交织在一起就会变得一团乱麻。正确的做法是维护一个清晰的状态机。4.1 状态机需要关心的几个状态我的程序里只用两个变量加一个枚举状态就够ee_mem[256]模拟EEPROM存储区ee_addr当前地址指针addr_phase标记下一个收到的数据是不是寄存器地址一个枚举用来看当前处于“写地址阶段”还是“写数据阶段”。从机的状态流转基本是空闲状态收到匹配的自身地址写方向进入写地址接收写地址接收状态下收到的第一个字节是寄存器地址之后再收到的字节都是数据写入模拟存储区并按页回卷规则更新地址如果中途收到重复起始读方向说明主机要做随机读地址指针保留读状态下收到SLAR立即把当前地址的数据放进发送寄存器之后每次主机ACK就继续发下个字节主机NACK就结束。4.2 核心代码框架提供一个精简但能跑通核心逻辑的框架具体寄存器操作细节以N76E003官方头文件和例程为准#define I2C_STAT_SLA_W 0x60 // 收到自身地址写方向 #define I2C_STAT_SLA_R 0xA8 // 收到自身地址读方向 #define I2C_STAT_RX_DATA 0x80 // 从机接收模式下收到数据 #define I2C_STAT_TX_ACK 0xB8 // 从机发送数据后主机回ACK #define I2C_STAT_TX_NACK 0xC0 // 从机发送数据后主机回NACK #define I2C_STAT_STOP 0xA0 // 收到STOP或重复START volatile uint8_t ee_mem[256]; volatile uint8_t ee_addr 0; volatile uint8_t addr_phase 0; void I2C_IRQHandler(void) interrupt 6 // 中断号以你的头文件为准 { uint8_t sta I2STAT 0xF8; // 状态码取高5位 uint8_t dat; switch (sta) { case I2C_STAT_SLA_W: addr_phase 1; // 等待寄存器地址 break; case I2C_STAT_SLA_R: // 收到读方向立即预载第一个数据字节 I2DAT ee_mem[ee_addr]; ee_addr; // 地址自增 break; case I2C_STAT_RX_DATA: dat I2DAT; if (addr_phase) { ee_addr dat; // 第一个字节是寄存器地址 addr_phase 0; } else { ee_mem[ee_addr] dat; // 页写回卷只允许在本页8字节内滚动 ee_addr (ee_addr 0xF8) | ((ee_addr 1) 0x07); } break; case I2C_STAT_TX_ACK: // 主机ACK继续发下一个字节 I2DAT ee_mem[ee_addr]; ee_addr; break; case I2C_STAT_TX_NACK: // 主机NACK读操作结束 break; case I2C_STAT_STOP: addr_phase 0; break; default: break; } // 清SI标志释放总线继续传输 I2CON ~0x08; // SI位清0方式以N76E003例程为准 }这套代码的逻辑和真实AT24C02基本一致。关键点在中断里收到SLAR后必须立刻把ee_mem[ee_addr]写入I2DAT再清SI。如果顺序反了或者漏写主机第一个读时钟到来时数据寄存器还是空的读回来的数据就是0xFF或者乱码。4.3 “先写数据再清SI”背后的原因I2C从机发送数据是跟着SCL时钟一位一位移出去的。N76E003的硬件I2C一旦清掉SI总线就继续跑如果这时发送寄存器还没有准备好数据硬件只能把线上的数据拉成不确定状态。而且在清SI之前写入I2DAT数据才能被硬件锁存到移位寄存器里。顺带说一下主机的NACK处理。很多人在读操作里只处理“发数据”的状态忘了处理“主机不想读了回NACK”的状态。如果你不处理NACK总线会一直停在等待状态主机等不到STOP后的空闲后续通信全部卡死。我在代码里虽然没有在NACK状态做额外动作但清SI和STOP后回到空闲状态是必须的。5. 实测验证从地址扫描到跨页读写标题里写了“已测试”这里把测试过程完整放出来。测试主机我用了一块STM32F407来做模拟I2C主机因为调试方便又想脱离具体主机环境验证协议的正确性。当然也可以用树莓派、另一块N76E003、甚至逻辑分析仪直接抓波形思路一样。5.1 测试平台和接线测试环境是面包板N76E003供电3.3V外部晶振16MHzSDA、SCL各接4.7kΩ上拉到3.3V主机STM32F407的I2C引脚也接同一组总线共地没有挂真实AT24C02避免地址冲突。5.2 测试步骤和结果我用主机依次执行这几组操作每组都串口打印结果测试项操作预期结果实测结果地址扫描主机扫描I2C总线0x50地址应答通过字节写读向0x10写入0xA5回读0x10读到0xA5通过页写验证从0x06连续写8字节数据落在0x06~0x05页内回卷通过顺序读从0x20连续读256字节地址自动递增到0xFF后回卷0x00通过随机读先写0x30再重复起始读读到0x30地址数据通过页写回卷的验证方法是向0x06写入[0,1,2,3,4,5,6,7]再回头读0x00到0x0F会发现0x06和0x07是前两个数0x00到0x05是后六个数。如果读出来数据是“顺序落进内存”说明页回卷逻辑没写对。5.3 测试过程中踩到的坑第一个坑地址扫描时主机发的是当前地址读而不是写。N76E003在刚上电时ee_addr初始化为0所以主机一上来读0x00地址是能正常应答的。如果初始化代码里忘了设初值ee_addr是随机值虽然也能应答但主机读走的数据不可控容易让人误判程序有问题。第二个坑中断优先级。I2C中断如果和定时器中断同时触发而I2C中断优先级不够高主机时钟不等人——一次延迟就可能错过SCL的采样窗口通信直接失败。实测中我把I2C中断设成最高优先级问题立刻消失。第三个坑页写回卷写成普通自增。这是逻辑bug不是硬件坑。很多人写ee_addr就完事结果主机做跨页写时数据写到了下一页。改成我上面的掩码写法后跨页行为就和真实芯片一致了。6. 扩展方向把“替身EEPROM”做成“智能外设”基础模拟器跑通之后可以往两个方向扩展这些都是我在实际项目里验证过有价值的做法。一是动态数据注入。既然主机读写的数据都经N76E003我完全可以在中断外面定时更新ee_mem里的某些地址。比如用ADC采集温度把温度值换算后填到某个地址主机按老协议读EEPROM时拿到的就是实时温度。某块地址做成只读主机写入时直接忽略相当于加了写保护。这类功能在真实AT24C02上要么做不到要么得外加逻辑电路。二是掉电保存。N76E003本身没有EEPROM但内置DataFlash。可以在主循环里检测ee_mem是否有变化有变化就把整个或部分数据搬进DataFlash上电再恢复。注意千万别在I2C中断里做DataFlash擦写那会卡住总线主机分分钟超时。我建议的做法中断里只改RAM主循环里有专门标志位触发保存保存期间如果I2C有读写数据先从RAM走等总线空闲再落Flash。三是模拟多个地址。N76E003的I2C地址寄存器支持多地址匹配可以把一块芯片同时伪装成0x50、0x51两颗EEPROM节省硬件成本。如果做产品还可以把设备序列号、出厂日期写死在DataFlash区主机读固定地址就能拿到身份信息。最后再分享一个小技巧调试时不要只盯着代码尽量用逻辑分析仪抓一下总线波形。I2C从机问题很多时候不是“数据不对”而是“时序不对”——应答位少了一个时钟、重复起始没被识别、NACK之后总线没释放这些东西靠串口打印很难定位。我第一次调通这个项目最大的功臣就是一台十几块钱的逻辑分析仪把地址扫描和单字节读写的波形抓出来对照着看状态机哪里有问题一目了然。本文还有配套的精品资源点击获取
RELATED

相关推荐

尼康相机SDK C#二次开发:从单拍到视频的完整控制指南

尼康相机SDK C#二次开发:从单拍到视频的完整控制指南

简介:尼康相机与电脑连接控制的二次开发资料,面向希望用 C# 或 VB.NET 编写桌面程序、通过尼康 SDK 实现远程拍摄的开发者,解决影棚拍摄、自动化采集、延时记录等场景中脱离机身按键操作的需求,为批量或无人值守拍摄提供程序化控制…

📅 2026/9/9 23:18:33
如何用 WinUtil 通过 Config 选项卡启用 WSL、Hyper-V 等 Windows 可选功能组件?

如何用 WinUtil 通过 Config 选项卡启用 WSL、Hyper-V 等 Windows 可选功能组件?

如何用 WinUtil 通过 Config 选项卡启用 WSL、Hyper-V 等 Windows 可选功能组件? 【免费下载链接】winutil Chris Titus Techs Windows Utility - Install Programs, Tweaks, Fixes, and Updates 项目地址: https://gitcode.com/GitHub_Trending/wi/winutil …

📅 2026/9/9 23:18:33
自动化测试模型详解:线性、模块化、数据驱动、关键字驱动对比与选型

自动化测试模型详解:线性、模块化、数据驱动、关键字驱动对比与选型

面试的时候被问“自动化测试模型有哪几种”,我一般会反问一句:你是想把脚本写得更稳,还是想把框架搭给别人用?这个问题看似刁钻,其实本质是在确认对方有没有真的从项目视角理解过自动化测试。我做了十年测试&#xff0…

📅 2026/9/9 23:18:33
MORE NEWS

更多资讯

📰

Hadoop+Spark+Hive实战:膳食健康大数据离线数仓项目全解析

每年这个时间点,总能在各种技术社区和课程群里看到同一个焦虑:大数据方向课设到底做什么?做电商用户行为分析吧,十个人里有八个在做;做推荐系统吧,数据和模型又够喝一壶的。如果你也有类似的烦恼&#xff0…

📰

第 10 章:JVM 调优实战(Java 人的主场)

第 10 章:JVM 调优实战(Java 人的主场)所属:第三部分 实战方法(进阶) 学习目标:会看 GC、会定位内存问题、会调基础 JVM 参数——这是你相对纯测试人的最大优势。 预计学习时间:4~5…

📰

GUI-MCP与HITL:大模型操作电脑的安全落地实战解析

最近大半年我一直在跟同一个问题拉扯:让大模型直接操作电脑上的界面,到底敢不敢放到生产环境里。阶跃星辰把 GUI-MCP 这个概念推到前台之后,很多团队都在讨论“模型能不能看懂屏幕”“工具调用顺不顺”,但真正决定一个 GUI Agent …

📰

LDPC编码误码率仿真详解:BPSK/QPSK/16QAM软解调与Eb/N0实用指南

简介:针对LDPC编码在不同调制方式下的误码率性能仿真,这套MATLAB项目面向通信工程、电子信息等相关专业学生与研究人员,可用于课程设计、毕业设计或技术预研。资源共12个m脚本,压缩包仅13KB,但模块划分完整&#xff0c…

📰

Conductor 系统任务指南:内置任务类型、配置参数与服务器端执行原理

Conductor 系统任务指南:内置任务类型、配置参数与服务器端执行原理 【免费下载链接】conductor Conductor is an event driven agentic workflow engine providing durable and highly resilient execution engine for applications and AI Agents 项目地址: htt…

📰

Halo 菜单层级模型重构剖析:从 `children` 聚合到 `menuName` + `parent` 引用式层级

Halo 菜单层级模型重构剖析:从 children 聚合到 menuName parent 引用式层级 【免费下载链接】halo Halo 是一款强大易用的开源建站工具,从个人博客、知识库,到企业官网、在线商城,Halo 都能助您轻松实现,一站式满足您…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬