正点原子 发表于 2021-10-9 14:53:43

《新起点V2之FPGA开发指南》第二十三章 RGB-LCD字符和图片显示

本帖最后由 正点原子 于 2021-10-30 10:34 编辑

1)实验平台:正点原子新起点V2FPGA开发板
2)章节摘自【正点原子】《新起点之FPGA开发指南 V2.1》
3)购买链接:https://detail.tmall.com/item.htm?id=609758951113
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-328002-1-1.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流QQ群:712557122








第二十三章 RGB-LCD字符和图片显示实验
       我们在“RGB-LCD彩条显示实验”中成功的在正点原子的RGB-LCD液晶屏模块上显示出了彩条。本章我们在“RGB-LCD彩条显示实验”的基础上稍作修改,在LCD上完成“正点原子logo图片”和汉字“正点原子”的显示。
       本章包括以下几个部分:
       1.1简介
       1.2实验任务
       1.3硬件设计
       1.4程序设计
       1.5下载验证

       1.1简介
       我们在“RGB-LCD彩条显示实验”中对正点原子的RGB-LCD液晶屏模块作了详细的介绍,包括数据输入时序、同步方式、以及分辨率等。如果大家对这部分内容不是很熟悉的话,请参考“RGB-LCD彩条显示实验”中的简介部分。
       1.2实验任务
       本节的实验任务是通过新起点开发板上的RGB-LCD接口,在正点原子的RGB-LCD液晶屏的左上角位置显示图片以及4个汉字“正点原子”。其中每个汉字的大小为32*32,图片的大小为100*100。
       1.3硬件设计
       RGB-LCD接口部分的硬件设计原理及本实验中各端口信号的管脚分配,与“RGB-LCD彩条显示实验”完全相同,请参考“RGB-LCD彩条显示实验”中的硬件设计部分。
       1.4程序设计
       下图是根据本章实验任务画出的系统框图。可以看出,其中本次实验的系统框图与“RGB-LCD彩条显示实验”基本一致,我们只需要修改LCD显示模块就可以实现在LCD液晶屏上显示字符和图片的功能。另外,由于图片的像素数据较多,因此我们在LCD显示模块中例化了一个ROM,用来存储图片数据。

图 23.4.1 字符和图片显示实验系统框图
       字符(包括汉字、字母和符号等)的本质都是点阵,在LCD屏幕上体现为字符显示区域内像素点的集合。字符的大小决定了字符显示区域内像素点的数目,而字符的样式(字体、颜色等)则决定了各像素点的颜色值。因此,在显示字符之前,我们需要先指定字符的大小、样式,然后获取该字符的点阵,这个过程我们称之为“提取字模”,或简称“取模”。
       我们一般使用0和1的组合来描述字符的点阵排列:点阵中每个像素点用一位(1 bit)数据来表示,其中用于表征字符的像素点用数字1来表示,其他的像素点作为背景用数字0来表示,如图 23.4.2所示。采用这种方式描述的字符是不包含颜色特征的,只能区分点阵中的字符和背景。

图 23.4.2 汉字“正”及其点阵描述
       字模的提取可通过字符取模软件来实现,在这里我们使用取模软件“PCtoLCD2002”来获取汉字“正点原子”的字模。首先在开发板所随附的资料盘(A盘)中“6_软件资料/1_软件/PCtoLCD2002完美版”目录下找到“PCtoLCD2002”并双击打开,如下图所示:

图 23.4.3 取模软件PCtoLCD2002
       打开之后会发现软件中的字体、字宽和字高都是无法设置的,这个时候点击菜单栏的“模式”,选择“字符模式”,如下图所示。

图 23.4.4 切换到字符模式
       切换到字符模式后,就可以设置字体、字宽和字高了。字宽和字高的值越高,显示在LCD屏上的字符就越大,但是代码也需要做相应的修改。这里将字体选择默认的“宋体”,字宽和字高设置成“32”,然后在下方文本框中输入汉字“正点原子”,如下图所示:

