一、PCF8563介绍
通过PCF8563模块完成实时时钟的数据产生。PCF8563具有报警功能、定时器功能、时
钟输出功能以及中断输出功能。
内存地址00H、01H用作控制寄存器和状态寄存器(CONTROL_STATUS);内存地址02H~08H 用作TIME 计时器(秒~年计时器);地址09H~0CH 用于报警(ALARM)寄存器(定义报警条件);地址0DH 控制CLKOUT 管脚的输出频率;地址0EH 和0FH 分别用于定时器控制寄存器和定时器寄存器。
如何使用该模块实现时钟显示?
首先需要等待器件初始化8ms,然后我们需要对该模块中写入一个初始时间,然后该模块就会自动向后计时。因此整体的控制模块是先写一次然后循环进行读。
该模块通过IIC协议与zynq进行通信,其在开发板上的器件地址是1010_001。因此我们需要在iic第一个周期选择该器件,第二个周期开始进行写数据,
其中
秒寄存器地址为02h,其7位表示是否准确1是不准确 0是准确,0-6这七位用BCD码表示秒数
分寄存器地址为03h,其7位无效,0-6位用BCD码表示分钟时间
时寄存器地址位04h,6-7位无效,0-5位用BCD码表示小时
天寄存器地址为05h,6-7位无效,0-5位用BCD码表示天 (注意没有06h)
月寄存器地址为07h,7位0表示当前世纪,1表示下个世纪,5-6位没用,0-4位BCD表示月份
年寄存器地址为08h,0-7位用BCD码表示年
二、系统框图
iic_dri是eeprom实验中的iic驱动模块,但是要记得把器件地址改为1010001
lcd_rgb_char也是之前lcd彩条显示实验的代码,但需要修改其中display模块以及顶层调用模块
pcf8565_ctrl模块是对pcf8563功能进行封装的模块
三、模块代码
3.1pcf8565模块
module pcf8563_ctrl #(
// 初始时间设置,从高到低为年到秒,各占8bit
parameter TIME_INIT = 48'h23_11_6_22_00_00)(
input clk , //时钟信号
input rst_n , //复位信号
//i2c interface
output reg i2c_rh_wl , //I2C读写控制信号
output reg i2c_exec , //I2C触发执行信号
output reg [15:0] i2c_addr , //I2C器件内地址
output reg [7:0] i2c_data_w, //I2C要写的数据
input [7:0] i2c_data_r, //I2C读出的数据
input i2c_done , //I2C一次操作完成
//PCF8563T的秒、分、时、日、月、年数据
output reg [7:0] sec, //秒
output reg [7:0] min, //分
output reg [7:0] hour, //时
output reg [7:0] day, //日
output reg [7:0] mon, //月
output reg [7:0] year //年
);
//parameter define
localparam WAIT_TIME = 13'd8_000;
//reg define
reg [3:0] flow_cnt ; // 转态流控制
reg [12:0] wait_cnt ; // 计数等待
//*****************************************************
//** main code
//*****************************************************
//先向PCF8563中写入初始化日期和时间,再从中读出日期和时间
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
sec <= 8'h0;
min <= 8'h0;
hour <= 8'h0;
day <= 8'h0;
mon <= 8'h0;
year <= 8'h0;
i2c_exec <= 1'b0;
i2c_rh_wl <= 1'b0;
i2c_addr <= 8'd0;
i2c_data_w <= 8'd0;
flow_cnt <= 4'd0;
wait_cnt <= 13'd0;
end
else begin
i2c_exec <= 1'b0;
case(flow_cnt)
//上电初始化
4'd0: begin
if(wait_cnt == (WAIT_TIME - 13'd1)) begin
wait_cnt<= 13'd0;
flow_cnt<= flow_cnt + 4'b1;
end
else
wait_cnt<= wait_cnt + 13'b1;
end
//写读秒
4'd1: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h02;
flow_cnt <= flow_cnt + 4'b1;
i2c_data_w<= TIME_INIT[7:0];
end
4'd2: begin
if(i2c_done == 1'b1) begin
sec <= i2c_data_r[6:0];
flow_cnt<= flow_cnt + 4'b1;
end
end
//写读分
4'd3: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h03;
flow_cnt <= flow_cnt + 4'b1;
i2c_data_w<= TIME_INIT[15:8];
end
4'd4: begin
if(i2c_done == 1'b1) begin
min <= i2c_data_r[6:0];
flow_cnt<= flow_cnt + 4'b1;
end
end
//写读时
4'd5: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h04;
flow_cnt <= flow_cnt + 4'b1;
i2c_data_w<= TIME_INIT[23:16];
end
4'd6: begin
if(i2c_done == 1'b1) begin
hour <= i2c_data_r[5:0];
flow_cnt<= flow_cnt + 4'b1;
end
end
//写读天
4'd7: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h05;
flow_cnt <= flow_cnt + 4'b1;
i2c_data_w<= TIME_INIT[31:24];
end
4'd8: begin
if(i2c_done == 1'b1) begin
day <= i2c_data_r[5:0];
flow_cnt<= flow_cnt + 4'b1;
end
end
//写读月
4'd9: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h07;
flow_cnt <= flow_cnt + 4'b1;
i2c_data_w<= TIME_INIT[39:32];
end
4'd10: begin
if(i2c_done == 1'b1) begin
mon <= i2c_data_r[4:0];
flow_cnt<= flow_cnt + 4'b1;
end
end
//写读年
4'd11: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h08;
flow_cnt <= flow_cnt + 4'b1;
i2c_data_w<= TIME_INIT[47:40];
end
4'd12: begin
if(i2c_done == 1'b1) begin
year <= i2c_data_r;
i2c_rh_wl<= 1'b1;
flow_cnt <= 4'd1;
end
end
default: flow_cnt <= 4'd0;
endcase
end
end
endmodule
此模块代码使用了一个状态机,配合读写选择信号,巧妙地实现了第一次写后面几次读的过程
3.2lcd_display模块
module lcd_display(
input lcd_pclk , //LCD像素时钟
input rst_n , //系统复位
//日历数据
input [7:0] sec, //秒
input [7:0] min, //分
input [7:0] hour, //时
input [7:0] day, //日
input [7:0] mon, //月
input [7:0] year, //年
//LCD数据接口
input [10:0] pixel_xpos, //像素点横坐标
input [10:0] pixel_ypos, //像素点纵坐标
output reg [23:0] pixel_data //像素点数据
);
//parameter define
localparam CHAR_POS_X_1 = 11'd1; //第1行字符区域起始点横坐标
localparam CHAR_POS_Y_1 = 11'd1; //第1行字符区域起始点纵坐标
localparam CHAR_POS_X_2 = 11'd17; //第2行字符区域起始点横坐标
localparam CHAR_POS_Y_2 = 11'd17; //第2行字符区域起始点纵坐标
localparam CHAR_WIDTH_1 = 11'd80; //第1行字符区域的宽度,第1行共10个字符(加空格)
localparam CHAR_WIDTH_2 = 11'd64; //第2行字符区域的宽度,第2行共8个字符(加空格)
localparam CHAR_HEIGHT = 11'd16; //单个字符的高度
localparam WHITE = 24'hffffff; //背景色,白色
localparam BLACK = 24'h000000; //字符颜色,黑色
//reg define
reg [127:0] char [9:0] ; //字符数组
//*****************************************************
//** main code
//*****************************************************
//字符数组初始值,用于存储字模数据(由取模软件生成,单个数字字体大小:16*8)
always @(posedge lcd_pclk ) begin
char[0] <= 128'h00000018244242424242424224180000 ; // "0"
char[1] <= 128'h000000107010101010101010107C0000 ; // "1"
char[2] <= 128'h0000003C4242420404081020427E0000 ; // "2"
char[3] <= 128'h0000003C424204180402024244380000 ; // "3"
char[4] <= 128'h000000040C14242444447E04041E0000 ; // "4"
char[5] <= 128'h0000007E404040586402024244380000 ; // "5"
char[6] <= 128'h0000001C244040586442424224180000 ; // "6"
char[7] <= 128'h0000007E444408081010101010100000 ; // "7"
char[8] <= 128'h0000003C4242422418244242423C0000 ; // "8"
char[9] <= 128'h0000001824424242261A020224380000 ; // "9"
end
//不同的区域绘制不同的像素数据
always @(posedge lcd_pclk or negedge rst_n ) begin
if (!rst_n) begin
pixel_data <= BLACK;
end
//在第一行显示年的千位 固定值"2"
else if((pixel_xpos >= CHAR_POS_X_1 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 1 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
if(char[2][(CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
pixel_data <= BLACK; //显示字符为黑色
else
pixel_data <= WHITE; //显示字符区域背景为白色
end
//在第一行显示年的百位 固定值"0"
else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 *1 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 *2 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
if(char[0][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第一行显示年的十位
else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 2 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 3 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
if(char[year[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第一行显示年的个位
else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 3 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 4 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
if(char[year[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第一行显示空格
//在第一行显示月的十位
else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 5 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 6 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
if(char[mon[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) -11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第一行显示月的个位
else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 6 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 7 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
if(char[mon[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第一行显示空格
//在第一行显示日的十位
else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 8 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 9 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT) ) begin
if(char[day[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) -11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第一行显示日的个位
else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 9 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_1)
&& (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
if(char[day[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) -11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第二行显示时的十位
else if((pixel_xpos >= CHAR_POS_X_2 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 1 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_2)
&& (pixel_ypos < CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
if(char[hour[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第二行显示时的个位
else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 1 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 2 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_2)
&& (pixel_ypos < CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
if(char[hour[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第二行显示分的十位
else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 3 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 4 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_2)
&& (pixel_ypos < CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
if(char[min[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) - 11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第二行显示分的个位
else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 4 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 5 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_2)
&& (pixel_ypos < CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
if(char[min[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第二行显示秒的十位
else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 6 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 7 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_2)
&& (pixel_ypos < CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
if(char[sec[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_2-1'b1))%8) - 11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
//在第二行显示秒的个位
else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 7 - 1'b1)
&& (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 - 1'b1)
&& (pixel_ypos >= CHAR_POS_Y_2)
&& (pixel_ypos < CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
if(char[sec[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
- ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
pixel_data <= BLACK;
else
pixel_data <= WHITE;
end
else
pixel_data <= WHITE; //屏幕背景为白色
end
endmodule