☰
FPGA实现EEPROM读写:I2C时序与Verilog状态机设计实战
2026/10/17 6:04:43 网站建设 项目流程

简介:本资源是一套基于FPGA实现EEPROM(AT24C64)读写功能的完整工程方案,面向嵌入式系统初学者、数字电路课程设计者及FPGA开发入门工程师,解决非易失性存储器在可穿戴设备、物联网终端等场景中数据持久化存储的实际需求。压缩包共234个文件,总计6.23MB,涵盖Verilog源码(.v)、Quartus综合/布局布线数据库(.cdb/.hdb/.rpt)、编译日志(.qmsg/.logdb)、约束与配置文件(.qsf/.pin/.sdc)以及HTML报告和README说明文档,结构完整,支持直接编译下载至开拓者开发板验证。已有104人学习下载,资源包含顶层模块e2prom_top.v及配套IP生成文件、时序分析报告与映射信息,便于理解I²C协议时序控制逻辑、EEPROM地址寻址机制及FPGA与外围存储器协同工作的全流程实现细节。 做FPGA开发这几年,隔三差五就会接到跟EEPROM读写相关的任务——保存DDR校准参数、记录设备运行次数、上电加载配置字、存储ADC校准系数,都是这一类。“FPGA实现eeprom的读写”这个话题看着简单,实际上很多人第一次做就卡在I2C时序和状态机上。这篇就专门聊聊FPGA怎么和EEPROM打交道,从接线、时序、状态机到Verilog实现和上板验证,全部串起来讲一遍,适合刚入门FPGA、正准备在板卡上挂一颗AT24C系列的工程师参考。

1. 为什么FPGA项目里会用到EEPROM,以及怎么选器件

1.1 EEPROM和Flash、SRAM的定位差异

EEPROM最大的特点是掉电不丢数据,还能按字节擦写。FPGA本身掉电后配置就丢了,需要从Flash加载,但Flash擦写一般按扇区来,改一个字节要整块搬移,很不方便。EEPROM容量虽然不大,但按字节改写非常省事,所以很多板卡上用它保存校准系数、序列号、运行参数这类“少量但经常要改”的数据。

对比项EEPROMSPI FlashBRAM/SRAM
掉电保持保持保持丢失
擦写单位字节扇区/块直接写
典型容量2Kb~1Mb4Mb~1Gb几十Kb到几Mb
擦写寿命约100万次约10万次不限
接口I2C/SPI/MicroWireSPI/QSPI并行
适合场景参数、配置、校准值程序镜像、固件运算缓冲、缓存

挑选EEPROM时,先看容量,再看接口。容量上,AT24C02有256字节,存几十个参数已经够用;如果要做启动日志、多组配置,可能就要AT24C08或AT24C16。接口上,I2C型的EEPROM只要两根线,占用FPGA引脚少,适合IO紧张的项目;如果追求速度,SPI型会更快一些,但代码量也大一些。新手练手首选I2C型的AT24C02,资料多、模型多、遇到问题好排查。

1.2 AT24C系列怎么选、硬件怎么接

以AT24C02为例,它内部有256字节,I2C器件地址占7位。地址高四位固定为1010,低三位由A2/A1/A0引脚决定。如果这三个引脚都接地,7位地址就是1010000,写地址0xA0,读地址0xA1。如果板上接了多颗EEPROM,就靠这三个引脚区分,三根脚最多拼出8个地址。

硬件连接非常关键。SCL和SDA都是开漏输出,必须分别接上拉电阻到电源。上拉电阻一般选4.7k,标准100kHz模式下很稳;如果总线走线比较长或者要跑400kHz,可以换2.2k,但不能太小,不然灌电流太大,总线低电平可能抬不上去。很多开发板上已经自带EEPROM和上拉电阻,可以直接用,但如果自己画板子,最容易漏的就是这两个电阻。

再说WP写保护脚。手册上写内部有下拉,悬空时允许写,但是实际项目中我强烈建议直接接到GND,明确让它处于可写状态。我碰到过几次因为WP悬空、外部干扰把它拉高,结果写操作一直NACK的情况。调试这种问题很费时间,不如一开始就绑死。

AT24C02的SCL最高可以到400kHz(快速模式),一般FPGA例程用100kHz写,兼容性更好。SCL频率并不是越高越好,400kHz对I2C时序的上升时间要求更严,如果总线上拉电阻选得不合适,很容易在边沿处出现错误。

2. I2C协议的根本是一条“SCL高电平期间SDA不许乱动”的规矩

I2C协议看起来有不少规则,但真正核心的就一句话:SCL为高电平的时候,SDA电平代表数据;SDA想要变化,必须等SCL回到低电平。这条规矩把起始、停止和数据位全串起来了。

2.1 起始、停止、数据位、ACK四条核心规则

