1. PCF8563 introduction
The data generation of the real-time clock is completed through the PCF8563 module. PCF8563 has alarm function, timer function, time
Clock output function and interrupt output function.
Memory addresses 00H and 01H are used as control registers and status registers (CONTROL_STATUS); memory addresses 02H~08H are used as TIME timers (seconds~year timers); addresses 09H~0CH are used for alarm (ALARM) registers (define alarm conditions) ;Address 0DH controls the output frequency of CLKOUT pin; addresses 0EH and 0FH are used for timer control register and timer register respectively.
How to use this module to implement clock display?
First we need to wait for 8ms for the device to initialize, and then we need to write an initial time into the module, and then the module will automatically time backwards. Therefore, the overall control module is written once and then read in a loop.
This module communicates with zynq through the IIC protocol, and its device address on the development board is 1010_001. Therefore, we need to select the device in the first cycle of iic, and start writing data in the second cycle.
in
The second register address is 02h, and its 7 bits indicate whether it is accurate. 1 is inaccurate, 0 is accurate, and the seven bits 0-6 use BCD code to represent the seconds.
The sub-register address is 03h, its 7 bits are invalid, and bits 0-6 use BCD code to represent the minute time.
Hour register address bit 04h, 6-7 bits are invalid, 0-5 bits use BCD code to represent the hour
The day register address is 05h, 6-7 digits are invalid, 0-5 digits use BCD code to represent the day (note that there is no 06h)
The month register address is 07h, 7 bits 0 represents the current century, 1 represents the next century, 5-6 bits are useless, 0-4 BCD bits represent the month
The year register address is 08h, and digits 0-7 represent the year in BCD code.
2. System block diagram
iic_dri is the iic driver module in the eeprom experiment, but remember to change the device address to 1010001
lcd_rgb_char is also the code for the previous LCD color bar display experiment, but the display module and top-level calling module need to be modified.
The pcf8565_ctrl module is a module that encapsulates the pcf8563 function.
3. Module code
3.1pcf8565 module
module pcf8563_ctrl #( // Initial time setting, from high to low, years to seconds, each occupying 8 bits parameter TIME_INIT = 48'h23_11_6_22_00_00)( input clk, //clock signal input rst_n , //reset signal //i2c interface output reg i2c_rh_wl , //I2C read and write control signal output reg i2c_exec, //I2C triggers execution signal output reg [15:0] i2c_addr , //I2C device internal address output reg [7:0] i2c_data_w, //data to be written by I2C input [7:0] i2c_data_r, //data read by I2C input i2c_done, //I2C operation completed once //PCF8563T second, minute, hour, day, month, year data output reg [7:0] sec, //seconds output reg [7:0] min, //minutes output reg [7:0] hour, //hour output reg [7:0] day, //day output reg [7:0] mon, //month output reg [7:0] year //year ); //parameter define localparam WAIT_TIME = 13'd8_000; //reg define reg [3:0] flow_cnt; // Transition flow control reg [12:0] wait_cnt; // count wait //************************************************ ***** //** main code //************************************************ ***** //First write the initialization date and time to PCF8563, and then read the date and time from it 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) //Power-on initialization 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 //Write and read seconds 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 //Write and read points 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 //When writing and reading 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 //Write and read days 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 //Write and read month 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 //Write and read year 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
This module code uses a state machine, combined with the read and write selection signals, to cleverly implement the process of writing for the first time and reading several times afterward.
3.2lcd_display module
module lcd_display( input lcd_pclk, //LCD pixel clock input rst_n , //system reset //calendar data input [7:0] sec, //seconds input [7:0] min, //minutes input [7:0] hour, //hour input [7:0] day, //day input [7:0] mon, //month input [7:0] year, //year //LCD data interface input [10:0] pixel_xpos, //pixel abscissa input [10:0] pixel_ypos, //pixel vertical coordinate output reg [23:0] pixel_data //pixel data ); //parameter define localparam CHAR_POS_X_1 = 11'd1; //The abscissa of the starting point of the character area in line 1 localparam CHAR_POS_Y_1 = 11'd1; //The ordinate of the starting point of the character area in line 1 localparam CHAR_POS_X_2 = 11'd17; //The abscissa of the starting point of the character area in line 2 localparam CHAR_POS_Y_2 = 11'd17; //The ordinate of the starting point of the character area in line 2 localparam CHAR_WIDTH_1 = 11'd80; //The width of the character area in the first line, a total of 10 characters in the first line (plus spaces) localparam CHAR_WIDTH_2 = 11'd64; //The width of the character area in the second line, a total of 8 characters in the second line (plus spaces) localparam CHAR_HEIGHT = 11'd16; //The height of a single character localparam WHITE = 24'hffffff; //Background color, white localparam BLACK = 24'h000000; //Character color, black //reg define reg [127:0] char [9:0] ; //Character array //************************************************ ***** //** main code //************************************************ ***** //Initial value of character array, used to store font model data (generated by modulo software, single number font size: 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 //Draw different pixel data in different areas always @(posedge lcd_pclk or negedge rst_n ) begin if (!rst_n) begin pixel_data <= BLACK; end //Display the thousands digit of the year in the first line, fixed value "2" else if((pixel_xpos >= CHAR_POS_X_1 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 1 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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; //Display characters in black else pixel_data <= WHITE; //The background of the display character area is white end //Display the hundred's digit of the year in the first line, fixed value "0" else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 *1 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 *2 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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 //Display the tenth digit of the year on the first line else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 2 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 3 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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 //Display the ones digit of the year in the first line else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 3 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 4 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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 //Display spaces on the first line //Display the tenth digit of the month on the first line else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 5 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 6 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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 //Display the ones digit of the month in the first line else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 6 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 7 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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 //Display spaces on the first line //Display the tenth digit of the day on the first line else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 8 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 9 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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 //Display the ones digit of the day on the first line else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 9 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_1) & amp; & amp; (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 //The tens digit when displayed on the second line else if((pixel_xpos >= CHAR_POS_X_2 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 1 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_2) & amp; & amp; (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 //The ones digit when displayed on the second line else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 1 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 2 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_2) & amp; & amp; (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 //Display the tens digit of the minute on the second line else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 3 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 4 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_2) & amp; & amp; (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 //Display the units digit of the minute on the second line else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 4 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 5 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_2) & amp; & amp; (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 //Display the tens digit of the second on the second line else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 6 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 7 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_2) & amp; & amp; (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 //Display the units digit of seconds on the second line else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 7 - 1'b1) & amp; & amp; (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2 - 1'b1) & amp; & amp; (pixel_ypos >= CHAR_POS_Y_2) & amp; & amp; (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; //The screen background is white end endmodule