图 23.4.5 字符设置
       由于PCtoLCD2002会给每个字符生成一个独立的字模,如果此时点击文本框右侧的“生成字模”按钮,我们将会得到四个32*32的字模。然而为了方便在LCD上显示,我们将四个汉字看作一个整体,从而获得一个字宽为128,字高为32的“大字模”。为了达到这个目的,我们首先将图 23.4.5中四个汉字的点阵保存为.BMP格式的图片。在菜单栏中点击“文件”并选择“另存为”,在保存界面中指定文件存储路径,并选择保存类型为“BMP图像文件”,然后输入文件名“正点原子_bmp”,最后点击“保存”。本次我们在工程路径下新建一个“doc”文件夹,将生成的BMP图片保存在doc文件夹下。如图 23.4.6和图 23.4.7所示。

图 23.4.6 点击“文件”并另存为图像

图 23.4.7 BMP格式图片保存界面
       我们在“画图”中打开刚刚保存的BMP格式的图片如下所示:

图 23.4.8 保存的BMP格式图片
      接下来我们将取模软件PCtoLCD2002切换至图形模式,在菜单栏中点击“模式”,选择“图形模式”。

图 23.4.9 切换至图形模式
       然后在菜单栏中点击“文件”并选择“打开”,指定图 23.4.7中存放BMP格式图片的路径并打开图片“正点原子_bmp”,图片打开后如下所示。

图 23.4.10 PCtoLCD2002图形模式
       请大家注意比较图 23.4.10与图 23.4.5的差异,在上图中,四个汉字“正点原子”被看作一个整体,而不再是四个独立的字符。实际上,这四个汉字也确实是作为一个整体以BMP图片的形式导入到取模软件中的。
       在生成字模之前,我们需要先设置字模的格式。在菜单栏中点击“选项”,并在弹出的配置界面中按照下图进行配置,配置完成后点击确定。

图 23.4.11 字模格式配置界面
       在配置界面中,当鼠标悬浮在各配置选项上时,软件会自动提示当前配置的含义。需要注意的是图 23.4.11左下角“每行显示数据”是以字节(Byte)为单位的,而一个字节的数据为8个bit,即可以表示一行点阵中的8个像素点。由于图 23.4.10中的点阵每行为128个像素点,所以需要16个Byte的数据来表示一行,因此将“每行显示数据—点阵”处设置为16。
       配置字模选项完成后,点击“生成字模”,即可得到汉字“正点原子”所对应的点阵数据,如下图所示:

图 23.4.12 生成字模
      最后点击保存字模,命名成“正点原子_字模”,可将生成的点阵数据保存在txt格式的文本文档中,如图 23.4.13所示。数据以十六进制显示,每行有16个Byte,对应每行四个汉字共128个像素点;共有32行,对应每个汉字的高度为32。

图 23.4.13 “正点原子”字模
       提取字模完成后,我们需要在LCD显示模块中将获取的点阵数据映射到液晶屏中心32*128个的像素点的字符显示区域,从而实现字符的显示。
       到这里提取字模的过程就已经完成了,接下来我们介绍图片像素数据的获取方法。
       LCD显示模块中的ROM是通过例化IP核来实现的只读存储器,它使用FPGA的片上存储资源,即RAM。由于FPGA的片上存储资源有限,所以ROM中存储的图片大小也受到限制,本次实验采用的图片分辨率为100*100。新起点开发板上的RGB-LCD接口采用RGB565数据格式,即每个像素点的颜色用16bit的数据来表示,因此大小为100*100*16bit = 160000bit=156.25Kbit。我们新起点开发板使用的FPGA芯片的RAM存储容量为414Kbit,能够满足本次实验中的图片存储需求。
       ROM作为只读存储器,在调用IP核时需要指定初始化文件,在这里就是写入存储器中的图片数据,各种格式的图片(bmp、jpg等)都是以MIF文件的形式导入到ROM中的。MIF是一种Quartus工具能识别的文件格式,在文件的开头定义了存储器的位宽和深度、地址格式、数据格式等信息,紧接着列出了存储单元地址以及写入各地址的数据。例如,一个位宽为16,深度为5的MIF文件内容如下图所示:

图 23.4.14 MIF文件格式
       当需要存储的数据量较小时,如果我们知道数据的内容,那么就可以仿照图 23.4.14的格式手动编写MIF文件。但是由于图片的数据量较大,并且我们无法直接看出各个像素点对应的颜色数据,因此需要借助工具来实现图片到MIF文件的转换。在这里我们使用正点原子提供的工具“PicToLCD”来实现这一转换过程,该工具位于开发板所随附的资料中“6_软件资料/1_软件/PicToLCD”目录下。
       我们在Windows自带的“画图”工具中将正点原子的LOGO图片大小调整100*100,并利用工具PicToLCD转换得到MIF文件“ZDYZ.mif”。
       双击运行“PicToLCD.exe”,点击“加载图片”并在弹出的界面中选择需要转换的图片(注意:待转换图片分辨率的大小必须是100*100,否则代码中访问ROM的最大地址需要修改)。图片加载成功后工具会在图片属性中指示出图片的文件名和大小;接下来选择图片转换的数据格式为RGB565;文件类型选择“MIF”;最后点击“一键转换” 按钮,在弹出的界面中选择MIF文件的存放路径并输入文件名。PicToLCD转换过程中的软件界面如图 23.4.15所示:

图 23.4.15 PicToLCD转换界面
      最终转换得到的MIF文件部分截图如下所示:

图 23.4.16 转换得到的MIF文件
      程序中各模块端口及信号连接如下图所示:

图 23.4.17 顶层模块原理图
       图 23.4.17中的顶层模块中的ID读取模块(rd_id)、时钟分频模块(clk_div)以及LCD驱动模块(lcd _driver)均与“RGB-LCD彩条显示实验”完全相同,本次实验只对LCD显示模块(lcd_display)作了修改。因此,这里我们重点讲解lcd_display模块,其他部分大家可以参考“RGB-LCD彩条显示实验”。
       LCD显示模块的代码如下:
1   module lcd_display(
2       input             lcd_pclk,   //时钟
3       input             rst_n,      //复位,低电平有效
4                                       
5       input       pixel_xpos,   //像素点横坐标
6       input       pixel_ypos,   //像素点纵坐标   
7       output reg pixel_data    //像素点数据,
8   );                                 
9                                       
10//parameter define                  
11localparam PIC_X_START = 11'd1;      //图片起始点横坐标
12localparam PIC_Y_START = 11'd1;      //图片起始点纵坐标
13localparam PIC_WIDTH   = 11'd100;    //图片宽度
14localparam PIC_HEIGHT= 11'd100;    //图片高度
15                        
16localparam CHAR_X_START= 11'd1;      //字符起始点横坐标
17localparam CHAR_Y_START= 11'd110;    //字符起始点纵坐标
18localparam CHAR_WIDTH= 11'd128;    //字符宽度,4个字符:32*4
19localparam CHAR_HEIGHT = 11'd32;   //字符高度
20                        
21localparam BACK_COLOR= 16'hE7FF;   //背景色,浅蓝色
22localparam CHAR_COLOR= 16'hF800;   //字符颜色,红色
23
24//reg define
25reg    char;//字符数组
26reg   rom_addr;//ROM地址
27
28//wire define   
29wirex_cnt;       //横坐标计数器
30wirey_cnt;       //纵坐标计数器
31wire          rom_rd_en ;//ROM读使能信号
32wirerom_rd_data ;//ROM数据
33
34//*****************************************************
35//**                  main code
36//*****************************************************
37
38assignx_cnt = pixel_xpos - CHAR_X_START; //像素点相对于字符区域起始点水平坐标
39assigny_cnt = pixel_ypos - CHAR_Y_START; //像素点相对于字符区域起始点垂直坐标
40assignrom_rd_en = 1'b1;                  //读使能拉高,即一直读ROM数据
41
42//给字符数组赋值,显示汉字“正点原子”,每个汉字大小为32*32
43always @(posedge lcd_pclk) begin
44      char<= 128'h00000000000000000000000000000000;
45      char<= 128'h00000000000000000000000000000000;
46      char<= 128'h00000000000100000000002000000000;
47      char<= 128'h000000100001800002000070000000C0;
48      char<= 128'h000000380001800003FFFFF803FFFFE0;
49      char<= 128'h07FFFFFC0001800003006000000001E0;
50      char<= 128'h0000C000000180600300600000000300;
51      char<= 128'h0000C0000001FFF00300C00000000600;
52      char<= 128'h0000C000000180000310804000001800;
53      char<= 128'h0000C00000018000031FFFE000003000;
54      char<= 128'h0000C00000018000031800400001C000;
55      char<= 128'h0000C00000018000031800400001C000;
56      char<= 128'h00C0C000018181800318004000018000;
57      char<= 128'h00C0C00001FFFFC0031FFFC000018010;
58      char<= 128'h00C0C060018001800318004000018038;
59      char<= 128'h00C0FFF001800180031800403FFFFFFC;
60      char<= 128'h00C0C000018001800318004000018000;
61      char<= 128'h00C0C000018001800218004000018000;
62      char<= 128'h00C0C00001800180021FFFC000018000;
63      char<= 128'h00C0C000018001800210304000018000;
64      char<= 128'h00C0C00001FFFF800200300000018000;
65      char<= 128'h00C0C000018001800606300000018000;
66      char<= 128'h00C0C000018001000607370000018000;
67      char<= 128'h00C0C00000000000060E31C000018000;
68      char<= 128'h00C0C000001000400418307000018000;
69      char<= 128'h00C0C000020830600430303800018000;
70      char<= 128'h00C0C010020C18300860301800018000;
71      char<= 128'h00C0C038060E18180883700800018000;
72      char<= 128'h3FFFFFFC0C0618181100F008003F8000;
73      char<= 128'h000000001C0408182000600000070000;
74      char<= 128'h00000000000000000000000000020000;
75      char<= 128'h00000000000000000000000000000000;
76end
77
78//为LCD不同显示区域绘制图片、字符和背景色
79always @(posedge lcd_pclk or negedge rst_n) begin
80      if (!rst_n)
81          pixel_data <= BACK_COLOR;
82      else if( (pixel_xpos >= PIC_X_START) && (pixel_xpos < PIC_X_START + PIC_WIDTH)
83            && (pixel_ypos >= PIC_Y_START) && (pixel_ypos < PIC_Y_START + PIC_HEIGHT) )
84          pixel_data <= rom_rd_data ;//显示图片
85      else if((pixel_xpos >= CHAR_X_START) && (pixel_xpos < CHAR_X_START + CHAR_WIDTH)
86         && (pixel_ypos >= CHAR_Y_START) && (pixel_ypos < CHAR_Y_START + CHAR_HEIGHT)) begin
87          if(char)
88            pixel_data <= CHAR_COLOR;    //显示字符
89          else
90            pixel_data <= BACK_COLOR;    //显示字符区域的背景色
91      end
92      else
93          pixel_data <= BACK_COLOR;      //屏幕背景色
94end
95
96//根据当前扫描点的横纵坐标为ROM地址赋值
97always @(posedge lcd_pclk or negedge rst_n) begin
98      if(!rst_n)
99          rom_addr <= 14'd0;
100   //当横纵坐标位于图片显示区域时,累加ROM地址   
101   else if((pixel_ypos >= PIC_Y_START) && (pixel_ypos < PIC_Y_START + PIC_HEIGHT)
102         && (pixel_xpos >= PIC_X_START) && (pixel_xpos < PIC_X_START + PIC_WIDTH))
103         rom_addr <= rom_addr + 1'b1;
104   //当横纵坐标位于图片区域最后一个像素点时,ROM地址清零   
105   else if((pixel_ypos >= PIC_Y_START + PIC_HEIGHT))
106         rom_addr <= 14'd0;
107 end
108
109 //ROM:存储图片
110 rom_10000x16b u_rom_10000x16b(
111   .address(rom_addr ),
112   .clock    (lcd_pclk ),   
113   .rden   (rom_rd_en),
114   .q      (rom_rd_data)
115 );
116
117 endmodule
       程序中第10行至22行定义了一系列的参数,方便大家修改图片的位置,字符位置和字符颜色等。
       程序中第25行定义了一个大小为32*128bit的二维数组char,用于存储取模得到的点阵数据。二维数组char共32行,每一行有128位数据,在程序的第42至76行完成了对该二维数组的赋值。赋值后数组中每一行数据从高位到低位分别对应点阵中该行从左向右的每一个像素点。
       程序中第78行至94行完成了字符和图片的显示,根据当前像素点扫描的坐标,为pixel_data赋值字符颜色、背景色或者图片数据(从ROM中读出的数据)。屏幕上字符显示区域内的像素点与字符数组char中的点阵数据一一映射。当点阵数据为1时,将像素点颜色赋值为红色,用来显示字符;当点阵数据为0时,将像素点颜色赋值为浅蓝色,用来作为字符显示区域的背景。屏幕上除字符和图片显示区域之外的其他区域内的像素点均赋值为浅蓝色。
       程序中第96行至107行根据当前的扫描坐标为ROM地址赋值。需要说明的是,我们将ROM的读使能信号固定为高电平,即一直读ROM,而ROM中的数据是由ROM地址来进行控制。
       程序中第109行至115行了ROM IP核,在工程中调用IP核时,需要设置ROM位宽为16bit,深度为选择10000(不能低于100*100),如图 23.4.19所示。此外,为了保证从ROM的读使能信号拉高到有效数据输出之间仅存在一个时钟周期的延时,需要取消寄存端口输出,如图 23.4.20红色方框所示。

