Verilog IIC驱动| FPGA驱动

cpp 复制代码
module iic(
    input                               clk                        ,
    input                               rst                       ,
    input              [   4:0]         cmd                        ,
    input                               cmd_vld                    ,
    input              [   7:0]         wr_data                    ,
    output             [   7:0]         rd_data                    ,
    output                              rd_data_vld                ,

    output reg                          rev_ack                    ,
    output                              done                       ,
    output reg                          scl                        ,
    inout                               sda                         

);

/*
模拟过程
1. cnt_num
2. 二阶状态机
3. scl的生成

cmd和cmd_vld是一起过来的!!!
*/

`define     START_BIT   5'b00001
`define     WRITE_BIT   5'b00010
`define     READ_BIT    5'b00100
`define     STOP_BIT    5'b01000
`define     ACK_BIT     5'b10000
`define     ACK         0
`define     NO_ACK      1

localparam STATUS_IDLE      = 7'b000_0001;
localparam STATUS_START     = 7'b000_0010;
localparam STATUS_WR_DATA   = 7'b000_0100;
localparam STATUS_RD_DATA   = 7'b000_1000;
localparam STATUS_T_ACK     = 7'b001_0000;
localparam STATUS_R_ACK     = 7'b010_0000;
localparam STATUS_STOP      = 7'b100_0000;
localparam T = 100_000;
localparam SCL_MAX = 50_000_000/T; // 一个完整的SCL周期

/*
localparam和define放在最前面
*/


reg	[3:0]cnt_num	   	;
reg [6:0]cstate;
reg [6:0]nstate;
reg [7:0]wr_data_r;
reg sda_out;


parameter   SCL_LOW_HALF  = (SCL_MAX * 1 / 4) - 1,
            SCL_HIGH_HALF = (SCL_MAX * 3 / 4) - 1;  
reg [25:0]cnt_bit;
// 下面是关键的 状态转换的检测信号






reg OE;
reg[8*20-1:0]state_name;
reg [7:0]rd_data_r;
reg [4:0]num;
wire end_cnt_num;

/*下面是输入输出信号*/
assign rd_data = rd_data_r; //读也从寄存器开始输出

assign sda = OE?sda_out:1'bz;
wire sda_in = sda;


reg [4:0]cmd_r;


wire IF_IDLE_START       = (cstate == STATUS_IDLE       )    && cmd_vld      && (cmd & `START_BIT)    ;
wire IF_START_WR_DATA    = (cstate == STATUS_START      )    && end_cnt_num  && (cmd_r & `WRITE_BIT)    ;
wire IF_WR_DATA_R_ACK    = (cstate == STATUS_WR_DATA    )    && end_cnt_num                             ;
wire IF_R_ACK_IDLE       = (cstate == STATUS_R_ACK      )    && end_cnt_num  && !(cmd_r & `STOP_BIT)    ; // 可以接收到响应后停止(比如说在读数据之前)
wire IF_IDLE_WR_DATA     = (cstate == STATUS_IDLE       )    && cmd_vld      && (cmd & `WRITE_BIT)    ;
wire IF_R_ACK_STOP       = (cstate == STATUS_R_ACK      )    && end_cnt_num  && (cmd_r & `STOP_BIT)     ;
wire IF_STOP_IDLE        = (cstate == STATUS_STOP       )    && end_cnt_num                             ;
wire IF_IDLE_RD_DATA     = (cstate == STATUS_IDLE       )    && cmd_vld      && (cmd & `READ_BIT)     ;
wire IF_RD_DATA_T_ACK    = (cstate == STATUS_RD_DATA    )    && end_cnt_num                             ;
wire IF_T_ACK_IDLE       = (cstate == STATUS_T_ACK      )    && end_cnt_num  && !(cmd_r & `STOP_BIT)    ;
wire IF_T_ACK_STOP       = (cstate == STATUS_T_ACK      )    && end_cnt_num  && (cmd_r & `STOP_BIT)     ;

/*
cmd_vld对应cmd   				是由外界的cmd触发的
end_cnt_num对应cmd_r			是由自动触发引起的
*/


assign add_cnt_bit = cstate != STATUS_IDLE;
assign end_cnt_bit = add_cnt_bit && cnt_bit == SCL_MAX - 1'd1;

assign add_cnt_num = end_cnt_bit;
assign end_cnt_num = add_cnt_num && cnt_num == num - 1;

assign done = IF_R_ACK_IDLE || IF_STOP_IDLE || IF_T_ACK_IDLE;//都是回到IDLE  用来对应eeprom的cnt_byte
assign rd_data_vld = IF_T_ACK_STOP || IF_T_ACK_IDLE;// 快发送ACK的时候

/*
    cnt_bit的目的是组成完整的scl时钟周期
*/ 
always @(posedge clk or negedge rst)
	if (!rst) begin
        cnt_bit <= 26'd0;
    end else if(add_cnt_bit)begin
        if(end_cnt_bit)
            cnt_bit <= 26'd0;
        else
            cnt_bit <= cnt_bit + 26'd1;
    end 

//IIC_SCL
always @(posedge clk or negedge rst) begin
	if (!rst) begin
		scl <= 1'b1;
    end else if(cnt_bit == (SCL_MAX - 1'd1)>>1 || IF_STOP_IDLE)begin
        scl <= 1'b1;
    end else if(end_cnt_bit)begin
        scl <= 1'b0;//start信号  默认是1 过了第一个end_cnt_bit才把scl拉低  妙啊
    end
end


// 能用寄存器   都用寄存器


always @(posedge clk or negedge rst) 
	if (!rst) begin
        OE = 1'b1;
    end else if(IF_IDLE_START || IF_START_WR_DATA || IF_IDLE_WR_DATA || IF_RD_DATA_T_ACK || IF_T_ACK_STOP || IF_R_ACK_STOP)begin// !!!添加了IF_T_ACK_STOP
        OE = 1'b1;
    end else if(IF_WR_DATA_R_ACK || IF_IDLE_RD_DATA || IF_STOP_IDLE)begin
        OE = 1'b0;
    end

 always @(posedge clk or negedge rst) begin
	if (!rst) begin
		sda_out <= 1;
		end
	else begin
		case (cstate)
			STATUS_IDLE    :sda_out <= 1;
			STATUS_START   :begin
                if(cnt_bit == SCL_LOW_HALF)
                    sda_out <= 1;
                else if(cnt_bit == SCL_HIGH_HALF)
                    sda_out <= 0;
				end
			STATUS_WR_DATA :begin
                if(cnt_bit == SCL_LOW_HALF)
                    sda_out <= wr_data_r[7-cnt_num];
				end
			STATUS_T_ACK   :begin//
                if(cnt_bit == SCL_LOW_HALF)
                    if(cmd_r & `ACK_BIT)
                        sda_out <= `NO_ACK;
                    else
                        sda_out <= `ACK;
				end
			STATUS_STOP    :begin
                if(cnt_bit == SCL_LOW_HALF)
                    sda_out <= 0;
                else if(cnt_bit == SCL_HIGH_HALF)
                    sda_out <= 1;
				end
			default: sda_out <= 'b1;
		endcase
	end
