8位片内地址的I2C SLAVE在OPENCORS.org上面有,但是我没有找到16位的,我打算用B210的接EEPROM的I2C总线实现跟FPGA通讯就对照24C256的数据手册写了一个。

以下代码2022-6-6更新已经实际运行通过。

 /*
i2c_slave # ( .I2C_ADR( 7'b1010_000 ) )
i2c_slave (
clk,
rst,
scl_i,
sda_i,
sda_oen,
addr ,
wr,
rd,
dout ,
din ,
st  ,
inr
);
*/ module i2c_slave # ( parameter I2C_ADR = 7'b1010_000 ) (
input clk,rst,input scl_i,sda_i,
output reg sda_oen, output reg  [15:0] addr ,
output reg wr,rd,
output reg [7:0] dout ,
input [7:0] din ,
output  reg [7:0 ] st  ,
output reg [6:0 ] inr);reg [7:0] dinr, doutr ;
reg [2:0] sdar; always @ (posedge clk) sdar<= { sdar[1:0] , sda_i };
reg [2:0] sclr; always @ (posedge clk) sclr<= { sclr[1:0] , scl_i };wire  sda_rise =  sdar[2:1]==2'b01 ;
wire  sda_fall =  sdar[2:1]==2'b10 ;wire  scl_rise =  sclr[2:1]==2'b01 ;
wire  scl_fall =  sclr[2:1]==2'b10 ;wire  start_con_w =  sclr[1] & sda_fall ;
wire  stop_con_w  =  sclr[1] & sda_rise ;
reg rd1wr0; /*
wire[35:0] CONTROL0 ;
reg  [31:0]  bus32 ; always @ (posedge clk)  bus32[7:0] <= st   ;
always @ (posedge clk)  bus32[9:8] <= {wr,rd} ;
always @ (posedge clk)  bus32[31:16] <= addr;
always @ (posedge clk)  bus32[15:14] <= {sdar,sclr} ;//dout[5:0];chipscope_ila_32 chipscope_ila_32_0 (.CONTROL(CONTROL0), // INOUT BUS [35:0].CLK(clk), // IN.TRIG0({bus32 }) // IN BUS [31:0]);chipscope_icon chipscope_icon_i0(.CONTROL0(CONTROL0) // INOUT BUS [35:0]);reg [7:0]d ;always @(posedge clk) case (st) 112,80,168:d<=d+1;default d<=0; endcase always @ (posedge clk)  bus32[7:0] <= st   ;
always @ (posedge clk)  bus32[9:8] <= {wr,rd} ;
always @ (posedge clk)  bus32[31:16] <= addr;
always @ (posedge clk)  bus32[15:14] <= {sdar,sclr} ;//dout[5:0];*/always @ (posedge clk) if (rst) st<=0;else if (start_con_w)st<=20; else case (st)
0  : st <= 10 ;
10 : begin sda_oen<=1; wr<=0; if (start_con_w) st <= 20 ;end
20 : begin inr[6]<=sdar[1];if (scl_rise)st<=st+1;end
21 : begin inr[5]<=sdar[1];if (scl_rise)st<=st+1;end
22 : begin inr[4]<=sdar[1];if (scl_rise)st<=st+1;end
23 : begin inr[3]<=sdar[1];if (scl_rise)st<=st+1;end
24 : begin inr[2]<=sdar[1];if (scl_rise)st<=st+1;end
25 : begin inr[1]<=sdar[1];if (scl_rise)st<=st+1;end
26 : begin inr[0]<=sdar[1];if (scl_rise)st<=st+1;end
27 : begin rd1wr0<=sdar[1];if (scl_rise)st<=st+1;end
28 : if ( inr[6:0] == I2C_ADR ) st<=30;else st<=10;30 : if (sclr[1]==0) begin  st<=st+1 ;end //you are visiting my device ,ack it now .
31 : begin sda_oen <= 0; if (scl_rise) st<=st+1; end // master read back this ack .
32 : if (sclr[1]==0) begin st<=st+1 ;end
33 : begin sda_oen  <= 1  ;if (rd1wr0==0) st <= 50 ;
else st <= 150 ; // read out
end
50 : begin addr[15]<=sdar[1];if (scl_rise)st<=st+1;end
51 : begin addr[14]<=sdar[1];if (scl_rise)st<=st+1;end
52 : begin addr[13]<=sdar[1];if (scl_rise)st<=st+1;end
53 : begin addr[12]<=sdar[1];if (scl_rise)st<=st+1;end
54 : begin addr[11]<=sdar[1];if (scl_rise)st<=st+1;end
55 : begin addr[10]<=sdar[1];if (scl_rise)st<=st+1;end
56 : begin addr[9 ]<=sdar[1];if (scl_rise)st<=st+1;end
57 : begin addr[8 ]<=sdar[1];if (scl_rise)st<=st+1;end
58 : if (sclr[1]==0) begin sda_oen  <= 0 ;st<=st+1;end
59 : if (scl_rise) st<=st+1;
60 : if (sclr[1]==0) begin sda_oen  <=1  ;st<=70 ;end70 : begin addr[7]<=sdar[1];if (scl_rise)st<=st+1;end
71 : begin addr[6]<=sdar[1];if (scl_rise)st<=st+1;end
72 : begin addr[5]<=sdar[1];if (scl_rise)st<=st+1;end
73 : begin addr[4]<=sdar[1];if (scl_rise)st<=st+1;end
74 : begin addr[3]<=sdar[1];if (scl_rise)st<=st+1;end
75 : begin addr[2]<=sdar[1];if (scl_rise)st<=st+1;end
76 : begin addr[1]<=sdar[1];if (scl_rise)st<=st+1;end
77 : begin addr[0]<=sdar[1];if (scl_rise)st<=st+1;end
78 : if (sclr[1]==0) begin sda_oen <= 0 ;st<=st+1 ;end
79 : if (scl_rise) begin  st<=st+1;end
80 : if (sclr[1]==0)begin  sda_oen <=1 ;st<=st+1;end //ack this address
81 : begin doutr[7]<=sdar[1]; if (scl_rise) st<=82;end
82 :  if (start_con_w)  st<=20;
else if (sclr[1]==0)   st<=101  ; // master write me data  150 :  begin st <= st+1;end  // set read current addr
151 :  begin st <= st+1; dinr<= din ; addr <= addr+1;end  //latch in dinr
152 :  begin sda_oen<= ~dinr[7]; if (scl_rise)st<=st+1;end  153 :  if(sclr[1]==0) st<=st+1;
154 :  begin sda_oen<= ~dinr[6]; if (scl_rise)st<=st+1;end  155 :  if(sclr[1]==0) st<=st+1;
156 :  begin sda_oen<= ~dinr[5]; if (scl_rise)st<=st+1;end  157 :  if(sclr[1]==0) st<=st+1;
158 :  begin sda_oen<= ~dinr[4]; if (scl_rise)st<=st+1;end  159 :  if(sclr[1]==0) st<=st+1;
160 :  begin sda_oen<= ~dinr[3]; if (scl_rise)st<=st+1;end  161 :  if(sclr[1]==0) st<=st+1;
162 :  begin sda_oen<= ~dinr[2]; if (scl_rise)st<=st+1;end  163 :  if(sclr[1]==0) st<=st+1;
164 :  begin sda_oen<= ~dinr[1]; if (scl_rise)st<=st+1;end  165 :  if(sclr[1]==0) st<=st+1;
166 :  begin sda_oen<= ~dinr[0]; if (scl_rise)st<=st+1;end  167 :  if(sclr[1]==0) st<=st+1;
168 :  st<=169;
169 :  begin sda_oen<= 1;if (scl_rise)st<=st+1 ;end
170 :  if (sdar[1]==0)  st<=180;//ACK : burst read;else st<=st+1;//NACK      180: if (sclr[1] == 0 ) st<=150;//wait next reading
171 :  if (stop_con_w) st<=10 ; //stop now
//now in write state
100 : begin doutr[7] <= sdar[1] ; if (scl_rise)st<=st+1;end
101 : begin doutr[6] <= sdar[1] ; if (scl_rise)st<=st+1;end //checked ok
102 : begin doutr[5] <= sdar[1] ; if (scl_rise)st<=st+1;end
103 : begin doutr[4] <= sdar[1] ; if (scl_rise)st<=st+1;end
104 : begin doutr[3] <= sdar[1] ; if (scl_rise)st<=st+1;end
105 : begin doutr[2] <= sdar[1] ; if (scl_rise)st<=st+1;end
106 : begin doutr[1] <= sdar[1] ; if (scl_rise)st<=st+1;end
107 : begin doutr[0] <= sdar[1] ; if (scl_rise)st<=st+1;end
108 : begin dout<=doutr ; wr<=1 ; st<=st+1; end
109 : begin addr<=addr+1; wr<=0; st<=st+1; end
110 : if (sclr[1]==0) st<=st+1;
111 : begin sda_oen<=0;if (scl_rise)st<=st+1;end
112 : if (sclr[1]==0) st<=st+1; //send ACK
113 : begin sda_oen <=1;doutr[7] <= sdar[1];if (scl_rise)st<=st+1;end
114 : if (stop_con_w) st<=20; else if (sclr[1]==0) st<=101;   default st<=0;
endcase
always @ (*) rd = st == 150 ;
endmodule 

