本帖最后由 正点原子运营 于 2023-12-5 16:45 编辑
第三十二章 RTC实时时钟LCD显示实验 1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
3)购买链接:https://detail.tmall.com/item_o.htm?id=692712955836
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)FPGA技术交流QQ群:435699340
我们在“RTC实时时钟数码管显示实验”中成功的在数码管上显示了日期和时间。本章我们在“RTC实时时钟数码管显示实验”的基础上稍作修改,在LCD屏上显示日期和时间。 本章包括以下几个部分: 1.1简介 1.2实验任务 1.3硬件设计 1.4程序设计 1.5下载验证
1.1 简介我们在“RTC实时时钟数码管显示实验”中对PCF8563实时时钟芯片作了详细的介绍,如果大家对这部分内容不是很熟悉的话,请参考“RTC实时时钟数码管显示实验”中的简介部分。
1.2 实验任务本节的实验任务是通过ATK-DFPGL22G开发板上的PCF8563实时时钟芯片,在RGB LCD液晶屏上来显示时间。
1.3 硬件设计PCF8563部分的硬件设计原理在“RTC实时时钟数码管实验”中已作了详细的介绍,请参考“RTC实时时钟数码管实验”中的硬件设计部分。
1.4 程序设计根据实验任务,我们可以大致规划出系统的控制流程:首先通过I2C总线向PCF8563写入初始日期值(年月日)和时间值(时分秒),然后不断地读取日期和时间数据,并将读到的时间数据显示到LCD上。由此画出系统的功能框图如下图所示: 图 32.4.1 PCF8563T实时时钟LCD显示系统框图 由系统框图可知,FPGA顶层模块例化了以下三个模块、分别是IIC驱动模块(i2c_dri)、PCF8563控制模块(pcf8563_ctrl)以及LCD字符显示模块(lcd_disp_char)。 各模块端口及信号连接如图 32.4.2所示: PCF8563控制模块通过调用IIC驱动模块来实现对PCF8563实时时钟数据的配置和读取;并将读到的值(年、月、日、时、分和秒)输出给LCD字符显示模块来显示。 顶层模块主要完成对其余模块的例化,其中I2C驱动模块(i2c_dri)程序与“EEPROM读写实验”章节中的IIC驱动模块(i2c_dri)程序完全相同,有关IIC驱动模块的详细介绍请大家参考“EEPROM读写实验”。pcf8563控制模块(pcf8563_ctrl)与“RTC实时时钟数码管显示实验”章节中的pcf8563控制模块完全相同,此处不再赘述。 LCD字符显示模块(lcd_disp_char)的代码由“RGB-LCD字符和图片显示”实验的代码修改而来,除lcd_disp_char顶层模块外,唯一不同的地方在LCD显示模块。 LCD显示模块的代码如下所示: - 1 module lcd_display(
- 2 input lcd_pclk ,
- 3 input rst_n ,
- 4
- 5 //日历数据
- 6 input [7:0 sec, //秒
- 7 input [7:0 min, //分
- 8 input [7:0 hour, //时
- 9 input [7:0 day, //日
- 10 input [7:0 mon, //月
- 11 input [7:0 year, //年
- 12
- 13 //LCD数据接口
- 14 input [10:0 pixel_xpos, //像素点横坐标
- 15 input [10:0 pixel_ypos, //像素点纵坐标
- 16 output reg [15:0 pixel_data //像素点数据
- 17 );
- 18
- 19 //parameter define
- 20 localparam CHAR_POS_X_1 = 11'd1; //第1行字符区域起始点横坐标
- 21 localparam CHAR_POS_Y_1 = 11'd1; //第1行字符区域起始点纵坐标
- 22 localparam CHAR_POS_X_2 = 11'd17; //第2行字符区域起始点横坐标
- 23 localparam CHAR_POS_Y_2 = 11'd17; //第2行字符区域起始点纵坐标
- 24 localparam CHAR_WIDTH_1 = 11'd80; //第1行字符区域的宽度,第1行共10个字符(加空格)
- 25 localparam CHAR_WIDTH_2 = 11'd64; //第2行字符区域的宽度,第2行共8个字符(加空格)
- 26 localparam CHAR_HEIGHT =11'd16; //单个字符的高度
- 27 localparam WHITE = 16'hffff; //背景色,白色
- 28 localparam BLACK = 16'h0000; //字符颜色,黑色
- 29
- 30 //reg define
- 31 reg [127:0 char [9:0 ; //字符数组
- 32
- 33 //*****************************************************
- 34 //** main code
- 35 //*****************************************************
- 36
- 37 //字符数组初始值,用于存储字模数据(由取模软件生成,单个数字字体大小:16*8)
- 38 always @(posedge lcd_pclk ) begin
- 39 char[0 <= 128'h00000018244242424242424224180000 ; // "0"
- 40 char[1 <= 128'h000000107010101010101010107C0000 ; // "1"
- 41 char[2 <= 128'h0000003C4242420404081020427E0000 ; // "2"
- 42 char[3 <= 128'h0000003C424204180402024244380000 ; // "3"
- 43 char[4 <= 128'h000000040C14242444447E04041E0000 ; // "4"
- 44 char[5 <= 128'h0000007E404040586402024244380000 ; // "5"
- 45 char[6 <= 128'h0000001C244040586442424224180000 ; // "6"
- 46 char[7 <= 128'h0000007E444408081010101010100000 ; // "7"
- 47 char[8 <= 128'h0000003C4242422418244242423C0000 ; // "8"
- 48 char[9 <= 128'h0000001824424242261A020224380000 ; // "9"
- 49 end
- 50
- 51 //不同的区域绘制不同的像素数据
- 52 always @(posedge lcd_pclk or negedge rst_n ) begin
- 53 if (!rst_n) begin
- 54 pixel_data <= BLACK;
- 55 end
- 56
- 57 //在第一行显示年的千位 固定值"2"
- 58 else if( (pixel_xpos >= CHAR_POS_X_1)
- 59 && (pixel_xpos < CHAR_POS_X_1 + CHAR_WIDTH_1/10*1)
- 60 && (pixel_ypos >= CHAR_POS_Y_1)
- 61 && (pixel_ypos < CHAR_POS_Y_1 + CHAR_HEIGHT) ) begin
- 62 if(char [2 [ (CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8
- 63 - (pixel_xpos-CHAR_POS_X_1) -1 )
- 64 pixel_data <= BLACK; //显示字符为黑色
- 65 else
- 66 pixel_data <= WHITE; //显示字符区域背景为白色
- 67 end
复制代码由于代码较长,此处省略部分代码。 - 245 //在第二行显示秒的个位
- 246 else if( (pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2/8*7)
- 247 && (pixel_xpos < CHAR_POS_X_2 + CHAR_WIDTH_2)
- 248 && (pixel_ypos >= CHAR_POS_Y_2)
- 249 && (pixel_ypos < CHAR_POS_Y_2 + CHAR_HEIGHT) ) begin
- 250 if(char [sec[3:0 [ (CHAR_HEIGHT+CHAR_POS_Y_2 - pixel_ypos)*8
- 251 - (pixel_xpos-(CHAR_POS_X_2 + CHAR_WIDTH_2/8*7)) -1 ])
- 252 pixel_data <= BLACK;
- 253 else
- 254 pixel_data <= WHITE;
- 255 end
- 256
- 257 else begin
- 258 pixel_data <= WHITE; //屏幕背景为白色
- 259 end
- 260 end
- 261
- 262 endmodule
复制代码我们显示的内容首先分成两行,第一行显示年月日,第二行显示时分秒。程序中第19至28行代码定义了一些参数,前4个参数定义每一行字符显示的参考点,结合具体参数值我们知道:第一行是(1,1),第二行是(17,17),接着后面两个参数分别定义了每一行各自显示的宽度(长度),分别是80和64,最后两个参数定义了字符的颜色和背景色,字符颜色为黑色,背景色为白色。 代码第31行定义了一个元素个数为10,每个元素的位宽为128位的数组(char),这10个元素分别对应阿拉伯数字0~9的字模数据,每一个字模(数字)所占的像素大小是长16个像素,宽8个像素,共16*8=128位像素数据。这里将一个数字的字模数据存放在了数组的一个元素中,因此数组元素的位宽是128位。 在代码的第38到49行分别为数组中的每一个元素进行赋值,将从字模软件中得到的字模数据(0~9)赋值给数组中的每一个元素,其中一个数字的字模数据共需要128个像素点,对应,每一个元素的128位。这里以数字0产生的字模数据为例,示意图如下图所示: 由上图可知,实际上我们把X方向和Y方向的的所有数据都放在了一行上,使用时可以把它看成一个二维数组,大小为16*8bit,即16行,每一行有8位数据。 代码第57到67行是一个具体的字符显示的逻辑。首先判断当前像素坐标的位置,如代码第58到61行,如果处在字符显示的区域则开始根据字符数组值来显示像素。显示时,数组参数pixel_xpos,pixel_ypos分别从小到大取不同的值时,代入数组,此时我们实际上就是在从左到右,从上到下扫描一个字符像素平面,pixel_xpos变化对于行扫描,pixel_ypos则对于列扫描。 对于62行的代码“(CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos)*8”,我们不难理解“*8”的由来,因为在查找数组元素的时候,pixel_ypos的每次变化代表换到下一行扫描,一行跨过8个数据,所有乘以8。这里总结一下:字符数组一行的128个数据从高位到低位,每8位代表另一行,分别对应点阵中该行从左向右的每一个像素点。 62行到63行是对数组的每个元素分别赋值,具体是数组元素为1的点赋值为黑色,否则为白色。 往下,每一个字符显示逻辑的分析和上面类似,请大家自行分析。 程序中第37至49行代码初始化字符数组的值,即数字“0”~“9”的字模数据,由取模软件生成,先将软件设置成字符模式,取模软件的设置如下: 这里将点阵设置为16,即一个数字的字符用一行来表示。 生成字模的界面如下: 程序中第51行至260行代码根据输入的日期、时间和字符区域的坐标显示在LCD上,字符颜色为黑色,背景色为白色。
1.5 下载验证 首先将FPC排线一端与RGB LCD模块上的J1接口连接,另一端与ATK-DFPGL22G开发板上的RGB TFTLCD接口连接。然后将下载器一端连电脑,另一端与开发板上的JTAG端口连接,最后连接电源线并打开电源开关。 接下来我们下载程序,验证RGB LCD字符和图片显示的功能。下载完成后观察RGB LCD液晶屏上显示出日期和时间,并且时间在不断的计时,如下图所示,说明RGB TFT-LCD字符和图片显示程序下载验证成功。
|