end


always@(*)
    case(cstate)
        STATUS_IDLE    :   state_name  = "IDLE   ";
        STATUS_START   :   state_name  = "START  ";
        STATUS_WR_DATA :   state_name  = "WR_DATA";
        STATUS_RD_DATA :   state_name  = "RD_DATA";
        STATUS_T_ACK   :   state_name  = "T_ACK  ";
        STATUS_R_ACK   :   state_name  = "R_ACK  ";
        STATUS_STOP    :   state_name  = "STOP   ";
        default 	   :   state_name  = "IDLE   ";
    endcase



always @(posedge clk or negedge rst) begin
	if (!rst) begin
		rev_ack <= 0;
		rd_data_r <= 8'b0;
	end else case (cstate)
        STATUS_RD_DATA:
            if(cnt_bit == SCL_HIGH_HALF)
	            rd_data_r[7 - cnt_num] <= sda;
        STATUS_R_ACK:
            if(cnt_bit == SCL_HIGH_HALF)
                rev_ack <= sda_in;    
        default:;
    endcase
end

/*
在scl的1/4发送数据,在3/4处接受数据
*/













always @(*) begin
	case (cstate)
		STATUS_IDLE    : num = 1;
		STATUS_START   : num = 1;
		STATUS_WR_DATA : num = 8;
		STATUS_RD_DATA : num = 8;
		STATUS_R_ACK   : num = 1;
		STATUS_T_ACK   : num = 1;
		STATUS_STOP    : num = 1;
		default : num = 1;
	endcase
