目录
实验平台
硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器,7寸液晶显示模块
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35
显示器简介
显示器属于计算机的IO设备,即输入输出设备。常见的有CRT显示器、液晶显示器、LED点阵显示器及OLED显示器。液晶显示器,简称LCD(Liquid Crystal Display),相对于上一代CRT显示器(阴极射线管显示器),LCD显示器功耗低、体积小、承载的信息量大。

液晶显示器
LCD液晶屏(Liquid Crystal Display)使用了目前新的全彩显示技术,而且原理简单易懂。基本上,整个液晶显示技术的概念是利用液晶的物理特性:通电时导通,排列变得有秩序,使光线容易通过;不过通电时排列混乱,阻止光线通过。让液晶如闸门般的阻隔或让光线穿过。就技术面而言,液晶面板包含了两片相当精致的无钠玻璃素材,称为Substrates,中间夹着一层液晶。当光束通过这层液晶时,液晶本身会排排站立或扭转呈不规则状,因而阻隔或使光束顺利通过。

注意液晶本身是不发光的,所以需要有一个背光灯提供光源,光线经过一系列处理过程才到输出,所以输出的光线强度是要比光源的强度低很多的, 比较浪费能源(当然,比CRT显示器还是节能多了)。而且这些处理过程会导致显示方向比较窄,也就是它的视角较小,从侧面看屏幕会看不清它的显示内容。 另外,输出的色彩变换时,液晶分子转动也需要消耗一定的时间,导致屏幕的响应速度低。
LED和OLED显示器
LED屏幕: LED屏幕使用了背光源,通常是LED灯珠或荧光管,来照亮屏幕。这种背光照亮液晶屏或其他类型的面板,使其显示内容。因此,LED屏幕实际上是液晶显示器(LCD)和 LED背光的组合。液晶用来调控光的透过与阻挡,而LED背光提供亮度。
LED技术发展了几十年,从LED芯片到灯珠封装再达LED显示屏国内有着完整的产业链,而目技术成熟,产品也非常稳定,从户外应用的产品到室内的小间距产品,有很多种规格。
LED显示屏的一大优势是拼接后没有拼接痕迹,整个屏幕完整度非常好,所以在显示画面时全屏显示效果好,没视觉上的影响,而OLED显示屏则会有拼接缝隙,就像LCD拼接一样。
LED显示屏还有一个优势就是能用在室外显示,因为它支持防水防晒防潮三防的特点,同时亮度也高,即使是阳光直接照在屏幕上仍然可以正常显示。

OLED屏幕: OLED屏幕是自发光的,它的每个像素都是一个小型有机发光二极管。每个像素可以独立发光或关闭,不需要背光源。这意味着OLED屏幕能够实现像素级的精确控制,有更高的对比度和更鲜艳的颜色。
OLED屏幕在显示黑色像素时不会消耗能量,因为黑色像素是关闭的,这使得OLED屏幕在显示暗色场景时更节能。而LED屏幕则会持续消耗能量,因为背光必须保持亮度。
OLED屏幕中的有机材料在长时间内可能会受到损害,导致屏幕出现图像保持或烧屏(burn-in)问题。LED屏幕没有这个问题,更耐用。
在图像显示的细腻度、柔和度、对比度方面,OLED显示屏是很有优势的,它一点甚至比LCD屏还要好。OLED显示屏分辨率高,能达到4K显示,分辨率更高,清晰度也就更好,特别适合近距离情况下的使用,这是它的一大优势。
OLED显示屏的功耗非常低,因为它是自发光,不需要背光源,所以非常节能,而且具有环保的特性,如果需要长时间连续开机运行的话这会非常省电,直接降低使用成本。
由于OLED屏幕纤薄轻巧,更容易弯曲,所以大尺寸曲面屏多为OLED屏幕。

