OV5640_top中的i2c_ctrl代码结合仿真波形理解

078c3b7d607c47d7b16df9dbcaac6548.png

db2a3fae53e4453ea922e8350b32b1a4.png

系统上电后,状态机处于 IDLE(初始状态);

接收到有效的单字节数据读/写开始信号i2c_start 后,状态机跳转到 START_1(起始状态);

FPGA 向 EEPROM 存储芯片发送起始信号,随后状态机跳转到 SEND_D_ADDR(发送器件地址状态);

在此状态下向 EEPROM 存 储芯片写入控制指令,控制指令高 7 位为器件地址,最低位为读写控制字,写入“0”,表 示执行写操作;控制指令写入完毕后,状态机跳转到 ACK_1(应答状态)。

 

在 ACK_1(应答状态)状态下,要根据存储地址字节数进行不同状态的跳转。 情况一:当 FPGA 接收到 EEPROM 回 传 的 应 答 信 号 且 存 储 地 址 字 节 为 2 字 节 , 状 态 机 跳 转 到SEND_B_ADDR_H(发送高字节地址状态);将存储地址的高 8 位写入 EEPROM,写入完成后,状态机跳转到 ACK_2(应答状态);FPGA 接收到应答信号后,状态机跳转到SEND_B_ADDR_L(发送低字节地址状态); 情况二:当 FPGA 接收到 EEPROM 回传的应答信号且存储地址字节为 单字节,状态机状态机直接跳转到 SEND_B_ADDR_L(发送低字节地址状态);在SEND_B_ADDR_L此状态低 8 位存储地址或单字节存储地址写入完成后,状态机跳转到 ACK_3(应答状态)。

 

在 ACK_3(应答状态)状态下,要根据读/写使能信号做不同的状态跳转。 情况一:当 FPGA 接收
到应答信号且 写使能信号有效,状态机跳转到 WR_DATA(写数据状态);在写数据状态, 向EEPROM 写入单字节数据后,状态机跳转到 ACK_4(应答状态);待 FPGA 接收到有效应答信号后,状态机跳转到 STOP(停止状态); 情况二:当 FPGA 接收到应答信号且 读使能信号有效, 状态机跳转到 START_2(起始状态);再次向 EEPROM 写入起始信号,状态跳转到SEND_RD_ADDR(发送读控制状态);再次向 EEPROM 写入控制字节,高 7 位器件地址不变,读写控制位写入“1”,表示进行读操作,控制字节写入完毕后,状态机跳转到 ACK_5(应答状态);待 FPGA 接收到有效应答信号后,状态机跳转到 RD_DATA(读数据状态);在 RD_DATA(读数据状态)状态,EEPROM 向 FPGA 发送存储地址对应存储单元下的单字节数据,待数据读取完成户,状态机跳转到 N_ACK(无应答状态),在此状态下向 EEPROM 写入一个时钟的高电平,表示数据读取完成,随后状态机跳转到 STOP(停止状态)。
 
 
 
 
在 STOP(停止状态)状态,FPGA 向 EEPROM 发送停止信号,一次单字节数据读/写操作完成,随后状态机跳回 IDLE(初始状态),等待下一次单字节数据读/写开始信号 i2c_start。
 
 
c61aabff450d448bbd158ec069b08b6e.png

b03b571939f543369e04ef9061f98edb.png

注:对 EERPROM 的数据读写操作均使用单字节读/写操作,即每次操作只读/写单字
节数据;若想要实现数据的连续读/写,可持续拉高读/写使能 rd_en/wr_en,并输入有效的
单字节数据读/写开始信号 i2c_start 即可。
 

 930cffb6745641e8bf765b41672707c7.png

5c56f4b8803342909c8a1950073536a2.png

02dce2e123f3434994a47521a17f818c.png

 

a743d174e5ba4006bfd7bf5ee092df19.png

 

下面讲一下配置寄存器的步骤: ov5640的每个寄存器配置之前都要先写入8位器件地址0X78,然后再写入高低16位寄存器地址,再写入8位数据值

前面第1-2红线之间的state=2在SEND_D_ADDR状态表示要写入器件地址,i2c_sda的0111_1000表示ov5640的器件地址0X78;  

第2-3条红线之间的state=3在ACK_1应答状态; 

第3-4和第5-6条红线总共表示写入第一个寄存器地址3103;其中第3-4条红线之间的state=4在SEND_B_ADDR_H状态表示要写ACK入寄存器地址高8位,i2c_sda的0011_0001表示0X31;第4-5条红线之间的state=5在ACK_2应答状态;第5-6条红线之间的state=6在SEND_B_ADDR_L状态表示要写入寄存器地址低8位,i2c_sda的0000_0011表示0X03;第6-7条红线之间的state=7在ACK_3应答状态; 

第7-8条红线之间的state=8在WR_DATA状态表示要写入第一个寄存器的数据值,i2c_sda的0001_0001表示写入的数据值是0x11;  

 

f6147acfa8d049ed8e70a3d913576755.png

第8-9条红线之间的state=9在ACK_4应答状态;

在9-10条红线之间的state=f在STOP结束状态;此后i2c_end拉高了一个时钟周期表示一个寄存器配置完成。

22ee367c137347d7b078e2571ac3db7a.png

每个寄存器配置都是从i2c_start开始,到i2c_end结束的(即两条黄线之间:从state=1~9,再到STOP结束状态f)

aaf07d0d77f64f95af19e767940a479f.png

7a5e39c954e84e5581f3850156698bbb.png7b37b3ee03ae4e2d95adcf0d4e304cc9.png6fdcdc8a70214615bb6f86ecd918ec83.png 1e515f6d6b8e41419df521bc5ff0e83c.png64ddf704288d4461b328188d3ddd9d49.pngc65856c032624072af5b999ab9dc5d3d.png48a93b46876749078476ca569b278249.png

//assign  i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz; 这是源代码
//由于仿真看到state状态一直是3,即ack_1跳转不出去。于是看了if语句,发现ack==1'b0不满足,因为此时仿真里的ack是蓝线(z高阻态)。于是去看了ack发现是由sda_in传入的,而sda_in又是i2c_sda,所以就做了下面的修改


assign  i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'b0; //这是修改后的代码

但是真实的上板是需要改成三态z的

本身ov5640的器件地址是0X78(即0111_1000)。但是野火写的器件地址是0X3c(即0011_1100), 但是在i2c_sda_reg的8位SEND_D_ADDR地址中,用cnt_bit计数8个,在前7个写入的是器件地址0X3c(取低位的7个数011_1100),最后一个是读写控制字0(0表示写),合起来也是0X78(即0111_1000)。

21c946549ec24741b5f9053e9a6995c5.png

写使能有效,读使能无效(即只是写入了ov5640的寄存器地址和数据值,但不读出),所以只有左边圈住的状态需要操作,右边的没圈住的状态不用管。e01bbc322bb04d08bea7bc062735b099.png

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值