PCF8563 是飞利浦公司推出的一款工业级内含 I2C 总线接口功能的具有极低功耗的多功能时钟/日历芯片。 PCF8563 的多种报警功能、定时器功能、时钟输出功能以及中断输出功能, 能完成各种复杂的定时服务。
PCF8563 内有 16(00~0F) 个 8 位寄存器:一个可自动增量的地址寄存器,一个内置 32.768KHz 的振荡器(带有一个内部集成的电容)一个分频器(用于给实时时钟 RTC 提供源时钟)一个可编程时钟输出,一个定时器,一个报警器,一个掉电检测器和一个 I2C 总线接口。
1、 前两个寄存器(内存地址 00H 和 01H) : 用于控制寄存器和状态寄存器。
2、内存地址 02H~08H 寄存器: 用于时钟计数器(秒到年计数器) 。
3、地址 09H~0CH 寄存器: 用于报警寄存器(定义报警条件) 。
4、地址 0DH 寄存器:用于控制 CLKOUT 管脚的输出频率。
5、地址 0EH 和 0FH 寄存器: 分别用于定时器控制和定时器。
秒、分钟、小时、日、月、年、分钟报警、小时报警、日报警寄存器都以二进制编码的十进制(BCD)格式编码, 星期和星期报警寄存器不以 BCD 格式编码。当一个 RTC 寄存器被读时,所有计数器的内容被锁存,因此,在传送条件下,可以防止对时钟日历芯片的错读
秒寄存器(02h)
分寄存器(03h)
时寄存器(04h)
日寄存器(05h)
月寄存器(07h)
年寄存器(08h)
年数据为当前年份的末两位。
PCF8563 通信接口
上电到 SCL 下降沿(起始信号)需等待 8ms 的时间,也就是我们上电之后需先等待 8ms 后再开始对 PCF8563 芯片进行配置。
实验目标
通过数码管显示实时时间,并通过按键切换显示年月日。
程序框图
状态跳转图
代码
pcf8563_ctrl RTC时钟芯片控制
输入时间19_09_07_16_00_00,需要循环读取数据
`timescale 1ns / 1ns
module pcf8563_ctrl
#(
parameter TIME_INIT = 48'h19_09_07_16_00_00
)
(
input wire sys_clk , //系统时钟,频率 50MHz
input wire i2c_clk , //i2c 驱动时钟
input wire sys_rst_n , //复位信号,低有效
input wire i2c_end , //i2c 一次读/写操作完成
input wire [7:0] rd_data , //输出 i2c 设备读取数据
input wire key_flag , //按键消抖后标志信号
output reg wr_en , //输出写使能信号
output reg rd_en , //输出读使能信号
output reg i2c_start , //输出 i2c 触发信号
output reg [15:0] byte_addr , //输出 i2c 字节地址
output reg [7:0] wr_data , //输出 i2c 设备数据
output reg [23:0] data_out //输出到数码管显示的 bcd 码数据
);
//reg define
reg [3:0] state ; //状态机状态
reg [12:0] cnt_wait ; //等待计数器
reg [7:0] year ; //年数据
reg [7:0] month ; //月数据
reg [7:0] day ; //日数据
reg [7:0] hour ; //小时数据
reg [7:0] minute ; //年数据
reg [7:0] second ; //秒数据
reg data_flag ; //数据切换标志信号
//parameter define
localparam S_WAIT = 4'd1 , //上电等待状态
INIT_SEC = 4'd2 , //初始化秒
INIT_MIN = 4'd3 , //初始化分
INIT_HOUR = 4'd4 , //初始化小时
INIT_DAY = 4'd5 , //初始化日
INIT_MON = 4'd6 , //初始化月
INIT_YEAR = 4'd7 , //初始化年
RD_SEC = 4'd8 , //读秒
RD_MIN = 4'd9 , //读分
RD_HOUR = 4'd10 , //读小时
RD_DAY = 4'd11 , //读日
RD_MON = 4'd12 , //读月
RD_YEAR = 4'd13 ; //读年
localparam CNT_WAIT_8MS = 8000 ; //8ms 时间计数值
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//状态计时
always @(posedge i2c_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_wait <= 13'd0;
else
case (state)
S_WAIT:
if(cnt_wait == CNT_WAIT_8MS)
cnt_wait <= 13'd0;
else
cnt_wait <= cnt_wait + 1'b1;
INIT_SEC,INIT_MIN,INIT_HOUR,INIT_DAY,INIT_MON,
INIT_YEAR,RD_SEC,RD_MIN,RD_HOUR,RD_DAY,RD_MON,RD_YEAR:
if(i2c_end == 1'b1)
cnt_wait <= 13'd0;
else
cnt_wait <= cnt_wait + 1'b1;
default: cnt_wait <= 13'd0;
endcase
end
//产生数据切换的标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_flag <= 1'b0;
else if(key_flag == 1'b1)
data_flag <= ~data_flag;
else
data_flag <= data_flag;
//data_flag 为 0 时显示时分秒,为 1 时显示年月日
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n )
data_out <= 24'd0;
else if(data_flag == 1'b0)
data_out <= {hour,minute,second};
else
data_out <= {year,month,day};
//状态机跳转
always @(posedge i2c_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
state <= 4'd0;
else
case (state)
S_WAIT: //上电等待8ms
if(cnt_wait == CNT_WAIT_8MS)
state <= INIT_SEC;
else
state <= state;
INIT_SEC://初始化秒状态
if(i2c_end == 1'b1)
state <= INIT_MIN;
else
state <= state;
INIT_MIN://初始化分状态
if(i2c_end == 1'b1)
state <= INIT_HOUR;
else
state <= state;
INIT_HOUR://初始化时状态
if(i2c_end == 1'b1)
state <= INIT_DAY;
else
state <= state;
INIT_DAY://初始化天状态
if(i2c_end == 1'b1)
state <= INIT_MON;
else
state <= state;
INIT_MON://初始化月状态
if(i2c_end == 1'b1)
state <= INIT_YEAR;
else
state <= state;
INIT_YEAR://初始化年状态
if(i2c_end == 1'b1)
state <= RD_SEC;
else
state <= state;
RD_SEC://读秒
if(i2c_end == 1'b1)
state <= RD_MIN;
else
state <= state;
RD_MIN://读分
if(i2c_end == 1'b1)
state <= RD_HOUR;
else
state <= state;
RD_HOUR://读时
if(i2c_end == 1'b1)
state <= RD_DAY;
else
state <= state;
RD_DAY://读天
if(i2c_end == 1'b1)
state <= RD_MON;
else
state <= state;
RD_MON://读月
if(i2c_end == 1'b1)
state <= RD_YEAR;
else
state <= state;
RD_YEAR://读年
if(i2c_end == 1'b1)
state <= RD_SEC;
else
state <= state;
default: state <= S_WAIT;
endcase
end
//各状态下的信号赋值
always @(posedge i2c_clk or negedge sys_rst_n) begin
if(!sys_rst_n)begin
wr_en <= 1'b0 ;
rd_en <= 1'b0 ;
i2c_start <= 1'b0 ;
byte_addr <= 16'd0 ;
wr_data <= 8'd0 ;
year <= 8'd0 ;
month <= 8'd0 ;
day <= 8'd0 ;
hour <= 8'd0 ;
minute <= 8'd0 ;
second <= 8'd0 ;
end
else
case (state)
S_WAIT:begin //上电等待状态
wr_en <= 1'b0;
rd_en <= 1'b0;
i2c_start <= 1'b0;
byte_addr <= 16'h0 ;
wr_data <= 8'h00 ;
end
INIT_SEC: //初始化秒
if(cnt_wait == 13'b1)begin
wr_en <= 1'b1;
i2c_start <= 1'b1;
byte_addr <= 16'h02 ;
wr_data <= TIME_INIT[7:0];
end
else begin
wr_en <= 1'b1;
i2c_start <= 1'b0;
byte_addr <= 16'h02 ;
wr_data <= TIME_INIT[7:0];
end
INIT_MIN: //初始化分
if(cnt_wait == 13'b1)begin
i2c_start <= 1'b1;
byte_addr <= 16'h03;
wr_data <= TIME_INIT[15:8];
end
else begin
i2c_start <= 1'b0;
byte_addr <= 16'h03 ;
wr_data <= TIME_INIT[15:8];
end
INIT_HOUR: //初始化时
if(cnt_wait == 13'b1)begin
i2c_start <= 1'b1;
byte_addr <= 16'h04;
wr_data <= TIME_INIT[23:16];
end
else begin
i2c_start <= 1'b0;
byte_addr <= 16'h04;
wr_data <= TIME_INIT[23:16];
end
INIT_DAY: //初始化天
if(cnt_wait == 13'b1)begin
i2c_start <= 1'b1;
byte_addr <= 16'h05;
wr_data <= TIME_INIT[31:24];
end
else begin
i2c_start <= 1'b0;
byte_addr <= 16'h05;
wr_data <= TIME_INIT[31:24];
end
INIT_MON: //初始化月
if(cnt_wait == 13'b1)begin
i2c_start <= 1'b1;
byte_addr <= 16'h07;
wr_data <= TIME_INIT[39:32];
end
else begin
i2c_start <= 1'b0;
byte_addr <= 16'h07;
wr_data <= TIME_INIT[39:32];
end
INIT_YEAR: //初始化年
if(cnt_wait == 13'b1)begin
i2c_start <= 1'b1;
byte_addr <= 16'h08;
wr_data <= TIME_INIT[47:40];
end
else begin
i2c_start <= 1'b0;
byte_addr <= 16'h08;
wr_data <= TIME_INIT[47:40];
end
RD_SEC: //读秒
if(cnt_wait == 13'b1)
i2c_start <= 1'b1;
else if(i2c_end == 1'b1)
second <= rd_data[6:0];
else begin
wr_en <= 1'b0;
rd_en <= 1'b1;
i2c_start <= 1'b0;
byte_addr <= 16'h02;
wr_data <= 8'd0 ;
end
RD_MIN: //读分
if(cnt_wait == 13'b1)
i2c_start <= 1'b1;
else if(i2c_end == 1'b1)
minute <= rd_data[6:0];
else begin
rd_en <= 1'b1;
i2c_start <= 1'b0;
byte_addr <= 16'h03;
end
RD_HOUR: //读时
if(cnt_wait == 13'b1)
i2c_start <= 1'b1;
else if(i2c_end == 1'b1)
hour <= rd_data[5:0];
else begin
rd_en <= 1'b1;
i2c_start <= 1'b0;
byte_addr <= 16'h04;
end
RD_DAY: //读天
if(cnt_wait == 13'b1)
i2c_start <= 1'b1;
else if(i2c_end == 1'b1)
day <= rd_data[5:0];
else begin
rd_en <= 1'b1;
i2c_start <= 1'b0;
byte_addr <= 16'h05;
end
RD_MON: //读月
if(cnt_wait == 13'b1)
i2c_start <= 1'b1;
else if(i2c_end == 1'b1)
month <= rd_data[4:0];
else begin
rd_en <= 1'b1;
i2c_start <= 1'b0;
byte_addr <= 16'h7;
end
RD_YEAR: //读年
if(cnt_wait == 13'b1)
i2c_start <= 1'b1;
else if(i2c_end == 1'b1)
year <= rd_data[7:0];
else begin
rd_en <= 1'b1;
i2c_start <= 1'b0;
byte_addr <= 16'h08;
end
default: begin
wr_en <= 1'b0 ;
rd_en <= 1'b0 ;
i2c_start <= 1'b0 ;
byte_addr <= 16'd0 ;
wr_data <= 8'd0 ;
year <= 8'd0 ;
month <= 8'd0 ;
day <= 8'd0 ;
hour <= 8'd0 ;
minute <= 8'd0 ;
second <= 8'd0 ;
end
endcase
end
endmodule
i2c协议控制代码 驱动RTC时钟芯片
`timescale 1ns/1ns
module i2c_ctrl
#(
parameter DEVICE_ADDR = 7'b1010_000 , //i2c设备地址
parameter SYS_CLK_FREQ = 26'd50_000_000 , //输入系统时钟频率
parameter SCL_FREQ = 18'd250_000 //i2c设备scl时钟频率
)
(
input wire sys_clk , //输入系统时钟,50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire wr_en , //输入写使能信号
input wire rd_en , //输入读使能信号
input wire i2c_start , //输入i2c触发信号
input wire addr_num , //输入i2c字节地址字节数
input wire [15:0] byte_addr , //输入i2c字节地址
input wire [7:0] wr_data , //输入i2c设备数据
output reg i2c_clk , //i2c驱动时钟
output reg i2c_end , //i2c一次读/写操作完成
output reg [7:0] rd_data , //输出i2c设备读取数据
output reg i2c_scl , //输出至i2c设备的串行时钟信号scl
inout wire i2c_sda //输出至i2c设备的串行数据信号sda
);
//************************************************************************//
//******************** Parameter and Internal Signal *********************//
//************************************************************************//
// parameter define
parameter CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ; //cnt_clk计数器计数最大值
parameter CNT_START_MAX = 8'd100; //cnt_start计数器计数最大值
parameter IDLE = 4'd00, //初始状态
START_1 = 4'd01, //开始状态1
SEND_D_ADDR = 4'd02, //设备地址写入状态 + 控制写
ACK_1 = 4'd03, //应答状态1
SEND_B_ADDR_H = 4'd04, //字节地址高八位写入状态
ACK_2 = 4'd05, //应答状态2
SEND_B_ADDR_L = 4'd06, //字节地址低八位写入状态
ACK_3 = 4'd07, //应答状态3
WR_DATA = 4'd08, //写数据状态
ACK_4 = 4'd09, //应答状态4
START_2 = 4'd10, //开始状态2
SEND_RD_ADDR = 4'd11, //设备地址写入状态 + 控制读
ACK_5 = 4'd12, //应答状态5
RD_DATA = 4'd13, //读数据状态
N_ACK = 4'd14, //非应答状态
STOP = 4'd15; //结束状态
// wire define
wire sda_in ; //sda输入数据寄存
wire sda_en ; //sda数据写入使能信号
// reg define
reg [7:0] cnt_clk ; //系统时钟计数器,控制生成clk_i2c时钟信号
reg [3:0] state ; //状态机状态
reg cnt_i2c_clk_en ; //cnt_i2c_clk计数器使能信号
reg [1:0] cnt_i2c_clk ; //clk_i2c时钟计数器,控制生成cnt_bit信号
reg [2:0] cnt_bit ; //sda比特计数器
reg ack ; //应答信号
reg i2c_sda_reg ; //sda数据缓存
reg [7:0] rd_data_reg ; //自i2c设备读出数据
//************************************************************************//
//******************************* Main Code ******************************//
//************************************************************************//
// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 8'd0;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
cnt_clk <= 8'd0;
else
cnt_clk <= cnt_clk + 1'b1;
// i2c_clk:i2c驱动时钟
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_clk <= 1'b1;
else if(cnt_clk == CNT_CLK_MAX - 1'b1)
i2c_clk <= ~i2c_clk;
// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk_en <= 1'b0;
else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
cnt_i2c_clk_en <= 1'b0;
else if(i2c_start == 1'b1)
cnt_i2c_clk_en <= 1'b1;
// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk <= 2'd0;
else if(cnt_i2c_clk_en == 1'b1)
cnt_i2c_clk <= cnt_i2c_clk + 1'b1;
// cnt_bit:sda比特计数器
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 3'd0;
else if((state == IDLE) || (state == START_1) || (state == START_2)
|| (state == ACK_1) || (state == ACK_2) || (state == ACK_3)
|| (state == ACK_4) || (state == ACK_5) || (state == N_ACK))
cnt_bit <= 3'd0;
else if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
cnt_bit <= 3'd0;
else if((cnt_i2c_clk == 2'd3) && (state != IDLE))
cnt_bit <= cnt_bit + 1'b1;
// state:状态机状态跳转
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE:
if(i2c_start == 1'b1)
state <= START_1;
else
state <= state;
START_1:
if(cnt_i2c_clk == 3)
state <= SEND_D_ADDR;
else
state <= state;
SEND_D_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_1;
else
state <= state;
ACK_1:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
begin
if(addr_num == 1'b1)
state <= SEND_B_ADDR_H;
else
state <= SEND_B_ADDR_L;
end
else
state <= state;
SEND_B_ADDR_H:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_2;
else
state <= state;
ACK_2:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= SEND_B_ADDR_L;
else
state <= state;
SEND_B_ADDR_L:
if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
state <= ACK_3;
else
state <= state;
ACK_3:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
begin
if(wr_en == 1'b1)
state <= WR_DATA;
else if(rd_en == 1'b1)
state <= START_2;
else
state <= state;
end
else
state <= state;
WR_DATA:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_4;
else
state <= state;
ACK_4:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= STOP;
else
state <= state;
START_2:
if(cnt_i2c_clk == 3)
state <= SEND_RD_ADDR;
else
state <= state;
SEND_RD_ADDR:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= ACK_5;
else
state <= state;
ACK_5:
if((cnt_i2c_clk == 3) && (ack == 1'b0))
state <= RD_DATA;
else
state <= state;
RD_DATA:
if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
state <= N_ACK;
else
state <= state;
N_ACK:
if(cnt_i2c_clk == 3)
state <= STOP;
else
state <= state;
STOP:
if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
state <= IDLE;
else
state <= state;
default: state <= IDLE;
endcase
// ack:应答信号
always@(*)
case (state)
IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
ack <= 1'b1;
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
if(cnt_i2c_clk == 2'd0)
ack <= sda_in ;
else
ack <= ack;
default: ack <= 1'b1;
endcase
// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
case (state)
IDLE:
i2c_scl <= 1'b1;
START_1:
if(cnt_i2c_clk == 2'd3)
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L,
ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK:
if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
i2c_scl <= 1'b1;
else
i2c_scl <= 1'b0;
STOP:
if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
i2c_scl <= 1'b0;
else
i2c_scl <= 1'b1;
default: i2c_scl <= 1'b1;
endcase
// i2c_sda_reg:sda数据缓存
always@(*)
case (state)
IDLE:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= 8'd0;
end
START_1:
if(cnt_i2c_clk <= 2'd0)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_D_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit];
else
i2c_sda_reg <= 1'b0;
ACK_1:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_H:
i2c_sda_reg <= byte_addr[15 - cnt_bit];
ACK_2:
i2c_sda_reg <= 1'b1;
SEND_B_ADDR_L:
i2c_sda_reg <= byte_addr[7 - cnt_bit];
ACK_3:
i2c_sda_reg <= 1'b1;
WR_DATA:
i2c_sda_reg <= wr_data[7 - cnt_bit];
ACK_4:
i2c_sda_reg <= 1'b1;
START_2:
if(cnt_i2c_clk <= 2'd1)
i2c_sda_reg <= 1'b1;
else
i2c_sda_reg <= 1'b0;
SEND_RD_ADDR:
if(cnt_bit <= 3'd6)
i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit];
else
i2c_sda_reg <= 1'b1;
ACK_5:
i2c_sda_reg <= 1'b1;
RD_DATA:
if(cnt_i2c_clk == 2'd2)
rd_data_reg[7 - cnt_bit] <= sda_in;
else
rd_data_reg <= rd_data_reg;
N_ACK:
i2c_sda_reg <= 1'b1;
STOP:
if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
i2c_sda_reg <= 1'b0;
else
i2c_sda_reg <= 1'b1;
default:
begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= rd_data_reg;
end
endcase
// rd_data:自i2c设备读出数据
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rd_data <= 8'd0;
else if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
rd_data <= rd_data_reg;
// i2c_end:一次读/写结束信号
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_end <= 1'b0;
else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
i2c_end <= 1'b1;
else
i2c_end <= 1'b0;
// sda_in:sda输入数据寄存
assign sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
assign sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2)
|| (state == ACK_3) || (state == ACK_4) || (state == ACK_5))
? 1'b0 : 1'b1;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;
endmodule
按键滤波代码实现
`timescale 1ns / 1ns
//
// Company:
// Engineer:
//
// Create Date: 2023/12/25 18:59:24
// Design Name:
// Module Name: key_filter
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module key_filter
#(parameter CNT_MAX = 20'd999_999
)
(
input wire sys_clk,
input wire sys_rst_n,
input wire key_in,
output reg key_flag
);
reg [19:0] cnt;
//cnt
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
cnt <= 20'b0;
else if (key_in == 1'b1)
cnt <= 20'b0;
else if (cnt == CNT_MAX && key_in == 1'b0)
cnt <= cnt ;
else
cnt <= cnt + 1'b1;
end
//key_flag
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
key_flag <= 1'b0;
else if (cnt == CNT_MAX - 1'b1)
key_flag <= 1'b1;
else
key_flag <= 1'b0;
end
endmodule
将RTC时钟芯片输出的数据显示
`timescale 1ns / 1ns
//
// Company:
// Engineer:
//
// Create Date: 2023/10/12 17:33:11
// Design Name:
// Module Name: display
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module seg_display(
input wire sys_clk,
input wire sys_rst_n,
input wire [23:0] data,
input wire [5:0] point,
input wire sign,
input wire seg_en,
output reg [5:0] seg_sel,
output reg [7:0] seg_led
);
//parameter define
parameter CNT_MAX = 16'd49_999;
//wire define
wire [3:0] unit;
wire [3:0] ten;
wire [3:0] hun;
wire [3:0] tho;
wire [3:0] t_tho;
wire [3:0] h_hun;
//reg define
reg [15:0] cnt_1ms;
reg flag_1ms;
reg [2:0] cnt_sel;
reg [5:0] sel_reg;
reg [3:0] data_disp;
reg dot_disp;
//cnt_1ms
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
cnt_1ms <= 16'd0;
else if(cnt_1ms == CNT_MAX)
cnt_1ms <= 16'd0;
else
cnt_1ms <= cnt_1ms + 1'b1;
end
//flag_1ms
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
flag_1ms <= 1'b0;
else if(cnt_1ms == CNT_MAX - 1'b1)
flag_1ms <= 1'b1;
else
flag_1ms <= 1'b0;
end
//cnt_sel
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
cnt_sel <= 3'd0;
else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
cnt_sel <= 3'd0;
else if(flag_1ms == 1'b1)
cnt_sel <= cnt_sel + 1'b1;
else
cnt_sel <= cnt_sel ;
end
//sel_reg
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
sel_reg <= 6'b111_111;
else if( flag_1ms == 1'b1)
case (cnt_sel)
3'd0: sel_reg <= 6'b111110;
3'd1: sel_reg <= 6'b111101;
3'd2: sel_reg <= 6'b111011;
3'd3: sel_reg <= 6'b110111;
3'd4: sel_reg <= 6'b101111;
3'd5: sel_reg <= 6'b011111;
default: sel_reg <= 6'b111_111;
endcase
else
sel_reg <= sel_reg;
end
//data_disp
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
data_disp <= 4'b0;
else if((flag_1ms == 1'b1) && (seg_en == 1'b1))
case (cnt_sel)
3'd0 : data_disp <= data[3:0] ;
3'd1 : data_disp <= data[7:4] ;
3'd2 : data_disp <= data[11:8] ;
3'd3 : data_disp <= data[15:12] ;
3'd4 : data_disp <= data[19:16] ;
3'd5 : data_disp <= data[23:20] ;
default: data_disp <= 4'b0 ;
endcase
else
data_disp <= data_disp;
end
//dot_disp
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
dot_disp <= 1'b1;
else if(flag_1ms == 1'b1)
dot_disp <= ~point[cnt_sel];
else
dot_disp <= dot_disp;
end
//seg_sel
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 1'b0)
seg_sel <= 6'b000_000;
else
seg_sel <= sel_reg;
end
//seg_led
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
seg_led <= 8'b1111_1111;
else
case(data_disp)
4'd0 : seg_led <= {dot_disp,7'b100_0000}; //显示数字 0
4'd1 : seg_led <= {dot_disp,7'b111_1001}; //显示数字 1
4'd2 : seg_led <= {dot_disp,7'b010_0100}; //显示数字 2
4'd3 : seg_led <= {dot_disp,7'b011_0000}; //显示数字 3
4'd4 : seg_led <= {dot_disp,7'b001_1001}; //显示数字 4
4'd5 : seg_led <= {dot_disp,7'b001_0010}; //显示数字 5
4'd6 : seg_led <= {dot_disp,7'b000_0010}; //显示数字 6
4'd7 : seg_led <= {dot_disp,7'b111_1000}; //显示数字 7
4'd8 : seg_led <= {dot_disp,7'b000_0000}; //显示数字 8
4'd9 : seg_led <= {dot_disp,7'b001_0000}; //显示数字 9
default:seg_led <= 8'b1100_0000;
endcase
endmodule
顶层模块
module rtc (
input wire sys_clk,
input wire sys_rst_n,
input wire key_in,
output wire [7:0] seg,
output wire [5:0] sel,
inout wire sda,
output wire scl
);
parameter TIME_INIT = 48'h23_12_26_16_00_00;
//wire define
wire i2c_clk ; //i2c 驱动时钟
wire i2c_end ; //i2c 一次读/写操作完成
wire [7:0] rd_data ; //输出 i2c 设备读取数据
wire key_flag ; //按键消抖后标志信号
wire wr_en ; //写使能信号
wire rd_en ; //读使能信号
wire i2c_start ; //i2c 触发信号
wire [15:0] byte_addr ; //i2c 字节地址
wire [7:0] wr_data ; //写入 i2c 设备数据
wire [23:0] data_out ; //数码管显示数据
pcf8563_ctrl
#(
.TIME_INIT (TIME_INIT)
)
pcf8563_ctrl_inst
(
.sys_clk (sys_clk),
.i2c_clk (i2c_clk),
.sys_rst_n (sys_rst_n),
.i2c_end (i2c_end),
.rd_data (rd_data),
.key_flag (key_flag),
.wr_en (wr_en),
.rd_en (rd_en),
.i2c_start (i2c_start),
.byte_addr (byte_addr),
.wr_data (wr_data),
.data_out (data_out)
);
seg_display seg_display_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.data (data_out),
.point (6'b000000),
.seg_en (1'b1),
.sign (1'b0),
.seg_sel (sel),
.seg_led (seg)
);
i2c_ctrl
#(
.DEVICE_ADDR (7'b101_0001 ), //i2c设备器件地址
.SYS_CLK_FREQ (26'd50_000_000 ), //i2c_ctrl模块系统时钟频率
.SCL_FREQ (18'd250_000 ) //i2c的SCL时钟频率
)
i2c_ctrl_inst
(
.sys_clk (sys_clk ), //输入系统时钟,50MHz
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效
.wr_en (wr_en ), //输入写使能信号
.rd_en (rd_en ), //输入读使能信号
.i2c_start (i2c_start ), //输入i2c触发信号
.addr_num (1'b0 ), //输入i2c字节地址字节数
.byte_addr (byte_addr ), //输入i2c字节地址
.wr_data (wr_data ), //输入i2c设备数据
.rd_data (rd_data ), //输出i2c设备读取数据
.i2c_end (i2c_end ), //i2c一次读/写操作完成
.i2c_clk (i2c_clk ), //i2c驱动时钟
.i2c_scl (scl ), //输出至i2c设备的串行时钟信号scl
.i2c_sda (sda ) //输出至i2c设备的串行数据信号sda
);
key_filter key_filter_inst
(
.sys_clk (sys_clk ), //系统时钟50Mhz
.sys_rst_n (sys_rst_n ), //全局复位
.key_in (key_in ), //按键输入信号
.key_flag (key_flag ) //key_flag为1时表示按键有效,0表示按键无效
);
endmodule