代码风格还是我用的本办法。

看图说话:

这里要注意以下几点:

1,SCL高电平期间SDA的输出保持不变。

2,SCL==0 时候,并且满足建立保持时间后,SDA允许数据变化。

3,SCL==1S时候如果SDA由高变低(下降沿)定义为START信号。

4,SCL==1S时候如果SDA由低变高(上升沿)定义为STOP信号。

5,当SLAVE(本代码)发送ACK后,在SCL的上升边缘被MASTER所采集到,并且MASTER要发送STOP信号,或者另外一个START信号,这时候需要考究一下SLAVE何时释放SDA输出是能信号SDA_OEN。数据手册里面描述的此时间最小是100ns。我们可以认为MASTER用SCL的上升边沿采集SLAVE发出的SDA,其保持时间只要不小于100ns就可以满足。(MASTER实际需要的保持时间应该更短),我们这里设置100ns多一些即便是和master的STOP或者START发生两输出冲突,及短也不会有什么损坏器件的影响。

6,I2C总线建立时间是说SCL上升边沿到来之前,SDA要保持的最短时间。

7,I2C总线的建立时间是说SCL下降边沿到来之后,SDA要保持的最短时间。

上述代码简单写好了,框架和思路没有问题,但是还没有调试,细节方面应该是有点问题,明天继续看看。