显示器基本参数
(1)像素:像素是组成图像的最基本单元要素,显示器的像素指它成像最小的点,即上文提到的一个基本显示单元。
(2)分辨率:一些嵌入式设备的显示器常常以"行像素值x列像素值"表示屏幕的分辨率。如分辨率800x480表示该显示器的每一行有800个像素点,每一列有480个像素点,也可理解为有800列,480行。
(3)色彩深度:色彩深度指显示器的每个像素点能表示多少种颜色,一般用"位"(bit)来表示。如单色屏的每个像素点能表示亮或灭两种状态(即实际上能显示2种颜色),用1个数据位就可以表示像素点的所有状态,所以它的色彩深度为1bit,其它常见的显示屏色深为16bit(2的16次方,RGB565)、24bit(2的24次方,RGB888)。
(4)点距:指两个相邻像素点之间的距离,它会影响画质的细腻度及观看距
离,相同尺寸的屏幕,若分辨率越高,则点距越小,画质越细腻。如现在有些手机的屏幕分辨率比电脑显示器的还大,这是手机屏幕点距小的原因;LED点阵显示屏的点距一般都比较大,所以适合远距离观看。
液晶控制原理

这个完整的显示屏由液晶显示面板、电容触摸面板以及PCB底板构成。图中的触摸面板带有触摸控制芯片,该芯片处理触摸信号并通过引出的信号线与外部器件通讯, 触摸面板中间是透明的,它贴在液晶面板上面,一起构成屏幕的主体,触摸面板与液晶面板引出的排线连接到PCB底板上,根据实际需要, PCB底板上可能会带有"液晶控制器芯片"。因为控制液晶面板需要比较多的资源, 所以大部分低级微控制器都不能直接控制液晶面板,需要额外配套一个专用液晶控制器来处理显示过程,外部微控制器只要把它希望显示的数据直接交给液晶控制器即可。 而不带液晶控制器的PCB底板 ,只有小部分的电源管理电路,液晶面板的信号线与外部微控制器相连,直接控制。STM32H743系列的芯片不需要额外的液晶控制器, 也就是说它把专用液晶控制器的功能集成到STM32H743芯片内部了,节约了额外的控制器成本。
液晶面板的控制信号

| 信号名称 | 说明 |
|---|---|
| R[7:0] | 红色数据 |
| G[7:0] | 绿色数据 |
| B[7:0] | 蓝色数据 |
| CLK | 像素同步时钟信号 |
| HSYNC | 水平同步信号 |
| VSYNC | 垂直同步信号 |
| DE | 数据使能信号 |
RGB
RGB信号线各有8根,分别用于表示液晶屏一个像素点的红、绿、蓝颜色分量。每条颜色数据线可以有多根,这决定了能显示多少种颜色。
RGB565(16位色) : 这是最常用的配置,尤其在MCU驱动LCD屏中。
R[4:0]: 5根红色数据线
G[5:0]: 6根绿色数据线(人眼对绿色更敏感)
B[4:0]: 5根蓝色数据线
总共 5+6+5 = 16根数据线,可表示 2^16 = 65,536 种颜色。
RGB888(24位真彩色) :
R[7:0]: 8根红色数据线
G[7:0]: 8根绿色数据线
B[7:0]: 8根蓝色数据线
总共 8+8+8 = 24根数据线,可表示 2^24 ≈ 1677万 种颜色。
同步时钟信号CLK
液晶屏与外部使用同步通讯方式,以CLK信号作为同步时钟,在同步时钟的驱动下,每个时钟传输一个像素点数据。
水平同步信号HSYNC
水平同步信号HSYNC(Horizontal Sync)用于表示液晶屏一行像素数据的传输结束, 每传输完成液晶屏的一行像素数据时,HSYNC会发生电平跳变, 如分辨率为800x480的显示屏(800列,480行),传输一帧的图像HSYNC的电平会跳变480次。
垂直同步信号VSYNC
垂直同步信号VSYNC(Vertical Sync)用于表示液晶屏一帧像素数据的传输结束,每传输完成一帧像素数据时,VSYNC会发生电平跳变。 其中"帧"是图像的单位,一幅图像称为一帧,在液晶屏中,一帧指一个完整屏液晶像素点。人们常常用"帧/秒"来表示液晶屏的刷新特性, 即液晶屏每秒可以显示多少帧图像,如液晶屏以60帧/秒的速率运行时,VSYNC每秒钟电平会跳变60次。
数据使能信号DE
数据使能信号DE(Data Enable)用于表示数据的有效性,当DE信号线为高电平时,RGB信号线表示的数据有效。
LCD-TFT显示控制器(LTDC) 简介
LCD-TFT(液晶显示器------薄膜晶体管)显示器控制器提供并行数字 RGB(红色、绿色、 蓝色)以及水平同步、垂直同步、像素时钟和数据使能信号,这些信号直接输出到不同 LCD 和 TFT 面板的接口。STM32H750xx 的 LTDC 主要特性如下:
⚫ 24 位 RGB 并行像素输出;每像素 8 位数据(RGB888)
⚫ 2 个带有专用 FIFO 的显示层(FIFO 深度 64x64 位)
⚫ 支持查色表 (CLUT),每层高达 256 种颜色(256x24 位)
⚫ 可针对不同显示面板编程时序
⚫ 可编程背景色
⚫ 可编程 HSync、VSync 和数据使能(DE)信号的极性
⚫ 每层有多达 8 种颜色格式可供选择:ARGB8888、RGB888、RGB565、ARGB1555、ARGB4444、L8(8 位 Luminance 或 CLUT)、AL44(4 位 alpha+4 位 luminance)和 AL88(8 位 alpha+8位 luminance)
⚫ 每通道的低位采用伪随机抖动输出(红色、绿色、蓝色的抖动宽度为 2 位)
⚫ 使用 alpha 值(每像素或常数)在两层之间灵活混合
⚫ 色键(透明颜色)
⚫ 可编程窗口位置和大小
⚫ 支持薄膜晶体管 (TFT) 彩色显示器
⚫ AXI 主接口支持 16 个双字的突发
⚫ 高达 4 个可编程中断事件
LTDC 控制器主要包含:信号线、图像处理单元、AXI 接口、配置和状态寄存器以及时钟部分,其框图如下所示:

