module iic_top(
clk,rst_n,
sw1,sw2,
scl,sda,
sm_cs1_n,sm_cs2_n,sm_db
);
input clk;
input rst_n;
input sw1,sw2;
output scl;
inout sda;
// 50MHz
//复位信号,低有效
// 24C02 的数据端口
//按键 1、2,(1 按下执行写入操作,2 按下执行读操作)
// 24C02 的时钟端口
output sm_cs1_n,sm_cs2_n;
output[6:0] sm_db; //7 段数码管(不包括小数点)
//数码管片选信号,低有效
wire[7:0] dis_data;
//在数码管上显示的 16 进制数
iic_com
iic_com(
.clk(clk),
.rst_n(rst_n),
.sw1(sw1),
.sw2(sw2),
.scl(scl),
.sda(sda),
.dis_data(dis_data)
);
.clk(clk),
.rst_n(rst_n),
.dis_data(dis_data),
.sm_cs1_n(sm_cs1_n),
.sm_cs2_n(sm_cs2_n),
.sm_db(sm_db)
);
led_seg7 led_seg7(
endmodule
module led_seg7(
clk,rst_n,
dis_data,
sm_cs1_n,sm_cs2_n,sm_db
);
input clk;
input rst_n;
// 50MHz
// 复位信号,低有效
input[7:0] dis_data; //显示数据
output sm_cs1_n,sm_cs2_n;
output[6:0] sm_db; //7 段数码管(不包括小数点)
//数码管片选信号,低有效
reg[7:0] cnt;
always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt <= 8'd0;
else cnt <= cnt+1'b1;
//-------------------------------------------------------------------------------
/* 共阴极 :不带小数点
3,
6,
7,
1,
2,
4, 5,
;0,
3fh,06h,5bh,4fh,66h,6dh,7dh,07h
;8,
7fh,6fh,77h,7ch,39h,5eh,79h,71h,00h*/
f , 灭
9, a,
b,
c,
d,
e,
db
db
parameterseg0 = 7'h3f,
seg1 = 7'h06,
seg2 = 7'h5b,
seg3 = 7'h4f,
seg4 = 7'h66,
seg5 = 7'h6d,
seg6 = 7'h7d,
seg7 = 7'h07,
seg8 = 7'h7f,
seg9 = 7'h6f,
sega = 7'h77,
segb = 7'h7c,
segc = 7'h39,
segd = 7'h5e,
sege = 7'h79,
segf = 7'h71;
reg[6:0] sm_dbr;
//7 段数码管(不包括小数点)
//显示数据
wire[3:0] num;
assign num = cnt[7] ? dis_data[7:4] : dis_data[3:0];
//数码管 1 常开
assign sm_cs1_n = cnt[7];
assign sm_cs2_n = ~cnt[7];
//数码管 2 常开
always @ (posedge clk)
case (num)
//NUM 值显示在两个数码管上
4'h0: sm_dbr <= seg0;
4'h1: sm_dbr <= seg1;
4'h2: sm_dbr <= seg2;
4'h3: sm_dbr <= seg3;
4'h4: sm_dbr <= seg4;
4'h5: sm_dbr <= seg5;
4'h6: sm_dbr <= seg6;
4'h7: sm_dbr <= seg7;
4'h8: sm_dbr <= seg8;
4'h9: sm_dbr <= seg9;
4'ha: sm_dbr <= sega;
4'hb: sm_dbr <= segb;
4'hc: sm_dbr <= segc;
4'hd: sm_dbr <= segd;
4'he: sm_dbr <= sege;
4'hf: sm_dbr <= segf;
default: ;
endcase
assign sm_db = sm_dbr;
endmodule
module iic_com(
clk,rst_n,
sw1,sw2,
scl,sda,
dis_data
);
// 50MHz
//复位信号,低有效
input clk;
input rst_n;
input sw1,sw2;
output scl;
inout sda;
output[7:0] dis_data;
//按键 1、2,(1 按下执行写入操作,2 按下执行读操作)
// 24C02 的时钟端口
// 24C02 的数据端口
//数码管显示的数据
//--------------------------------------------
//按键检测
reg sw1_r,sw2_r;
reg[19:0] cnt_20ms;
//键值锁存寄存器,每 20ms 检测一次键值
//20ms 计数寄存器
always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt_20ms <= 20'd0;
else cnt_20ms <= cnt_20ms+1'b1; //不断计数
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
sw1_r <= 1'b1;
sw2_r <= 1'b1;
end
//键值寄存器复位,没有键盘按下时键值都为 1
else if(cnt_20ms == 20'hfffff) begin
sw1_r <= sw1;//按键 1 值锁存
sw2_r <= sw2;//按键 2 值锁存
end
//---------------------------------------------
//分频部分
// cnt=0:scl 上升沿,cnt=1:scl 高电平中间,cnt=2:scl 下降沿,cnt=3:scl 低电平
reg[2:0] cnt;
中间
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;
else cnt_delay <= cnt_delay+1'b1; //时钟计数
always @ (posedge clk or negedge rst_n) begin
//计数到 10us 为 scl 的周期,即 100KHz
if(!rst_n) cnt <= 3'd5;
else begin
case (cnt_delay)
cnt <= 3'd1;
cnt <= 3'd2;
cnt <= 3'd3;
cnt <= 3'd0;
9'd124:
9'd249:
9'd374:
9'd499:
default: cnt <= 3'd5;
endcase
end
end
//cnt=1:scl 高电平中间,用于数据采样
//cnt=2:scl 下降沿
//cnt=3:scl 低电平中间,用于数据变化
//cnt=0:scl 上升沿
`define SCL_POS
`define SCL_HIG
`define SCL_NEG
`define SCL_LOW
(cnt==3'd0)
(cnt==3'd1)
(cnt==3'd2)
(cnt==3'd3)
//cnt=0:scl 上升沿
//cnt=1:scl 高电平中间,用于数据采样
//cnt=2:scl 下降沿
//cnt=3:scl 低电平中间,用于数据变化
always @ (posedge clk or negedge rst_n)
if(!rst_n) scl_r <= 1'b0;
else if(cnt==3'd0) scl_r <= 1'b1;
else if(cnt==3'd2) scl_r <= 1'b0;
//scl 信号上升沿
//scl 信号下降沿
assign scl = scl_r;
//---------------------------------------------
//产生 iic 所需要的时钟
//需要写入 24C02 的地址和数据
`define DEVICE_READ
`define DEVICE_WRITE
`define WRITE_DATA
`define BYTE_ADDR
reg[7:0] db_r;
reg[7:0] read_data; //读出 EEPROM 的数据寄存器
//在 IIC 上传送的数据寄存器
8'b1010_0001 //被寻址器件地址(读操作)
8'b1010_0000 //被寻址器件地址(写操作)
8'b0001_0001 //写入 EEPROM 的数据
8'b0000_0011 //写入/读出 EEPROM 的地址寄存器
//---------------------------------------------
//读、写时序
parameter
parameter
parameter
parameter
parameter
parameter
parameter
parameter
parameter
parameter
parameter
parameter
parameter
= 4'd0;
= 4'd1;
= 4'd2;
= 4'd3;
= 4'd4;
= 4'd5;
IDLE
START1
ADD1
ACK1
ADD2
ACK2
START2
= 4'd7;
ADD3
ACK3
= 4'd8;
DATA = 4'd9;
ACK4
= 4'd10;
STOP1 = 4'd11;
STOP2 = 4'd12;
= 4'd6;
reg[3:0] cstate;
reg sda_r;
reg sda_link;
reg[3:0] num; //
//状态寄存器
//输出数据寄存器
//输出数据 sda 信号 inout 方向控制位
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;
read_data <= 8'b0000_0000;
end
else
case (cstate)
IDLE:
begin
sda_link <= 1'b1;
sda_r <= 1'b1;
if(!sw1_r || !sw2_r) begin
//数据线 sda 为 input
//SW1,SW2 键有一个被按下
db_r <= `DEVICE_WRITE;
cstate <= START1;
end
//送器件地址(写操作)
else cstate <= IDLE;
//没有任何键被按下
end
START1: begin
if(`SCL_HIG) begin
//scl 为高电平期间
sda_link <= 1'b1;
sda_r <= 1'b0;
cstate <= ADD1;
num <= 4'd0;
end
//数据线 sda 为 output
//拉低数据线 sda,产生起始位信号
//num 计数清零
else cstate <= START1; //等待 scl 高电平中间位置到来
end
ADD1:
begin
if(`SCL_LOW) begin
//num 计数清零
//sda 置为高阻态(input)
if(num == 4'd8) begin
num <= 4'd0;
sda_r <= 1'b1;
sda_link <= 1'b0;
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];
开始
应答位
//
end
ACK1:
end
ADD2:
4'd7: sda_r <= db_r[0];
default: ;
endcase
//
sda_r <= db_r[4'd7-num];
//送器件地址,从高位
end
end
else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};
else cstate <= ADD1;
//器件地址左移 1bit
begin
if(/*!sda*/`SCL_NEG) begin //注:24C01/02/04/08/16 器件可以不考虑
cstate <= ADD2;
db_r <= `BYTE_ADDR;
//从机响应信号
// 1 地址
end
else cstate <= ACK1;
//等待从机响应
begin
if(`SCL_LOW) begin
if(num==4'd8) begin
num <= 4'd0;
sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <= ACK2;
//num 计数清零
//sda 置为高阻态(input)
end
else begin
sda_link <= 1'b1;
num <= num+1'b1;
case (num)
//sda 作为 output
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
bit 开始)
//
sda_r <= db_r[4'd7-num];
//送 EEPROM 地址(高
cstate <= ADD2;
end
//
end
ACK2:
end
else if(`SCL_POS) db_r <= {db_r[6:0],1'b0};
else cstate <= ADD2;
//器件地址左移 1bit
begin
if(/*!sda*/`SCL_NEG) begin
if(!sw1_r) begin
//从机响应信号
cstate <= DATA;
db_r <= `WRITE_DATA;
//写操作
//写入的数据
end
else if(!sw2_r) begin
特定地址读需要执行该步骤以下操作
cstate <= START2;
//读操作
db_r <= `DEVICE_READ;
//送器件地址(读操作),
end
end
else cstate <= ACK2;
//等待从机响应
end
START2: begin
//读操作起始位
if(`SCL_LOW) begin
sda_link <= 1'b1;
sda_r <= 1'b1;
cstate <= START2;
end
//sda 作为 output
//拉高数据线 sda
else if(`SCL_HIG) begin//scl 为高电平中间
//拉低数据线 sda,产生起始位信号
sda_r <= 1'b0;
cstate <= ADD3;
end
else cstate <= START2;
end
ADD3:
//送读操作地址
begin
if(`SCL_LOW) begin
if(num==4'd8) begin
num <= 4'd0;
sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <= ACK3;
//num 计数清零
//sda 置为高阻态(input)
end
else begin
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];