起始条件是在SCL保持高电平期间,SDA从高电平跳变到低电平。停止条件则反过来,SCL保持高电平期间,SDA从低电平跳变到高电平。这里要注意,如果在SCL高电平期间SDA发生跳变,总线上的设备不会认为这是数据,而是会把它当作起始或停止信号,所以其他数据位的跳变必须安排在SCL低电平期间。

数据传输按字节进行,每字节8位,高位先发。比如要发送0xA0,对应的8个bit就是1 0 1 0 0 0 0 0,在SCL低电平期间依次把SDA驱动成对应电平,SCL高电平期间从机去采样。一个bit占一个SCL周期。

第9个SCL周期是ACK位。对于写操作,主机在第9个时钟前释放SDA(置高阻),从机在SCL低电平期间把SDA拉低,主机在SCL高电平期间采样到低电平,就知道从机收到了。读操作时角色互换:从机发数据,主机在第9个时钟拉低表示“继续读”,拉高表示“我读够了”。

2.2 EEPROM的写时序、读时序和写周期tWR

对AT24C02做单字节写,完整时序是:

  • 起始条件
  • 发送写地址0xA0,等待ACK
  • 发送要写的字地址(比如0x12),等待ACK
  • 发送数据字节,等待ACK
  • 停止条件

停止条件发出之后,EEPROM内部才开始真正执行擦写,这个阶段叫写周期,时间tWR最大约5ms。在tWR结束前,芯片不响应任何命令。如果主机以为stop完就能立刻发下一条,大概率会等不到ACK,或者读到旧数据。

读操作比写操作要绕一环,因为I2C协议里没有“读这个地址”这样的单条命令,需要先“指路”再“取数”。最常用的随机读时序是:

  • 起始条件
  • 发送写地址0xA0,等待ACK
  • 发送要读的字地址,等待ACK
  • 重复起始条件
  • 发送读地址0xA1,等待ACK
  • 读取数据字节,发送NACK
  • 停止条件

第二步到第三步看起来是“写”,实际上只是在设置内部地址指针,所以叫伪写。真正的数据读取在后面重复起始之后才发生。

有的设计图省事,伪写结束后直接发停止,再重新起始进入读模式。协议上也允许,但多了一次总线切换,还耽误时间。重复起始的做法更紧凑,我推荐直接按手册里的时序走。

2.3 页写的边界陷阱

AT24C02页大小是8字节,一次最多连续写8个字节,而且这8个字节必须是同一页内的8个地址。页地址按字地址的低三位划分,即0x00~0x07是第0页,0x08~0x0F是第1页,以此类推。

页写最阴险的地方在于地址回卷。如果从0x06开始连续写4个字节,EEPROM会在写完0x07后回卷到0x00,而不是跑到0x08,最终0x00、0x01也被改掉。几乎所有第一次写页写的人都会踩这个坑。所以控制逻辑里要判断:剩余待写字节数不能跨越页边界,一旦跨页,就把一次连续写拆成多次,每次最多写到页尾。

2.4 时序参数速查表

下面这张表是我调试时反复对照的,AT24C02在标准模式(100kHz)下的关键参数:

参数典型值备注
SCL频率100kHz / 400kHz快速模式需要配合合适上拉
SCL高电平最小时间4.0us(100kHz)由主机分频保证
SCL低电平最小时间4.7us(100kHz)由主机分频保证
起始条件保持时间4.0us不能在SCL高时早动
写周期tWR5ms(最大)stop后必须等
总线空闲时间4.7us连续操作间建议等待

这些时间在FPGA实现里,只要SCL频率不超过规格,一般都会满足。真正要小心的是tWR,因为它是毫秒级,比I2C数据位的微秒级长得多,不是分频器顺手能覆盖的,必须单独处理。

3. 状态机设计:把读写流程拆成“有限步走位”

很多第一次写I2C控制器的人容易直接在always块里堆一堆if else,最后状态乱成一锅粥。我的经验是,一定先把“字节级”和“位级”分开。位级负责在SCL的一个周期内把数据比特移到SDA上,字节级负责一个字节一个字节地组织起始、地址、数据、停止这些动作。

3.1 两层结构:I2C底层驱动 + EEPROM操作控制

底层I2C驱动模块是纯通用的,它不认识EEPROM,只负责完成发送一个字节、接收一个字节、产生起始停止、等待ACK这些动作。上层EEPROM控制模块才是状态机主体,它根据AT24C02的时序来调度底层驱动。

这样做的好处非常明显。项目里以后要挂温度传感器、实时时钟、ADC配置寄存器,全都是I2C接口,底层驱动直接复用,只改上层状态机就可以。如果一开始就把EEPROM协议

本文还有配套的精品资源,点击获取

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

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

立即咨询