LCD的DE同步模式和HV同步模式
通常,STM32H7都是用SDRAM作为LCD的显存,LTDC控制器会从SDRAM读取数据刷新到LCD显示屏上。刷新模式有DE同步模式和HV同步模式两种。一般大分辨率显示屏用DE同步模式,小分辨率的显示屏用HV同步模式。
DE同步模式
DE模式需要LCD_DE和LCD_CLK信号来控制刷新。比如一个800x480分辨率的裸屏,在DE有效信号的时候(高电平或低电平),就有800个LCD_CLK输出时钟来确认行中800个点。每个时钟有效的时候,从显存读取一次RGB数据。因为存在回扫信号,所以DE是个方波。一个周期的LCD_DE信号,裸屏就扫描一行。扫描480行后,又从第一行扫描开始。这个规律由裸屏的驱动IC所决定的。
HV同步模式
HV模式需要LCD_CLK时钟信号,行同步信号LCD_HSYNC和场同步信号LCD_VSYNC来控制刷新。比如一个480x272分辨率的裸屏,有一个行同步信号LCD_HSYNC产生时(高电平或者低电平脉冲),就有480个LCD_CLK输出时钟来确认行中480个点。每个时钟有效的时候,从显存读取一次RGB数据。再来一个行同步信号LCD_HSYNC产生时(高电平或者低电平脉冲),切换到下一行,继续行同步和时钟输出,扫描272行后,发送一个场同步信号LCD_VSYNC,又重新从第一行扫描开始。
LTDC的时序配置

