
顶层
`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