利用FPGA实现简单的iic通信协议

最近博主马不停蹄的学习FPGA就是为了可以尽快用FPGA来实现图像处理,好让我们的博客恢复正常内容哈哈哈,我说几句题外话,其实关于图像处理,工程应用和写论文是完全不一样的,如果是以找工作为目的,在牛逼的算法其实很难进行实际运用,归结到底就是速度以及实际效果。所以如果是以找工作为目的的小伙伴们其实不妨把时间花在积累工程经验中来,毕业论文啥的随便找两个算法复现了调调参就可以了。当然我这么说,读博的朋友肯定很看不起哈哈哈。。。

好了进入正题,什么是iic通信,我来讲一下它的通信的基本协议。简单来讲,任何数据你要发送或者接受需要两根基本的总线,即时钟和数据,那么对于iic协议来说,基本流程如下:

1.起始信号的判断,从机什么时候知道要开始发送或者接受数据呢

很简单,就是在slk时钟信号还是高电平的时候,此时数据线拉低,则代表起始信号,即下降沿触发,需要注意的是必须在slk时钟信号是高电平拉低数据线信号。

2.那么开始信号产生后,我们首先要确定是哪个从机响应,那么需要先通过数据线输入从机的地址,以EEPROM 24LC04为例,它的地址为10100000,其中高四位数据固定,低四位的前面三位代表是哪个eeprom,如果只有一个,默认是000,最多可以八个。最后一位是读写位,0代表写数据,1代表读数据。那么怎么样才能把设备地址写入呢

就是这样,在clk每来一个上升沿的时候数据会锁存,所以需要提前将数据输入给sda信号线

3.将地址写完之后呢?此时从机会产生一个应答信号,那么这个应答信号我们什么时候取才是最好的,当然是时钟高电平的一半啊,如下图所示:

画的很抽象哈哈,但是应该可以理解,就是在最中间的时候将应答信号输出出来,理由还是高电平触发。。但是需要注意一点的就是,此时sda信号不在作为写数据,而是读数据了,所以说sda信号线不再由我们控制,所以应该把它设为高阻态。然后在高电平的最中间时刻去捕获数据总线上的应答值。

4.送完地址信号,然后就是写数据地址了,即那个地址存放你要的数据需要让从机知道。过程很相似,重复2,3两步即可

5.最后就是写你要的数据,同样重复2,3两个步骤。

6.最后就是结束信号,需要在时钟信号为高的时候,拉高数据总线,如下:

好了至此,iic协议如何发送一个字节就结束了,下面来讲如何读一个字节

1.首先还是起始信号的判断,和之前一样

2.依然是写入从机的地址,注意,这里是写,所以地址的最低位还是0,写完检测应答信号然后继续。还是注意应答信号由于是数据总线输出,所以应提前置为高阻态。

3.然后是写入需要读哪个数据的地址,过程参考写数据。也是讲要读的地址写入后,通过下一个时钟高电平去检测应答信号。

4.然后再来一个起始信号

5.接着读从机地址,注意这里是读,所以最后一位是1,然后继续检测应答信号

6.然后就是从sda数据线读数据,读完之后可以不用检测应答信号

7.输入结束信号

通过verilog博主编写了一个简单的数据发送和接受模块,为了验证准确性,将发送出来的数据接到led灯来验证,下面给出代码

module iic_test(
clk,
rst,
led,
slk,
sda
);

input clk;
input rst;
output reg [3:0]led;
output slk;
inout sda;


wire isdone;
wire [7:0]rdata;
iic(
.clk(clk),
.rst(rst),
.choose(choose),
.device(device),
.address(address),
.data(data),
.rdata(rdata),
.slk(slk),
.sda(sda),
.isdone(isdone)
);

reg [1:0]choose;
reg [1:0]i;
reg [7:0]device;
reg [7:0]address;
reg [7:0]data;
always@(posedge clk or negedge rst)
if(!rst)
begin
	choose<=2'b00;
	i<=2'b0;
	device<=8'b0;
	address<=8'b0;
	data<=8'b0;
	led<=4'd0;
end
else 
	case(i)
		0: if(isdone)
		begin
			choose<=2'b00;
			i<=i+1'b1;
		end
		else
		begin
			choose<=2'b01;
			device<=8'b10100000;
			address<=8'b00000000;
			data<=8'h4e;
		end
		1: if(isdone)
		begin
			choose<=2'b00;
			i<=i+1'b1;
		end
		else
		begin
			choose<=2'b10;
			device<=8'b10100000;
			address<=8'b00000000;
			//data<=8'h2a;
		end
		2: led<=rdata[3:0];
	endcase
//assign led=rdata[3:0];
endmodule
module iic(
clk,
rst,
choose,
device,
address,
data,
rdata,
slk,
sda,
isdone
);
input clk;
input rst;
input [1:0]choose;  //选择读、写
input [7:0]device;
input [7:0]address;
input [7:0]data;
output reg [7:0]rdata;
output reg slk;
inout reg sda;
output reg isdone;

parameter r100K = 9'd500;

reg [5:0]i;
reg [15:0]clkcount;
reg isask;
always@(posedge clk or negedge rst)
if(!rst)
begin
	rdata<=8'b0;
	slk<=1'b1;
	sda<=1'b1;
	i<=6'd0;
	clkcount<=16'd0;
	isask<=1'b0;
	isdone<=1'b0;