LTDC的时序控制就是下面几个参数的设置,这几个参数都可以通过寄存器进行配置。
⚫ HSYNC width水平同步宽度设置,以LCD_CLK的像素时钟输出为单位。
⚫ HBP(horizontal back porch period)水平后沿周期设置,以LCD_CLK的像素时钟输出为单位。
⚫ Active width有效宽度设置,以LCD_CLK的像素时钟输出为单位。以480272分辨率为例,Active width = 480。
⚫ HFP(horizontal front porch period)水平前沿周期设置,以LCD_CLK的像素时钟输出为单位。
⚫ VSYNC width垂直同步宽度设置,以LCD_CLK的像素时钟输出为单位。
⚫ VBP(vertical back porch period)垂直后沿周期设置,以LCD_CLK的像素时钟输出为单位。
⚫ Active height有效高度设置,以LCD_CLK的像素时钟输出为单位。以480 272分辨率为例,Active height = 272。
⚫ VFP(vertical front porch period)垂直前沿周期设置,以LCD_CLK的像素时钟输出为单位。
窗口
可为每个层定位和调整大小,各个层必须位于有效显示区域内。
窗口位置和大小通过左上和右下的 X/Y 位置以及包含同步、后沿大小和有效数据区域的内部时序发生器配置。
可编程层位置和大小定义了一行中的第一个/最后一个可见像素和窗口中的第一个/最后一个可见行。它允许显示完整的图像帧,也允许只显示图像帧的一部分。
⚫层中的第一个和最后一个可见像素通过配置 LTDC_LxWHPCR 寄存器中WHSTPOS[11:0] 和WHSPPOS[11:0] 进行设置。
⚫层中的第一个和最后一个可见行通过配置 LTDC_LxWVPCR 寄存器中的 WVSTPOS[10:0] 和 WVSPPOS[10:0] 进行设置。

LTDC层混合
LTDC除了图层1和图层2两个硬件图层以外,还有一个背景层。由于背景层的刷新不需要显存空间,所以可以用这个图层验证LTDC时序配置是否有问题。

背景层仅支持单色设置,固定颜色格式RGB888(LTDC_HandleTypeDef hltdc)我们这里将背景设置为白色:
hltdc.Init.Backcolor.Blue = 255
hltdc.Init.Backcolor.Green = 255
hltdc.Init.Backcolor.Red = 255
对于图层1和图层2来说,支持如下8种颜色格式:
⚫ARGB8888
⚫RGB888
⚫ RGB565
⚫ ARGB1555
⚫ ARGB4444
⚫ L8(8 位 Luminance 或 CLUT)
⚫ AL44(4 位 alpha + 4 位 luminance)
⚫ AL88(8 位 alpha + 8 位 luminance)
实现Alpha混合的关键是要有一个变量可以设置各种透明度。对此,STM32H7准备了两个Alpha供使用:
一个是常数Alpha(0x00表示完全透明,0xFF表示完全不透明),所有颜色格式都可以使用。
另一个是像素Alpha,也就是ARGB8888,ARGB1555,ARGB4444等颜色格式的Alpha通道数值,也就是我们为图层每个位置绘制的实际颜色值。
STM32H7的参考手册给出了具体的混合公式:
BC = BF1 x C + BF2 x Cs
混合后的颜色= 混合系数1 x 当前层颜色 + 混合系数2 x 底层混合后的颜色
STM32CubeMX生成工程
我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示。我们来看配置LTDC部分配置如下图所示:


