Verilog IIC通讯实验代码
顶层
`timescale 1ns / 1ps module iicmax( clk,rst_n, sw1,sw2,sw3,sw4, scl,sda, leddata,ledcom ); input clk; // 50MHz input rst_n; //复位信号,低有效 input sw1,sw2,sw3,sw4; //按键,(1按下执行写入操作,2按下执行读操作,3按下执行连写操作,4按下执行连读操作) output scl; // 24C08的时钟端口 inout sda; // 24C08的数据端口 output [7:0] ledcom; //数码管片选信号,低有效 output [7:0] leddata; //7段数码管(不包括小数点) wire[7:0] tansdata; //传送的数据 wire[2:0] ackflag; //标志 iiccom iiccom( .clk(clk), .rst_n(rst_n), .sw1(sw1), .sw2(sw2), .sw3(sw3), .sw4(sw4), .sda(sda), .scl(scl), .ackflag(ackflag), .outdata(tansdata) ); ledshow ledshow( .clk(clk), .rst_n(rst_n), .ackflag(ackflag), .ledcom(ledcom), .leddata(leddata), .indata(tansdata) ); endmodule
IICCOM 模块
`timescale 1ns / 1ps module iiccom( clk,rst_n, sw1,sw2,sw3,sw4, scl,sda, ackflag, outdata ); input clk; // 50MHz input rst_n; //复位信号,低有效 input sw1,sw2,sw3,sw4; //按键,(1按下执行写入操作,2按下执行读操作,3按下执行连写操作,4按下执行连读操作) output scl; // 24C08的时钟端口 output [2:0]ackflag;//后面显示接收到数据的标准 inout sda; // 24C08的数据端口 output [7:0] outdata; //数码管显示的数据 //-------------------------------------------- //按键检测 reg sw1_r,sw2_r,sw3_r,sw4_r; //键值锁存寄存器,每20ms检测一次键值 reg[19:0] cnt_20ms; //20ms计数寄存器 always @ (posedge clk or negedge rst_n) if(!rst_n) cnt_20ms <= 20'd0; else if(cnt_20ms == 20'hfffff) cnt_20ms <= 20'h0; else cnt_20ms <= cnt_20ms+1'b1; //不断计数 always @ (posedge clk or negedge rst_n) if(!rst_n) begin sw1_r <= 1'b1; //键值寄存器复位,没有键盘按下时键值都为1 sw2_r <= 1'b1; sw3_r <= 1'b1; sw4_r <= 1'b1; end else if(cnt_20ms == 20'hffff0) begin sw1_r <= sw1; //按键值锁存 sw2_r <= sw2; sw3_r <= sw3; sw4_r <= sw4; end //--------------------------------------------- //分频部分 reg[2:0] cnt; // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间 reg[8:0] cnt_delay; //500循环计数,产生iic所需要的时钟 reg scl_r; //时钟脉冲寄存器 always @ (posedge clk or negedge rst_n) if(!rst_n) cnt_delay <= 9'd0; else if(cnt_delay == 9'd499) cnt_delay <= 9'd0; //计数到10us为scl的周期,即100KHz else cnt_delay <= cnt_delay+1'b1; //时钟计数 always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 3'd5; else begin case (cnt_delay) 9'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样 9'd255: cnt <= 3'd2; //cnt=2:scl下降沿后面点 9'd374: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化 9'd495: cnt <= 3'd0; //cnt=0:scl上升沿前面点 default: cnt <= 3'd5; endcase end end `define SCL_POS (cnt==3'd0) //cnt=0:scl上升沿前面点 `define SCL_HIG (cnt==3'd1) //cnt=1:scl高电平中间,用于数据采样 `define SCL_NEG (cnt==3'd2) //cnt=2:scl下降沿后面点 `define SCL_LOW (cnt==3'd3) //cnt=3:scl低电平中间,用于数据变化 always @ (posedge clk or negedge rst_n) if(!rst_n) scl_r <= 1'b0; else if(cnt_delay==9'd499) scl_r <= 1'b1; //scl信号上升沿 else if(cnt_delay==9'd249) scl_r <= 1'b0; //scl信号下降沿 assign scl = scl_r; //产生iic所需要的时钟 //--------------------------------------------- //需要写入24C02的地址和数据 `define DEVICE_READ 8'b1010_1001 //被寻址器件地址(读操作) `define DEVICE_WRITE 8'b1010_1000 //被寻址器件地址(写操作) `define WRITE_DATA0 8'b1000_1000 `define WRITE_DATA1 8'b0010_0001 //写入EEPROM的数据 `define WRITE_DATA2 8'b0100_0011 `define WRITE_DATA3 8'b0110_0101 `define WRITE_DATA4 8'b1000_0111 `define BYTE_ADDR 8'b0000_0100 //写入/读出EEPROM的地址寄存器 reg[7:0] db_r; //在IIC上传送的数据寄存器 reg[7:0] read_data; //读出EEPROM的数据寄存器 reg[7:0] outdata_r; //输出数据贮存器 parameter PAGEDATA_NUM = 3'd4; //页写数据个数 //--------------------------------------------- //读、写时序 parameter IDLE = 17'b0_0000_0000_0000_0001;//初始态 parameter START1 = 17'b0_0000_0000_0000_0010;//起始信号 parameter ADD1 = 17'b0_0000_0000_0000_0100;//写入器件地址 parameter ACK1 = 17'b0_0000_0000_0000_1000;//应答 parameter ADD2 = 17'b0_0000_0000_0001_0000;//写入字节地址 parameter ACK2 = 17'b0_0000_0000_0010_0000;//应答 parameter START2 = 17'b0_0000_0000_0100_0000;//读操作开始前的起始信号 parameter ADD3 = 17'b0_0000_0000_1000_0000;//写入器件地址 parameter ACK3 = 17'b0_0000_0001_0000_0000;//应答 parameter ACKR = 17'b1_0000_0000_0000_0000;//fpga给应答 parameter DATA = 17'b0_0000_0010_0000_0000;//字节读写 parameter PAGER = 17'b0_0000_0100_0000_0000;//页读 parameter PAGEW = 17'b0_0000_1000_0000_0000;//页写 parameter ACK4 = 17'b0_0001_0000_0000_0000;//应答 parameter HIGH = 17'b0_0010_0000_0000_0000;//高电平 parameter STOP1 = 17'b0_0100_0000_0000_0000;//停止位 parameter STOP2 = 17'b0_1000_0000_0000_0000;//延时同步 reg[16:0] cstate; //状态寄存器 reg sda_r; //输出数据寄存器 reg sda_link; //输出数据sda信号inout方向控制位 reg[3:0] num; //读写的字节计数 reg[2:0] ackflag;//连读时的数据标志 reg[2:0] pagecnt;//连读连写时的数据计数器 reg[7:0] pagedata_r;//连读储存器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin pagedata_r <= 8'd0; end else begin case(pagecnt) 3'd0: pagedata_r <= `WRITE_DATA1; 3'd1: pagedata_r <= `WRITE_DATA2; 3'd2: pagedata_r <= `WRITE_DATA3; 3'd3: pagedata_r <= `WRITE_DATA4; default:; endcase end end //---------------------------------------状态机---------------------------------------------// always@(posedge clk or negedge rst_n) begin if(!rst_n) begin cstate <= IDLE; sda_r <= 1'b1; sda_link <= 1'b0; num <= 4'd0; ackflag <= 3'd0; pagecnt <= 3'd0; read_data <= 8'b0000_0000; outdata_r <= 8'b0000_0000; end else case (cstate) IDLE: begin sda_link <= 1'b1; //数据线sda为input sda_r <= 1'b1; read_data <= 8'b0000_0000; //ackflag <= 3'd0; if(!sw1_r || !sw2_r || !sw3_r || !sw4_r) begin //SW1,SW2,SW3,SW4键有一个被按下 db_r <= `DEVICE_WRITE; //送器件地址(写操作) cstate <= START1; end else cstate <= IDLE; //没有任何键被按下 end START1: begin if(`SCL_HIG) begin //scl为高电平期间 sda_link <= 1'b1; //数据线sda为output sda_r <= 1'b0; //拉低数据线sda,产生起始位信号 cstate <= ADD1; ackflag <= 1'b0; num <= 4'd0; //num计数清零 end else cstate <= START1; //等待scl高电平中间位置到来 end ADD1: begin if(`SCL_LOW) begin if(num == 4'd8) begin num <= 4'd0; //num计数清零 sda_r <= 1'b1; sda_link <= 1'b0; //sda置为高阻态(input) cstate <= ACK1; end else begin cstate <= ADD1; num <= num+1'b1; case (num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase end end else cstate <= ADD1; end ACK1: begin if(`SCL_NEG) begin cstate <= ADD2; //从机响应信号 db_r <= `BYTE_ADDR; // 1地址 end else cstate <= ACK1; //等待从机响应 end ADD2: begin if(`SCL_LOW) begin if(num==4'd8) begin num <= 4'd0; //num计数清零 sda_r <= 1'b1; sda_link <= 1'b0; //sda置为高阻态(input) cstate <= ACK2; end else begin sda_link <= 1'b1; //sda作为output num <= num+1'b1; case (num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase cstate <= ADD2; end end else cstate <= ADD2; end ACK2: begin if(`SCL_NEG) begin //从机响应信号 if(!sw1_r) begin cstate <= DATA; //写操作 db_r <= `WRITE_DATA0; //写入的数据1 end else if(!sw2_r || !sw4_r) begin //读或着是连读 db_r <= `DEVICE_READ;//送器件地址(读操作),特定地址读需要执行该步骤以下操作 cstate <= START2; //读操作 end else if(!sw3_r) begin //连写 //db_r <= pagedata_r; //ackflag <= ackflag + 1'd1; cstate <= PAGEW; end else cstate <= ACK2; //等待从机响应 end end START2: begin //读操作起始位 if(`SCL_LOW) begin sda_link <= 1'b1; //sda作为output sda_r <= 1'b1; //拉高数据线sda cstate <= START2; end else if(`SCL_HIG) begin //scl为高电平中间 sda_r <= 1'b0; //拉低数据线sda,产生起始位信号 cstate <= ADD3; end else cstate <= START2; end ADD3: begin //送读操作地址 if(`SCL_LOW) begin if(num==4'd8) begin num <= 4'd0; //num计数清零 sda_r <= 1'b1; sda_link <= 1'b0; //sda置为高阻态(input) cstate <= ACK3; end else begin num <= num+1'b1; case (num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase cstate <= ADD3; end end else cstate <= ADD3; end ACK3: begin if(`SCL_NEG && !sw2_r) begin cstate <= DATA; //从机响应信号 sda_link <= 1'b0; end else if(`SCL_NEG && !sw4_r) begin cstate <= PAGER; //从机响应信号 ackflag <= ackflag +1'd1; sda_link <= 1'b0; end else cstate <= ACK3; //等待从机响应 end ACKR: begin sda_r <= 0; //主控制器应答 if(`SCL_NEG && !sw4_r) begin cstate <= PAGER; ackflag <= ackflag +1'd1; sda_link <= 1'b0; end else cstate <= ACKR; end DATA: begin if(!sw2_r) begin //读操作 if(num<=4'd7) begin cstate <= DATA; if(`SCL_HIG) begin num <= num+1'b1; case (num) 4'd0: read_data[7] <= sda; 4'd1: read_data[6] <= sda; 4'd2: read_data[5] <= sda; 4'd3: read_data[4] <= sda; 4'd4: read_data[3] <= sda; 4'd5: read_data[2] <= sda; 4'd6: read_data[1] <= sda; 4'd7: read_data[0] <= sda; default: ; endcase end end else if((`SCL_LOW) && (num==4'd8)) begin num <= 4'd0; //num计数清零 //cstate <= ACK4; sda_link <= 1'b1; //无应答 outdata_r <= read_data; ackflag <= 3'd1; //1个数 cstate <= HIGH; end else cstate <= DATA; end else if(!sw1_r) begin //写操作 sda_link <= 1'b1; if(num<=4'd7) begin cstate <= DATA; if(`SCL_LOW) begin sda_link <= 1'b1; //数据线sda作为output num <= num+1'b1; case (num) 4'd0: sda_r <= db_r[7]; 4'd1: sda_r <= db_r[6]; 4'd2: sda_r <= db_r[5]; 4'd3: sda_r <= db_r[4]; 4'd4: sda_r <= db_r[3]; 4'd5: sda_r <= db_r[2]; 4'd6: sda_r <= db_r[1]; 4'd7: sda_r <= db_r[0]; default: ; endcase end end else if((`SCL_LOW) && (num==4'd8)) begin num <= 4'd0; sda_r <= 1'b1; sda_link <= 1'b0; //sda置为高阻态 cstate <= ACK4; end else cstate <= DATA; end end PAGEW:begin sda_link <= 1'b1; //sda为输出 //if(pagecnt < PAGEDATA_NUM)begin if(num<=4'd7) begin cstate <= PAGEW; if(`SCL_LOW) begin sda_link <= 1'b1; //数据线sda作为output num <= num+1'b1; case (num) 4'd0: sda_r <= pagedata_r[7]; 4'd1: sda_r <= pagedata_r[6]; 4'd2: sda_r <= pagedata_r[5]; 4'd3: sda_r <= pagedata_r[4]; 4'd4: sda_r <= pagedata_r[3]; 4'd5: sda_r <= pagedata_r[2]; 4'd6: sda_r <= pagedata_r[1]; 4'd7: sda_r <= pagedata_r[0]; default: ; endcase end end else if((`SCL_LOW) && (num==4'd8)&&(pagecnt < PAGEDATA_NUM-1'b1)) begin num <= 4'd0; pagecnt <= pagecnt +1'd1; sda_r <= 1'b1; sda_link <= 1'b0; //sda置为高阻态 cstate <= ACK2; end else if((`SCL_LOW) && (num==4'd8) && (pagecnt == PAGEDATA_NUM -1'b1)) begin num <= 4'd0; //pagecnt <= pagecnt +1'd1; pagecnt <= 1'd0; sda_r <= 1'b1; sda_link <= 1'b0; cstate <= ACK4; end else cstate <= PAGEW; end //end PAGER:begin //if(pagecnt < PAGEDATA_NUM)begin if(num<=4'd7) begin cstate <= PAGER; if(`SCL_LOW) begin num <= num+1'b1; case (num) 4'd0: read_data[7] <= sda; 4'd1: read_data[6] <= sda; 4'd2: read_data[5] <= sda; 4'd3: read_data[4] <= sda; 4'd4: read_data[3] <= sda; 4'd5: read_data[2] <= sda; 4'd6: read_data[1] <= sda; 4'd7: read_data[0] <= sda; default: ; endcase end end else if((`SCL_LOW) && (num==4'd8)&& (pagecnt < PAGEDATA_NUM-1'b1)) begin num <= 4'd0; pagecnt <= pagecnt +1'd1; outdata_r <= read_data; //ackflag <= ackflag +1'd1; sda_r <= 1'b1; sda_link <= 1'b1; //主控制器应答 cstate <= ACKR; end else if((`SCL_LOW) && (num==4'd8) && (pagecnt == PAGEDATA_NUM -1'b1)) begin num <= 4'd0; //pagecnt <= pagecnt +1'd1; outdata_r <= read_data; ackflag <= ackflag +1'b1; pagecnt <= 1'd0; sda_r <= 1'b1; sda_link <= 1'b1; cstate <= HIGH; end else cstate <= PAGER; end //end ACK4: begin //写操作最后个应答 if(`SCL_NEG) begin cstate <= STOP1; end else cstate <= ACK4; end HIGH: begin if(`SCL_NEG)begin sda_r <= 1'd1; //ackflag <= ackflag +1'd1; cstate <= STOP1; end else cstate <= HIGH; end STOP1: begin if(`SCL_LOW) begin sda_link <= 1'b1; sda_r <= 1'b0; cstate <= STOP1; end else if(`SCL_HIG) begin sda_r <= 1'b1; //scl为高时,sda产生上升沿(结束信号) cstate <= STOP2; end else cstate <= STOP1; end STOP2: begin if(`SCL_LOW) sda_r <= 1'b1; else if(cnt_20ms==20'hffff0) cstate <= IDLE;//同步采样 else cstate <= STOP2; end default: cstate <= IDLE; endcase end assign sda = sda_link ? sda_r:1'bz; assign outdata = outdata_r; //--------------------------------------------------------- endmodule ledshow模块
`timescale 1ns / 1ps module ledshow( indata, clk,rst_n, leddata,ledcom, ackflag); input clk,rst_n; input [2:0]ackflag; input [7:0]indata; output [7:0]ledcom; output [7:0]leddata; reg [7:0]ledcom; parameter ZERO = 8'hc0; /*0*/ parameter ONE = 8'hf9; /*1*/ parameter TWO = 8'ha4; /*2*/ parameter THREE = 8'hb0; /*3*/ parameter FOUR = 8'h99; /*4*/ parameter FIVE = 8'h92; /*5*/ parameter SIX = 8'h82; /*6*/ parameter SEVEN = 8'hf8; /*7*/ parameter EIGHT = 8'h80; /*8*/ parameter NINE = 8'h90; /*9*/ parameter APPLE = 8'h88; /*A*/ parameter BOY = 8'h83; /*b*/ parameter CAR = 8'hc6; /*C*/ parameter DOG = 8'ha1; /*d*/ parameter EGG = 8'h86; /*E*/ parameter FINAL = 8'h8e; /*F*/ reg [15:0]cnt; reg [3:0]num; //reg divclk;//1kHZ频率的时钟 always@(posedge clk or negedge rst_n) if(!rst_n) cnt <= 16'd0; else if( cnt==16'd50000) cnt <= 16'd0; else cnt<=cnt+1'd1; reg [7:0]data1_r; reg [7:0]data2_r; reg [7:0]data3_r; reg [7:0]data4_r;//数据存储器 always@(posedge clk or negedge rst_n) if(!rst_n)begin data1_r <= 8'd0; data2_r <= 8'd0; data3_r <= 8'd0; data4_r <= 8'd0; end else case(ackflag) 3'd1: data1_r <= indata; 3'd2: data2_r <= indata; 3'd3: data3_r <= indata; 3'd4: data4_r <= indata; 3'd5: data4_r <= indata; default: begin data1_r <= 8'd0; data2_r <= 8'd0; data3_r <= 8'd0; data4_r <= 8'd0; end endcase reg [7:0]leddata_r; reg [3:0]disdata_r; always@(posedge clk or negedge rst_n)//转换成LED需要的编码 if(!rst_n)begin leddata_r <= 8'd0; end else case(disdata_r) 4'b0000: leddata_r <= ZERO; 4'b0001: leddata_r <= ONE; 4'b0010: leddata_r <= TWO; 4'b0011: leddata_r <= THREE; 4'b0100: leddata_r <= FOUR; 4'b0101: leddata_r <= FIVE; 4'b0110: leddata_r <= SIX; 4'b0111: leddata_r <= SEVEN; 4'b1000: leddata_r <= EIGHT; 4'b1001: leddata_r <= NINE; 4'b1010: leddata_r <= APPLE; 4'b1011: leddata_r <= BOY; 4'b1100: leddata_r <= CAR; 4'b1101: leddata_r <= DOG; 4'b1110: leddata_r <= EGG; 4'b1111: leddata_r <= FINAL; default: leddata_r <= ZERO; endcase assign leddata=leddata_r;//输出数据 always@(posedge clk or negedge rst_n)//led灯扫描频率1KHZ一个 if(!rst_n) begin num <= 4'd0; end else if(num==4'd8) num <= 4'd0; else if(cnt == 16'd24999) begin num <= num+1'd1; end always@(posedge clk or negedge rst_n) if(!rst_n) begin disdata_r <= 4'h0; ledcom <= 8'hff; end else case(num) 4'd0: begin disdata_r <= data1_r[3:0]; ledcom <= 8'b1111_1110; end 4'd1: begin disdata_r <= data1_r[7:4]; ledcom <= 8'b1111_1101; end 4'd2: begin disdata_r <= data2_r[3:0]; ledcom <= 8'b1111_1011; end 4'd3: begin disdata_r <= data2_r[7:4]; ledcom <= 8'b1111_0111; end 4'd4: begin disdata_r <= data3_r[3:0]; ledcom <= 8'b1110_1111; end 4'd5: begin disdata_r <= data3_r[7:4]; ledcom <= 8'b1101_1111; end 4'd6: begin disdata_r <= data4_r[3:0]; ledcom <= 8'b1011_1111; end 4'd7: begin disdata_r <= data4_r[7:4]; ledcom <= 8'b0111_1111; end default: ledcom<=8'hff; endcase endmodule
posted on 2010-11-20 14:41 GiantTank 阅读(5950) 评论(2) 编辑 收藏 举报