基本知识
IIC: inter-integrated circuit bus ,即 集成电路总线,串行通信,多主从架构,半双工(对讲机),小数据量场合,短距离传输。
速率:100kb/s 、 400kb/s 、 3.4Mkb/s
传输单位:8位(一个字节)(双向)
架构:每个IIC器件都可作为主/从器件,一个时间只有一个主设备
两条总线线路:SCL(串行时钟线)、 SDA(串行数据线)总线拓扑结构如图
图中,每个IIC器件拥有唯一地址(识别码)。
主机往总线上发送地址,所有的从机都能接收到主机发出的地址,然后每个从机都将主机发出的地址与自己的地址比较,如果匹配上了,这个从机就会向总线发出一个响应信号。主机收到响应信号后,开始向总线上发送数据,与这个从机的通讯就建立起来了。如果主机没有收到响应信号,则表示寻址失败。
IIC帧格式
默认规定:SCL高电平时,SDA稳定;SCL低电平时,SDA变化。(依此设定SDA的数据变化时刻,在SCL的低电平中点变化较为靠谱).在scl高电平时读/写数据。
响应位:当 IIC 主机(不一定是发送端还是接受端)将8位数据或命令传出后, SDA释放,即设置为输入,然后等待从机应答(低电平 0 表示应答,1 表示非 应答),此时的时钟仍然是主机提供的。
起始信号:SCL高且SDA出现下降沿。
停止信号:SCL高且SDA出现上升沿。
器件地址
每个I2C器件都有一个器件地址,有的器件地址在出厂时地址就设置好了,用户不可以 更改(例如 OV7670 器件地址为固定的 0x42),有的确定了几位,剩下几位由硬件确定(比 如常见的I2C接口的EEPROM存储器,留有3个控地址的引脚,由用户自己在硬件设计时确定)。
对于AT24C64这样一颗EEPROM器件,其器件地址为1010加3位的片选信号。3位片选信号由硬件连接设定。当硬件电路上分别将这三个引脚连接到 GND 或 VCC 时,就可以设置不同的片选地址。
I2C传输时,按照从高到低的位序进行传输。控制字节的最低位为读写控制位,当该位 为0时表示主机对从机进行写操作,当该位为1时表示主机对从机进行读操作。
在某些IIC器件中,A2 A1 A0可以当作存储单元地址来用。存储器地址一般为1或2字节。取决于IIC器件容量。
IIC写时序
时序:
1、主机设置 SDA 为输出;发送起始信号;
3、主机传输器件地址字节,其中最低位为 0,表明为写操作;
4、主机设置 SDA 为三态门输入,读取从机应答信号;
5、读取应答信号成功,主机设置 SDA 为输出,传输地址数据高字节;
6、主机设置 SDA 为三态门输入,读取从机应答信号;
7、读取应答信号成功,主机设置 SDA 为输出,传输地址数据低字节;
8、设置 SDA 为三态门输入,读取从机应答信号;
9、读取应答信号成功,主机设置 SDA 为输出,传输待写入的数据;
10、设置 SDA 为三态门输入,读取从机应答信号;
11、读取应答信号成功,主机产生 STOP 位,终止传输。
IIC页写时序(连续写)
主机连续写多个字节数据到从机,I2C连续写时序仅部分器件支持。
时序类似上面,只是变成了连续写n个数据。
IIC读时序
注意:虽然是读时序,但是这里先用了写信号告诉从机要读的存储器地址,然后再换成读信号把该地址的数据读到主机。
1、主机设置 SDA 为输出;发送起始信号;
3、主机传输器件地址字节,其中最低位为 0,表明为写操作;
4、主机设置 SDA 为三态门输入,读取从机应答信号;
5、读取应答信号成功,主机设置 SDA 为输出,传输地址数据高字节;
6、主机设置 SDA 为三态门输入,读取从机应答信号;
7、读取应答信号成功,主机设置 SDA 为输出,传输地址数据低字节;
8、设置 SDA 为三态门输入,读取从机应答信号;
9、读取应答信号成功,主机发起起始信号;
10、主机传输器件地址字节,其中最低位为 1,表明为读操作;
11、设置 SDA 为三态门输入,读取从机应答信号;
12、读取应答信号成功,主机设置 SDA 为三态门输入,读取 SDA 总线上的一个字节的 数据;
13、主机设置 SDA 输出,产生无应答信号(高电平)(不想继续读数据了)(无需设置为输出高电平,因为总线会被自动拉高);
14、主机产生 STOP 位,终止传输。
IIC页读时序
读数据时用ACK应答,不想继续读时用NOACK应答即可。
IIC控制器设计
对完整的读和写时序进行分析,可以总结为五种情况:
每段在传输的时候,只需要确定当前这个字节的传输 之前是否需要加入起始位,以及当前这个字节的传输结束后是否需要加入停止位就结束了。
注意事项:
verilog inout类型设计要点:三态门:用 x = ctrl ? A : 1'bz 即可控制为输出或者高阻态(成为输入)
对于IIC器件,由于芯片无法输出高电平(防止多个芯片同时产生高电平,导致冲突),故需在电路上连接上拉电阻与电源,然后让芯片输出高阻态来实现高电平,故逻辑设计为:IIC_SDA = ctrl ?( a ? 1'bz : 0 ) : 1'bz (这是inout类型的最好写法)化简合并:IIC_SDA = ( ctrl && !a )1‘b0 : 1'bz
如果不修改,直接用 x = ctrl ? A : 1'bz ,可能出现冲突的情况如下:
仿真时可以在testbench文件中用pullup() 或者 pulldown() 来模拟上拉和下拉。(高阻态时即被上拉或者下拉)
Modelsim仿真时,如果有模块只在testbench文件中存在,没有在quartus中编译(如:引用别人的模块),则需要把该模块文件加入modelsim的编译中,如下图:
verilog设计
1.时钟设计
根据IIC时序图,设计时钟需要从两点出发:
①在IIC_scl高电平中点读取数据,则我们需要在IIC_scl低电平中点改变数据,从而保证读取数据时数据的稳定性。
②起始信号和终止信号是指在IIC_scl高电平时IIC_sda出现变化沿。
所以,我们需要用比IIC_scl频率更高的时钟来设计IIC_sda的输出。
IIC_scl的时钟为100kb/s、400kb/s和3.4Mb/s,这里分别用400kb/s、1600kb/s来设计IIC_sda(即4倍时钟频率)。
由于IIC_sda数据的变化点在IIC_scl的前一个周期(当前周期数据需要稳定可读取),而变化沿的产生又在同一个周期(设定),如果直接用IIC_scl来设计状态机的话,会出现如图情况(即需要在start_bit_state状态里同时设计上升沿和第一个要写的数据,非常不方便)。为了状态机的书写方便,用于设计状态机的驱动时钟相对IIC_scl移相90°(这样就可以在start_bit_state状态只设计上升沿,所有要写的数据都在wr_data_state状态里设计),具体看图(可以先看状态机再回头看这儿)
2.架构设计
IIC协议的目的是为了:
1.向某个IIC器件的某个存储单元写入数据;
2.从某个IIC器件的某个存储单元读出数据;
2.1 驱动逻辑模块
为了实现这两个功能,需要根据时序图,用两个线性序列机分别设计读和写的状态顺序和时序。其中,写控制信息、写存储单元地址、写数据均可以归结为写数据一个状态,具体状态划分如下:
写逻辑:空闲 --> 产生起始位 --> 写控制信息 --> 检查应答 --> 写存储器高位地址 --> 检查应答 --> 写存储器低位地址 --> 检查应答 --> 写数据 --> 检查应答 --> 产生结束位 --> 空闲
即: idel_state --> start_bit_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> stop_bit_state --> idel_state_state
读逻辑:空闲 --> 产生起始位 --> 写控制信息 --> 检查应答 --> 写存储器高位地址 --> 检查应答 --> 写存储器低位地址 --> 检查应答 --> 产生起始位 --> 写控制信息 --> 检查应答 --> 读数据 --> 产生应答 -->产生结束位 --> 空闲
即: idel_state --> start_bit_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> wr_data_state --> ack_check_state --> start_bit_state --> wr_data_state --> ack_check_state --> rd_data_state --> gen_noack_state--> stop_bit_state --> idel_state
从而,IIC的驱动模块架构如下:
其中,根据wr_sig / rd_sig 来产生开启一次读/写的完整操作,由cmd控制状态,通过时序逐个把IIC_id、wr_addr、rd_addr、wr_data通过write_data输出,通过接受trans_done信号来结束一次读/写操作。
2.2 底层逻辑模块
这一层模块用于接受驱动模块发出的命令(即状态指令),根据指令对接受的wr_data进行输出;设计8种状态需要对IIC_sda总线的输出和输入进行的操作。根据时序图,结合线性序列机和状态机进行编写即可,重点在于数据变化的时刻以及IIC_scl这种inout三态门的输入输出设计(前面已经介绍)。
3.代码设计
module IIC_design(
input fpga_clk,//50MHz
input rst_n ,
input rate_sel ,
input read_pulse ,
input write_pulse ,
input [7:0]write_data ,
input [7:0]IIC_id ,
input [15:0]wr_addr ,
input [15:0]rd_addr ,
output trans_done ,
output [7:0]read_data ,
output IIC_scl ,
inout IIC_sda
);
//******* IIC_scl设计 ******* //
wire clk_16M ; //
wire clk_4M ;
wire clk_4M90 ;
wire clk_1M ;
wire clk_1M90 ;
wire clk_1600k ;
wire clk_400k ;//对应 scl 400kb/s 速率
wire clk_400k108 ;
wire clk_100k ;//对应 scl 100kb/s 速率
wire clk_100k108 ;
wire sda_clk ;//控制时序的时钟
wire drive_clk ; //产生cmd的时钟
//锁相环分频
pll pll_inst1(
.inclk0(fpga_clk),
.c0(clk_16M),
.c1(clk_4M),
.c2(clk_1M)
);
//10分频
fenpin_10 fenpin_10_inst1(
.clk(clk_4M),
.rst_n(rst_n),
.outClk(clk_400k)
);
fenpin_10 fenpin_10_inst2(
.clk(clk_1M),
.rst_n(rst_n),
.outClk(clk_100k)
);
fenpin_10 fenpin_10_inst3(
.clk(clk_16M),
.rst_n(rst_n),
.outClk(clk_1600k)
);
fenpin_10_108du fenpin_10_108du_inst1(
.clk(clk_4M),
.rst_n(rst_n),
.outClk(clk_400k108)
);
fenpin_10_108du fenpin_10_108du_inst2(
.clk(clk_1M),
.rst_n(rst_n),
.outClk(clk_100k108)
);
//设置IIC总线时钟 400kb/s 或 100kb/s
assign IIC_scl = rate_sel ? clk_400k : clk_100k ;
assign sda_clk = rate_sel ? clk_1600k : clk_400k ;
assign drive_clk = rate_sel ? clk_400k108 : clk_100k108 ;
//**********************************//
wire [7:0]rd_data ;
wire ack_result ;
wire [7:0]command ;
wire [7:0]wr_data ;
IIC_drive IIC_drive_inst(
.fpga_clk(fpga_clk) ,//50MHz
.drive_clk(drive_clk),
.rst_n(rst_n) ,
.wr_sig(write_pulse) ,
.rd_sig(read_pulse) ,
.wr_data(write_data) ,
.rd_data(rd_data) ,
.IIC_id(IIC_id) ,
.wr_addr(wr_addr) ,
.rd_addr(rd_addr) ,
.trans_done(trans_done) ,
.ack_result(ack_result) ,
.cmd(command) ,
.read_data(read_data) ,
.write_data(wr_data)
);
IIC_underlying_logic IIC_underlying_logic_inst(
.sda_clk(sda_clk) ,//
.rst_n(rst_n) ,
.cmd(command) ,
.wr_data(wr_data) ,
.rd_data(rd_data) ,
.ack_result(ack_result) ,
.trans_done(trans_done) ,
.IIC_sda(IIC_sda)
);
endmodule
module IIC_drive(
input fpga_clk ,//50MHz
input drive_clk,
input rst_n ,
input wr_sig ,
input rd_sig ,
input [7:0]wr_data ,
input [7:0]rd_data ,
input [7:0]IIC_id ,
input [15:0]wr_addr ,
input [15:0]rd_addr ,
input trans_done ,
input ack_result ,
output reg [7:0]cmd ,
output reg [7:0]read_data ,
output reg [7:0]write_data
);
reg rd_pulse_reg ;
reg wr_pulse_reg ;
always@(posedge fpga_clk)
begin
wr_pulse_reg <= wr_sig ;
rd_pulse_reg <= rd_sig ;
end
reg read_sig ;
reg write_sig ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
begin
read_sig <= 0 ;
end
else if( rd_pulse_reg == 0 && rd_sig == 1 )
read_sig <= 1 ;
else if( trans_done )
read_sig <= 0 ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
begin
write_sig <= 0 ;
end
else if( wr_pulse_reg == 0 && wr_sig == 1 )
write_sig <= 1 ;
else if(trans_done)
write_sig <= 0 ;
//定义状态机7个状态参数
localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
localparam start_bit_state = 8'b01000000 ; //产生起始位
localparam wr_data_state = 8'b00100000 ; // 写数据
localparam rd_data_state = 8'b00010000 ; // 读数据
localparam ack_check_state = 8'b00001000 ; //检查响应
localparam stop_bit_state = 8'b00000100 ; //产生停止位
localparam gen_ack_state = 8'b00000010 ; //产生停止位
localparam gen_noack_state = 8'b00000001 ; //产生停止位
//写逻辑
reg [5:0]write_cnt ;
//读逻辑
reg [5:0]read_cnt ;
always@(posedge drive_clk or negedge rst_n)
if(!rst_n)
begin
cmd <= idel_state ;
write_cnt <= 0 ;
write_data <= 0 ;
read_cnt <= 0 ;
read_data <= 0 ;
end
else if( write_sig == 1 )
begin
write_cnt <= write_cnt + 1 ;
if(ack_result)
begin
cmd <= ack_check_state ;
write_cnt <= 0 ;
end
else
begin
if(write_cnt == 1)
cmd <= idel_state ;//进入空闲态
else if ( write_cnt == 2 )
begin
cmd <= start_bit_state ;//产生起始位
end
else if ( write_cnt == 3 )
begin
cmd <= wr_data_state ;
write_data <= IIC_id ;//发送IIC器件ID
end
else if ( write_cnt == 11)
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 12 )
begin
cmd <= wr_data_state ;
write_data <= wr_addr[15:8] ;//写存储器高位地址
end
else if ( write_cnt == 20 )
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 21 )
begin
cmd <= wr_data_state ;
write_data <= wr_addr[7:0] ;//写存储器低位地址
end
else if ( write_cnt == 29 )
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 30 )
begin
cmd <= wr_data_state ;
write_data <= wr_data ; //写数据
end
else if ( write_cnt == 38 )
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 39 )
begin
cmd <= stop_bit_state ;//产生停止位
end
else if ( write_cnt == 40 )
begin
cmd <= idel_state ;//进入空闲态
write_cnt <= 0 ;
end
end
end
else if( read_sig == 1 )
begin
read_cnt <= read_cnt + 1 ;
if(ack_result)
begin
cmd <= ack_check_state ;
read_cnt <= 0 ;
end
else
begin
if(read_cnt == 1)
cmd <= idel_state ;//进入空闲态
else if ( read_cnt == 2 )
begin
cmd <= start_bit_state ;//产生起始位
end
else if ( read_cnt == 3 )
begin
cmd <= wr_data_state ;
write_data <= IIC_id ;//发送IIC器件ID 和写指令
end
else if ( read_cnt == 11)
begin
cmd <= ack_check_state ;
end
else if ( read_cnt == 12 )
begin
cmd <= wr_data_state ;
write_data <= rd_addr[15:8] ;//写存储器高位地址
end
else if ( read_cnt == 20 )
begin
cmd <= ack_check_state ;
end
else if ( read_cnt == 21 )
begin
cmd <= wr_data_state ;
write_data <= rd_addr[7:0] ;//写存储器低位地址
end
else if ( read_cnt == 29 )
begin
cmd <= ack_check_state ;
end
else if ( read_cnt == 30 )
begin
cmd <= start_bit_state ;
end
else if ( read_cnt == 31 )
begin
cmd <= wr_data_state ;
write_data <= ( IIC_id | 6'b000001 ) ;//写IIC器件ID 和读指令
end
else if ( read_cnt == 39 )
begin
cmd <= ack_check_state ;
end
else if ( read_cnt == 40 )
begin
cmd <= rd_data_state ;
end
else if ( read_cnt == 48 )
begin
cmd <= gen_noack_state ;
end
else if ( read_cnt == 49 )
begin
cmd <= stop_bit_state ;//产生停止位
read_data <= rd_data ; //寄存读数据
end
else if ( read_cnt == 50 )
begin
cmd <= idel_state ;//进入空闲态
write_cnt <= 0 ;
end
end
end
else
begin
cmd <= idel_state ;
write_cnt <= 0 ;
read_cnt <= 0 ;
end
endmodule
module IIC_underlying_logic(
input sda_clk ,//
input rst_n ,
input [7:0]cmd ,
input [7:0]wr_data ,
output reg [7:0]rd_data ,
output reg ack_result ,
output reg trans_done ,
inout IIC_sda
);
//********* IIC_sda设计 ************//
reg sda_out ;
reg sda_ctrl;
assign IIC_sda = sda_ctrl ? ( sda_out ? 1'bz : 1'b0 ) : 1'bz ; //三态门,高阻态时,要么由总线上的上拉电阻拉至高电平,即输出/输出1;要么由从机拉至0,输入0;
//定义状态机7个状态参数
localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
localparam start_bit_state = 8'b01000000 ; //产生起始位
localparam wr_data_state = 8'b00100000 ; // 写数据
localparam rd_data_state = 8'b00010000 ; // 读数据
localparam ack_check_state = 8'b00001000 ; //检查响应
localparam stop_bit_state = 8'b00000100 ; //产生停止位
localparam gen_ack_state = 8'b00000010 ; //产生停止位
localparam gen_noack_state = 8'b00000001 ; //产生停止位
reg [7:0]rw_state ;//实时状态
always@( posedge sda_clk or negedge rst_n )
if(!rst_n)
begin
rw_state <= idel_state ;
end
else
begin
if( cmd & idel_state )
rw_state <= idel_state ;
else if ( cmd & start_bit_state )
rw_state <= start_bit_state ;
else if ( cmd & wr_data_state )
rw_state <= wr_data_state ;
else if ( cmd & rd_data_state )
rw_state <= rd_data_state ;
else if ( cmd & ack_check_state )
rw_state <= ack_check_state ;
else if ( cmd & stop_bit_state )
rw_state <= stop_bit_state ;
else if ( cmd & gen_ack_state )
rw_state <= gen_ack_state ;
else if ( cmd & gen_noack_state )
rw_state <= gen_noack_state ;
end
reg [5:0]cnt ;
always@( posedge sda_clk or negedge rst_n )
if(!rst_n)
begin
sda_out <= 1'b1 ;
sda_ctrl<= 1'b0 ;
trans_done <= 0 ;
ack_result <= 0 ;
rd_data <= 0 ;
cnt <= 1'b0 ;
end
else
begin
case(rw_state)
idel_state:
begin
cnt <= 1'b0 ;
sda_ctrl<= 1'b0 ;//释放sda控制权
sda_out <= 1'b1 ;
trans_done <= 0 ;
ack_result <= 0 ;
end
start_bit_state:
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
if(cnt == 2 )
begin
sda_out <= 1'b0 ;
end
cnt <= cnt + 1 ;
if(cnt == 3 )
cnt <= 0 ;
end
wr_data_state:
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
if( cnt == 0 )
sda_out <= wr_data[7] ;
else if ( cnt == 4 )
sda_out <= wr_data[6] ;
else if ( cnt == 8 )
sda_out <= wr_data[5] ;
else if ( cnt == 12 )
sda_out <= wr_data[4] ;
else if ( cnt == 16 )
sda_out <= wr_data[3] ;
else if ( cnt == 20 )
sda_out <= wr_data[2] ;
else if ( cnt == 24 )
sda_out <= wr_data[1] ;
else if ( cnt == 28 )
sda_out <= wr_data[0] ;
cnt <= cnt +1 ;
if( cnt == 31 )
cnt <= 0 ;
end
rd_data_state:
begin
sda_ctrl<= 1'b0 ;//释放sda控制权
if( cnt == 2 )
rd_data[7] <= IIC_sda ;
else if ( cnt == 6 )
rd_data[6] <= IIC_sda ;
else if ( cnt == 10 )
rd_data[5] <= IIC_sda ;
else if ( cnt == 14 )
rd_data[4] <= IIC_sda ;
else if ( cnt == 18 )
rd_data[3] <= IIC_sda ;
else if ( cnt == 22 )
rd_data[2] <= IIC_sda ;
else if ( cnt == 26 )
rd_data[1] <= IIC_sda ;
else if ( cnt == 30 )
rd_data[0] <= IIC_sda ;
cnt <= cnt +1 ;
if( cnt == 31 )
cnt <= 0 ;
end
ack_check_state:
begin
sda_ctrl<= 1'b0 ;//释放sda控制权
if( cnt == 2 )
ack_result <= IIC_sda ;
cnt <= cnt + 1 ;
if( cnt == 3 )
cnt <= 0 ;
end
stop_bit_state:
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
sda_out <= 1'b0 ;
if( cnt == 2 )
begin
sda_ctrl<= 1'b0 ;//释放sda控制权
end
cnt <= cnt + 1 ;
if(cnt == 3 )
begin
cnt <= 0 ;
trans_done <= 1 ;
end
end
gen_ack_state:
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
sda_out <= 1'b0 ;
cnt <= cnt + 1 ;
if(cnt == 3 )
cnt <= 0 ;
end
gen_noack_state:
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
sda_out <= 1'b1 ;
cnt <= cnt + 1 ;
if(cnt == 3 )
cnt <= 0 ;
end
default: ;
endcase
end
endmodule
`timescale 1ns/1ns
module IIC_design_tb();
reg fpga_clk ;
reg rst_n ;
reg rate_sel ;
reg read_pulse ;
reg write_pulse ;
reg [7:0]write_data ;
reg [7:0]IIC_id ;
reg [15:0]wr_addr ;
reg [15:0]rd_addr ;
wire trans_done ;
wire [7:0]read_data ;
wire IIC_scl ;
wire IIC_sda ;
pullup(IIC_sda) ;
IIC_design IIC_design_inst(
.fpga_clk(fpga_clk),//50MHz
.rst_n(rst_n) ,
.rate_sel(rate_sel) ,
.read_pulse(read_pulse) ,
.write_pulse(write_pulse) ,
.write_data(write_data) ,
.IIC_id(IIC_id) ,
.wr_addr(wr_addr) ,
.rd_addr(rd_addr) ,
.trans_done(trans_done) ,
.read_data(read_data) ,
.IIC_scl(IIC_scl) ,
.IIC_sda(IIC_sda)
);
M24LC04B M24LC04B_inst(
.A0(0),
.A1(0),
.A2(0),
.WP(0),
.SDA(IIC_sda),
.SCL(IIC_scl),
.RESET(~rst_n)
);
initial fpga_clk = 1 ;
always #10 fpga_clk = ! fpga_clk ;
initial begin
rst_n = 0 ;
rate_sel = 1 ;
read_pulse = 0 ;
write_pulse = 0 ;
write_data = 0 ;
IIC_id = 0 ;
wr_addr = 0 ;
rd_addr = 0 ;
#200 ;
rst_n = 1 ;
#1000 ;
IIC_id = 8'b10100110 ;
wr_addr = 16'd1 ;
write_data = 8'd20 ;
write_pulse = 1 ;
#100 ;
write_pulse = 0 ;
#125000 ;
rd_addr = 16'd1 ;
read_pulse = 1 ;
#100 ;
read_pulse = 0 ;
#250000 ;
end
endmodule
4.板级验证
为验证代码可行性,设计如下验证程序 : fpga通过IIC协议向内部EEPROM存储器的某个单元写入数据,并能读取出来。
设置两个按键:key1 产生写信号write_sig ;key2 产生写信号read_sig ;
通过 in-systerm probes and sources 功能验证:
引脚分配要注意IIC_scl和IIC_sda的引脚要根据手册对应连接,且IIC_id也要对应(这里eeprom存储器M24LC04B对应的器件ID为A0)
module IIC_eeprom(
input clk ,
input rst_n ,
input [1:0]key ,
output IIC_scl ,
inout IIC_sda
);
wire [7:0]read_data ;
wire [15:0]addr ;
wire [7:0]write_data ;
wire write_sig ;
wire read_sig ;
//assign addr = 16'd1 ;
//assign write_data = 8'd250 ;
IIC_design IIC_design_inst(
.fpga_clk(clk),//50MHz
.rst_n(rst_n) ,
.rate_sel(1) ,
.read_pulse(~read_sig) ,
.write_pulse(~write_sig) ,
.write_data(write_data) ,
.IIC_id(8'ha0) ,
.wr_addr(addr) ,
.rd_addr(addr) ,
.trans_done() ,
.read_data(read_data) ,
.IIC_scl(IIC_scl) ,
.IIC_sda(IIC_sda)
);
buttopn_debounde buttopn_debounde_inst1(
.clk(clk),
.tx(key[1]),
.reset(rst_n),
.bd_tx(write_sig)
);
buttopn_debounde buttopn_debounde_inst2(
.clk(clk),
.tx(key[0]),
.reset(rst_n),
.bd_tx(read_sig)
);
insys_wrdata insys_wrdata_inst(
.probe(), // probes.probe
.source(write_data) // 向addr写入数据
);
insys_rddata insys_rddata_inst(
.probe(read_data), // probes.probe
.source(addr) // 手动输入地址,读取addr的数据
);
endmodule
初始状态:
修改addr:
向addr写入数据66:
从addr读出数据:
串口-fpga-eeprom芯片
功能:
上位机(PC)通过uart串口协议与FPGA通信,发送想要向eeprom芯片写入的地址和数据,以及想要读取某地址的数据。FPGA通过IIC协议与eeprom芯片M24LC04B通信,读写数据。
EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。是相对于紫外擦除的rom来讲的。但是今天已经存在多种EEPROM的变种,变成了一类存储器的统称。这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。
flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。flash做的改进就是擦除时不再以字节为单位,而是以块为单位,一次简化了电路,数据密度更高,降低了成本。上M的rom一般都是flash。
要求:
① 要有一定的抗干扰能力
②能舍弃传输错误的数据,又不丢失正确的数据
方法:
① 定义一个传输帧(协议),有帧头帧尾(最好定义为5 或 A 这两个电平不断翻转的),这个帧包括地址、器件ID、读写数据等。
② 利用移位寄存器,每接受一个新的数据,就对移位寄存器内所有数据进行一次帧判别,这样就不会丢失。
实现:
定义帧格式如下:
帧头 IIC_id haddr laddr option data 帧尾
帧头定义为:AA AA 帧尾定义为:55
option定义: 读(5A);写(A5)
整帧:
AA AA ID haddr laddr r/w data 55
设计:
每次uart接受模块接受到一个新数据,就通过移位寄存器保存下来,并进行整帧检测,如果符合帧格式,就产生读/写脉冲,通过IIC模块对eeprom存储器进行读写操作,然后通过uart发送模块传输到上位机。
代码:
module uart_IIC_eeprom(
input fpga_clk ,
input rst_n ,
input uart_rx ,
output rx_done ,
output uart_tx ,
output IIC_scl ,
inout IIC_sda
);
//receive uart data
//wire rx_done ;
wire [7:0]rx_data ;
uart_receive uart_rec_inst(
.clk(fpga_clk) ,
.reset(rst_n) ,
.baud_rate('d5) ,//115200
.uart_rx(uart_rx),
.data(rx_data) ,
.rx_done(rx_done)
);
//Shift register
reg [7:0]rec_shift_reg[7:0] ;
reg rw_flag ;
wire iic_done ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
begin
rec_shift_reg[0] <= 0 ;
rec_shift_reg[1] <= 0 ;
rec_shift_reg[2] <= 0 ;
rec_shift_reg[3] <= 0 ;
rec_shift_reg[4] <= 0 ;
rec_shift_reg[5] <= 0 ;
rec_shift_reg[6] <= 0 ;
rec_shift_reg[7] <= 0 ;
rw_flag <= 0 ;
end
else if(rx_done)
begin
rec_shift_reg[0] <= rec_shift_reg[1] ;
rec_shift_reg[1] <= rec_shift_reg[2] ;
rec_shift_reg[2] <= rec_shift_reg[3] ;
rec_shift_reg[3] <= rec_shift_reg[4] ;
rec_shift_reg[4] <= rec_shift_reg[5] ;
rec_shift_reg[5] <= rec_shift_reg[6] ;
rec_shift_reg[6] <= rec_shift_reg[7] ;
rec_shift_reg[7] <= rx_data ;
rw_flag <= 1 ;
end
else if(iic_done)
rw_flag <= 0 ;
reg [7:0]IIC_id ;
reg [15:0]write_addr ;
reg [15:0]read_addr ;
reg [7:0]write_data ;
reg [7:0]read_data ;
wire [7:0]rd_data ;
reg write_pulse ;
reg read_pulse ;
IIC_design IIC_rw(
.fpga_clk(fpga_clk),//50MHz
.rst_n(rst_n) ,
.rate_sel(1) ,
.read_pulse(read_pulse) ,
.write_pulse(write_pulse) ,
.write_data(write_data) ,
.IIC_id(IIC_id) ,
.wr_addr(write_addr) ,
.rd_addr(read_addr) ,
.trans_done(iic_done) ,
.read_data(rd_data) ,
.IIC_scl(IIC_scl) ,
.IIC_sda(IIC_sda)
);
wire uart_send_done ;
reg uart_send_en ;
uart_send uart_send(
.clk(fpga_clk),
.reset(rst_n),
.data(read_data),
.send_en(uart_send_en),
.baud_rate(5),//115200
.uart_tx(uart_tx),
.tx_done(uart_send_done)
);
//Parsing frames
localparam frames_header = 8'hAA ;
localparam frames_tail = 8'h55 ;
localparam read_option = 8'h5A ;
localparam write_option = 8'hA5 ;
reg [15:0]wr_cnt ;
reg [15:0]rd_cnt ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
begin
IIC_id <= 0 ;
write_addr <= 0 ;
read_addr <= 0 ;
write_data <= 0;
read_data <= 0 ;
write_pulse <= 0 ;
read_pulse <= 0 ;
//uart_send_en <= 0 ;
wr_cnt <= 0 ;
rd_cnt <= 0 ;
end
else if( rw_flag )
begin
if ( (rec_shift_reg[0] == frames_header) && (rec_shift_reg[1] == frames_header) && (rec_shift_reg[7] == frames_tail) )
begin
IIC_id <= rec_shift_reg[2] ;
if(rec_shift_reg[5] == write_option )
begin
write_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
write_data <= rec_shift_reg[6] ;
wr_cnt <= wr_cnt + 1 ;
//write signal
if( wr_cnt < 3 )
write_pulse <= 1 ;
else if(iic_done)
begin
wr_cnt <= 0 ;
end
else
write_pulse <= 0 ;
end
else if ( rec_shift_reg[5] == read_option )
begin
read_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
//read signal
rd_cnt <= rd_cnt + 1 ;
if(iic_done)
begin
read_data <= rd_data ;
read_pulse <= 0 ;
//uart_send_en <= 1 ;
rd_cnt <= 0 ;
end
//else if(uart_send_done)
// uart_send_en <= 0 ;
else if(rd_cnt <3)
read_pulse <= 1 ;
else
read_pulse <= 0 ;
end
end
end
else
begin
write_pulse <= 0 ;
read_pulse <= 0 ;
//uart_send_en <= 0 ;
wr_cnt <= 0 ;
rd_cnt <= 0 ;
end
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
uart_send_en <= 0 ;
else if(iic_done && (rec_shift_reg[5] == read_option) )
uart_send_en <= 1 ;
else if(uart_send_done)
uart_send_en <= 0 ;
endmodule
板级验证成功。
可是可是,这样用分频器设计出来的sda_clk和drive_clk 时钟质量都很差,因为该时钟是从寄存器发出的信号,不是利用fpga的全局时钟资源,所以到达其他不同的寄存器的延时会因为布线长度的不同而不一样。这里上板验证可以成功,因为本设计对时钟质量要求不高。
重新设计时,应该用fpga_clk或者PLL出来的时钟来设计。
优化设计
思路
为了提高时钟质量,这里改变设计思路,选择用PLL出来的10MHz时钟作为驱动时钟,如图,只要把状态的转移设置从时钟下降沿开始,就可以兼顾起始位状态需要在scl高电平改变数据,读写数据需要在scl低电平修改数据的问题了。
module IIC_design(
input fpga_clk ,
input rst_n ,
input read_pulse ,
input write_pulse ,
input [7:0]write_data ,
input [7:0]IIC_id ,
input [15:0]wr_addr ,
input [15:0]rd_addr ,
output trans_done ,
output [7:0]read_data ,
output IIC_scl ,
inout IIC_sda
);
wire [7:0]wr_data ;
wire [7:0]rd_data ;
wire [7:0]command ;
IIC_drive IIC_drive_inst(
.fpga_clk(fpga_clk) ,
.rst_n(rst_n) ,
.write_sig(write_pulse) ,
.read_sig(read_pulse) ,
.IIC_id(IIC_id) ,
.wr_addr(wr_addr) ,
.rd_addr(rd_addr) ,
.wr_data(write_data) ,
.rd_data(rd_data) ,
.trans_done(trans_done) ,
.read_data(read_data) ,
.write_data(wr_data) ,
.cmd(command)
);
IIC_underlying_logic IIC_underlying_logic_inst(
.fpga_clk(fpga_clk) ,
.rst_n(rst_n) ,
.write_data(wr_data) ,
.cmd(command),
.read_data(rd_data) ,
.trans_done(trans_done) ,
.ack_result() ,
.IIC_scl(IIC_scl) ,
.IIC_sda(IIC_sda)
);
endmodule
module IIC_drive(
input fpga_clk ,
input rst_n ,
input write_sig ,
input read_sig ,
input [7:0]IIC_id ,
input [15:0]wr_addr ,
input [15:0]rd_addr ,
input [7:0]wr_data ,
input [7:0]rd_data ,
input trans_done ,
output reg[7:0]read_data ,
output reg[7:0]write_data ,
output reg [7:0]cmd
);
wire clk_10m ;
//PLL
pll pll_inst(
.inclk0(fpga_clk),
.c0(clk_10m)
);
// IIC rate : 400k clk : 10Mhz
localparam cnt400k_max = 10000000 / 400000 ;
reg [15:0]IIC_cnt ;
always@(posedge clk_10m or negedge rst_n)
if(!rst_n)
IIC_cnt <= 0 ;
else if( IIC_cnt >= 24 )
IIC_cnt <= 0 ;
else
IIC_cnt <= IIC_cnt + 1 ;
//定义状态机8个状态参数
localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
localparam start_bit_state = 8'b01000000 ; //产生起始位
localparam wr_data_state = 8'b00100000 ; // 写数据
localparam rd_data_state = 8'b00010000 ; // 读数据
localparam ack_check_state = 8'b00001000 ; //检查响应
localparam stop_bit_state = 8'b00000100 ; //产生停止位
localparam gen_ack_state = 8'b00000010 ; //产生响应位
localparam gen_noack_state = 8'b00000001 ; //产生非响应位
//移位寄存器。寄存读写信号
reg rd_pulse_reg ;
reg wr_pulse_reg ;
always@(posedge fpga_clk)
begin
wr_pulse_reg <= write_sig ;
rd_pulse_reg <= read_sig ;
end
reg rd_sig ;
reg wr_sig ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
rd_sig <= 0 ;
else if( rd_pulse_reg == 0 && read_sig == 1 )
rd_sig <= 1 ;
else if( trans_done )
rd_sig <= 0 ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
wr_sig <= 0 ;
else if( wr_pulse_reg == 0 && write_sig == 1 )
wr_sig <= 1 ;
else if( trans_done )
wr_sig <= 0 ;
//读写逻辑
reg [5:0]write_cnt ;
reg [5:0]read_cnt ;
always@(posedge clk_10m or negedge rst_n)
if(!rst_n)
begin
cmd <= idel_state ;
write_data <= 0 ;
read_data <= 0 ;
write_cnt <= 0 ;
read_cnt <= 0 ;
end
else if( wr_sig )
begin
if(write_cnt == 0)
cmd <= idel_state ; //空闲态
else if ( write_cnt == 1 )
begin
cmd <= start_bit_state ;//产生起始位
end
else if ( write_cnt == 2 )
begin
cmd <= wr_data_state ;
write_data <= IIC_id ;//发送IIC器件ID
end
else if ( write_cnt == 10)
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 11 )
begin
cmd <= wr_data_state ;
write_data <= wr_addr[15:8] ;//写存储器高位地址
end
else if ( write_cnt == 19 )
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 20 )
begin
cmd <= wr_data_state ;
write_data <= wr_addr[7:0] ;//写存储器低位地址
end
else if ( write_cnt == 28 )
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 29 )
begin
cmd <= wr_data_state ;
write_data <= wr_data ; //写数据
end
else if ( write_cnt == 37 )
begin
cmd <= ack_check_state ;
end
else if ( write_cnt == 38 )
begin
cmd <= stop_bit_state ;//产生停止位
end
if(IIC_cnt == 23)
write_cnt <= write_cnt + 1 ;
if(write_cnt >= 39)
begin
write_cnt <= 0 ;
cmd <= idel_state ; //空闲态
end
end
else if( rd_sig )
begin
if(read_cnt == 0)
cmd <= idel_state ; //空闲态
else if ( read_cnt == 1 )
begin
cmd <= start_bit_state ;//产生起始位
end
else if ( read_cnt == 2 )
begin
cmd <= wr_data_state ;
write_data <= IIC_id ;//发送IIC器件ID
end
else if ( read_cnt == 10)
begin
cmd <= ack_check_state ;
end
else if ( read_cnt == 11 )
begin
cmd <= wr_data_state ;
write_data <= rd_addr[15:8] ;//写存储器高位地址
end
else if ( read_cnt == 19 )
begin
cmd <= ack_check_state ;
end
else if ( read_cnt == 20 )
begin
cmd <= wr_data_state ;
write_data <= rd_addr[7:0] ;//写存储器低位地址
end
else if ( read_cnt == 28 )
begin
cmd <= ack_check_state ;
end
else if( read_cnt == 29 )
cmd <= start_bit_state ;
else if ( read_cnt == 30 )
begin
cmd <= wr_data_state ;
write_data <= (IIC_id | 8'b00000001 ) ; //写IIC器件ID 和读指令
end
else if ( read_cnt == 38 )
begin
cmd <= ack_check_state ;
end
else if ( read_cnt == 39 )
begin
cmd <= rd_data_state ;//
end
else if ( read_cnt == 47 )
begin
cmd <= gen_noack_state ;//
end
else if ( read_cnt == 48 )
begin
cmd <= stop_bit_state ;
read_data <= rd_data ;
end
if(IIC_cnt == 23)
read_cnt <= read_cnt + 1 ;
if ( read_cnt == 49 )
begin
cmd <= idel_state ;
read_cnt <= 0 ;
end
end
else
begin
cmd <= idel_state ;
write_cnt <= 0 ;
read_cnt <= 0 ;
end
endmodule
module IIC_underlying_logic(
input fpga_clk ,
input rst_n ,
input [7:0]write_data ,
input [7:0]cmd ,
output reg [7:0]read_data ,
output reg trans_done ,
output reg ack_result ,
output reg IIC_scl ,
inout IIC_sda
);
wire clk_10m ;
//PLL
pll pll_inst(
.inclk0(fpga_clk),
.c0(clk_10m)
);
// IIC rate : 100k 400k clk : 10Mhz
localparam cnt400k_max = 10000000 / 400000 ;
localparam cnt100k_max = 10000000 / 100000 ;
reg [15:0]IIC_cnt ;
always@(posedge clk_10m or negedge rst_n)
if(!rst_n)
IIC_cnt <= 0 ;
else if( IIC_cnt >= 24 )
IIC_cnt <= 0 ;
else
IIC_cnt <= IIC_cnt + 1 ;
//IIC SCLK
always@(posedge clk_10m or negedge rst_n)
if(!rst_n)
IIC_scl <= 0 ;
else if( IIC_cnt == 24 | IIC_cnt == 11 )
IIC_scl <= !IIC_scl ;
//IIC SDATA
reg sda_ctrl ;
reg sda_out ;
assign IIC_sda = sda_ctrl ? ( sda_out ? 1'bz : 1'b0 ) : 1'bz ; //三态门,高阻态时,要么由总线上的上拉电阻拉至高电平,即输出/输出1;要么由从机拉至0,输入0;
//定义状态机8个状态参数
localparam idel_state = 8'b10000000 ; //空闲态:释放sda控制权
localparam start_bit_state = 8'b01000000 ; //产生起始位
localparam wr_data_state = 8'b00100000 ; // 写数据
localparam rd_data_state = 8'b00010000 ; // 读数据
localparam ack_check_state = 8'b00001000 ; //检查响应
localparam stop_bit_state = 8'b00000100 ; //产生停止位
localparam gen_ack_state = 8'b00000010 ; //产生响应位
localparam gen_noack_state = 8'b00000001 ; //产生非响应位
//状态机实时状态
wire [7:0]rw_state;
assign rw_state = cmd ;
reg [8:0]state_cnt ;
always@(posedge clk_10m or negedge rst_n)
if(!rst_n)
begin
sda_ctrl <= 0 ;
sda_out <= 1 ;
trans_done <= 0 ;
ack_result <= 0 ;
read_data <= 0 ;
state_cnt <= 0 ;
end
else
begin
case(rw_state)
idel_state :
begin
sda_ctrl <= 0 ;
trans_done <= 0 ;
ack_result <= 0 ;
state_cnt <= 0 ;
end
start_bit_state :
begin
sda_ctrl <= 1 ;
state_cnt <= state_cnt + 1 ;
if( state_cnt >= 17 )
sda_out <= 0 ;
else
sda_out <= 1 ;
if ( state_cnt >= 24 )
state_cnt <= 0 ;
end
wr_data_state :
begin
sda_ctrl <= 1 ;
state_cnt <= state_cnt + 1 ;
if( state_cnt == 5 )
sda_out <= write_data[7] ;
else if( state_cnt == 30 )
sda_out <= write_data[6] ;
else if( state_cnt == 55 )
sda_out <= write_data[5] ;
else if( state_cnt == 80 )
sda_out <= write_data[4] ;
else if( state_cnt == 105 )
sda_out <= write_data[3] ;
else if( state_cnt == 130 )
sda_out <= write_data[2] ;
else if( state_cnt == 155 )
sda_out <= write_data[1] ;
else if( state_cnt == 180 )
sda_out <= write_data[0] ;
if( state_cnt >= 199 )
state_cnt <= 0 ;
end
rd_data_state :
begin
sda_ctrl <= 0 ;
state_cnt <= state_cnt + 1 ;
if( state_cnt == 15 )
read_data[7] <= IIC_sda ;
else if( state_cnt == 40 )
read_data[6] <= IIC_sda ;
else if( state_cnt == 65 )
read_data[5] <= IIC_sda ;
else if( state_cnt == 90 )
read_data[4] <= IIC_sda ;
else if( state_cnt == 115 )
read_data[3] <= IIC_sda ;
else if( state_cnt == 140 )
read_data[2] <= IIC_sda ;
else if( state_cnt == 165 )
read_data[1] <= IIC_sda ;
else if( state_cnt == 190 )
read_data[0] <= IIC_sda ;
if( state_cnt >= 199 )
state_cnt <= 0 ;
end
ack_check_state :
begin
sda_ctrl <= 0 ;//释放sda控制权
state_cnt <= state_cnt + 1 ;
if( state_cnt == 1 )
ack_result <= IIC_sda ;
if(state_cnt >= 24)
state_cnt <= 0 ;
end
stop_bit_state :
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
sda_out <= 1'b0 ;
state_cnt <= state_cnt + 1 ;
if( state_cnt >= 17 )
sda_out <= 1 ;
else
sda_out <= 0 ;
if ( state_cnt >=24 )
begin
state_cnt <= 0 ;
trans_done <= 1 ;
end
end
gen_ack_state :
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
state_cnt <= state_cnt + 1 ;
if( state_cnt >= 10 )
sda_out <= 1'b0 ;
if(state_cnt >= 24 )
state_cnt <= 0 ;
end
gen_noack_state :
begin
sda_ctrl<= 1'b1 ;//获取sda控制权
state_cnt <= state_cnt + 1 ;
if( state_cnt >= 10 )
sda_out <= 1'b1 ;
if(state_cnt >= 24 )
state_cnt <= 0 ;
end
default: ;
endcase
end
endmodule
//***************** ******************//
// 串口波特率:115200 //
// 通信帧格式: //
// AA AA ID HA LA RW DA 55 //
// 其中,AA AA 为帧头;55为帧尾; //
// ID为IIC器件ID,HA、LA为存储器地址 //
// RW为读写控制,读:5A ;写:A5 //
// DA为要写入的数据,读的时候可为任意值 //
// ********************************* //
module uart_IIC_eeprom(
input fpga_clk ,
input rst_n ,
input uart_rx ,
output rx_done ,
output uart_tx ,
output IIC_scl ,
inout IIC_sda
);
//receive uart data
//wire rx_done ;
wire [7:0]rx_data ;
uart_receive uart_rec_inst(
.clk(fpga_clk) ,
.reset(rst_n) ,
.baud_rate('d5) ,//115200
.uart_rx(uart_rx),
.data(rx_data) ,
.rx_done(rx_done)
);
//Shift register
reg [7:0]rec_shift_reg[7:0] ;
reg rw_flag ;
wire iic_done ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
begin
rec_shift_reg[0] <= 0 ;
rec_shift_reg[1] <= 0 ;
rec_shift_reg[2] <= 0 ;
rec_shift_reg[3] <= 0 ;
rec_shift_reg[4] <= 0 ;
rec_shift_reg[5] <= 0 ;
rec_shift_reg[6] <= 0 ;
rec_shift_reg[7] <= 0 ;
rw_flag <= 0 ;
end
else if(rx_done)
begin
rec_shift_reg[0] <= rec_shift_reg[1] ;
rec_shift_reg[1] <= rec_shift_reg[2] ;
rec_shift_reg[2] <= rec_shift_reg[3] ;
rec_shift_reg[3] <= rec_shift_reg[4] ;
rec_shift_reg[4] <= rec_shift_reg[5] ;
rec_shift_reg[5] <= rec_shift_reg[6] ;
rec_shift_reg[6] <= rec_shift_reg[7] ;
rec_shift_reg[7] <= rx_data ;
rw_flag <= 1 ;
end
else if(iic_done)
rw_flag <= 0 ;
reg [7:0]IIC_id ;
reg [15:0]write_addr ;
reg [15:0]read_addr ;
reg [7:0]write_data ;
reg [7:0]read_data ;
wire [7:0]rd_data ;
reg write_pulse ;
reg read_pulse ;
IIC_design IIC_rw(
.fpga_clk(fpga_clk),//50MHz
.rst_n(rst_n) ,
.read_pulse(read_pulse) ,
.write_pulse(write_pulse) ,
.write_data(write_data) ,
.IIC_id(IIC_id) ,
.wr_addr(write_addr) ,
.rd_addr(read_addr) ,
.trans_done(iic_done) ,
.read_data(rd_data) ,
.IIC_scl(IIC_scl) ,
.IIC_sda(IIC_sda)
);
wire uart_send_done ;
reg uart_send_en ;
uart_send uart_send(
.clk(fpga_clk),
.reset(rst_n),
.data(read_data),
.send_en(uart_send_en),
.baud_rate(5),//115200
.uart_tx(uart_tx),
.tx_done(uart_send_done)
);
//Parsing frames
localparam frames_header = 8'hAA ;
localparam frames_tail = 8'h55 ;
localparam read_option = 8'h5A ;
localparam write_option = 8'hA5 ;
reg [15:0]wr_cnt ;
reg [15:0]rd_cnt ;
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
begin
IIC_id <= 0 ;
write_addr <= 0 ;
read_addr <= 0 ;
write_data <= 0;
read_data <= 0 ;
write_pulse <= 0 ;
read_pulse <= 0 ;
wr_cnt <= 0 ;
rd_cnt <= 0 ;
end
else if( rw_flag )
begin
if ( (rec_shift_reg[0] == frames_header) && (rec_shift_reg[1] == frames_header) && (rec_shift_reg[7] == frames_tail) )
begin
IIC_id <= rec_shift_reg[2] ;
if(rec_shift_reg[5] == write_option )
begin
write_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
write_data <= rec_shift_reg[6] ;
wr_cnt <= wr_cnt + 1 ;
//write signal
if( wr_cnt < 3 )
write_pulse <= 1 ;
else if(iic_done)
begin
wr_cnt <= 0 ;
end
else
write_pulse <= 0 ;
end
else if ( rec_shift_reg[5] == read_option )
begin
read_addr <= { rec_shift_reg[3] , rec_shift_reg[4] } ;
//read signal
rd_cnt <= rd_cnt + 1 ;
if(iic_done)
begin
read_data <= rd_data ;
read_pulse <= 0 ;
rd_cnt <= 0 ;
end
else if(rd_cnt <3)
read_pulse <= 1 ;
else
read_pulse <= 0 ;
end
end
end
else
begin
write_pulse <= 0 ;
read_pulse <= 0 ;
wr_cnt <= 0 ;
rd_cnt <= 0 ;
end
always@(posedge fpga_clk or negedge rst_n)
if(!rst_n)
uart_send_en <= 0 ;
else if(iic_done && (rec_shift_reg[5] == read_option) )
uart_send_en <= 1 ;
else if(uart_send_done)
uart_send_en <= 0 ;
endmodule
module uart_receive(
clk ,
reset ,
baud_rate ,
uart_rx,
data ,
rx_done
);
input clk ;
input reset ;
input [2:0]baud_rate ;
input uart_rx ;
output reg [7:0]data ;
output reg rx_done ;
reg [2:0]r_data[7:0] ;
reg [2:0]sta_bit ;
reg [2:0]sto_bit ;
reg [17:0]bit_tim ;
always@(baud_rate)
begin
case(baud_rate)
3'd0 : bit_tim = 1000000000/300/20 ;
3'd1 : bit_tim = 1000000000/1200/20 ;
3'd2 : bit_tim = 1000000000/2400/20 ;
3'd3 : bit_tim = 1000000000/9600/20 ;
3'd4 : bit_tim = 1000000000/19200/20 ;
3'd5 : bit_tim = 1000000000/115200/20 ;
default bit_tim = 1000000000/9600/20 ;
endcase
end
wire [17:0]bit_tim_16 ;
assign bit_tim_16 = bit_tim / 16;
wire [8:0]bit16_mid ;
assign bit16_mid = bit_tim_16 / 2 ;
reg [1:0]edge_detect ;
always @( posedge clk or negedge reset )
begin
if (!reset )
edge_detect <= 2'd0 ;
else
begin
edge_detect[0] <= uart_rx ;
edge_detect[1] <= edge_detect[0] ;
end
end
wire byte_sta_neg ;
assign byte_sta_neg = ( edge_detect == 2'b10 ) ? 1 : 0 ;
reg receive_en ;
reg [17:0]div_cnt ;
reg [7:0]bit16_cnt ;
always @( posedge clk or negedge reset )
begin
if (!reset )
receive_en <= 1'd0 ;
else if ( byte_sta_neg )
receive_en <= 1'd1 ;
else if ( (rx_done) || (sta_bit >= 3'd4 ))
receive_en <= 1'd0 ;
else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )
receive_en <= 1'd0 ;
end
always@( posedge clk or negedge reset )
begin
if ( ! reset )
div_cnt <= 18'd0 ;
else if (receive_en)
begin
if ( div_cnt == bit_tim_16 - 1'd1 )
div_cnt <= 18'd0 ;
else
div_cnt <= div_cnt + 1'b1 ;
end
else
div_cnt <= 18'd0 ;
end
reg bit16_pulse ;
always@( posedge clk or negedge reset )
begin
if ( ! reset )
bit16_pulse <= 18'd0 ;
else if (receive_en)
if ( div_cnt == bit16_mid )
bit16_pulse <= 1'd1 ;
else
bit16_pulse <= 1'd0 ;
else
bit16_pulse <= 1'd0 ;
end
always@( posedge clk or negedge reset )
begin
if ( ! reset )
bit16_cnt <= 8'd0 ;
else if (receive_en)
begin
if (( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ))
bit16_cnt <= 8'd0 ;
else if ( div_cnt == bit_tim_16 - 1'd1 )
bit16_cnt <= bit16_cnt + 1'b1 ;
end
end
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
sta_bit <= 3'd0 ;
r_data[0] <= 3'd0 ;
r_data[1] <= 3'd0 ;
r_data[2] <= 3'd0 ;
r_data[3] <= 3'd0 ;
r_data[4] <= 3'd0 ;
r_data[5] <= 3'd0 ;
r_data[6] <= 3'd0 ;
r_data[7] <= 3'd0 ;
sto_bit <= 3'd0 ;
end
else if (bit16_pulse)
case(bit16_cnt)
0:
begin
sta_bit <= 3'd0 ;
r_data[0] <= 3'd0 ;
r_data[1] <= 3'd0 ;
r_data[2] <= 3'd0 ;
r_data[3] <= 3'd0 ;
r_data[4] <= 3'd0 ;
r_data[5] <= 3'd0 ;
r_data[6] <= 3'd0 ;
r_data[7] <= 3'd0 ;
sto_bit <= 3'd0 ;
end
5,6,7,8,9,10,11 : sta_bit <= sta_bit + uart_rx ;
21,22,23,24,25,26,27 : r_data[0] <= r_data[0] + uart_rx ;
37,38,39,41,42,43,44 : r_data[1] <= r_data[1] + uart_rx ;
53,54,55,56,57,58,59 : r_data[2] <= r_data[2] + uart_rx ;
69,70,71,72,73,74,75 : r_data[3] <= r_data[3] + uart_rx ;
85,86,87,88,89,90,91 : r_data[4] <= r_data[4] + uart_rx ;
101,102,103,104,105,106,107 : r_data[5] <= r_data[5] + uart_rx ;
117,118,119,120,121,122,123 : r_data[6] <= r_data[6] + uart_rx ;
133,134,135,136,137,138,139 : r_data[7] <= r_data[7] + uart_rx ;
149,150,151,152,153,154,155 : sto_bit <= sto_bit + uart_rx ;
default ;
endcase
end
always@( posedge clk or negedge reset )
begin
if ( ! reset )
rx_done <= 8'd0 ;
else if ( ( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ) )
rx_done <= 8'd1 ;
else if (rx_done <= 8'd1 )
rx_done <= 8'd0 ;
end
always@( posedge clk or negedge reset )
begin
if ( ! reset )
data <= 8'd0 ;
//else if ( rx_done )
else if(( bit16_cnt == 8'd159 ) && (div_cnt == bit_tim_16 - 1'd1 ))
begin
data[0] = ( r_data[0] >3 ) ? 1 : 0 ;
data[1] = ( r_data[1] >3 ) ? 1 : 0 ;
data[2] = ( r_data[2] >3 ) ? 1 : 0 ;
data[3] = ( r_data[3] >3 ) ? 1 : 0 ;
data[4] = ( r_data[4] >3 ) ? 1 : 0 ;
data[5] = ( r_data[5] >3 ) ? 1 : 0 ;
data[6] = ( r_data[6] >3 ) ? 1 : 0 ;
data[7] = ( r_data[7] >3 ) ? 1 : 0 ;
end
//else if ( receive_en )
//data <= 8'd0 ;
end
endmodule
module uart_send(
clk,
reset,
data,
send_en,
baud_rate,
uart_tx,
tx_done
);
input clk;
input reset;
input [7:0]data;
input send_en;
input [2:0]baud_rate;
output reg uart_tx;
output reg tx_done;
reg [17:0]bit_tim;
always@(baud_rate) //
begin
case(baud_rate)
3'd0 : bit_tim = 1000000000/300/20 ;
3'd1 : bit_tim = 1000000000/1200/20 ;
3'd2 : bit_tim = 1000000000/2400/20 ;
3'd3 : bit_tim = 1000000000/9600/20 ;
3'd4 : bit_tim = 1000000000/19200/20 ;
3'd5 : bit_tim = 1000000000/115200/20 ;
default bit_tim = 1000000000/9600/20 ;
endcase
end
reg [17:0]counter1 ;
always@(posedge clk or negedge reset)
begin
if(!reset)
counter1 <=17'b0 ;
else if (send_en )
begin
if( counter1 == bit_tim - 1'b1 )
counter1 <= 17'b0 ;
else
counter1 <= counter1 + 1'b1 ;
end
else counter1 <= 17'b0 ;
end
reg [3:0]counter2 ;
always@(posedge clk or negedge reset)
begin
if(!reset)
counter2 <= 4'b0 ;
else if ( send_en )
begin
if(counter2 == 0)
counter2 <= counter2 +1'b1 ;
else if( counter1 == bit_tim - 1'b1 )
counter2 <= counter2 + 4'b1 ;
else
counter2 <= counter2 ;
end
else
counter2 <= 4'b0 ;
end
always@(posedge clk or negedge reset)
begin
if(!reset)
begin
uart_tx <= 4'b1 ;
end
else if ( send_en )
case(counter2)
0:begin uart_tx <= 1'b1 ; end
1:uart_tx <= 1'b0 ;
2:uart_tx <= data[0] ;
3:uart_tx <= data[1] ;
4:uart_tx <= data[2] ;
5:uart_tx <= data[3] ;
6:uart_tx <= data[4] ;
7:uart_tx <= data[5] ;
8:uart_tx <= data[6] ;
9:uart_tx <= data[7] ;
10:uart_tx <= 1'b1 ;
11:begin uart_tx <= 1'b1 ; end
default uart_tx <= 1'b1 ;
endcase
else
uart_tx <= 1'b1 ;
end
always@(posedge clk or negedge reset)
begin
if(!reset)
tx_done <= 1'b0 ;
else if (send_en )
begin
if( counter2 == 0 )//
tx_done <= 1'b0 ;
else if ( counter2 == 11 )
tx_done <= 1'b1 ;
end
else if (tx_done == 1'b1)
tx_done <= 1'b0 ;
end
endmodule
完结!只要串口按照帧格式发送数据就可以遵循IIC协议对AC609板子的eeprom存储器进行读写数据了,其他的IIC器件则修改对应IIC_id即可。
本设计仍存在问题,scl一直在输出,这是不应该的,得在读写的时候才输出才是。