end



// 把输入的东西先存放在寄存器里面  确保在状态机运行期间,这些信号的值保持稳定
always @(posedge clk or negedge rst) begin
	if (!rst) begin
		wr_data_r <= 'd0;
		cmd_r <= 'd0;
		end
	else if (cmd_vld) begin
		wr_data_r <= wr_data;
		cmd_r <= cmd;
		end
end




always@(posedge clk or negedge rst)
    if(!rst)begin
        cstate <= STATUS_IDLE;
    end else begin
        cstate <= nstate;
    end

always @(*) begin
	case(cstate)
		STATUS_IDLE    : begin
			if (IF_IDLE_START) begin
				nstate = STATUS_START;
				end
			else if (IF_IDLE_WR_DATA) begin
				nstate = STATUS_WR_DATA;
				end
			else if (IF_IDLE_RD_DATA) begin
				nstate = STATUS_RD_DATA;
				end
			else begin
				nstate = cstate;    //这个要注意
				end
			end 
		STATUS_START   : begin
			if (IF_START_WR_DATA) begin
				nstate = STATUS_WR_DATA;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_WR_DATA : begin
			if (IF_WR_DATA_R_ACK) begin
				nstate = STATUS_R_ACK;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_RD_DATA : begin
			if (IF_RD_DATA_T_ACK) begin
				nstate = STATUS_T_ACK;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_R_ACK   : begin
			if (IF_R_ACK_STOP) begin
				nstate = STATUS_STOP;
				end
			else if (IF_R_ACK_IDLE) begin
				nstate = STATUS_IDLE;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_T_ACK   : begin
			if (IF_T_ACK_STOP) begin
				nstate = STATUS_STOP;
				end
			else if (IF_T_ACK_IDLE) begin
				nstate = STATUS_IDLE;
				end
			else begin
				nstate = cstate;
				end
			end 
		STATUS_STOP    : begin
			if (IF_STOP_IDLE) begin
				nstate = STATUS_IDLE;
				end
			else begin
				nstate = cstate;
				end
			end 
		default : nstate = cstate;
	endcase
end




always @(posedge clk or negedge rst)begin 
	if(!rst)begin
		cnt_num <= 'd0;
		end 
	else if(add_cnt_num)begin 
		if(end_cnt_num)begin 
			cnt_num <= 'd0;
			end
		else begin 
			cnt_num <= cnt_num + 1'd1;
		end 
	end
end 










endmodule
相关推荐
Hello-FPGA12 小时前
基于RFSOC FPGA ZCU216 的CoaXPress 采集demo
fpga开发
9527华安13 小时前
FPGA基于ADV7182解码PAL视频,Video Processing Subsystem去隔行,提供6套工程源码和技术支持
fpga开发·音视频·pal·adv7182
hahaha601614 小时前
数字图像时域滤波或者3D降噪算法FPGA实现
算法·计算机视觉·fpga开发
GateWorld14 小时前
Xilinx 7系列FPGA时钟网络详解
fpga开发·时钟网络·xilinx 7系列
szxinmai主板定制专家1 天前
X86 工控机 vs RK3588 异构主控:AMR 控制器 BOM、散热、长期运维对比
运维·stm32·单片机·嵌入式硬件·fpga开发·am62x
szxinmai主板定制专家2 天前
AMR 项目落地:机器人控制器硬件定制化需求复盘
arm开发·单片机·fpga开发·系统架构·机器人·am62x
weixin_404380632 天前
ESP32-S3 项目展示:SHT30 温湿度写入设备状态,串口 / 局域网页 / 手机三端可见(ESP-IDF v6.1)
乐鑫·esp32-s3·i2c·esp-idf·sht30·温湿度
FPGA小迷弟3 天前
FPGA 与卫星:从星载硬件到在轨智能的完整技术解析
fpga开发·架构·fpga·1024程序员节·无线电
NamoAmitabha1283 天前
CPU 内部结构是什么?---deepseek
fpga开发
abc light3 天前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发