end
else if(choose[0]) //代表开始写一个数的地址
begin
	case(i)
		0:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b1;
				  else if(clkcount==16'd350)
						slk<=1'b0;
						
				  if(clkcount==16'd0)
						sda<=1'b1;
				  else if(clkcount==16'd250)
						sda<=1'b0;						
		end
		//写设备地址
	  1,2,3,4,5,6,7,8:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=device[8-i];		
	  end
	  //释放总线设为高阻态
	  9:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
						//总线重新让主机掌握
						sda<=1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=1'bz;
				  if(clkcount==16'd200)
					   isask<=sda;
	  end
     10: if(isask!=0)
				i<=6'd0;
			else
				i<=i+1'b1;
	  //写字节地址
	  11,12,13,14,15,16,17,18:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=address[18-i];		
	  end
	  //释放总线设为高阻态
	  19:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
						//总线重新让主机掌握
						sda<=1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=1'bz;
				  if(clkcount==16'd200)
					   isask<=sda;
	  end
     20: if(isask!=0)
				i<=6'd0;
			else
				i<=i+1'b1;
	  21,22,23,24,25,26,27,28: begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=data[28-i];		
	  end
	  //释放总线设为高阻态
	  29:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
						//总线重新让主机掌握
						sda<=1'b0;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=1'bz;
				  if(clkcount==16'd200)
					   isask<=sda;
	  end
     30: if(isask!=0)
				i<=6'd0;
			else
				i<=i+1'b1;
	  31:begin
	  if(clkcount==16'd499)
	  begin
			clkcount<=16'd0;
			i<=i+1'b1;
	  end
	  else
			clkcount<=clkcount+1'b1;
			
	  if(clkcount==16'd0)
			slk<=1'b0;
	  else if(clkcount==16'd200)
			slk<=1'b1;
			
	  if(clkcount==16'd0)
			sda<=1'b0;
	  else if(clkcount==16'd300)
			sda<=1'b1;
	  end
	  32: begin i<=i+1'b1;
					isdone<=1'b1;
	  end
	  33: begin i<=6'd0;
					isdone<=1'b0;
	  end
	endcase
end
else if(choose[1])
begin
	case(i)
		0: begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b1;
				  else if(clkcount==16'd350)
						slk<=1'b0;
						
				  if(clkcount==16'd0)
						sda<=1'b1;
				  else if(clkcount==16'd250)
						sda<=1'b0;						
		end
		//写设备地址
	  1,2,3,4,5,6,7,8:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=device[8-i];		
	  end
	  //释放总线设为高阻态
	  9:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
						//总线重新让主机掌握
						sda<=1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=1'bz;
				  if(clkcount==16'd200)
					   isask<=sda;
	  end
     10: if(isask!=0)
				i<=6'd0;
			else
				i<=i+1'b1;
	  //写字节地址
	  11,12,13,14,15,16,17,18:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=address[18-i];		
	  end
	  //释放总线设为高阻态
	  19:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
						//总线重新让主机掌握
						sda<=1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=1'bz;
				  if(clkcount==16'd200)
					   isask<=sda;
	  end
     20: if(isask!=0)
				i<=6'd0;
			else
				i<=i+1'b1;
	 //再来一次起始信号
	  21: begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b1;
				  else if(clkcount==16'd350)
						slk<=1'b0;
						
				  if(clkcount==16'd0)
						sda<=1'b1;
				  else if(clkcount==16'd250)
						sda<=1'b0;						
		end
		22,23,24,25,26,27,28: begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=device[29-i];		
	  end
	  29: begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
				   //最后一位表示读	
				  sda<=1'b1;		
	  end
	  //释放总线设为高阻态
	  30:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
						//总线重新让主机掌握
						//sda<=1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=1'bz;
				  if(clkcount==16'd200)
					   isask<=sda;
	  end
     31: if(isask!=0)
				i<=6'd0;
			else
				i<=i+1'b1;
		//开始读数据
	  32,33,34,35,36,37,38,39: begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
					
				  if(clkcount==16'd200)
						rdata[39-i]<=sda;
						
				  sda<=1'bz;	
				 end
		 //释放总线设为高阻态
	  40:begin if(clkcount==16'd499)
				  begin
						clkcount<=16'd0;
						i<=i+1'b1;
				  end
				  else
						clkcount<=clkcount+1'b1;
					
				  if(clkcount==16'd0)
						slk<=1'b0;
				  else if(clkcount==16'd100)
						slk<=1'b1;
				  else if(clkcount==16'd300)
						slk<=1'b0;
						
				  sda<=1'b0;
	  end
	  41: begin
	  if(clkcount==16'd499)
	  begin
			clkcount<=16'd0;
			i<=i+1'b1;
	  end
	  else
			clkcount<=clkcount+1'b1;
			
	  if(clkcount==16'd0)
			slk<=1'b0;
	  else if(clkcount==16'd200)
			slk<=1'b1;
			
	  if(clkcount==16'd0)
			sda<=1'b0;
	  else if(clkcount==16'd300)
			sda<=1'b1;
	  end
	  42: begin i<=i+1'b1;
					isdone<=1'b1;
	  end
	  43: begin i<=6'd0;
					isdone<=1'b0;
	  end
	endcase
end
endmodule

 

  • 3
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值