===========================以下内容2022-6-6添加============================

1,ACK或者NACK是都是独立的一个BIT,没有和STOP和START混合在一起。

2,读的过程首先是设置地址,这个过程伪写,就是将地址写入内部寄存器。之后不STO而立即START,开始读出数据。每个数据之后MASTER给一个ACK,最后一个字节MASTER给一个NACK之后下个BIT发送STOP命令结束总线对话。最后一个字节虽然发送的是NACK实际上MASTER也收到了正确数据,只是为了告诉SLAVE接下来不要传输了,就发送了NACK。

3,PAGE是连续操作的字节个数,的在24C256数据手册里面看到写的是16字节,实际进行读操作时候发现读操作不受PAGE字节数限制,可以一次读出所有的字节。

4,地址是一个默认参数,每次读写操作都会使得地址寄存器加1。

5,写的操作都必须携带地址,而读的操作实际是包含了设置地址和真正读操作。这两个操作序列可以在一次I2C会话中实现,也可以分开来。

6,SCL=0期间允许数据表换,SCL上升边沿SDA的数据被采集,

SCL=1期间如果SDA由高变低被认为是I2C总线会话的开始,

SCL=1期间如果SDA由低变高认为是I2C总线会话的结束。

===========================以下内容2022-6-7添加============================

1,在case之前加一个对起始位的判断,只要有起始位到来,就认为是新的会会话开始。这样的目的是为了防止状态机进入未知状态而死机。

2,一个传输开始之后,首先是固定的发送dev_addr+读写请求,如果这时候器件允许读,则就发送ACK,否则不发送任何内容会被认为是NACK。