注:本章实验我们使用的是7寸普清屏,分辨率为800*480,如果使用高清屏修改对应参数即可
实验代码
1.主函数
c
int main(void)
{
MPU_Config();
SCB_EnableICache();
SCB_EnableDCache();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_LTDC_Init();
MX_FMC_Init();
MX_SDMMC1_SD_Init();
MX_USART6_UART_Init();
BSP_SDRAM_Init();
uart6.initialize(115200);
uart6.printf("\x0c");
uart6.printf("\033[1;32;40m");
uart6.printf("Hello,I am GT7000!\r\n");
//帧缓冲区地址映射二维数组
for(int i = 0;i < LCD_HEIGHT;i ++)address_sdram[i] = LCD_SDRAM_ADDRESS + (i * LCD_WIDTH) * 2;
LCD_ON; //背光开
demo();
while (1)
{
}
}
2.弹球demo函数
c
void demo(void)
{
int bg = RED; //背景色
int colo[7] = {RED, BLUE, YELLOW, GREEN, 0x7BEF,0x0000,0x03E0}; //颜色列表
int x=35,y=35,xs=1,ys=2; //圆心开始坐标,xy速度分量
int oldx,oldy,co=0,i,j;
int r=30; //圆半径
clear_screen(bg); //设置屏幕背景颜色
draw_circle(x, y, r, RED, 1); //画圆
while(1){
oldx = x;
oldy = y;
x = x + xs;
y = y + ys;
if((x+r)>=LCD_WIDTH || (x-r)<=0){
xs=-1*xs;
x = x + 2*xs;
co++;
if(co==7)co=0;}
if((y+r)>=LCD_HEIGHT || (y-r)<=0){
ys=-1*ys;
y = y + 2*ys;
co++;
if(co==7)co=0;}
draw_circle(x, y, r, colo[co], 1); //画圆
for(j = oldx-r-2;j < oldx+r+2;j ++){
for(i = oldy-r-2;i < oldy+r+2;i ++){
if(j<=0 || j>=LCD_WIDTH || i<=0 || i>=LCD_HEIGHT)continue;
if( (int)(x-j)*(x-j) + (int)(y-i)*(y-i) > (int)r*r ){
*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = bg;
}
}
}
HAL_Delay(10); //延时
}
}
3.LTDC驱动配置
c
void MX_LTDC_Init(void)
{
LTDC_LayerCfgTypeDef pLayerCfg = {0};
hltdc.Instance = LTDC;
hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
hltdc.Init.HorizontalSync = 19;
hltdc.Init.VerticalSync = 2;
hltdc.Init.AccumulatedHBP = 159;
hltdc.Init.AccumulatedVBP = 22;
hltdc.Init.AccumulatedActiveW = 1183;
hltdc.Init.AccumulatedActiveH = 622;
hltdc.Init.TotalWidth = 1343;
hltdc.Init.TotalHeigh = 634;
hltdc.Init.Backcolor.Blue = 255;
hltdc.Init.Backcolor.Green = 255;
hltdc.Init.Backcolor.Red = 255;
if (HAL_LTDC_Init(&hltdc) != HAL_OK)
{
Error_Handler();
}
pLayerCfg.WindowX0 = 0;
pLayerCfg.WindowX1 = 1024;
pLayerCfg.WindowY0 = 0;
pLayerCfg.WindowY1 = 600;
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
pLayerCfg.Alpha = 0xff;
pLayerCfg.Alpha0 = 0;
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
pLayerCfg.FBStartAdress = 0xC0000000;
pLayerCfg.ImageWidth = 1024;
pLayerCfg.ImageHeight = 600;
pLayerCfg.Backcolor.Blue = 255;
pLayerCfg.Backcolor.Green = 255;
pLayerCfg.Backcolor.Red = 255;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
{
Error_Handler();
}
}
4.显示画面相关操作函数
c
#include "mygui.h"
#include "ltdc.h"
//--------------------- Function Prototype ----------------------//
void clear_screen(int color);
int set_pixel(int x, int y, int color);
int fill_rect(int x0, int y0, int x1, int y1,int color);
int draw_circle(int x, int y, int r, int color, int fill);
//--------------------------- Variable --------------------------//
int address_sdram[LCD_HEIGHT] = {0};
//清屏函数
void clear_screen(int color)
{
int i,j;
for(j = 0;j < LCD_WIDTH;j ++){
for(i = 0;i < LCD_HEIGHT;i ++){
*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = color;
}
}
}
//画点函数
int set_pixel(int x, int y, int color) {
if(x<0 || x>LCD_WIDTH || y<0 || y>LCD_HEIGHT)return 0;
*(volatile unsigned short int *) (address_sdram[y] + (x << 1)) = color;
return 0;
}
//边界 修改颜色函数
int fill_rect(int x0, int y0, int x1, int y1,int color) {
int x;
for (; y0 <= y1; y0++) {
for (x = x0; x <= x1; x++) {
set_pixel(x, y0, color);
}
}
return 0;
}
//画圆函数
int draw_circle(int x, int y, int r, int color, int fill) {
int i,j;
if(x<0 || x>LCD_WIDTH || y<0 || y>LCD_HEIGHT)return 0;
for(j = x-r;j < x+r;j ++){
for(i = y-r;i < y+r;i ++){
if(fill == 1){
if( (int)(x-j)*(x-j) + (int)(y-i)*(y-i) <= (int)r*r ){
*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = color;
}
}
else{
if( (x-i)*(x-i) + (y-j)*(y-j) >= (r-2)*(r) && (x-i)*(x-i) + (y-j)*(y-j) <= (r+1)*(r) ){
*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = color;
}
}
}
}
return 0;
}
实验现象
屏幕有一个小球在屏幕中来回弹,触碰到边界时小球颜色改变。
normal video