图 23.4.18 新建ROM IP核

图 23.4.19 配置ROM位宽及深度

图 23.4.20 取消寄存端口输出
       最后,在“Mem Init”页选择前面生成的初始化文件“ZDYZ.mif”,如图 23.4.21所示。注意需要将该MIF文件置于工程目录下,本工程中的MIF文件位于lcd_rgb_char/doc文件夹下。

图 23.4.21 选择存储器初始化文件
       随后一路点击“NEXT”,最后点击“Finish”即可。
       图 23.4.22为LCD显示模块显示图片时SignalTap抓取的波形图。从图中可以看到,ROM读使能信号rden一直为高电平,读地址address依次累加,图中从ROM中读出的数据为16’hffff。

图 23.4.22 LCD显示模块SignalTap波形图
       1.5下载验证
       首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后将FPC排线一端与RGBLCD模块上的J1接口连接,另一端与新起点开发板上的J1接口连接,如图 22.5.1、图 22.5.2所示。连接时,先掀开FPC连接器上的黑色翻盖,将FPC排线蓝色面朝上插入连接器,最后将黑色翻盖压下以固定FPC排线。

图 23.5.1 ATK-7’RGBLCD模块FPC连接器

图 23.5.2 新起点开发板FPC连接器
       接下来连接电源线并打开电源开关,最后我们下载程序,验证RGB TFT-LCD字符和图片显示功能。 下载完成后观察ATK-7’RGBLCD模块显示的图案如图 22.5.3所示,说明RGB TFT-LCD字符和图片显示程序下载验证成功。

图 23.5.3 RGB TFT-LCD字符和图片显示
页: [1]
查看完整版本: 《新起点V2之FPGA开发指南》第二十三章 RGB-LCD字符和图片显示