3,发送ACK相应了器件寻址之后,就会进入都或者写的模式。

4,进入写的模式,首先必须是写操作地址,写完操作地址后,实际又有两种情况,一种是直接继续传输写入内容,另外一种MASTER从新发送START开始新的会话(这种情况下,之前的写操作实际就是之写了地址寄存器)。

5,对4的分析,我们在处理写命令的时候,首先将前两个数据作为地址保存好,之后继续进入收取I2C写来的内容并进行实际写的状态,如果I2C主控要实际进行写,会继续往下进行写操作,如果I2C主控只是为了读而设置地址寄存器,主控会从新发出START命令,被我们检测到以后从新进入判断读写状态。

6,上述4,5说的都是写的情况,读的情况只有一种:在7位的器件地址后面紧接一位是1,说明是读,之后I2C的从设备从之前已经写好的地址寄存器里面获取地址,取数并每个字节发送到I2C的MASTER,并将地址寄存器递增加1。主机收到一个字节后就ACK一下,让I2C从设备再次发送下一个字节,如果主机不打算再接受就发送NACK,之后接着下位发送一个STOP,终止这次传输。

===================2022-6-7添加的关于I2C总线硬件接口的分析==============

1,首先有一个很重要的概念叫做“线与”,这里的“与”就是对应逻辑and,我们知道逻辑and,多个说如进行and逻辑操作,只要有一个是0,则结果是0。

2,我们看下面的总线:

我们看到红色框内只能将I2C的两个线进行拉低,这样在I2C总线上电平是0,而红色框内那个MOS管的接入方式叫做OPEN DRAIN 或者说“开漏”方式,只能吸收电流拉低为0,而无法实现输出1。I2C总线要输出1则要依靠外部的上拉电阻RP。

3,在2的图中,我们只看SDA,同种任意一个外设的拉低总线都会导致总线上电平表现为0.这就是实现了1中所讲到的“线与”。

4,我们进一步分析图中MOS管的控制,当MOS管处于导通状态,实际输出0,当MOS管处理截至状态,是没有输出的,可以认为是输入状态,由于上拉电阻拉到高电平也可以认为是输出1。

5,既然输出1是上拉,因此不存在多个I2C设备同时输出不一样的电平造成的电流冲突,这方面真是精髓。

6,由于上述5这条实现了多个输出发生碰撞不会冲突,所以I2C可以允许总线上不但可以有多个从设备外还可以有多个主设备。

7,还有一个几乎是俗成的约定,就是对器件进行寻址的时候,如果从设备不此时不具备响应条件(比如正在执行上一个写操作发下来的命令对数据正在烧写到实际存储区域),直到具备相应条件才进行相应。这实际就是要求在读写前确保器件不忙,可以在

A,每次操作后反复进行写操作寻址的寻址,如果得到ACK后就退出,这就保证实际的操作起作用,并且了下次操作直接进行;

B,或者是在每次操作之前进行器件寻址等到回应ACK后才继续下一步。

我实际推荐A方式,确保生效后再退出。

8,上述7.A提到每次设置后要等待生效,下面的逻辑分析仪的抓图就是例证:

由于图片大小限制只能抓部分图片,大家可以看到执行完写操作后,反复寻址器件,而器件一直NACK,知道器件回复ACK,I2C的主设备才停止询问,认为一次写操作完成。

9,I2C接口要用到三态输入,最好将三态门实例化在TOP层。这写实现细节可能在之后FPGA具体实现中讲到。

