目录
背景:
为了应付实验需要,用fpga实现iic双机通信,通过自己改写的iic协议发送数据给另外一个fpga进行通信,同时两个fpga都具备接收发送功能。
实验内容:
iic协议:
IIC也称I2C,是一个多主从的串行总线,由飞利浦公司发明的通讯总线,属于半双工同步传输类总线,仅由两条线就能完成多机通讯,一条SCL时钟线,另外一条双向数据线SDA,IIC总线要求每个设备SCL/SDA线都是漏极开路模式,因此必须带上拉电阻才能正常工作。I2C协议占用引脚少,硬件实现简单,可扩展性强,I2C数据传输速率有标准模式(100kbps)、快速模式(400kbps)和高速模式(3.4Mbps)。
通过网上找的图可以大概的了解到这个iic的工作流程
然后我们就可以根据这个过程设计出自己的iic工作
我设计的流程是:
address(8bit)+ack(1bit)+data lenth(8bit)+ack(1bit)+data(max:256bit)+ack(1bit)
IIC总线的SDA和SCL两根总线需要上拉,使总线处于空闲状态。IIC总线一共有两种状态、四种信号。同时,SDA 和SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电源电压。当总线空闲时,这两条线路都是高电平。连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能。
因此在fpga的实现中需要把这个SDA和SCL配置成inout口,还要定义一个生态门来进行控制(SCL时钟配置成inout是为了实现多主机模式)。
fpga的代码:
读函数 iic_read_bit.v:
`timescale 1ns / 1ps
//
// Company: gdpu
// Engineer: xinhe
//
// Create Date: 2023/11/05 16:33:07
// Design Name:
// Module Name: iic_read_bit
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module iic_read_bit
#(
parameter iic_len = 8'd64, //发送多少个二进制
parameter IIC_ADDR = 8'b00101011
)
(
input sys_clk,
input iic_sck,
input sys_rst,
input iic_rcv_en,
// inout iic_sda,
input sda_in,
output reg sda_dir,
output reg sda_out,
output reg iic_rcv_busy,
output reg iic_rcv_error,
output reg [127:0] iic_rcv_bit,
output reg iic_r_done,
output reg [7:0]iic_rcv_len
);
parameter IDLE = 'd0,
ADDR = 'd1,
LENTH = 'd2,
DATA = 'd3,
ACK = 'd4,
ACK_1 = 'd6,
ACK_2='d7,
ERROR='d8,
DONE='d5;
reg [10:0] len,len_bit,len_ack;
reg [3:0] state,next_state;
reg [63:0]rcv_bit;
reg [7:0]rcv_addr;
reg len_clc;
// reg sda_dir; //三态门控制信号
// reg sda_out; //三态门输出数据
// wire sda_in; //三态门输入数据
// assign iic_sda = sda_dir?sda_out:iic_sda;
// assign sda_in = iic_sda;//取决于外部数据
always @(posedge sys_clk or posedge sys_rst) begin
if(sys_rst)begin
iic_r_done<=0;
iic_rcv_busy<=0;
iic_rcv_error<=0;
sda_dir<=0;
len_clc<=0;
state<=IDLE;
end
else
if(!iic_rcv_en) begin
iic_r_done<=0;
state<=IDLE;
len_ack<=0;
iic_rcv_busy<=0;
iic_rcv_error<=0;
sda_dir<=0;
end
else begin
case(state)
IDLE:begin
len_clc<=1;
iic_r_done<=0;
sda_dir<=0;
len_ack<=0;
rcv_addr<=0;
iic_rcv_busy<=0;
iic_rcv_error<=0;
sda_dir<=0;
len_bit<=0;
rcv_bit<=0;
if(sda_in == 0) begin
state<=ADDR;
iic_rcv_busy<=1;
end
end
ADDR:begin
if(len<=8 & len>0)
begin
rcv_addr[len-1]<=sda_in;
end
else begin
state<=ACK;
next_state<=LENTH;
len_ack<=len;
end
end
LENTH:begin
if(len<=17 & rcv_addr==IIC_ADDR )
begin
len_bit[len-10]<=sda_in;
end
else if(rcv_addr!=IIC_ADDR)begin
state<=ERROR;
end
else begin
state<=ACK;
next_state<=DATA;
len_ack<=len;
end
end
DATA:begin
if(len<=len_bit+19)
begin
rcv_bit[len-19]<=sda_in;
end
else begin
state<=ACK_1;
next_state<=ACK_2;
len_ack<=len;
end
end
ACK:begin
if((len_ack == 9 |len_ack == 18 ) & !iic_sck) begin
sda_dir<=1;
sda_out<=0;
len_ack= len;
end
if((len == 10 | len == 19 ) & !iic_sck) begin
sda_dir<=0;
len_ack = len;
state<=next_state;
end
end
ACK_1:begin
if((len_ack == len_bit+20 ) & !iic_sck) begin
sda_dir<=1;
sda_out<=0;
len_ack= len;
end
if((len == len_bit+21 ) & !iic_sck) begin
sda_dir<=0;
len_ack = len;
state<=next_state;
end
end
ACK_2:begin
len_clc<=0;
sda_out<=0;
if(len == 0)begin
sda_out<=1;
state<=DONE;
end
end
ERROR:begin
len_clc<=0;
if(len == 0)begin
sda_dir<=0;
iic_rcv_busy<=0;
iic_rcv_error<=1;
state<=IDLE;
end
end
DONE:begin
iic_r_done<='d1;
iic_rcv_busy<=0;
iic_rcv_bit<=rcv_bit;
state<=IDLE;
iic_rcv_len<=len_bit;
end
default:state<=IDLE;
endcase
end
end
always @(posedge iic_sck or posedge sys_rst) begin
if(sys_rst)begin
len <= 9'b0;
end
else begin
if(iic_rcv_en & len_clc)begin
len<=len+1;
end
else
len<=9'b0;
end
end
endmodule
写函数 iic_write_bit.v:
`timescale 1ns / 1ps
//
// Company: gdpu
// Engineer: xinhe
//
// Create Date: 2023/11/05 16:33:07
// Design Name:
// Module Name: iic_wirte_bit
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module iic_wirte_bit #(
parameter iic_len = 8'd64, //发送多少个二进制
parameter IIC_ADDR = 8'b00101011
)
(
input sys_clk,
input iic_sck,
input sys_rst,
input [7:0]iic_lenth,
input [127:0] iic_send_bit,
input iic_send_en,
// inout iic_sda,
input sda_in,
output reg sda_dir,
output reg sda_out,
output reg iic_write_busy,
output reg iic_write_error,
output reg iic_w_done
);
parameter IDLE = 'd0,
ADDR = 'd1,
LENTH = 'd2,
DATA = 'd3,
ACK = 'd4,
DONE='d5,
ERROR='d7,
ACK_1='d6;
reg [10:0] len,len_bit;
reg [4:0] state,next_state;
reg [63:0]send_bit;
reg [4:0]wait_ack;
reg len_clc;
// reg sda_dir; //三态门控制信号
// reg sda_out; //三态门输出数据
// wire sda_in; //三态门输入数据
// assign iic_sda = sda_dir?sda_out:1'bz;
// assign sda_in = iic_sda;//取决于外部数据
always @(posedge sys_clk) begin
if(sys_rst)begin
iic_w_done<=0;
sda_out<=0;
iic_write_busy<=0;
iic_write_error<=0;
sda_dir<=0;
state<=IDLE;
wait_ack<=0;
len_clc<=0;
end
else
if(!iic_send_en) begin
iic_w_done<=0;
state<=IDLE;
sda_out<=0;
iic_write_busy<=0;
iic_write_error<=0;
sda_dir<=0;
wait_ack<=0;
len_clc<=0;
end
else begin
case(state)
IDLE:begin
len_bit<=iic_lenth+'d19;
iic_w_done<=0;
len_clc<=1;
send_bit <= iic_send_bit;
iic_write_busy<=0;
iic_write_error<=0;
sda_dir<=0;
wait_ack<=0;
if(len == 'b1) begin
state<=ADDR;
sda_dir<=1;
sda_out<=0;
iic_write_busy<=1;
end
end
ADDR:begin
if(len<=8)
begin
sda_out<=IIC_ADDR[len-1];
end
else begin
state<=ACK;
next_state<=LENTH;
sda_dir<=0;
end
end
LENTH:begin
if(len<=17)
begin
sda_out<=iic_lenth[len-10];
end
else begin
state<=ACK;
next_state<=DATA;
sda_dir<=0;
end
end
DATA:begin
if(len<=len_bit)
begin
sda_out<=send_bit[len-19];
end
else begin
state<=ACK;
next_state<=ACK_1;
sda_dir<=0;
end
end
ACK:begin
if ((len == 9 | len == 18 |len == len_bit) & !iic_sck)begin
sda_dir<=0;
end
else if((len == 9 | len == 18 |len == len_bit) & iic_sck) begin
if(sda_in) begin
iic_write_error<=1;
iic_write_busy<=0;
state<=IDLE;
end
end
if ((len == 10 | len == 19 |len == len_bit+1) & !iic_sck)begin
if(wait_ack==2) begin
sda_dir<=1;
state<=next_state;
wait_ack<=0;
end
wait_ack<=wait_ack+1;
end
end
ACK_1:begin
sda_out<=0;
len_clc<=0;
if(len == 0)begin
sda_out<=1;
state<=DONE;
end
end
ERROR:begin
len_clc<=0;
if(len == 0)begin
sda_dir<=0;
iic_write_busy<=0;
iic_write_error<=1;
state<=IDLE;
end
end
DONE:begin
len_clc<=0;
iic_w_done<='d1;
iic_write_busy<=0;
state<=IDLE;
end
default:state<=IDLE;
endcase
end
end
always @(negedge iic_sck or posedge sys_rst) begin
if(sys_rst)begin
len <= 9'b0;
end
else begin
if(iic_send_en & len_clc)begin
len<=len+1;
end
else
len<=9'b0;
end
end
endmodule
整合写和读的函数 iic_wr.v:
`timescale 1ns / 1ps
//
// Company: gdpu
// Engineer: xinhe
//
// Create Date: 2023/11/05 22:23:03
// Design Name:
// Module Name: iic_wr
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module iic_wr
#(
parameter IIC_ADDR = 8'b00101011 //发送多少个二进制
)
(
input sys_clk,
input sys_rst,
input [127:0]iic_data,
input [7:0]iic_data_length,
input iic_en,
input iic_w_or_r,
inout iic_scl,
inout iic_sda,
output reg iic_error,
output reg data_valid,
output reg iic_done,
output reg iic_busy,
output reg [127:0]data,
output reg [7:0]iw_rcv_len,
output reg led4,
output reg led5,
output reg led6,
output reg led7
);
parameter IDEL='d0,
WRITE='d1,
READ='d2,
WAIT='d3,
DONE='d4;
reg [4:0]state,next_state;
// clock 分频
wire iic_sck_d;
reg iic_sck_en;
//三态门
wire scl_in; //三态门输入数据
assign iic_scl = iic_sck_en?iic_sck_d:1'bz;
assign scl_in = iic_scl;
wire iic_done_wait_w;
wire iic_done_wait_r;
reg write_en;
reg read_en;
reg[127:0] write_bit;
wire[127:0] read_bit;
reg [7:0]write_bit_length;
wire iic_rcv_busy;
wire iic_rcv_error;
wire iic_write_busy;
wire iic_write_error;
reg sda_en_rw;
wire sda_out_r;
wire sda_out_w;
wire sda_dir; //三态门控制信号
wire sda_dir_r;
wire sda_dir_w;
wire sda_out; //三态门输出数据
wire sda_in; //三态门输入数据
assign sda_dir =sda_en_rw?sda_dir_w:sda_dir_r;
assign sda_out =sda_en_rw?sda_out_w:sda_out_r;
assign iic_sda = sda_dir?sda_out:1'bz;
assign sda_in = iic_sda;//取决于外部数据
always @(posedge sys_clk or posedge sys_rst) begin
if(sys_rst) begin
iic_sck_en<=0;
write_en<=0;
read_en<=1;
write_bit<=0;
iic_error<=0;
iic_done<=0;
sda_en_rw<=0;
iic_busy<=0;
data_valid<=0;
state<=IDEL;
data<=0;
iw_rcv_len<=0;
led4<=1;
led5<=1;
led6<=1;
led7<=1;
end
else begin
led4<=0;
led5<=0;
led6<=0;
led7<=0;
if(iic_en) begin
case (state)
IDEL: begin
led4<=1;
led5<=0;
led6<=0;
led7<=0;
write_en<=0;
read_en<=1;
write_bit<=0;
iic_error<=0;
iic_done<=0;
sda_en_rw<=0;
iic_busy<=0;
data_valid<=0;
if(iic_w_or_r)begin
state<=WRITE;
sda_en_rw<=1;
iic_busy<=1;
end
if(iic_rcv_busy) begin
state<=WAIT;
iic_busy<=1;
end
end
WRITE: begin
led4<=0;
led5<=1;
led6<=0;
led7<=0;
iic_sck_en<=1;
write_en<=1;
read_en<=0;
write_bit_length<=iic_data_length;
write_bit<=iic_data;
state<=WAIT;
end
WAIT: begin
led4<=1;
led5<=1;
led6<=0;
led7<=0;
if(iic_done_wait_w | iic_done_wait_r) begin
iic_sck_en<=0;
write_en<=0;
read_en<=0;
state<=DONE;
iic_done<=1;
end
else if(iic_rcv_error|iic_write_error)begin
state<=IDEL;
iic_sck_en<=0;
iic_error<=1;
iic_busy<=0;
end
end
DONE: begin
led4<=0;
led5<=0;
led6<=1;
led7<=0;
if(iic_w_or_r) begin
end
else begin
data<=read_bit;
iw_rcv_len<=iic_rcv_len;
end
data_valid<=1;
iic_done<=1;
write_en<=0;
read_en<=0;
state<=IDEL;
iic_busy<=0;
sda_en_rw<=0;
iic_sck_en<=0;
end
default: state<=IDEL;
endcase
end
end
end
iic_clock_div iic_clock_d(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.dvi_en(iic_sck_en),
.dri_clk(iic_sck_d) //iic驱动时钟
);
iic_read_bit irb(
.sys_clk(sys_clk),
.iic_sck(scl_in),
.sys_rst(sys_rst),
.iic_rcv_en(read_en),
// .iic_sda(iic_sda),
.sda_in(sda_in),
.sda_dir(sda_dir_r),
.sda_out(sda_out_r),
.iic_rcv_busy(iic_rcv_busy),
.iic_rcv_error(iic_rcv_error),
.iic_rcv_bit(read_bit),
.iic_r_done(iic_done_wait_r),
.iic_rcv_len(iic_rcv_len)
);
iic_wirte_bit iwb(
.sys_clk(sys_clk),
.iic_sck(scl_in),
.sys_rst(sys_rst),
.iic_lenth(write_bit_length),
.iic_send_bit(write_bit),
.iic_send_en(write_en),
// .iic_sda(iic_sda),
.sda_in(sda_in),
.sda_dir(sda_dir_w),
.sda_out(sda_out_w),
.iic_write_error(iic_write_error),
.iic_write_busy(iic_write_busy),
.iic_w_done(iic_done_wait_w)
);
wait_clock_div my_w_c_d(
.sys_clk(sys_clk),
.delayEn(startdelay),
.delayDone(delayDone)
);
endmodule
module iic_clock_div
#(
parameter sys_clk_fre = 27'd100_000_000, //系统时钟频率
parameter sclk_fre = 27'd250_000 //sclk时钟频率
)
(
input sys_clk,
input sys_rst,
input dvi_en,
output reg dri_clk //iic驱动时钟
);
parameter wr_cnt_max = 10_000; //写操作时钟计数
parameter read_data_add = 8'd10; //验证读出数据的位置
parameter wr_data_num = 8'd64; //写入数据个数
//使用4状态状态机
parameter idle = 4'b0001;
parameter state_write = 4'b0010;
parameter state_read = 4'b0100;
parameter state_vf = 4'b1000;
reg [3:0] current_state;
reg [3:0] next_state = 4'b0000;
//产生驱动时钟
wire [8:0] fre_divi;
reg [9:0] divi_cnt;
assign fre_divi = (sys_clk_fre/sclk_fre)>>2'd2;
always@(posedge sys_clk or posedge sys_rst)begin
if(sys_rst)begin
dri_clk <= 1'b0;
divi_cnt <= 10'd0;
end
else if (dvi_en) begin
if(divi_cnt == (fre_divi[8:1]-1'b1))begin
dri_clk <= ~dri_clk;
divi_cnt <= 10'd0;
end
else begin
dri_clk <= dri_clk;
divi_cnt <= divi_cnt + 1'b1;
end
end
else begin
dri_clk<=1'b1;
divi_cnt <= 10'd0;
end
end
endmodule
module wait_clock_div (
input sys_clk,
input delayEn,
output reg delayDone
);
reg [17:0]counter;
always @(posedge sys_clk ) begin
if(delayEn & counter != 20)begin
counter <= counter + 1;
end
else begin
counter <= 0;
end
end
always @(posedge sys_clk ) begin
if(counter == 20)
delayDone<= 1'b1;
else
delayDone<= 1'b0;
end
endmodule
仿真文件代码:
`timescale 1ns / 1ps
//
// Company: gdpu
// Engineer: xinhe
//
// Create Date: 2023/11/09 19:32:50
// Design Name:
// Module Name: my_iic_sim_1
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module my_iic_sim_1(
);
reg sys_clk;
reg sys_rst;
reg [63:0]iic_data;
reg [7:0]iic_data_length;
reg iic_en;
reg iic_w_or_r_1,iic_w_or_r_2;
wire iic_scl;
wire iic_sda;
wire data_valid_1,data_valid_2;
wire iic_done_1,iic_done_2;
wire iic_busy_1,iic_busy_2;
wire [63:0]data_1,data_2;
wire error_1,error_2;
always #5 sys_clk=~sys_clk;
initial begin
#0;
sys_clk=0;
sys_rst=0;
#200;
sys_rst=1;
#200;
sys_rst=0;
iic_en=1;
#(100000000/9600);
iic_data_length=8;
iic_data=8'h12;
iic_w_or_r_1=1;
iic_w_or_r_2=0;
#20000;
$stop;
end
iic_wr sim_iic_wr_1(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.iic_data(iic_data),
.iic_data_length(iic_data_length),
.iic_en(iic_en),
.iic_w_or_r(iic_w_or_r_1),
.iic_scl(iic_scl),
.iic_sda(iic_sda),
.data_valid(data_valid_1),
.iic_error(error_1),
.iic_done(iic_done_1),
.iic_busy(iic_busy_1),
.data(data_1)
);
iic_wr sim_iic_wr_2(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.iic_data(iic_data),
.iic_data_length(iic_data_length),
.iic_en(iic_en),
.iic_w_or_r(iic_w_or_r_2),
.iic_scl(iic_scl),
.iic_sda(iic_sda),
.iic_error(error_2),
.data_valid(data_valid_2),
.iic_done(iic_done_2),
.iic_busy(iic_busy_2),
.data(data_2)
);
endmodule