FPGA SCCB协议

FPGA SCCB

SCCB写操作与IIC写操作一致

SCCB读操作与IIC读操作区别

IIC读操作

在这里插入图片描述

SCCB读操作

在这里插入图片描述
在这里插入图片描述

与IIC代码相比增加了读中点停止状态,当写操作时,SCCB与IIC无异,读操作时在写地址状态后跳转到读中点停止状态,产生上升沿停止位,之后跳转到读开始状态(在此状态中产生起始位)。

module SCCB(
	Clk                 ,
	Rst_n               ,

	Wr                  ,
	Wr_data             ,
	Wr_data_vaule       ,
	Wr_done             ,

	Word_adrr           ,           //存储单元地址

	Device_adress       ,           //器件地址

    Read                ,
	Rd_data             ,
	Rd_data_vaule       ,
    Rd_done             ,

	Sda                 ,
	Sccb_clk
);
	input Clk;
	input Rst_n;
	input Wr;
	input Read;
	input [7:0] Device_adress; //器件地址
	input [7:0] Word_adrr; //存储单元地址

	input [7:0]Wr_data;//写入的数据

	output reg[7:0]Rd_data;//读出的数据
	output Wr_data_vaule;//写数据有效标志信号
	output reg Rd_data_vaule;//读数据有效标志信号
	inout  Sda;
	output reg Wr_done;
    output reg Rd_done;
	output  Sccb_clk;

	parameter CLK_FREQUENCY = 50_000_000;
	parameter Sccb_clk_FREQUENCY = 100_000; //sccb clk时钟频率100KHZ
	localparam COUNT_MAX = CLK_FREQUENCY/Sccb_clk_FREQUENCY;
	//独热码
	localparam      IDLE      =     10'b0_000_000_001,
                    WR_START  =     10'b0_000_000_010,
                    WR_CTRL   =     10'b0_000_000_100,//写控制 写器件地址
                    WR_ADDRE  =     10'b0_000_001_000,//写地址
                    RDMSTOP   =     10'b0_000_010_000,
                    WR_DATA   =     10'b0_000_100_000,//写数据
                    RD_START  =     10'b0_001_000_000,//读开始
                    RD_CTRL   =     10'b0_010_000_000,//读控制
                    RD_DATA   =     10'b0_100_000_000,//读数据
                    STOP      =     10'b1_000_000_000;

	reg iic_free_n;//i2c空闲标志位,低电平有效
	wire [7:0]waddress_data;//器件地址数据写
	wire [7:0]raddress_data;//器件地址数据读
	reg [15:0]iic_count; //产生i2C_CLK信号计数寄存器
	reg clk_high;
	reg clk_low;
	reg [7:0]half_clk_cnt;//计数一个状态中高低电平计数器
	reg [9:0]main_state;//主状态机状态寄存器
	reg ack;//数据接收方对发送方的响应标志位
	reg send_en;//控制Sda线输入输出开关
	reg sda_reg;//sda信号线寄存器
	reg task_en_n;//任务开始结束控制寄存器
	reg [7:0]sda_data_out;//sda待输出的数据
	reg [7:0]sda_data_in;//sda待写入的数据
	wire be_rd_data_vaule;//读数据有效前寄存器
	reg w_flag;
	reg r_flag;
    reg clk_r;

	//写/读器件地址8位
	assign waddress_data = {Device_adress[7:1],1'b0 };
	assign raddress_data = {Device_adress[7:1],1'b1 };

    assign  Sccb_clk    =   ((main_state    ==  RD_START)   ||  (main_state ==  RDMSTOP)) ? 1'b1 : clk_r;

	assign Sda = send_en?sda_reg:1'bz;

	//iic_free_n状态判断
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	iic_free_n <= 1'b0;
	else	if(Wr_done  ||  Rd_done)
	iic_free_n <= 1'b0;
	else	if(Wr|Read)
	iic_free_n <= 1'b1;
	else
	iic_free_n <= iic_free_n;

	//iic_count计数
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	iic_count <= 1'b0;
	else	if(iic_free_n)
	begin
		if(iic_count == COUNT_MAX - 1'b1)
		iic_count <= 16'd0;
		else
		iic_count <= iic_count + 1'b1;
	end
	else
	iic_count <= 1'b0;

	//Sccb_clk产生
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	clk_r <= 1'b1;
	else if(iic_count == COUNT_MAX >> 1)
		clk_r <= 1'b0;
    else if(iic_count == 1'b0)
		clk_r <= 1'b1;
	else
	clk_r <= clk_r;

	//标志Sccb_clk高电平 clk_high产生
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	clk_high <= 1'b0;
	else if(iic_count == (COUNT_MAX >> 2) )
	clk_high <= 1'b1;
	else
	clk_high <= 1'b0;

	//标志Sccb_clk低电平 clk_low产生
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	clk_low <= 1'b0;
	else if(iic_count == (COUNT_MAX >> 2 ) + (COUNT_MAX >> 1) )
	clk_low <= 1'b1;
	else
	clk_low <= 1'b0;

	//sda串行收发Sccb_clk高低电平计数器
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	half_clk_cnt <= 1'b0;
	else
	if((main_state == WR_CTRL) || (main_state ==WR_ADDRE)|| (main_state ==WR_DATA) || (main_state ==RD_CTRL) ||(main_state ==RD_DATA))
	begin
		if(clk_high | clk_low)
		begin
			if(half_clk_cnt == 8'd17)
			half_clk_cnt <= 8'b0;
			else
			half_clk_cnt <= half_clk_cnt + 1'b1;
		end
	   else
		half_clk_cnt <= half_clk_cnt;
	end
	else
	half_clk_cnt <= 1'b0;

	//数据接收方对发送方的响应标志位ACK
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	ack <= 1'b0;
	else
	if((half_clk_cnt == 8'd16) && clk_high && (Sda == 1'b0))
	ack <= 1'b1;
	else
	if((half_clk_cnt == 8'd17 )&& clk_low)
	ack <= 1'b0;
	else
	ack <= ack;

	//输出串行数据任务
	task send_eight_data;
	if(clk_high && (half_clk_cnt == 8'd16))
		task_en_n <= 1'b1;
	else
	if(half_clk_cnt < 8'd17)
	begin
		sda_reg <= sda_data_out[7];
		if(clk_low)
		sda_data_out <= {sda_data_out[6:0],1'b0};
		else
		sda_data_out <= sda_data_out;
	end
	endtask

	//输入串行任务
	task get_eight_data;
	if(clk_low && (half_clk_cnt == 8'd15))
		task_en_n <= 1'b1;
	else
		if(half_clk_cnt < 8'd15)
		begin
			if(clk_high)
			sda_data_in <= {sda_data_in[6:0],Sda};
			else
			sda_data_in <= sda_data_in;
		end
	endtask


	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	begin
		sda_reg <= 1'b1;
		Wr_done <= 1'b0;
		w_flag <= 1'b0;
		r_flag <= 1'b0;
        Rd_done <= 1'b0;
	end
	else
	begin
		case(main_state)

		IDLE :
		begin
		sda_reg <= 1'b1;
		w_flag <= 1'b0;
		r_flag <= 1'b0;
		Wr_done <= 1'b0;
        Rd_done <= 1'b0;
			if(Wr)
			begin
			main_state <= WR_START;
			w_flag <= 1'b1;
			end
			else if(Read)
			begin
			main_state <= WR_START;
			r_flag <= 1'b1;
			end
            else
                main_state  <=  IDLE    ;
		end

		WR_START:
		begin
		if(clk_low)
			begin
			main_state <= WR_CTRL;
			sda_data_out <= waddress_data;
			task_en_n <= 1'b0;
			end
		else if(clk_high)
			begin
			sda_reg <= 1'b0;
			main_state <= WR_START;
			end
		else
			main_state <= WR_START;
		end

		WR_CTRL:
		begin
		if(task_en_n == 1'b0)
		send_eight_data;
		else
		if(ack == 1'b1)
			begin
			if(clk_low)
				begin
				main_state <= WR_ADDRE;
				task_en_n <= 1'b0;
				sda_data_out <= Word_adrr;
				end
			else
			main_state <= WR_CTRL;
			end
		else
		main_state <= IDLE;
		end

		WR_ADDRE:
		begin
		if(task_en_n == 1'b0)
		send_eight_data;
		else
            begin
                if(ack == 1'b1)
                    begin
                        if(clk_low && w_flag)
                        begin
                            main_state <= WR_DATA;
                            task_en_n <= 1'b0;
                            sda_data_out <= Wr_data;
                        end
                        else
                        if(clk_low && r_flag)
                        begin
                            main_state <= RDMSTOP;
                            sda_reg <= 1'b0; //产生上升沿停止位
                        end
                        else
                        main_state <= WR_ADDRE;
                    end
                else
                main_state <= IDLE;
            end
		end

        RDMSTOP:
        begin
        if(clk_low)
            main_state  <=  RD_START    ;
        else
        if(clk_high)
            begin
                sda_reg <=  1'b1;           //产生上升沿
                main_state  <=  RDMSTOP ;
            end
        else
            main_state  <=  RDMSTOP ;
        end

		WR_DATA: //10_000
		begin
		if(task_en_n == 1'b0)
		send_eight_data;
		else
            begin
                if(ack == 1'b1)
                begin
                    if(clk_low)
                    begin
                    main_state <= STOP;
                    sda_reg <= 1'b0; //上升沿停止
                    end
                    else
                    main_state <= WR_DATA;
                end
                else
                main_state <= IDLE;
            end
		end

		RD_START:
		begin
			if(clk_low)
			begin
			main_state <= RD_CTRL;
			task_en_n <= 1'b0;
			sda_data_out <= raddress_data;
			end
			else
			if(clk_high)
			begin
			main_state <= RD_START;
			sda_reg <= 1'b0;//产生下降沿起始位
			end
			else
			main_state <= RD_START;
		end

		RD_CTRL:
		begin
			if(task_en_n == 1'b0)
			send_eight_data;
			else
            begin
                if(ack == 1'b1)
                begin
                    if(clk_low)
                    begin
                    main_state <= RD_DATA;
                    task_en_n <= 1'b0;
                    end
                    else
                    main_state <= RD_CTRL;
                end
                else
                main_state <= IDLE;
            end
		end

		RD_DATA:
		begin
			if(task_en_n == 1'b0)
                get_eight_data;
			else
			begin
                sda_reg <= 1'b1; //产生NOack信号表示不读
                    if(clk_low)
                    begin
                        main_state <= STOP;
                        sda_reg <= 1'b0; //产生上升沿停止位
                    end
                    else
                        main_state <= RD_DATA;
			end
		end

		STOP:
		begin
            if(clk_high)
            begin
            sda_reg <= 1'b1; //产生上升沿停止态
            main_state <= IDLE;
                if(w_flag)
                    Wr_done <=  1'b1;
                else
                if(r_flag)
                    Rd_done <=  1'b1;
            end
            else
            main_state <= STOP;
		end



		default : main_state <= IDLE;
		endcase
	end

		//控制sda开关闭合关闭
	always @(*)  //描述组合逻辑电路
	begin
		case(main_state)
		IDLE:
		send_en = 1'b0;

		RD_START,WR_START,RDMSTOP,STOP:  //写/读开始 停止位
		send_en = 1'b1;

		RD_CTRL,WR_CTRL,WR_ADDRE,WR_DATA:
		begin
		if(half_clk_cnt < 8'd16)
		send_en = 1'b1;
		else
		send_en = 1'b0;
		end

		RD_DATA:
		begin
		if(half_clk_cnt < 8'd16)
		send_en = 1'b0;
		else
		send_en = 1'b1;
		end

		default:send_en = 1'b0;
		endcase

	end

	//写输入有效标志位
    assign Wr_data_vaule = ((main_state == WR_ADDRE) && (w_flag && clk_low) && (ack == 1'b1));

	//读数据有效标志位前寄存器
	assign be_rd_data_vaule = ((main_state == RD_DATA) && (half_clk_cnt == 8'd15) && clk_low);

	//读数据有效标志位
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	Rd_data_vaule <= 1'b0;
	else if(be_rd_data_vaule)
	Rd_data_vaule <= 1'b1;
	else
	Rd_data_vaule <= 1'b0;gai

	//读出的有效数据
	always @(posedge Clk or negedge Rst_n)
	if(!Rst_n)
	Rd_data <= 8'd0;
	else if(be_rd_data_vaule)
	Rd_data <= sda_data_in;
	else
	Rd_data <= Rd_data;

endmodule

此模块在后续的系統中将会用到,所以在此不做仿真验证。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值