赞
踩
前两天老师布置了一个I2C协议的手绘图,我心想这么简单的协议我就用verilog写一个呗。
初步设想是双机,能够实现主从机相互转换,可以相互读写,但是现在只实现了主机写从机读从机,手头上目前还没有FPGA,主从任意转换就没有写了,今天就把目前做到的写下来,以后有机会就填坑。
瞌睡的洋葱博客
这位博主说的十分清楚了,再结合一下百度百科基本上就能搞清楚了。
时序图:
写寄存器的标准流程为:
- Master发起START
- Master发送I2C addr(7bit)和w操作0(1bit),等待ACK
- Slave发送ACK
- Master发送reg addr(8bit),等待ACK
- Slave发送ACK
- Master发送data(8bit),即要写入寄存器中的数据,等待ACK
- Slave发送ACK
- 第6步和第7步可以重复多次,即顺序写多个寄存器
- Master发起STOP
读寄存器的标准流程为:
- Master发送I2C addr(7bit)和w操作1(1bit),等待ACK
- Slave发送ACK
- Master发送reg addr(8bit),等待ACK
- Slave发送ACK
- Master发起START
- Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
- Slave发送ACK
- Slave发送data(8bit),即寄存器里的值
- Master发送ACK
- 第8步和第9步可以重复多次,即顺序读多个寄存器
大家不懂的可以看看上面那一篇博客。
既然了解了I2C协议的过程下面就是要解决问题了。
I2C协议的步骤如此清晰,我们自然而然想到使用状态机,状态机可以用FPGA系统时钟,这里为了方便我使用的是1Mhz的FPGA时钟,I2C_SCL时钟为系统时钟的10分频.
状态机我分为6个状态:
parameter s_wait=3'b000,s_start=3'b001,s_adder1=3'b010,s_adder2=3'b011,s_doing=3'b100,s_ask=3'b101,s_stop=3'b110;
完整代码如下
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/05/13 17:34:09 // Design Name: // Module Name: I2Cpro_top // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module I2Cpro_top( input clk, input rst_n, input enable, //使能I2C模块 input wr_cont,//0写,1读 input [6:0]Device_addr,//设备地址 input [7:0]Store_addr,//储存地址 input [7:0]data_in, output [7:0]data_out, inout SDA, inout SCL); parameter s_wait=3'b000,s_start=3'b001,s_adder1=3'b010,s_adder2=3'b011,s_doing=3'b100,s_ask=3'b101,s_stop=3'b110; parameter data_size=8; reg doing=1'b0; //SDA数据有效,开始到结束 reg ready=1'b0; //进行数据读写 reg [7:0]data_reg[7:0]; //暂存输入的所有数据 reg [7:0]data_wr=8'h00,data_rd=8'h00; //暂存收发数据 reg [7:0]rDevice_wr_addr=8'b000; //设备地址和读写控制 reg [7:0]rStore_addr=8'h00; //读写地址 reg rSDA=1'b1; //SDA reg [2:0]rState=s_wait; //I2C控制状态机 reg [2:0]bit_cnt=3'b000; //读写bit计数器 reg [7:0]data_cnt=data_size+2; //读写byte计数器 reg [4:0]buffer=5'b00001; //在clk驱动的状态机中与SCL时钟规则匹配 reg s_cnt=1'b0; //用于控制两次数据传输间隙 //调用偶分频div模块 //SCL时钟由clk系统时钟10分频产生 sysclk2I2CSCL #( .N(10) //分频数为10 )sysclk2I2CSCL2( .clk(clk), .rst_n(rst_n), .SCL(SCL) ); // 如果SCL高电平的时候检测到了SDA的高转低说明开始 always@(negedge SDA)begin if(SCL==1) doing<=1'b1; end // 如果SCL高电平的时候检测到SDA的低转高说明结束 always@(posedge SDA)begin if(SCL==1) doing<=1'b0; end always@(posedge clk)begin if(enable)begin if(!rst_n)begin rSDA<=1; rState<=s_wait; buffer<=5'b00001; data_cnt<=data_size+2; bit_cnt<=3'b000; end else begin case(rState) s_wait:begin if(SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2])begin if(s_cnt==1)begin rSDA<=1'b1; rState<=s_start; rDevice_wr_addr<={Device_addr,wr_cont}; rStore_addr<=Store_addr; data_wr<=data_in; data_cnt<=data_size+2; bit_cnt<=3'b000; end buffer<=5'b00001; s_cnt<=s_cnt+1; end end end s_start:begin if(SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2]) begin rSDA<=1'b0; rState<=s_adder1; buffer<=5'b00001; end end end s_adder1:begin if(!SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2]) begin rSDA<=rDevice_wr_addr[0]; rDevice_wr_addr<={rDevice_wr_addr[0],rDevice_wr_addr[7:1]}; bit_cnt<=bit_cnt+1'b1; if(bit_cnt==7) begin data_cnt<=data_cnt-1; rState<=s_ask; buffer<=5'b00001; end end end end s_adder2:begin if(!SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2]) begin rSDA<=rStore_addr[0]; rStore_addr<={rStore_addr[0],rStore_addr[7:1]}; bit_cnt<=bit_cnt+1'b1; if(bit_cnt==7) begin data_cnt<=data_cnt-1; rState<=s_ask; buffer<=5'b00001; end end end end s_doing:begin if(wr_cont==0)begin//如果是写,写入数据改变SDA if(!SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2])begin rSDA<=data_wr[0]; data_wr<={data_wr[0],data_wr[7:1]}; bit_cnt<=bit_cnt+1'b1; if(bit_cnt==7)begin data_cnt<=data_cnt-1; rState<=s_ask; buffer<=5'b00001; end end end end else begin if(SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2])begin data_rd[0]<=SDA; data_rd<={data_rd[0],data_rd[7:1]}; bit_cnt<=bit_cnt+1; if(bit_cnt==7)begin data_reg[data_cnt]<=data_rd; data_cnt<=data_cnt+1; end end end end end s_ask:begin if(wr_cont==0)begin //等待回复ask if(SCL && !SDA)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2])begin if(data_cnt==data_size+1) rState<=s_adder2; else if(data_cnt==0) rState<=s_stop; else rState<=s_doing; end end end/* else begin //主动回复ask 从机时候 if(!SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2])begin rSDA<=1'b0; buffer<=5'b00001; if(data_cnt==data_size+1) rState<=s_adder2; else if(data_cnt==0) rState<=s_stop; else rState<=s_doing; end end end */ end s_stop:begin //结束 if(SCL)begin buffer<={buffer[3:0],buffer[4]}; if(buffer[2]) begin if(s_cnt==1)begin rSDA<=1'b1; rState<=s_wait; end s_cnt<=s_cnt+1; buffer<=5'b00001; end end end endcase end end end // 读数据 assign SDA=rSDA; assign data_out=data_rd; endmodule
tsetbench
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/05/11 15:07:11 // Design Name: // Module Name: I2Ctb // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module I2Ctb(); reg clk; reg rst_n; reg enable; reg wr_cont; reg[6:0]Device_addr;//设备地址 reg[7:0]Store_addr;//储存地址 reg[7:0]data_in; wire [7:0]data_out; wire SDA; wire SCL; I2Cpro_top I2C_top1( .clk(clk), .rst_n(rst_n), .enable(enable), .wr_cont(wr_cont), .Device_addr(Device_addr), .Store_addr(Store_addr), .data_in(data_in), .data_out(data_out), .SDA(SDA), .SCL(SCL) ); always #5 clk=~clk; initial begin clk=0; rst_n=0; enable=0; wr_cont=0; Device_addr=7'h78; Store_addr=8'haa; data_in=8'hff; #10 enable=1; #10 rst_n=1; #20000 wr_cont=1; end endmodule
有人读的话再继续完善
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。