【GD32450I-EVAL】+ 04液晶屏层叠显示与透明度调整测试
本帖最后由 DDZZ669 于 2020-9-20 15:44 编辑<p>上篇 "<a href="https://bbs.eeworld.com.cn/thread-1141611-1-1.html" target="_blank"> 【GD32450I-EVAL】+ 03库函数基础使用方法-以按键中断为例</a>" 介绍了GD32库开发的基础使用方法,本篇来研究一下液晶显示屏。</p>
<p></p>
<p> </p>
<p> </p>
<p><strong><span style="font-size:26px;">1 RGB液晶屏</span></strong></p>
<p cid="n4" mdtype="paragraph"> </p>
<p cid="n4" mdtype="paragraph">GD32450I开发板上带了一块4.3英寸的480x272的RGB接口显示屏,类型为TFT-LCD(Thin Film Transistor-Liquid Crystal Display),即薄膜晶体管液晶显示器。</p>
<p cid="n5" mdtype="paragraph">之前只用过MCU接口的屏幕,这里简单说一下区别:</p>
<ul>
<li cid="n6" mdtype="paragraph"><strong>MCU-LCD</strong>:最初是为单片机(MCU)设计的,因单片机内存较小,把显存内置在LCD模块内部,通过专门的显示命令来更新显存,MCU屏不能做得很大。MCU屏显示图像,显示需要发送画点的命令来修改MCU内部RAM。</li>
<li cid="n7" mdtype="paragraph"><strong>RGB-LCD</strong>:其显存是由系统内存充当,只要系统内存够大,RGB-LCD就可以做出较大尺寸。RGB屏显示图像,只需显存组织好数据,启动显示后,LCD-DMA会自动把显存通过RGB接口送到显示屏,因此RGB屏的刷新速度较快。</li>
</ul>
<p cid="n8" mdtype="paragraph">两种屏的工作方式示意图如下:</p>
<p cid="n8" mdtype="paragraph"></p>
<p cid="n8" mdtype="paragraph"> </p>
<p cid="n8" mdtype="paragraph"><strong>关于屏幕的两种驱动模式:</strong></p>
<p cid="n8" mdtype="paragraph"></p>
<p>RGB屏一般有2种驱动模式:DE模式和SYNC模式(或称HV模式)。DE模式使用DE信号来确定有效数据,而SYNC模式,则需要行同步(Hsync)和场同步(Vsync),来表示扫描的行和列。</p>
<p> </p>
<p><strong>关于屏幕的一些参数:</strong></p>
<p></p>
<p>可以看到,虽然屏幕分辨率是480*272,但水平和垂直的总周期会大于这个数,边缘多出来的像素具体是什么作用可以先不用管,程序中屏幕初始化的时候会用到这些数,先知道如何对应起来即可。</p>
<p cid="n8" mdtype="paragraph"> </p>
<p cid="n8" mdtype="paragraph"> </p>
<p><strong><span style="font-size:26px;">2 GD32的TLI</span></strong></p>
<p> </p>
<p>TLI(TFT-LCD Interface)即液晶屏接口,它是GD32单片机自身提供的一种驱动RGB-LCD的一种控制接口。TLI支持两个独立的显示层,并支持层窗口和层混叠功能。其层叠显示过程如下图所示:</p>
<p></p>
<p>此图中,<strong>层0</strong>和<strong>层1</strong>即两个独立的显示层,另外还有一个<strong>BG层</strong>,即背景层,可以指定显示某种颜色,该层处于最底层。层0叠加在BG层之上,通过调节层0的透明度,可以与BG层融合显示。层1叠加在最上面,也可以调节透明度。这三个层叠加在一起,就是屏幕展现出来的效果。</p>
<p> </p>
<p> </p>
<p><span style="font-size:26px;"><strong>3 图片转RGB565</strong></span></p>
<p> </p>
<p>这款LCD支持多种像素格式,具体如下表,此次测试先使用RGB565格式,即红、绿、蓝分别占用5、6、5位来表示颜色值,所以描述一个彩色像素需要16位,即2个字节,也就是一个字。</p>
<p></p>
<p> </p>
<p><strong>图片转换成RGB565的16进制数据</strong>,可以使用一些小工具来转换,转换后保存成数组。</p>
<p>转换方法如下,先找一张自己需要的图片,然后使用Picture2Hex这个小软件,指定转换后图片的宽度和高度,选择图片转换即可,然后产生一个logo.c文件,将该文件中的数组放到自己的代码工程中使用。</p>
<p></p>
<p> </p>
<p><span style="font-size:26px;"><strong>4 层叠与透明度显示测试</strong></span></p>
<p> </p>
<p>上面介绍了一些基本原理,下面写一个测试程序来看一下层叠显示的效果。</p>
<p> </p>
<p><strong>首先是主函数部分</strong>,就是各种初始化,最后在按键中断中会来改变3个层的颜色或透明度:</p>
<pre>
<code class="language-cpp">int main(void)
{
system_clock_config();
systick_config();
mytimer_init();
key_config();//通过按键来改变3个层的颜色或透明度
lcd_init();//LCD的GPIO等的初始化
lcd_layer_init();//LCD的显示层的初始化
tli_layer_enable(LAYER0);
tli_layer_enable(LAYER1);
tli_enable();//使能两个显示层
//设置两个显示层的透明度
lcd_layer_set(LCD_LAYER_FOREGROUND);
lcd_transparency_set(125);//0~255
lcd_layer_set(LCD_LAYER_BACKGROUND);
lcd_transparency_set(155);//0~255
while (1)
{
//主循环,什么也不做,在按键中断中会只改变透明度
}
}</code></pre>
<p> </p>
<p>初始化部分主要开看LCD的初始化和层的初始化。</p>
<p><strong>先看LCD的初始化</strong>:</p>
<pre>
<code class="language-cpp">void lcd_init(void)
{
//--------此处省去GPIO、时钟等的初始化
/* TLI initialization */
tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;
tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;
tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;
tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI;
/* LCD display timing configuration */
tli_init_struct.synpsz_hpsz = 40;
tli_init_struct.synpsz_vpsz = 9;
tli_init_struct.backpsz_hbpsz = 42;
tli_init_struct.backpsz_vbpsz = 11;
tli_init_struct.activesz_hasz = 522;
tli_init_struct.activesz_vasz = 283;
tli_init_struct.totalsz_htsz = 524;
tli_init_struct.totalsz_vtsz = 285;
/* LCD background color configure*///最底层
tli_init_struct.backcolor_red = 0xFF;
tli_init_struct.backcolor_green = 0xFF;
tli_init_struct.backcolor_blue = 0xFF;
tli_init(&tli_init_struct);
}
</code></pre>
<p>第1部分中,DE参数为Low,所以采用的是SYNC模式。</p>
<p>第2部分中,好多参数,可以先对照上面的屏幕参数表格。</p>
<p>第2部分中,就是叠加层中的“BG层”,即最底层,这里先都给0xFF,即白色。</p>
<p> </p>
<p><strong>再来看层的初始化:</strong></p>
<pre>
<code class="language-cpp">void lcd_layer_init(void)
{
tli_layer_parameter_structtli_layer_init_struct;
/* TLI layer1 configuration */ //layer1 前景层(最上层)
tli_layer_init_struct.layer_window_leftpos = (0 + 43);//最左侧从0开始
tli_layer_init_struct.layer_window_rightpos = (0 + 400 + 43 - 1); //最右侧到400结束
tli_layer_init_struct.layer_window_toppos = (0 + 12);//最上侧从0开始
tli_layer_init_struct.layer_window_bottompos = (0 + 200 + 12 - 1);//最下侧到200结束
tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;//RGB565格式
tli_layer_init_struct.layer_sa = 0x0;//其余部分透明显示
tli_layer_init_struct.layer_default_blue = 0xFF;
tli_layer_init_struct.layer_default_green = 0xFF;
tli_layer_init_struct.layer_default_red = 0xFF;
tli_layer_init_struct.layer_default_alpha = 0x0;//图片部分的透明度,之后可以通过按键修改
tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;//LAYER_ACF1_SA;
tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;//LAYER_ACF2_SA;
tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)image_eeworld;//eeworld图片的RGB565数组
tli_layer_init_struct.layer_frame_line_length = ((LCD_PIXEL_WIDTH * 2) + 3);
tli_layer_init_struct.layer_frame_buf_stride_offset = (LCD_PIXEL_WIDTH * 2);
tli_layer_init_struct.layer_frame_total_line_number = LCD_PIXEL_HEIGHT;
tli_layer_init(LAYER1, &tli_layer_init_struct);
/* TLI layer0 configuration */ //layer0 背景层(中间层)
tli_layer_init_struct.layer_window_leftpos = (80 + 43);//最左侧从80开始
tli_layer_init_struct.layer_window_rightpos = (80 + 400 + 43 - 1);//最右侧到80+400结束
tli_layer_init_struct.layer_window_toppos = (72 + 12);//最上侧从72开始
tli_layer_init_struct.layer_window_bottompos = (72+ 200 + 12 - 1);//最下侧到72+200结束
tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
tli_layer_init_struct.layer_sa = 0x0;//其余部分透明显示
tli_layer_init_struct.layer_default_blue = 0xFF;
tli_layer_init_struct.layer_default_green = 0xFF;
tli_layer_init_struct.layer_default_red = 0xFF;
tli_layer_init_struct.layer_default_alpha = 0x0;
tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)image_gd32;//gd32图片的RGB565数组
tli_layer_init_struct.layer_frame_line_length = ((LCD_PIXEL_WIDTH * 2) + 3);
tli_layer_init_struct.layer_frame_buf_stride_offset = (LCD_PIXEL_WIDTH * 2);
tli_layer_init_struct.layer_frame_total_line_number = LCD_PIXEL_HEIGHT;
tli_layer_init(LAYER0, &tli_layer_init_struct);
tli_reload_config(TLI_REQUEST_RELOAD_EN);
lcd_font_set(&LCD_DEFAULT_FONT);
tli_dither_config(TLI_DITHER_ENABLE);
}
</code></pre>
<p>这里的layer0与layer1即叠加层中的“层0”和“层1”,两个显示层都配置为宽400高200,其余的部分透明显示。</p>
<p> </p>
<p><strong>使用按键来修改BG层的颜色:</strong></p>
<pre>
<code class="language-cpp">void change_backcolor(int n)
{
int i = n%4;
switch(i)
{
case 0:
tli_init_struct.backcolor_red = 0xFF;
tli_init_struct.backcolor_green = 0xFF;
tli_init_struct.backcolor_blue= 0xFF;
break;
case 1:
tli_init_struct.backcolor_red = 0xFF;
tli_init_struct.backcolor_green = 0x00;
tli_init_struct.backcolor_blue= 0x00;
break;
case 2:
tli_init_struct.backcolor_red = 0x00;
tli_init_struct.backcolor_green = 0xFF;
tli_init_struct.backcolor_blue= 0x00;
break;
case 3:
tli_init_struct.backcolor_red = 0x00;
tli_init_struct.backcolor_green = 0x00;
tli_init_struct.backcolor_blue= 0xFF;
break;
}
tli_init(&tli_init_struct);
}
</code></pre>
<p> </p>
<p><strong>使用按键来修改层0和层1的透明度:</strong></p>
<pre>
<code class="language-cpp">void lcd_layer_set(uint32_t layer)
{
if (layer == LCD_LAYER_BACKGROUND)
{
current_framebuffer = (uint32_t)image_gd32;//LCD_FRAME_BUFFER;
current_layer = LCD_LAYER_BACKGROUND;
}else
{
current_framebuffer = (uint32_t)image_eeworld;//LCD_FRAME_BUFFER + BUFFER_OFFSET;
current_layer = LCD_LAYER_FOREGROUND;
}
}
void lcd_transparency_set(uint8_t trans)
{
if (current_layer == LCD_LAYER_BACKGROUND)
{
TLI_LxSA(LAYER0) = trans;
}
else
{
TLI_LxSA(LAYER1) = trans;
}
tli_reload_config(TLI_REQUEST_RELOAD_EN);
}
//改变层0的透明度-------------
void change_layer0_transparency(int n)
{
int i = n%6;//0 1 2 3 4 5
lcd_layer_set(LCD_LAYER_BACKGROUND);
lcd_transparency_set(i*50);
}
//改变层1的透明度-------------
void change_layer1_transparency(int n)
{
int i = n%6;//0 1 2 3 4 5
lcd_layer_set(LCD_LAYER_FOREGROUND);
lcd_transparency_set(i*50);
}
</code></pre>
<p> </p>
<p> </p>
<p><span style="font-size:26px;"><strong>5 下载验证</strong></span></p>
<p> </p>
<p>编译一下程序:</p>
<pre>
<code class="language-bash">compiling picture.c...
linking...
Program Size: Code=4360 RO-data=334236 RW-data=80 ZI-data=1088
FromELF: creating hex file...
".\GD32F4xx-OBJ\USBH-HID.axf" - 0 Error(s), 5 Warning(s).
Build Time Elapsed:00:00:03
</code></pre>
<p>可以看到Code为4360字节,即4K多点,而<strong>RO-data有334236字节</strong>,即326K多,主要是RGB565的数组占用了较大的空间。</p>
<p>这里用到了了2个400x200的RGB565的数组,总大小为400x200x2x2=320000字节,即320K,占据了RO-data的大部分。</p>
<p>关于这几个大小的含义,可参考上篇 "<a href="https://bbs.eeworld.com.cn/thread-1141611-1-1.html" target="_blank">【GD32450I-EVAL】+ 03库函数基础使用方法-以按键中断为例</a>"中 "<strong>程序编译下载</strong>" 处的介绍。</p>
<p> </p>
<p>由于图片占用较大空间,<strong>下载程序时间较长</strong>(约半分钟),之后可以考虑如何先把图片烧写到flash的某个位置,然后程序来读取,这样不用每次都花时间来烧写图片数据。</p>
<p> </p>
<p><strong>演示视频如下</strong>,通过调节各层的透明度,可以清楚的看到各个层的叠加关系:</p>
<p><iframe allowfullscreen="true" border="0" frameborder="no" framespacing="0" height="450px" scrolling="no" src="//player.bilibili.com/player.html?aid=287198624&bvid=BV1ef4y1D7uR&cid=237198373&page=1" width="750px"></iframe><br />
</p>
<p><a href="https://bbs.eeworld.com.cn/thread-1140981-1-1.html" target="_blank">兆易GD32450I-EVAL</a></p>
<p>汇总贴:<a href="https://bbs.eeworld.com.cn/thread-1140981-1-1.html">https://bbs.eeworld.com.cn/thread-1140981-1-1.html</a></p>
<p>LCD驱动IC寄存器不需要初始化?</p>
<p>您好,请问这个的源代码,以及项目工程,可以共享一下吗?谢啦</p>
laoganzheng 发表于 2021-2-1 12:03
LCD驱动IC寄存器不需要初始化?
<p>抱歉,时间有点久了,找不到那个工程了,前段时间也有人私信过我这个代码<img height="51" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/sweat.gif" width="50" /></p>
页:
[1]