16位片内地址的I2C SLAVE接口设计相关推荐

  1. sql 数字减去null_减去两个16位数字| 8086微处理器

    sql 数字减去null Problem: Write a program to subtract two 16-bit numbers where starting address is 2000 ...

  2. 16位I2C寄存器地址读写接口

    16位I2C寄存器地址读写接口 I2C读写接口 static int max96722_read_reg(struct i2c_client *client, unsigned char reg_ad ...

  3. I2C总线时序图: 8位设备地址 16位寄存器地址 16位数据

    有关I2C总线时序,下面的文章写得很好,推荐阅读 https://www.cnblogs.com/BitArt/archive/2013/05/28/3103917.html#commentform ...

  4. arduino i2c 如何写16位寄存器_基于STM32使用I2C读取传感器数据

    撑腰会儿:I2C通信协议介绍​zhuanlan.zhihu.com 上文介绍了I2C协议的基本结构,今天,使用STM32和LM75A温度传感器来实现I2C读取信息. 首先,为了使用I2C读取传感器测量 ...

  5. 计算机组成与结构课内实验:16位模型机的设计

    我们当时是有两个实验的.一个是计组课内的实验:16位模型机的设计.还有一个是计组的最终大课设:计算机组成与结构综合实验,另一篇文章我将给出综合实验的报告 第一个是课内的设计实验: 引言 1.1 设计目 ...

  6. 读写EEPROM遇到的问题:16位地址的内容都是最后写入的数据。

    #define EE_TYPE AT24C64 如下图所示,往EEPROM的地址0x1000.0x1001.0x1002和0x1003分别写入0xAA.0xBB.0xCC和0xDD,读出来的数据都是最 ...

  7. STM32 FSMC 16位寻址 地址移位的解读

    一. 当 Bank1 接的是 16 位宽度存储器的时候:HADDR[25:1] FSMC_A[24:0]. 当 Bank1 接的是 8 位宽度存储器的时候:HADDR[25:0] FSMC_A[2 ...

  8. arduino i2c 如何写16位寄存器_Arduino只是拿来玩的?你错了!教你用它自制一个非常实用的小产品|智能灌溉控制器...

    利用动态水循环,具有下雨探测,日出探测功能,智能灌溉你的院子或花园. 一定要看文章最后哦,结尾有惊喜! 部件和材料 Arduino NANO开发板 1块 IO扩展板 1块 IO扩展模块包 1套 app ...

  9. 假设指令字长为16位,操作数的地址码为6位,指令有零地址、一地址、二地址三种格式......

    假设指令字长为16位,操作数的地址码为6位,指令有零地址.一地址.二地址三种格式.  (1)设操作码固定,若零地址指令有M种,一地址指令有N种,则二地址指令最多有几种?  (2)采用扩展操作码技术,二 ...

最新文章

  1. codevs 2075 yh女朋友的危机
  2. MIT科学家首次发现只对歌唱有反应的神经元,对,只能人声带伴奏的那种歌
  3. C语言获取当前工作路径
  4. 详细讲解Java中log4j的使用方法
  5. mysql怎样修改my ini_mysql修改my.ini报错怎么办
  6. ui automator viewer 怎么获取界面名_ui交互设计怎么样
  7. spring mvc 和ajax异步交互完整实例
  8. T-SQL高级查询语句
  9. OpenXR+Runtime:OpenXR SDK Source Code源码编译
  10. t检验临界值表中的n是什么_t检验临界值分布表
  11. spring cloud bus
  12. word:回车替换成空格
  13. C语言中getchar
  14. SNN综述(2):生物可解释的脉冲神经网络
  15. 带顶点动画的护盾效果——UnityShader学习笔记
  16. Windows 11操作系统 ndis.sys 驱动无限蓝屏问题修复
  17. 悔不当初:没人能随随便便成功
  18. 【已解决】error: ‘xxx’ is not a member of ‘xxx’
  19. 宝塔面板安装和创建网站/必看教程
  20. 【CS224n】(lecture4)Dependency Parsing 依存句法分析

热门文章

  1. 【直播活动】阿里巴巴开源大家族加入中科院开源之夏2022
  2. @JsonFormat将时间字符串2021-02-25T15:32:54+08:00解析成date
  3. Verilog轮询仲裁器设计——Round Robin Arbiter
  4. 一文通关苦涩难懂的Java泛型
  5. php 射影定理,射影定理是哪年级学的
  6. 大数据之------------数据中台
  7. UE4《大象无形》学习笔记
  8. php织梦源码安装,如何安装网上下载的织梦源码
  9. [深度学习论文笔记]医学图像分割U型网络大合集
  10. php 获取字符串首歌,PHP爬虫 网易云音乐歌手和热门歌曲信息抓取