fpga iic 双机通信----学习笔记

目录

背景:

实验内容:

iic协议:

fpga的代码:

读函数 iic_read_bit.v:

写函数 iic_write_bit.v:

整合写和读的函数 iic_wr.v:

仿真文件代码:

运行结果:


背景:

        为了应付实验需要,用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

运行结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值