MCU屏和RGB屏

发布于:2025-04-13 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、MCU屏

MCU屏‌:全称为单片机控制屏(Microcontroller Unit Screen),在显示屏背后集成了单片机控制器,因此,MCU屏里面有专用的驱动芯片。驱动芯片如:ILI9488、ILI9341、SSD1963等。驱动芯片里面自带了显存,CPU只需要把显示数据传给驱动芯片,由驱动芯片把数据保存到显存中,最后再把显存中的数据显示到屏幕上。由于需要通过“中间媒介芯片”才能实现显示,所以动画效果较差。当显示内容的数据量较大时,显示数据更新慢,导致出现卡顿现象,因此,MCU屏更适合做静态图片显示。

市面上卖的那种带有驱动芯片的驱动板和RGB屏做成的显示模块,就是一种MCU屏,也有的MCU屏本身就集成了驱动芯片,就无需驱动板了,但是背光电源还是需要自己设计。

在设计时,MCU屏一般使用8080通讯接口。其通讯时序可以使用STM32普通I/O接口进行模拟,但这样效率太低,其次容易受到中断影响,因此建议使用使用“FSMC接口”实现8080时序。对于STM32少于等于100脚的芯片,由于没有FSMC,就只能使用SPI的方式来控制。

使用“FSMC接口”实现8080时序的原理,就是将LCD看作是“外部数据存储器”中某两个固定的地址单元,可以读写这两个存储区,来实现对LCD执行“写命令”和“读写数据”。

1、LCD模块和CPU之间的接线图:

注意:使用MCU屏,和外部是否扩展了SRAM没有任何关系,因为,专用的驱动芯片有自己RAM作为显示缓存

2、8080接口:

1)、8080接口控制信号线

CS(片选信号):用于选中或取消选中LCD屏,低电平有效。

‌DC(数据/命令信号):用于区分传输的是数据还是命令,高电平表示数据,低电平表示命令。

‌WR(写使能信号)‌:用于将数据的写入LCD,低电平有效。

‌RD(读使能信号)‌:用于从LCD读取数据,低电平有效。

‌RES(复位信号)‌:用于复位LCD,低电平有效。

2)、8080接口数据线

8080接口的数据线通常为8位、9位或16位并行数据线,用于传输数据或命令。具体定义如下:

‌D[0:7]‌:8位数据线,用于传输数据或命令。

‌D‌9:第9位数据线,用于扩展数据传输。

‌D[0:15]:16位数据线,用于高速数据传输。

3、CPU的BANK1

FSMC_NE1表示使用CPU的BANK1的第1区:0x60000000~0x63FFFFFF

FSMC_NE2表示使用CPU的BANK1的第2区:0x64000000~0x67FFFFFF

FSMC_NE3表示使用CPU的BANK1的第3区:0x68000000~0x6BFFFFFF,SRAM使用

FSMC_NE4表示使用CPU的BANK1的第4区:0x6C000000~0x6FFFFFFF,LCD使用

HADDR[27:26]来确定当前使用的是哪个址块;由于使用FSMC_NE4引脚作为LCD的

片选信号,地址位HADDR[27,26]=11,使用CPUBANK1的第4CPUBANK1的第4

区的首地址为0x6C000000; HADDR[25:0]保存的是外部存储器的地址

翻译如下:

在“外部存储器”宽度为16位的情况下,FSMC将在内部使用HADDR[25:1]来生成“外部内存地址FSMC_A[24:0] ”;

无论“外部存储器”的宽度是16位还是8位,FSMC_A[0]都应该连接到“外部存储器地址线A[0]”。

1)、若“外部存储器”的数据线宽为8位,则FSMC将在内部使用HADDR[25:0]来生成“外部内存地址FSMC_A[25:0]”;FSMC的26条地址信号线FSMC_A[25:0]和HADDR[25:0]成一一映射连接,就可以实现最大寻址空间为64MB;

当最高地址线为FSMC_A10时,则最大偏移地址就是0x000007FF;

2)、若“外部存储器”的数据线宽为16位,则FSMC将在内部使用HADDR[25:1]来生成“外部内存地址FSMC_A[24:0]”;因此, SMC_A[24:0]=HADDR[25:1],FSMC的25条地址信号线FSMC_A[24:0]和HADDR[25:1]成一一映射连接,就可以实现最大寻址空间为64MB;

当最高地址线为FSMC_A10,则最大偏移地址就是0x000007FF/2=0x000002FF;

为了能让FSMC_A[24:0]输出0x000002FF,根据SMC_A[24:0]=HADDR[25:1],得到HADDR[25:0]=FSMC_A[24:0]<<1=0x000002FF<<1=0x000007FE;

3)、MCU屏通常使用RGB565 :16根数据线

u16 RGB888_To_RGB565(u32 n888Color)

如果我们使用最大地址(0x6C000000|0x000007FE)作为LCD的命令寄存器和数据寄存器的地址分界线,就可以正确读写LCD的寄存器和数据了。

#define LCD_BASE        ((u32)(0x6C000000 | 0x000007FE))

typedef struct

{

         vu16 LCD_REG; //LCD寄存器,地址为0x6C0007FE,FSMC_A10输出低电平

         vu16 LCD_RAM; //LCD数据,地址为0x6C000800,FSMC_A10输出高电平

}LCD_TypeDef;

#define LCD  ((LCD_TypeDef *) LCD_BASE) //定义LCD数据接口

LCD->LCD_REG就是用来读写寄存器;

LCD->LCD_RAM就是用来读写LCD显示数据。

4、正点原子的MCU屏幕尺寸:

1,ATK-2.8寸 TFTLCD模块

分辨率:240*320,驱动IC:ILI9341,电阻触摸屏,16位并口驱动(在用)

2,ATK-3.5寸 TFTLCD模块

分辨率:320*480,驱动IC:NT35310,电阻触摸屏,16位并口驱动

3,ATK-4.3寸 TFTLCD模块

分辨率:480*800,驱动IC:NT35510,电容触摸屏,16位并口驱动

4,ATK-7寸 TFTLCD模块(V1版本)

分辨率:480*800,驱动IC:CPLD+SDRAM,电容触摸屏,16位并口驱动

5,ATK-7寸 TFTLCD模块(V2版本)

分辨率:480*800,驱动IC:SSD1963,电容触摸屏,8/9/12/16位并口驱动

二、RGB屏

RGB屏的内部没有GRAM,因此,它的分辨率可以轻松做到480*800以上。

注意:如果RGB屏增加了带有“专用驱动芯片的驱动板”,它就变成了LCD模块,此时,就变成了类似MCU屏的功能

1、RGB屏的控制引脚:

HSYNC(水平同步):用于指示一行数据的开始。

VSYNC(垂直同步):用于指示一帧数据的开始。

DE(数据使能):指示当前传输的数据是有效的。

CLK(时钟信号):用于同步数据传输。

2、RGB屏的数据引脚:

RGB888 :24根数据线

RGB565 :16根数据线

u16 RGB888_To_RGB565(u32 n888Color)

{

  u16 n565Color = 0;

  u8 cRed = (n888Color & 0x00ff0000) >> 19;   //得到R[7:3]

  u8 cGreen = (n888Color & 0x0000ff00) >> 10; //得到G[7:2]

  u8 cBlue = (n888Color & 0x000000ff) >> 3;   //得到G[7:3]

  n565Color = (cRed << 11) + (cGreen << 5) + (cBlue << 0);

  return n565Color;

}

u32 RGB565_To_RGB888(u16 n565Color)

{

u32 n888Color = 0;

u8 cRed = (n565Color & 0xf800) >> 8;   //先截取R[7:3],右移8,最低3位默认为0

u8 cGreen = (n565Color & 0x07e0) >> 3; //先截取G[7:2] ,右移3,最低2位默认为0

u8 cBlue = (n565Color & 0x001f) << 3;  //先截取G[7:3] ,左移3,最低3位默认为0

n888Color = (cRed << 16) + (cGreen << 8) + (cBlue << 0);

return n888Color;

}

RGB666:18根数据线

3、RGB屏的背光控制引脚:用来控制LCD背光灯的亮灭。

4、RGB屏的功能选择引脚:

有些屏是通过引脚来配置其工作模式。如IM0~IM3,通过外接上下拉电阻,可以选择RGB模式,还是MIPI模式,以及SPI工作模式。

5、RGB屏的通讯配置引脚:

有些屏需要使用IIC接口或者SPI接口去配置屏幕信息。

HSPW(Horizontal Sync Pulse Width):水平同步脉冲宽度。它决定了每行扫描的开始时,HSYNC信号保持低电平的时间。

VSPW(Vertical Sync Pulse Width):垂直同步脉冲宽度。它决定了每帧扫描的开始时,VSYNC信号保持低电平的时间。

HBPD(Horizontal Back Porch Duration):水平后沿消隐时间,指的是在水平同步脉冲结束后,实际显示数据开始之前的时间间隔。它用于给显示器提供时间来准备下一行的显示数据。

HFPD(Horizontal Front Porch Duration):水平前沿消隐时间,指的是在一行显示数据结束后,水平同步脉冲开始之前的时间间隔。它用于给显示器提供时间来完成当前行的处理。

VBPD(Vertical Back Porch Duration):垂直后沿消隐时间,指的是在垂直同步脉冲结束后,实际显示数据开始之前的时间间隔。它用于给显示器提供时间来准备下一帧的显示数据。

VFPD(Vertical Front Porch Duration):垂直前沿消隐时间,指的是在一帧显示数据结束后,垂直同步脉冲开始之前的时间间隔。它用于给显示器提供时间来完成当前帧的处理。

这些参数共同定义了显示器的时序特性,定义了一部分延时间隔,确保图像数据可以被正确的接受和处理,以此在屏幕上准确的展示出图像。

RGB接口方式适合用于视频和动画的显示。这是因为RGB接口方式的数据写入速度更快,能够实时更新屏幕内容,动画效果好。

由于RGB屏的内部没有专用的驱动芯片,因此需要CPULTDC控制器,显示数据位于MCU外部的SDRAM如果CPU内部的SRAM够大的话,就不用扩展SDRAM

LTDC(Layered Display Controller)是一种图形显示的控制器,可直接驱动LCD显示屏。由于LCD显示屏需要大量的数据来显示图像和视频,因此需要一个很大的内存来存储这些数据。SDRAM配置‌通过FMC(Flexible Memory Controller)配置SDRAM。

‌显示内容更新‌:将需要显示的内容存储在SDRAM中,LTDC会自动从SDRAM中读取数据并显示在LCD屏幕上。因此,通过改变SDRAM中的数据,可以更新显示内容‌。

6、SDRAM芯片

W9825G6KH-6是SDRAM芯片,有4个BANK,每个BANK有4M个字,每个字有两个字节。

行地址为A0~A12,列地址为A0~A8,其中A0~A8是多路复用引脚,所以列位数为9,行位数为13。

DQ0~DQ15为多路复用,数据宽度为16位,可以用作输入或输出数据。

BS0和BS1用来选择芯片的BANK区;

CS为芯片选择;

RAS为行地址选通,Row Address strobe;

CAS为列地址选通,Column Address strobe;

CKE为时钟使能信号;

CLK为时钟输入,在时钟上升沿处采集输入数据;

WE为写使能信号;

LDQM和UDQM:在读取周期中,DQM采样为高时,输出为高阻态;在写入周期中,DQM采样为高时,将以零延迟阻止写入操作;

在STM32F429xx中FMC硬件接口

PC0---FMC_SDNWE

PC2---FMC_SDNE0

PC3---FMC_SDCKE0

PD0---FMC_D2

PD1---FMC_D3

PD8---FMC_D13

PD9---FMC_D14

PD10---FMC_D15

PD14---FMC_D0

PD15---FMC_D1

PE0---FMC_NBL0

PE1---FMC_NBL1

PE7---FMC_D4

PE8---FMC_D5

PE9---FMC_D6

PE10---FMC_D7

PE11---FMC_D8

PE12---FMC_D9

PE13---FMC_D10

PE14---FMC_D11

PE15---FMC_D12

PF0---FMC_A0

PF1---FMC_A1

PF2---FMC_A2

PF3---FMC_A3

PF4---FMC_A4

PF5---FMC_A5

PF11---FMC_SDNRAS

PF12---FMC_A6

PF13---FMC_A7

PF14---FMC_A8

PF15---FMC_A9

PG0---FMC_A10

PG1---FMC_A11

PG2---FMC_A12

PG4---FMC_BA0

PG5---FMC_BA1

PG8---FMC_SDCLK

PG15---FMC_SDNCAS

SDRAM.c程序

#include "sdram.h"
#include "delay.h"

/*
W9825G6KH-6是SDRAM芯片,有4个BANK,每个BANK有4M个字,每个字有两个字节
行地址为A0~A12,列地址为A0~A8,其中A0~A8是多路复用引脚,所以列位数为9,行位数为13
DQ0~DQ15为多路复用,数据宽度为16位,可以用作输入或输出数据
BS0和BS1用来选择芯片的BANK区
CS为芯片选择
RAS为行地址选通,Row Address strobe
CAS为列地址选通,Column Address strobe
CKE为时钟使能信号
CLK为时钟输入,在时钟上升沿处采集输入数据
WE为写使能信号
LDQM和UDQM:在读取周期中,DQM采样为高时,输出为高阻态;在写入周期中,DQM采样为高时,将以零延迟阻止写入操作;
*/

SDRAM_HandleTypeDef SDRAM_Handler;   //SDRAM句柄

void SDRAM_Initialise(void);
u8 SDRAM_Send_Cmd(u8 bankx,u8 cmd,u8 refresh,u16 regval);
void FMC_SDRAM_WriteBuffer(u8 *pBuffer,u32 WriteAddr,u32 n);
void FMC_SDRAM_ReadBuffer(u8 *pBuffer,u32 ReadAddr,u32 n);
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram);
void Read_Total_Capacity_From_FMC_SDRAM(void);

//函数功能;SDRAM初始化
void SDRAM_Initialise(void)
{
  FMC_SDRAM_TimingTypeDef SDRAM_Timing;

  SDRAM_Handler.Instance=FMC_SDRAM_DEVICE;                             //SDRAM在BANK5,6  
  SDRAM_Handler.Init.SDBank=FMC_SDRAM_BANK1;                           //第一个SDRAM BANK
  SDRAM_Handler.Init.ColumnBitsNumber=FMC_SDRAM_COLUMN_BITS_NUM_9;     //列数量
  SDRAM_Handler.Init.RowBitsNumber=FMC_SDRAM_ROW_BITS_NUM_13;          //行数量
  SDRAM_Handler.Init.MemoryDataWidth=FMC_SDRAM_MEM_BUS_WIDTH_16;       //数据宽度为16位
  SDRAM_Handler.Init.InternalBankNumber=FMC_SDRAM_INTERN_BANKS_NUM_4;  //一共4个BANK
  SDRAM_Handler.Init.CASLatency=FMC_SDRAM_CAS_LATENCY_3;               //CAS为3
  SDRAM_Handler.Init.WriteProtection=FMC_SDRAM_WRITE_PROTECTION_DISABLE;//失能写保护
  SDRAM_Handler.Init.SDClockPeriod=FMC_SDRAM_CLOCK_PERIOD_2;           //SDRAM时钟为HCLK/2=180M/2=90M=11.1ns
  SDRAM_Handler.Init.ReadBurst=FMC_SDRAM_RBURST_ENABLE;                //使能突发
  SDRAM_Handler.Init.ReadPipeDelay=FMC_SDRAM_RPIPE_DELAY_1;            //读通道延时

  SDRAM_Timing.LoadToActiveDelay=2;                                   //加载模式寄存器到激活时间的延迟为2个时钟周期
  SDRAM_Timing.ExitSelfRefreshDelay=8;                                //退出自刷新延迟为8个时钟周期
  SDRAM_Timing.SelfRefreshTime=6;                                     //自刷新时间为6个时钟周期                                 
  SDRAM_Timing.RowCycleDelay=6;                                       //行循环延迟为6个时钟周期
  SDRAM_Timing.WriteRecoveryTime=2;                                   //恢复延迟为2个时钟周期
  SDRAM_Timing.RPDelay=2;                                             //行预充电延迟为2个时钟周期
  SDRAM_Timing.RCDDelay=2;                                            //行到列延迟为2个时钟周期
  HAL_SDRAM_Init(&SDRAM_Handler,&SDRAM_Timing);
  //HAL_SDRAM_Init()调用HAL_SDRAM_MspInit()

  SDRAM_Initialization_Sequence(&SDRAM_Handler);//发送SDRAM初始化序列

  //刷新频率计数器(以SDCLK频率计数),计算方法:
  //COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数
  //我们使用的SDRAM刷新周期为64ms,SDCLK=180/2=90Mhz,行数为8192(2^13).
  //所以,COUNT=64*1000*90/8192-20=683
  HAL_SDRAM_ProgramRefreshRate(&SDRAM_Handler,683);//设置刷新频率
}

//函数功能;发送SDRAM初始化序列
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram)
{
  u32 temp=0;

  //SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM
  SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_CLK_ENABLE,1,0); //时钟配置使能
  delay_us(500);                                  //至少延时200us
  SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_PALL,1,0);       //对所有存储区预充电
  SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_AUTOREFRESH_MODE,8,0);//设置自刷新次数

  //配置模式寄存器,SDRAM的bit0~bit2为指定突发访问的长度,
  //bit3为指定突发访问的类型,bit4~bit6为CAS值,bit7和bit8为运行模式
  //bit9为指定的写突发模式,bit10和bit11位保留位
  temp=(u32)SDRAM_MODEREG_BURST_LENGTH_1           |  //设置突发长度:1(可以是1/2/4/8)
            SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL    |  //设置突发类型:连续(可以是连续/交错)
            SDRAM_MODEREG_CAS_LATENCY_3            |  //设置列地址脉冲选通潜伏期,CAS值:3(可以是2/3)
            SDRAM_MODEREG_OPERATING_MODE_STANDARD  |  //设置操作模式:0,标准模式
            SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;     //设置突发写模式:1,单点访问
  SDRAM_Send_Cmd(0,FMC_SDRAM_CMD_LOAD_MODE,1,temp);  //设置SDRAM的模式寄存器
}

/*
FMC硬件接口
PC0---FMC_SDNWE
PC2---FMC_SDNE0
PC3---FMC_SDCKE0

PD0---FMC_D2
PD1---FMC_D3
PD8---FMC_D13
PD9---FMC_D14
PD10---FMC_D15
PD14---FMC_D0
PD15---FMC_D1

PE0---FMC_NBL0
PE1---FMC_NBL1
PE7---FMC_D4
PE8---FMC_D5
PE9---FMC_D6
PE10---FMC_D7
PE11---FMC_D8
PE12---FMC_D9
PE13---FMC_D10
PE14---FMC_D11
PE15---FMC_D12

PF0---FMC_A0
PF1---FMC_A1
PF2---FMC_A2
PF3---FMC_A3
PF4---FMC_A4
PF5---FMC_A5
PF11---FMC_SDNRAS
PF12---FMC_A6
PF13---FMC_A7
PF14---FMC_A8
PF15---FMC_A9

PG0---FMC_A10
PG1---FMC_A11
PG2---FMC_A12
PG4---FMC_BA0
PG5---FMC_BA1
PG8---FMC_SDCLK
PG15---FMC_SDNCAS
//在STM32F429xx复用功能映射表中,映射到FMC接口都是AF12,所以使用GPIO_AF12_FMC
*/
//函数功能;SDRAM底层驱动,引脚配置,时钟使能
//HAL_SDRAM_Init()调用HAL_SDRAM_MspInit()
//hsdram:SDRAM句柄
void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef *hsdram)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  //使用GPIO_InitTypeDef定义一个结构变量GPIO_InitStructure;
//输出:推挽,开漏,带上拉,带下拉,不带上拉和下拉,外设复用;
//输入:浮空,带上拉,带下拉,不带上拉和下拉,外设复用

  __HAL_RCC_FMC_CLK_ENABLE();    //使能FMC时钟
  __HAL_RCC_GPIOC_CLK_ENABLE();  //使能GPIOC时钟
  __HAL_RCC_GPIOD_CLK_ENABLE();  //使能GPIOD时钟
  __HAL_RCC_GPIOE_CLK_ENABLE();  //使能GPIOE时钟
  __HAL_RCC_GPIOF_CLK_ENABLE();  //使能GPIOF时钟
  __HAL_RCC_GPIOG_CLK_ENABLE();  //使能GPIOG时钟

//初始化PC0,PC2和PC3
//PC0映射到FMC_SDNWE,PC2映射到FMC_SDNE0,PC3映射到FMC_SDCKE0
  GPIO_InitStructure.Pin=GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3;//选择第0脚,第2脚和第3脚
  GPIO_InitStructure.Mode=GPIO_MODE_AF_PP;    //外设复用使用GPIO_MODE_AF_PP
  GPIO_InitStructure.Pull=GPIO_PULLUP;        //设置引脚pull状态为上拉
  GPIO_InitStructure.Speed=GPIO_SPEED_HIGH;   //设置引脚的工作速率为高速
  GPIO_InitStructure.Alternate=GPIO_AF12_FMC; //在STM32F429xx复用功能映射表中,映射到FMC接口都是AF12,所以使用GPIO_AF12_FMC
  HAL_GPIO_Init(GPIOC,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOC的外设寄存器

//初始化PD0,PD1,PD8,PD9,PD10,PD14和PD15
//PD0映射到FMC_D2,PD1映射到FMC_D3,PD8映射到FMC_D13,PD9映射到FMC_D14,PD10映射到FMC_D15,PD14映射到FMC_D0,PD15映射到FMC_D1
  GPIO_InitStructure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14|GPIO_PIN_15;
  HAL_GPIO_Init(GPIOD,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOD的外设寄存器

//初始化PE0,PE1,PE7~PE15
//PE0映射到FMC_NBL0,PE1映射到FMC_NBL1,PE7映射到FMC_D4,PE8映射到FMC_D5,PE9映射到FMC_D6,PE10映射到FMC_D7,PE11映射到FMC_D8,PE12映射到FMC_D9,PE13映射到FMC_D10,PE14映射到FMC_D11,PE15映射到FMC_D12
  GPIO_InitStructure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10| GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  HAL_GPIO_Init(GPIOE,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOE的外设寄存器

//初始化PF0~PF5,PF11~PF15
//PF0映射到FMC_A0,PF1映射到FMC_A1,PF2映射到FMC_A2,PF3映射到FMC_A3,PF4映射到FMC_A4,PF5映射到FMC_A5
//PF11映射到FMC_SDNRAS,PF12映射到FMC_A6,PF13映射到FMC_A7,PF14映射到FMC_A8,PF15映射到FMC_A9
  GPIO_InitStructure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  HAL_GPIO_Init(GPIOF,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOF的外设寄存器

//初始化PG0~PG2,PG4,PG5,PG8,PG15
//PG0映射到FMC_A10,PG1映射到FMC_A11,PG2映射到FMC_A12,PG4映射到FMC_BA0,PG5映射到FMC_BA1,PG8映射到FMC_SDCLK,PG15映射到FMC_SDNCAS
  GPIO_InitStructure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15;
  HAL_GPIO_Init(GPIOG,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOG的外设寄存器
}

//函数功能;向SDRAM发送命令
//bankx:0,向BANK5上面的SDRAM发送指令
//      1,向BANK6上面的SDRAM发送指令
//cmd:指令(0,正常模式/1,时钟配置使能/2,预充电所有存储区/3,自动刷新/4,加载模式寄存器/5,自刷新/6,掉电)
//refresh:自刷新次数
//regval:模式寄存器的定义
//返回值:0,正常;1,失败.
u8 SDRAM_Send_Cmd(u8 bankx,u8 cmd,u8 refresh,u16 regval)
{
  u32 target_bank=0;
  FMC_SDRAM_CommandTypeDef Command;

  if(bankx==0) target_bank=FMC_SDRAM_CMD_TARGET_BANK1;       //选择BANK1,即向BANK5上面的SDRAM发送指令
  else if(bankx==1) target_bank=FMC_SDRAM_CMD_TARGET_BANK2;  //选择BANK2,即向BANK6上面的SDRAM发送指令

  Command.CommandMode=cmd;                //命令
  Command.CommandTarget=target_bank;      //目标SDRAM存储区域
  Command.AutoRefreshNumber=refresh;      //自刷新次数
  Command.ModeRegisterDefinition=regval;  //要写入模式寄存器的值
  if(HAL_SDRAM_SendCommand(&SDRAM_Handler,&Command,0X1000)==HAL_OK)//向SDRAM发送命令
  {
    return 0;
  }
  else return 1;
}

//函数功能;在指定地址(WriteAddr+Bank5_SDRAM_ADDR)开始,连续写入n个字节.
//pBuffer:字节指针
//WriteAddr:要写入的地址
//n:要写入的字节数
void FMC_SDRAM_WriteBuffer(u8 *pBuffer,u32 WriteAddr,u32 n)
{
  for(;n!=0;n--)
  {
    *(vu8*)(Bank5_SDRAM_ADDR+WriteAddr)=*pBuffer;
    WriteAddr++;
    pBuffer++;
  }
}

//函数功能;在指定地址((WriteAddr+Bank5_SDRAM_ADDR))开始,连续读出n个字节.
//pBuffer:字节指针
//ReadAddr:要读出的起始地址
//n:要写入的字节数
void FMC_SDRAM_ReadBuffer(u8 *pBuffer,u32 ReadAddr,u32 n)
{
  for(;n!=0;n--)
  {
    *pBuffer++=*(vu8*)(Bank5_SDRAM_ADDR+ReadAddr);
    ReadAddr++;
  }
}

//函数功能;读取SDRAM的总容量    
void Read_Total_Capacity_From_FMC_SDRAM(void)
{
  u32 i=0;
  u32 temp;
  u32 sval;//在地址0读到的数据

  temp=0;
  for(i=0;i<32*1024*1024;i+=16*1024)
	{//每隔16K字节,写入一个数据,总共写入2048个数据,刚好是32M字节
    *(vu32*)(Bank5_SDRAM_ADDR+i)=temp;//将temp的值写入SDRAM 
		temp++;
	}

  sval=0;
  for(i=0;i<32*1024*1024;i+=16*1024)
  {//依次读出之前写入的数据,进行校验
    temp=*(vu32*)(Bank5_SDRAM_ADDR+i);//从SDRAM中读取一个32位的数据
    if(i==0) sval=temp;
    else if( temp<=sval )
    {
      printf("SDRAM Capacity:%dKB\r\n",(u16)(temp-sval+1)*16);//打印SDRAM容量
      break;//后面读出的数据一定要比第一次读到的数据大
    }

    printf("SDRAM Capacity:%dKB\r\n",(u16)(temp-sval+1)*16);//打印SDRAM容量
  }
}

SDRAM.h代码

#ifndef _SDRAM_H
#define _SDRAM_H
#include "sys.h"
//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
//使能s8,u8,s16,u16,s32,u32
//使能常数数据类型sc8,uc8,sc16,uc16,sc32,uc32
//使能vs8,vu8,vs16,vu16,vs32,vu32
//使能vsc8,vuc8,vsc16,vuc16,vsc32,vuc32

extern SDRAM_HandleTypeDef  SDRAM_Handler;//SDRAM句柄
#define Bank5_SDRAM_ADDR    ((u32)(0XC0000000)) //SDRAM开始地址

//SDRAM配置参数
//突发(Burst)是指在同一行中,相邻的存储单元连续进行数据传输的方式;连续传输所涉及到存储单元列的数量就是突发长度,简称BL(Burst Lengths)
#define SDRAM_MODEREG_BURST_LENGTH_1             ((u16)0x0000)  //突发长度(Burst Length):连续传输所涉及到存储单元列的数量为1
#define SDRAM_MODEREG_BURST_LENGTH_2             ((u16)0x0001)  //突发长度(Burst Length):连续传输所涉及到存储单元列的数量为2
#define SDRAM_MODEREG_BURST_LENGTH_4             ((u16)0x0002)  //突发长度(Burst Length):连续传输所涉及到存储单元列的数量为4
#define SDRAM_MODEREG_BURST_LENGTH_8             ((u16)0x0004)  //突发长度(Burst Length):连续传输所涉及到存储单元列的数量为8,
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((u16)0x0000)  //突发类型:连续sequential
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((u16)0x0008)  //突发类型:交错interlrave
#define SDRAM_MODEREG_CAS_LATENCY_2              ((u16)0x0020)  //列地址脉冲选通潜伏期为2
#define SDRAM_MODEREG_CAS_LATENCY_3              ((u16)0x0030)  //列地址脉冲选通潜伏期为3
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((u16)0x0000)  //操作模式:0,标准模式
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((u16)0x0000)  //突发写模式:编程
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((u16)0x0200)  //突发写模式:1,单点访问

extern void SDRAM_Initialise(void);
extern u8 SDRAM_Send_Cmd(u8 bankx,u8 cmd,u8 refresh,u16 regval);
extern void FMC_SDRAM_WriteBuffer(u8 *pBuffer,u32 WriteAddr,u32 n);
extern void FMC_SDRAM_ReadBuffer(u8 *pBuffer,u32 ReadAddr,u32 n);
extern void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram);
extern void Read_Total_Capacity_From_FMC_SDRAM(void);
#endif

7、LTDC硬件接口

在STM32F429xx中LTDC硬件接口:

颜色格式为RGB565

PB5---LCD_BL

PF10---LCD_DE

PG6---LCD_R7

PG7---LCD_CLK

PG11---LCD_B3

PH9---LCD_R3

PH10---LCD_R4

PH11---LCD_R5

PH12---LCD_R6

PH13---LCD_G2

PH14---LCD_G3

PH15---LCD_G4

PI0---LCD_G5

PI1---LCD_G6

PI2---LCD_G7

PI4---LCD_B4

PI5---LCD_B5

PI6---LCD_B6

PI7---LCD_B7

PI9---LCD_VSYNC

PI10---LCD_HSYNC

LTDC.c程序

#include "ltdc.h"
#include "lcd.h"

LTDC_HandleTypeDef  LTDC_Handler;  //LTDC句柄
DMA2D_HandleTypeDef DMA2D_Handler; //DMA2D句柄

//根据不同的颜色格式,定义帧缓存数组
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
  u32 ltdc_lcd_framebuf[1280][800] __attribute__( (at(LCD_FRAME_BUF_ADDR)) );
//定义最大屏分辨率时,LCD所需的帧缓存数组大小
//定义LCD帧缓存数组ltdc_lcd_framebuf[][],共1280*800*4个字节空间,其首地址为0XC0000000,位于外部SDRAM中
#else
  u16 ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR)));
//定义最大屏分辨率时,LCD所需的帧缓存数组大小
//定义LCD帧缓存数组ltdc_lcd_framebuf[][],共1280*800*4个字节空间,其首地址为0XC0000000,位于外部SDRAM中
#endif

u32 *ltdc_framebuf[2];  //LTDC LCD帧缓存数组指针,必须指向对应大小的内存区域
_ltdc_dev lcdltdc;			//管理LCD LTDC的重要参数

函数声明开始///
void LTDC_Switch(u8 sw);                     //LTDC开关
void LTDC_Layer_Switch(u8 layerx,u8 sw);     //层开关
void LTDC_Select_Layer(u8 layerx);           //层选择
void LTDC_Display_Dir(u8 dir);               //显示方向控制
void LTDC_Draw_Point(u16 x,u16 y,u32 color); //画点函数
u32 LTDC_Read_Point(u16 x,u16 y);            //读点函数
void LTDC_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color);        //矩形单色填充函数
void LTDC_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color); //矩形彩色填充函数
void LTDC_Clear(u32 color);  //清屏函数
u8 LTDC_Clk_Set(u32 pllsain,u32 pllsair,u32 pllsaidivr);//LTDC时钟配置
void LTDC_Layer_Window_Config(u8 layerx,u16 sx,u16 sy,u16 width,u16 height);//LTDC层窗口设置
void LTDC_Layer_Parameter_Config(u8 layerx,u32 bufaddr,u8 pixformat,u8 alpha,u8 alpha0,u8 bfac1,u8 bfac2,u32 bkcolor);//LTDC基本参数设置
u16 LTDC_PanelID_Read(void); //LCD ID读取函数
void LTDC_Initialise(void);        //LTDC初始化函数
函数声明结束///

//函数功能:sw=1使能LTDC功能;sw=0不使能LTDC功能
void LTDC_Switch(u8 sw)
{
	if(sw==1) __HAL_LTDC_ENABLE(&LTDC_Handler);       //使能LTDC功能,Enable the LTDC
	else if(sw==0)__HAL_LTDC_DISABLE(&LTDC_Handler);  //不使能LTDC功能,Disable the LTDC
}

//函数功能:sw=1使能LTDC的第layerx层;sw=0不使能LTDC的第layerx层
//layerx:层号,0,第一层; 1,第二层
//sw:1 打开;0关闭
void LTDC_Layer_Switch(u8 layerx,u8 sw)
{
	if(sw==1) __HAL_LTDC_LAYER_ENABLE(&LTDC_Handler,layerx);      //使能LTDC的第layerx层,Enable the LTDC Layer
	else if(sw==0) __HAL_LTDC_LAYER_DISABLE(&LTDC_Handler,layerx);//不使能LTDC的第layerx层,Disable the LTDC Layer
	__HAL_LTDC_RELOAD_CONFIG(&LTDC_Handler);//重新加载层配置,Reload  Layer Configuration
}

//函数功能:选择LTDC的第layerx层,lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=2表示选择第2层
//layerx:层号;0,第一层;1,第二层;
void LTDC_Select_Layer(u8 layerx)
{
	lcdltdc.activelayer=layerx;
}

//函数功能:设置LCD显示方向,1设置为横屏,0设置为竖屏
//dir:0,竖屏;1,横屏
void LTDC_Display_Dir(u8 dir)
{
  lcdltdc.dir=dir; //记录显示方向
  if(dir==0)//竖屏
  {
    lcdltdc.width=lcdltdc.pheight; //记录竖屏的宽度
    lcdltdc.height=lcdltdc.pwidth; //记录竖屏的高度
  }
  else if(dir==1)//横屏
  {
    lcdltdc.width=lcdltdc.pwidth;   //记录横屏的宽度
    lcdltdc.height=lcdltdc.pheight; //记录横屏的高度
  }
}

//函数功能:画点函数
//x,y:写入坐标
//color:颜色值
void LTDC_Draw_Point(u16 x,u16 y,u32 color)
{
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
  if(lcdltdc.dir)//横屏
  {
    *(u32*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*y+x) )=color;
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示横屏的宽度
  }
  else//竖屏
  {
    *(u32*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*( lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y ) )=color;
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示竖屏的高度
    //lcdltdc.pheight表示竖屏的宽度
  }
#else
  if(lcdltdc.dir)//横屏
  {
    *(u16*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*y+x) )=color;
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示横屏的宽度
  }
  else//竖屏
  {
    *(u16*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y) )=color;
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示竖屏的高度
    //lcdltdc.pheight表示竖屏的宽度
  }
#endif
}

//函数功能:读点函数
//x,y:读取点的坐标
//返回值:颜色值
u32 LTDC_Read_Point(u16 x,u16 y)
{ 
#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
	if(lcdltdc.dir)//横屏
  {
		return *(u32*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*y+x) );
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示横屏的宽度
	}
  else//竖屏
  {
    return *(u32*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*( lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y ) );
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示竖屏的高度
    //lcdltdc.pheight表示竖屏的宽度
  }
#else
  if(lcdltdc.dir)//横屏
  {
    return *(u16*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*y+x) );
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示横屏的宽度
	}
  else//竖屏
  {
    return *(u16*)( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*(lcdltdc.pheight-x-1)+y) );
    //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
    //lcdltdc.pixsize表示每个像素所占的字节数
    //lcdltdc.pwidth表示竖屏的高度
    //lcdltdc.pheight表示竖屏的宽度
  }
#endif 
}

//函数功能:LTDC填充矩形,DMA2D填充
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
//有时候需要频繁的调用填充函数,所以为了速度,填充函数采用寄存器版本
//不过下面有对应的库函数版本的代码
void LTDC_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color)
{
  u32 psx,psy,pex,pey;//以LCD面板为基准的坐标系,不随横竖屏变化而变化
  u32 timeout=0;
  u16 offline;
  u32 addr;

  //坐标系转换
	if(lcdltdc.dir)//横屏
	{
		psx=sx;psy=sy;//记录横屏起始坐标点
		pex=ex;pey=ey;//记录横屏结束坐标点
	}
  else//竖屏
  {
    psx=sy;psy=lcdltdc.pheight-ex-1;//记录竖屏起始坐标点
    pex=ey;pey=lcdltdc.pheight-sx-1;//记录竖屏结束坐标点
  }

  offline=lcdltdc.pwidth-(pex-psx+1);
	addr=( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx) );
  //lcdltdc.activelayer=0表示选择第1层;lcdltdc.activelayer=1表示选择第2层
  //lcdltdc.pixsize表示每个像素所占的字节数

	__HAL_RCC_DMA2D_CLK_ENABLE(); //使能DMA2D时钟

  DMA2D->CR&=~(DMA2D_CR_START);             //先停止DMA2D
  DMA2D->CR=DMA2D_R2M;                      //寄存器到存储器模式
  DMA2D->OPFCCR=LCD_PIXFORMAT;              //设置颜色格式
  DMA2D->OOR=offline;                       //设置行偏移

  DMA2D->OMAR=addr;                         //输出存储器地址
  DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16); //设定行数寄存器
  DMA2D->OCOLR=color;                       //设定输出颜色寄存器
  DMA2D->CR|=DMA2D_CR_START;                //启动DMA2D

  while((DMA2D->ISR&(DMA2D_FLAG_TC))==0)//等待传输完成
  {
    timeout++;
    if(timeout>0X1FFFFF) break;//超时退出
  }
  DMA2D->IFCR|=DMA2D_FLAG_TC;  //清除传输完成标志
}

//函数功能:在指定区域内填充指定颜色块,DMA2D填充
//此函数仅支持u16,RGB565格式的颜色数组填充.
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!
//color:要填充的颜色数组首地址
void LTDC_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color)
{
  u32 psx,psy,pex,pey;	//以LCD面板为基准的坐标系,不随横竖屏变化而变化
  u32 timeout=0;
  u16 offline;
  u32 addr;

  //坐标系转换
  if(lcdltdc.dir)//横屏
  {
    psx=sx;psy=sy;//记录横屏起始坐标点
    pex=ex;pey=ey;//记录横屏结束坐标点
  }
  else//竖屏
  {
    psx=sy;psy=lcdltdc.pheight-ex-1;//记录竖屏起始坐标点
    pex=ey;pey=lcdltdc.pheight-sx-1;//记录竖屏结束坐标点
  }

  offline=lcdltdc.pwidth-(pex-psx+1);
  addr=( (u32)ltdc_framebuf[lcdltdc.activelayer] + lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx) );

  __HAL_RCC_DMA2D_CLK_ENABLE();            //使能DM2D时钟
  DMA2D->CR&=~(DMA2D_CR_START);            //先停止DMA2D
  DMA2D->CR=DMA2D_M2M;                     //存储器到存储器模式
  DMA2D->FGPFCCR=LCD_PIXFORMAT;            //设置颜色格式
  DMA2D->FGOR=0;                           //前景层行偏移为0
  DMA2D->OOR=offline;                      //设置行偏移

  DMA2D->FGMAR=(u32)color;                 //源地址
  DMA2D->OMAR=addr;                        //输出存储器地址
  DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16);//设定行数寄存器
  DMA2D->CR|=DMA2D_CR_START;               //启动DMA2D

  while((DMA2D->ISR&(DMA2D_FLAG_TC))==0)//等待传输完成
  {
    timeout++;
    if(timeout>0X1FFFFF)break;//超时退出
  }
  DMA2D->IFCR|=DMA2D_FLAG_TC;//清除传输完成标志
}

//函数功能:LCD清屏
//color:颜色值
void LTDC_Clear(u32 color)
{
  LTDC_Fill(0,0,lcdltdc.width-1,lcdltdc.height-1,color);
}

//函数功能:设置LTDC输出像素时钟Fdclk,需要根据自己所使用的LCD数据手册来配置
//Fvco=Fin*pllsain;
//LTDC输出像素时钟Fdclk=Fvco/pllsair/2*2^pllsaidivr=Fin*pllsain/pllsair/2*2^pllsaidivr;

//Fvco:VCO频率
//Fin:输入时钟频率一般为1Mhz(来自系统时钟PLLM分频后的时钟,见时钟树图)
//pllsain:SAI时钟倍频系数N,取值范围:50~432.
//pllsair:SAI时钟的分频系数R,取值范围:2~7
//pllsaidivr:LCD时钟分频系数,取值范围:RCC_PLLSAIDIVR_2/4/8/16,对应分频2~16
//假设:外部晶振为25M,pllm=25的时候,Fin=1Mhz.
//例如:要得到20M的LTDC时钟,则可以设置:pllsain=400,pllsair=5,pllsaidivr=RCC_PLLSAIDIVR_4
//Fdclk=1*400/5/4=400/20=20Mhz
//返回值:0,成功;1,失败
u8 LTDC_Clk_Set(u32 pllsain,u32 pllsair,u32 pllsaidivr)
{
  RCC_PeriphCLKInitTypeDef PeriphClkIniture;

  //LTDC输出像素时钟,需要根据自己所使用的LCD数据手册来配置
  PeriphClkIniture.PeriphClockSelection=RCC_PERIPHCLK_LTDC;//LTDC时钟
  PeriphClkIniture.PLLSAI.PLLSAIN=pllsain;
  PeriphClkIniture.PLLSAI.PLLSAIR=pllsair;
  PeriphClkIniture.PLLSAIDivR=pllsaidivr;
  if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkIniture)==HAL_OK)//配置像素时钟
  {
    return 0;//成功
  }
  else return 1;//失败
}

//函数功能:设置LTDC窗口的位置和LTDC窗口的大小,窗口以LCD面板坐标系为基准,
//注意:此函数必须在LTDC_Layer_Parameter_Config()之后再设置.
//layerx:层值,0/1.
//sx,sy:起始坐标
//width,height:宽度和高度
void LTDC_Layer_Window_Config(u8 layerx,u16 sx,u16 sy,u16 width,u16 height)
{
  HAL_LTDC_SetWindowPosition(&LTDC_Handler,sx,sy,layerx);    //设置窗口的位置,Set the LTDC window position
  HAL_LTDC_SetWindowSize(&LTDC_Handler,width,height,layerx); //设置窗口的大小,Set the LTDC window size
}

//函数功能:LTDC层参数配置.
//注意:此函数,必须在LTDC_Layer_Window_Config()之前设置.
//layerx:层值,0/1.
//bufaddr:层颜色帧缓存起始地址
//pixformat:颜色格式.0,ARGB8888;1,RGB888;2,RGB565;3,ARGB1555;4,ARGB4444;5,L8;6;AL44;7;AL88
//alpha:层颜色Alpha值,0,全透明;255,不透明
//alpha0:默认颜色Alpha值,0,全透明;255,不透明
//bfac1:混合系数1,4(100),恒定的Alpha;6(101),像素Alpha*恒定Alpha
//bfac2:混合系数2,5(101),恒定的Alpha;7(111),像素Alpha*恒定Alpha
//bkcolor:层默认颜色,32位,低24位有效,RGB888格式
//返回值:无
void LTDC_Layer_Parameter_Config(u8 layerx,u32 bufaddr,u8 pixformat,u8 alpha,u8 alpha0,u8 bfac1,u8 bfac2,u32 bkcolor)
{
  LTDC_LayerCfgTypeDef pLayerCfg;

  pLayerCfg.WindowX0=0;               //窗口起始X坐标
  pLayerCfg.WindowY0=0;               //窗口起始Y坐标
  pLayerCfg.WindowX1=lcdltdc.pwidth;  //窗口终止X坐标
  pLayerCfg.WindowY1=lcdltdc.pheight; //窗口终止Y坐标
  
  pLayerCfg.PixelFormat=pixformat;
  //像素格式
  //颜色格式.0,ARGB8888;1,RGB888;2,RGB565;3,ARGB1555;4,ARGB4444;5,L8;6;AL44;7;AL88

	pLayerCfg.Alpha=alpha;              //Alpha值设置,0~255,255为完全不透明
	pLayerCfg.Alpha0=alpha0;            //默认Alpha值

  pLayerCfg.BlendingFactor1=(u32)bfac1<<8;//设置层混合系数
  pLayerCfg.BlendingFactor2=(u32)bfac2<<8;//设置层混合系数

  pLayerCfg.FBStartAdress=bufaddr;        //设置层颜色帧缓存起始地址
  pLayerCfg.ImageWidth=lcdltdc.pwidth;    //设置颜色帧缓冲区的宽度
  pLayerCfg.ImageHeight=lcdltdc.pheight;  //设置颜色帧缓冲区的高度

  pLayerCfg.Backcolor.Red=(u8)(bkcolor&0X00FF0000)>>16;   //背景颜色红色部分
  pLayerCfg.Backcolor.Green=(u8)(bkcolor&0X0000FF00)>>8;  //背景颜色绿色部分
  pLayerCfg.Backcolor.Blue=(u8)bkcolor&0X000000FF;        //背景颜色蓝色部分

  HAL_LTDC_ConfigLayer(&LTDC_Handler,&pLayerCfg,layerx);//设置所选中的层
}

//函数功能:读取RGB屏的ID
//返回0X4342,表示RGB屏为4.3寸(480*272)
//返回0X4384,表示RGB屏为4.3寸(800*480)
//返回0X7084,表示RGB屏为7寸(800*480)
//返回0X7016,表示RGB屏为7寸(1024*600)
//返回0X7018,表示RGB屏为7寸(1280*800)
//返回0X1018,表示RGB屏为10.1寸(1280*800)
//PG6=R7(M0);PI2=G7(M1);PI7=B7(M2);
//M2:M1:M0
//0 :0 :0	//4.3寸480*272 RGB屏,ID=0X4342
//0 :0 :1	//7寸800*480 RGB屏,ID=0X7084
//0 :1 :0	//7寸1024*600 RGB屏,ID=0X7016
//0 :1 :1	//7寸1280*800 RGB屏,ID=0X7018
//1 :0 :0	//4.3寸800*480 RGB屏,ID=0X4384
//1 :0 :1	//10.1寸1280*800 RGB屏,ID=0X1018
//返回值:LCD ID:0,非法;其他值,ID
u16 LTDC_PanelID_Read(void)
{
  u8 idx=0;
  GPIO_InitTypeDef GPIO_InitStructure;

  __HAL_RCC_GPIOG_CLK_ENABLE();//在配置外设之前,必须先使能GPIOG的外设时钟
	__HAL_RCC_GPIOI_CLK_ENABLE();//在配置外设之前,必须先使能GPIOI的外设时钟

  GPIO_InitStructure.Pin=GPIO_PIN_6;         //选择第6脚PG6
  GPIO_InitStructure.Mode=GPIO_MODE_INPUT;   //设置引脚为输入口
  GPIO_InitStructure.Pull=GPIO_PULLUP;       //设置引脚工作模式为上拉
  GPIO_InitStructure.Speed=GPIO_SPEED_HIGH;  //设置引脚的工作速率为高速
  HAL_GPIO_Init(GPIOG,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOG的外设寄存器

  GPIO_InitStructure.Pin=GPIO_PIN_2|GPIO_PIN_7;//选择第2脚和第7脚P,PI2和PI7
  HAL_GPIO_Init(GPIOI,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOI的外设寄存器

  idx=(u8)HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_6);     //读取PG6,即为M0
  idx|=(u8)HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_2)<<1; //读取PI2,即为M1
  idx|=(u8)HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_7)<<2; //读取PI7,即为M2
  switch(idx)
  {
    case 0:return 0X4342;//4.3寸屏,480*272分辨率
    case 1:return 0X7084;//7寸屏,800*480分辨率
    case 2:return 0X7016;//7寸屏,1024*600分辨率
    case 3:return 0X7018;//7寸屏,1280*800分辨率
    case 4:return 0X4384;//4.3寸屏,800*480分辨率
    case 5:return 0X1018;//10.1寸屏,1280*800分辨率
    default:return 0;
  }
}

//函数功能:LCD初始化函数
void LTDC_Initialise(void)
{
  u16 lcdid=0;

  lcdid=LTDC_PanelID_Read();
  //读取RGB屏的ID
  //返回0X4342,表示RGB屏为4.3寸(480*272)
  //返回0X4384,表示RGB屏为4.3寸(800*480)
  //返回0X7084,表示RGB屏为7寸(800*480)
  //返回0X7016,表示RGB屏为7寸(1024*600)
  //返回0X7018,表示RGB屏为7寸(1280*800)
  //返回0X1018,表示RGB屏为10.1寸(1280*800)

  if(lcdid==0X4342)//RGB屏为4.3寸(480*272)
  {
//Parallel 24-bit RGB Timing Table,这个表中的参数需要在程序中体现出来
    lcdltdc.pwidth=480;   //4.3寸RGB屏的面板宽度,单位:像素
    lcdltdc.pheight=272;  //4.3寸RGB屏的面板高度,单位:像素
    lcdltdc.hsw=1;        //水平同步宽度,HYSNC pulse width:Thwh=1DCLK
    lcdltdc.vsw=1;        //垂直同步宽度,VYSNC pulse width:Tvwh=1DCLK
    lcdltdc.hbp=40;       //水平后廊,HSD back porch:Thbp=40DCLK
    lcdltdc.vbp=8;        //垂直后廊,VSD back porch:Tvbp=8DCLK
    lcdltdc.hfp=5;        //水平前廊,HSD front porch:Thfp=5DCLK
    lcdltdc.vfp=8;        //垂直前廊,VSD front porch:Tvfp=8DCLK
    LTDC_Clk_Set(288,4,RCC_PLLSAIDIVR_8);
    //设置像素时钟9Mhz
    //当外部晶振为25M,pllm=25时,则Fin=1Mhz,所以Fdclk=1*288/4/8=72/8=9Mhz
  }
  else if(lcdid==0X7084)//RGB屏为7寸(800*480)
  {
    lcdltdc.pwidth=800;   //7寸RGB屏的面板宽度,单位:像素
    lcdltdc.pheight=480;  //7寸RGB屏的面板高度,单位:像素
    lcdltdc.hsw=1;        //水平同步宽度,HYSNC pulse width:Thwh=1DCLK
    lcdltdc.vsw=1;        //垂直同步宽度,VYSNC pulse width:Tvwh=1DCLK
    lcdltdc.hbp=46;       //水平后廊,HSD back porch:Thbp=46DCLK
    lcdltdc.vbp=23;       //垂直后廊,VSD back porch:Tvbp=23DCLK
    lcdltdc.hfp=210;      //水平前廊,HSD front porch:Thfp=210DCLK
    lcdltdc.vfp=22;       //垂直前廊,VSD front porch:Tvfp=22DCLK
    LTDC_Clk_Set(396,3,RCC_PLLSAIDIVR_4);
    //设置像素时钟33M(如果开双显,需要降低DCLK到:18.75Mhz,300/4/4,才会比较好)
    //当外部晶振为25M,pllm=25时,则Fin=1Mhz,所以Fdclk=1*396/3/4=132/4=33Mhz
  }
  else if(lcdid==0X7016)//RGB屏为7寸(1024*600)
  {
    lcdltdc.pwidth=1024; //面板宽度,单位:像素
    lcdltdc.pheight=600; //面板高度,单位:像素
    lcdltdc.hsw=20;      //水平同步宽度,HYSNC pulse width:Thwh=20DCLK
    lcdltdc.vsw=3;       //垂直同步宽度,VYSNC pulse width:Tvwh=3DCLK
    lcdltdc.hbp=140;     //水平后廊,HSD back porch:Thbp=140DCLK
    lcdltdc.vbp=20;      //垂直后廊,VSD back porch:Tvbp=20DCLK
    lcdltdc.hfp=160;     //水平前廊,HSD front porch:Thfp=160DCLK
    lcdltdc.vfp=12;      //垂直前廊,VSD front porch:Tvfp=12DCLK
    LTDC_Clk_Set(360,2,RCC_PLLSAIDIVR_4);
    //设置像素时钟45Mhz
    //当外部晶振为25M,pllm=25时,则Fin=1Mhz,所以Fdclk=1*360/2/4=180/4=45Mhz
	}
  else if(lcdid==0X7018)
  {
    lcdltdc.pwidth=1280; //面板宽度,单位:像素
    lcdltdc.pheight=800; //面板高度,单位:像素
    //其他参数待定.
  }
  else if(lcdid==0X4384)
  {
    lcdltdc.pwidth=800;   //面板宽度,单位:像素
    lcdltdc.pheight=480;  //面板高度,单位:像素
    lcdltdc.hsw=48;       //水平同步宽度,HYSNC pulse width:Thwh=48DCLK
    lcdltdc.vsw=3;        //垂直同步宽度,VYSNC pulse width:Tvwh=3DCLK
    lcdltdc.hbp=88;       //水平后廊,HSD back porch:Thbp=88DCLK
    lcdltdc.vbp=32;       //垂直后廊,VSD back porch:Tvbp=32DCLK
    lcdltdc.hfp=40;       //水平前廊,HSD front porch:Thfp=40DCLK
    lcdltdc.vfp=13;       //垂直前廊,VSD front porch:Tvfp=13DCLK
    
    LTDC_Clk_Set(396,3,RCC_PLLSAIDIVR_4);
    //设置像素时钟33M
    //当外部晶振为25M,pllm=25时,则Fin=1Mhz,所以Fdclk=1*396/3/4=132/4=33Mhz
  }
  else if(lcdid==0X1018)//10.1寸1280*800 RGB屏
  {
    lcdltdc.pwidth=1280;  //面板宽度,单位:像素
    lcdltdc.pheight=800;  //面板高度,单位:像素
    lcdltdc.hsw=10;       //水平同步宽度,HYSNC pulse width:Thwh=10DCLK
    lcdltdc.vsw=3;        //垂直同步宽度,VYSNC pulse width:Tvwh=3DCLK
    lcdltdc.hbp=140;      //水平后廊,HSD back porch:Thbp=140DCLK
    lcdltdc.vbp=10;       //垂直后廊,VSD back porch:Tvbp=10DCLK
    lcdltdc.hfp=10;       //水平前廊,HSD front porch:Thfp=10DCLK
    lcdltdc.vfp=10;       //垂直前廊,VSD front porch:Tvfp=10DCLK
    LTDC_Clk_Set(360,2,RCC_PLLSAIDIVR_4);//设置像素时钟45Mhz
    //当外部晶振为25M,pllm=25时,则Fin=1Mhz,所以Fdclk=1*360/2/4=180/4=45Mhz
  }

  lcddev.width=lcdltdc.pwidth;   //装载RGB屏的宽度
	lcddev.height=lcdltdc.pheight; //装载RGB屏的高度

#if LCD_PIXFORMAT==LCD_PIXFORMAT_ARGB8888||LCD_PIXFORMAT==LCD_PIXFORMAT_RGB888
  lcdltdc.pixsize=4;                         //ARGB8888和RGB888格式,则每个像素占4个字节
  ltdc_framebuf[0]=(u32*)&ltdc_lcd_framebuf; //装载帧缓存数组
#else
  lcdltdc.pixsize=2;                          //RGB565格式,则每个像素占2个字节
  ltdc_framebuf[0]=(u32*)&ltdc_lcd_framebuf;  //装载帧缓存数组
#endif

//LTDC配置
  LTDC_Handler.Instance=LTDC;  //设置LTDC寄存器的基地址,LTDC Register base address
  LTDC_Handler.Init.HSPolarity=LTDC_HSPOLARITY_AL;//水平同步极性
  LTDC_Handler.Init.VSPolarity=LTDC_VSPOLARITY_AL;//垂直同步极性
  LTDC_Handler.Init.DEPolarity=LTDC_DEPOLARITY_AL;//数据使能极性
  if(lcdid==0X1018)
    LTDC_Handler.Init.PCPolarity=LTDC_PCPOLARITY_IIPC;//像素时钟极性
  else
    LTDC_Handler.Init.PCPolarity=LTDC_PCPOLARITY_IPC;//像素时钟极性
  LTDC_Handler.Init.HorizontalSync=lcdltdc.hsw-1;  //水平同步宽度
  LTDC_Handler.Init.VerticalSync=lcdltdc.vsw-1;    //垂直同步宽度
  LTDC_Handler.Init.AccumulatedHBP=lcdltdc.hsw+lcdltdc.hbp-1; //水平同步后沿宽度
  LTDC_Handler.Init.AccumulatedVBP=lcdltdc.vsw+lcdltdc.vbp-1; //垂直同步后沿高度
  LTDC_Handler.Init.AccumulatedActiveW=lcdltdc.hsw+lcdltdc.hbp+lcdltdc.pwidth-1;      //有效宽度
  LTDC_Handler.Init.AccumulatedActiveH=lcdltdc.vsw+lcdltdc.vbp+lcdltdc.pheight-1;     //有效高度
  LTDC_Handler.Init.TotalWidth=lcdltdc.hsw+lcdltdc.hbp+lcdltdc.pwidth+lcdltdc.hfp-1;  //总宽度
  LTDC_Handler.Init.TotalHeigh=lcdltdc.vsw+lcdltdc.vbp+lcdltdc.pheight+lcdltdc.vfp-1; //总高度
  LTDC_Handler.Init.Backcolor.Red=0;   //屏幕背景层红色部分
  LTDC_Handler.Init.Backcolor.Green=0; //屏幕背景层绿色部分
  LTDC_Handler.Init.Backcolor.Blue=0;  //屏幕背景色蓝色部分
  HAL_LTDC_Init(&LTDC_Handler);
  //使用LTDC_Handler结构数据初始化LTDC
  //HAL_LTDC_Init()会调用HAL_LTDC_MspInit()

//层配置
  LTDC_Layer_Parameter_Config(0,(u32)ltdc_framebuf[0],LCD_PIXFORMAT,255,0,6,7,0X000000);
  //LTDC层参数配置
  //注意:此函数,必须在LTDC_Layer_Window_Config()之前设置.
  LTDC_Layer_Window_Config(0,0,0,lcdltdc.pwidth,lcdltdc.pheight);
  //层窗口配置,以LCD面板坐标系为基准,不要随便修改!
  //设置LTDC窗口的位置和LTDC窗口的大小,窗口以LCD面板坐标系为基准,

	LTDC_Select_Layer(0);//选择第1层
  LCD_LED=1;//点亮背光
  LTDC_Clear(0XFFFFFFFF);//清屏
}

/*
LTDC硬件接口:颜色格式为RGB565
PB5---LCD_BL
PF10---LCD_DE
PG6---LCD_R7
PG7---LCD_CLK
PG11---LCD_B3
PH9---LCD_R3
PH10---LCD_R4
PH11---LCD_R5
PH12---LCD_R6
PH13---LCD_G2
PH14---LCD_G3
PH15---LCD_G4
PI0---LCD_G5
PI1---LCD_G6
PI2---LCD_G7
PI4---LCD_B4
PI5---LCD_B5
PI6---LCD_B6
PI7---LCD_B7
PI9---LCD_VSYNC
PI10---LCD_HSYNC
在STM32F429xx复用功能映射表中,映射到LCD接口都是AF14,所以使用GPIO_AF14_LTDC
*/
//函数功能:LTDC底层IO初始化和时钟使能
//HAL_LTDC_Init()会调用HAL_LTDC_MspInit()
//hltdc:LTDC句柄
void HAL_LTDC_MspInit(LTDC_HandleTypeDef* hltdc)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  //使用GPIO_InitTypeDef定义一个结构变量GPIO_InitStructure;
//输出:推挽,开漏,带上拉,带下拉,不带上拉和下拉,外设复用;
//输入:浮空,带上拉,带下拉,不带上拉和下拉,外设复用

  __HAL_RCC_LTDC_CLK_ENABLE();  //使能LTDC时钟
  __HAL_RCC_DMA2D_CLK_ENABLE(); //使能DMA2D时钟
  __HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
  __HAL_RCC_GPIOF_CLK_ENABLE(); //使能GPIOF时钟
  __HAL_RCC_GPIOG_CLK_ENABLE(); //使能GPIOG时钟
  __HAL_RCC_GPIOH_CLK_ENABLE(); //使能GPIOH时钟
  __HAL_RCC_GPIOI_CLK_ENABLE(); //使能GPIOI时钟

//初始化PB5,背光引脚
  GPIO_InitStructure.Pin=GPIO_PIN_5;           //选择第5脚
  GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP; //设置引脚工作模式为推挽输出方式
  GPIO_InitStructure.Pull=GPIO_PULLUP;         //设置引脚pull状态为上拉
  GPIO_InitStructure.Speed=GPIO_SPEED_HIGH;    //设置引脚的工作速率为高速
  HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOB的外设寄存器

//初始化PF10
//PF10脚重新映射LCD_DE
  GPIO_InitStructure.Pin=GPIO_PIN_10;          //选择第10脚 
  GPIO_InitStructure.Mode=GPIO_MODE_AF_PP;     //外设复用使用GPIO_MODE_AF_PP
  GPIO_InitStructure.Pull=GPIO_NOPULL;         //设置引脚pull状态为没有激活上拉或下拉
  GPIO_InitStructure.Speed=GPIO_SPEED_HIGH;    //设置引脚的工作速率为高速
  GPIO_InitStructure.Alternate=GPIO_AF14_LTDC; //在STM32F429xx复用功能映射表中,映射到LCD接口都是AF14,所以使用GPIO_AF14_LTDC将PF10脚重新映射LCD_DE
  HAL_GPIO_Init(GPIOF,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOF的外设寄存器

//初始化PG6,PG7,PG11
//PG6映射到LCD_R7,PG7映射到LCD_CLK,PG11映射到LCD_B3
  GPIO_InitStructure.Pin=GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_11;//选择第6脚,第7脚和第11脚
  HAL_GPIO_Init(GPIOG,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOG的外设寄存器

//初始化PH9,PH10,PH11,PH12,PH13,PH14,PH15
//PH9映射到LCD_R3,PH10映射到LCD_R4,PH11映射到LCD_R5,PH12映射到LCD_R6,PH13映射到LCD_G2,PH14映射到LCD_G3,PH15映射到LCD_G4
  GPIO_InitStructure.Pin=GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|\
                         GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;//选择第9脚~第15脚
  HAL_GPIO_Init(GPIOH,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOH的外设寄存器

//初始化PI0,PI1,PI2,PI4,PI5,PI6,PI7,PI9,PI10
//PI0映射到LCD_G5,PI1映射到LCD_G6,PI2映射到LCD_G7,PI4映射到LCD_B4,PI5映射到LCD_B5,PI6映射到LCD_B6,PI7映射到LCD_B7,PI9映射到LCD_VSYNC,PI10映射到LCD_HSYNC
  GPIO_InitStructure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|\
                         GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_9|GPIO_PIN_10;
  HAL_GPIO_Init(GPIOI,&GPIO_InitStructure);
  //根据GPIO_InitStructure结构变量指定的参数初始化GPIOI的外设寄存器
}

LTDC.h程序

#ifndef __LCD_H
#define __LCD_H		
#include "sys.h"
//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
//使能s8,u8,s16,u16,s32,u32
//使能常数数据类型sc8,uc8,sc16,uc16,sc32,uc32
//使能vs8,vu8,vs16,vu16,vs32,vu32
//使能vsc8,vuc8,vsc16,vuc16,vsc32,vuc32

用户使用到的变量开始//
//LCD重要参数集 
extern u32  POINT_COLOR;//LCD的画笔颜色,默认红色    
extern u32  BACK_COLOR; //LCD的背景颜色.默认为白色
typedef struct
{
  u16 width;//LCD 宽度
	u16 height;//LCD 高度
	u16 id;//LCD ID
	u8  dir;//横屏还是竖屏控制:0,竖屏;1,横屏。	
	u16	wramcmd;//开始写gram指令
	u16 setxcmd;//设置x坐标指令
	u16 setycmd;//设置y坐标指令
}_lcd_dev;
extern _lcd_dev lcddev;	//管理LCD重要参数 
用户使用到的变量结束//

//画笔颜色
#define WHITE    0xFFFF
#define BLACK    0x0000
#define BLUE     0x001F
#define BRED     0XF81F
#define GRED     0XFFE0
#define GBLUE    0X07FF
#define RED      0xF800
#define MAGENTA  0xF81F
#define GREEN    0x07E0
#define CYAN     0x7FFF
#define YELLOW   0xFFE0
#define BROWN    0XBC40 //棕色
#define BRRED    0XFC07 //棕红色
#define GRAY     0X8430 //灰色
//GUI颜色

#define DARKBLUE   0X01CF  //深蓝色
#define LIGHTBLUE  0X7D7C  //浅蓝色
#define GRAYBLUE   0X5458  //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN     0X841F //浅绿色
//#define LIGHTGRAY    0XEF5B //浅灰色(PANNEL)
#define LGRAY 			   0XC618 //浅灰色(PANNEL),窗体背景色

#define LGRAYBLUE  0XA651  //浅灰蓝色(中间层颜色)
#define LBBLUE     0X2B12  //浅棕蓝色(选择条目的反色)

extern void LCD_Clear(u32 color);
extern void LCD_Display_Dir(u8 dir);
extern void LCD_Init(void);
extern void LCD_DisplayOn(void);
extern void LCD_DisplayOff(void);
extern void LCD_DrawPoint(u16 x,u16 y);
extern void LCD_Fast_DrawPoint(u16 x,u16 y,u32 color);
extern u32 LCD_ReadPoint(u16 x,u16 y);
extern void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color);
extern void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color);
extern void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);
extern void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);
extern void LCD_Draw_Circle(u16 x0,u16 y0,u8 r);
extern void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);
extern void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);
extern void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);
extern void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p);
#endif

LCD.c程序

#include "lcd.h"
#include "font.h"
#include "ltdc.h"
#include "sdram.h"

用户使用到的变量开始//
u32 POINT_COLOR=0xFF000000;  //画笔颜色
u32 BACK_COLOR =0xFFFFFFFF;  //背景色
_lcd_dev lcddev;             //管理LCD重要参数
用户使用到的变量结束//

/函数声明开始/
void LCD_Clear(u32 color);
void LCD_Display_Dir(u8 dir);
void LCD_Init(void);
void LCD_DisplayOn(void);
void LCD_DisplayOff(void);
void LCD_DrawPoint(u16 x,u16 y);
void LCD_Fast_DrawPoint(u16 x,u16 y,u32 color);
u32 LCD_ReadPoint(u16 x,u16 y);
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color);
void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color);
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);
void LCD_Draw_Circle(u16 x0,u16 y0,u8 r);
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);
u32 LCD_Pow(u8 m,u8 n);
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p);
/函数声明结束

//函数功能:RGB屏清屏函数
//color:要清屏的填充色
void LCD_Clear(u32 color)
{
  LTDC_Clear(color);
}

//函数功能:设置LCD显示方向
//dir:0,竖屏;1,横屏
void LCD_Display_Dir(u8 dir)
{
  lcddev.dir=dir;//记录当前是横屏还是竖屏

  LTDC_Display_Dir(dir);
  lcddev.width=lcdltdc.width;
  lcddev.height=lcdltdc.height;
}

//函数功能:初始化RGB屏
void LCD_Init(void)
{
  SDRAM_Initialise();

  lcddev.id=LTDC_PanelID_Read();
  //检查是否有RGB屏接入
  //读取RGB屏的ID
  //返回0X4342,表示RGB屏为4.3寸(480*272)
  //返回0X4384,表示RGB屏为4.3寸(800*480)
  //返回0X7084,表示RGB屏为7寸(800*480)
  //返回0X7016,表示RGB屏为7寸(1024*600)
  //返回0X7018,表示RGB屏为7寸(1280*800)
  //返回0X1018,表示RGB屏为10.1寸(1280*800)

  if(lcddev.id!=0) LTDC_Initialise();//ID非零,说明有RGB屏接入
  LCD_Display_Dir(1); //1设置为横屏,0设置为竖屏
  LCD_LED=1;          //点亮背光
  LCD_Clear(WHITE);   //RGB屏清屏函数
}

//函数功能;LCD开启显示
void LCD_DisplayOn(void)
{
  LTDC_Switch(1);//开启LCD
}

//函数功能:LCD关闭显示
void LCD_DisplayOff(void)
{
  LTDC_Switch(0);//关闭LCD
}

//函数功能:RGB屏画点
//x,y:坐标
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(u16 x,u16 y)
{
  LTDC_Draw_Point(x,y,POINT_COLOR);
}

//函数功能:RGB屏快速画点
//x,y:坐标
//color:颜色
void LCD_Fast_DrawPoint(u16 x,u16 y,u32 color)
{
  LTDC_Draw_Point(x,y,color);
}

//函数功能:读取RGB屏某个点的颜色值
//x,y:坐标
//返回值:此点的颜色
u32 LCD_ReadPoint(u16 x,u16 y)
{
  if(x>=lcddev.width||y>=lcddev.height)return 0;//超过了范围,直接返回
  return LTDC_Read_Point(x,y);
}

//函数功能:在指定区域内填充单个颜色
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color)
{
  LTDC_Fill(sx,sy,ex,ey,color);
} 

//函数功能:在指定区域内填充指定颜色块
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color)
{
  LTDC_Color_Fill(sx,sy,ex,ey,color);
}

//函数功能:画线
//x1,y1:起点坐标
//x2,y2:终点坐标
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)
{
  u16 t;
  int xerr=0,yerr=0,delta_x,delta_y,distance;
  int incx,incy,uRow,uCol;

  delta_x=x2-x1;//计算坐标增量
  delta_y=y2-y1;
  uRow=x1;
  uCol=y1;
  if(delta_x>0)incx=1;//设置单步方向
  else if(delta_x==0)incx=0;//垂直线
  else {incx=-1;delta_x=-delta_x;}
  if(delta_y>0)incy=1;
  else if(delta_y==0)incy=0;//水平线
  else{incy=-1;delta_y=-delta_y;}
  if( delta_x>delta_y)distance=delta_x;//选取基本增量坐标轴
  else distance=delta_y;
  for(t=0;t<=distance+1;t++ )//画线输出
  {
    LCD_DrawPoint(uRow,uCol);//画点
    xerr+=delta_x;
    yerr+=delta_y;
    if(xerr>distance)
    {
      xerr-=distance;
      uRow+=incx;
    }
    if(yerr>distance)
    {
      yerr-=distance;
      uCol+=incy;
    }
  }
}

//函数功能:画矩形
//(x1,y1),(x2,y2):矩形的对角坐标
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)
{
  LCD_DrawLine(x1,y1,x2,y1);
  LCD_DrawLine(x1,y1,x1,y2);
  LCD_DrawLine(x1,y2,x2,y2);
  LCD_DrawLine(x2,y1,x2,y2);
}

//函数功能:在指定位置画一个指定大小的圆
//(x,y):中心点
//r:半径
void LCD_Draw_Circle(u16 x0,u16 y0,u8 r)
{
  int a,b;
  int di;

  a=0;b=r;
  di=3-(r<<1);//判断下个点位置的标志
  while(a<=b)
  {
    LCD_DrawPoint(x0+a,y0-b);//5
    LCD_DrawPoint(x0+b,y0-a);//0
    LCD_DrawPoint(x0+b,y0+a);//4
    LCD_DrawPoint(x0+a,y0+b);//6
    LCD_DrawPoint(x0-a,y0+b);//1
    LCD_DrawPoint(x0-b,y0+a);
    LCD_DrawPoint(x0-a,y0-b);//2
    LCD_DrawPoint(x0-b,y0-a);//7
    a++;
    //使用Bresenham算法画圆
    if(di<0)di +=4*a+6;
    else
    {
      di+=10+4*(a-b);
      b--;
    }
  }
}

//函数功能:在指定位置,显示一个字符
//x,y:起始坐标
//num:要显示的字符:" "--->"~"
//size:字体大小 12/16/24/32
//mode:叠加方式(1)还是非叠加方式(0)
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode)
{
  u8 temp,t1,t;
  u16 y0=y;
  u8 csize=(size/8+((size%8)?1:0))*(size/2);//得到字体一个字符对应点阵集所占的字节数	
  num=num-' ';//得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)
  for(t=0;t<csize;t++)
  {
    if(size==12)temp=asc2_1206[num][t];//调用1206字体
    else if(size==16)temp=asc2_1608[num][t];//调用1608字体
    else if(size==24)temp=asc2_2412[num][t];//调用2412字体
    else if(size==32)temp=asc2_3216[num][t];//调用3216字体
    else return;//没有的字库

    for(t1=0;t1<8;t1++)
    {
      if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
      else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
      temp<<=1;
      y++;
      if(y>=lcddev.height)return;//超区域了
      if((y-y0)==size)
      {
        y=y0;
        x++;
        if(x>=lcddev.width)return;//超区域了
        break;
      }
    }
  }
}

//函数功能:m^n函数
//返回值:m^n次方.
u32 LCD_Pow(u8 m,u8 n)
{
  u32 result=1;
  while(n--)result*=m;
  return result;
}

//函数功能:显示一个数字,高位为0,则不显示
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//color:颜色 
//num:数值(0~4294967295);
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size)
{
  u8 t,temp;
  u8 enshow=0;
  for(t=0;t<len;t++)
  {
    temp=(num/LCD_Pow(10,len-t-1))%10;
    if(enshow==0&&t<(len-1))
    {
      if(temp==0)
      {
        LCD_ShowChar(x+(size/2)*t,y,' ',size,0);
        continue;
      }
      else enshow=1;
    }
    LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,0);
  }
}

//函数功能:显示数字,高位为0,还是显示
//x,y:起点坐标
//num:数值(0~999999999);
//len:长度(即要显示的位数)
//size:字体大小
//mode:
//[7]:0,不填充;1,填充0.
//[6:1]:保留
//[0]:0,非叠加显示;1,叠加显示.
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode)
{
  u8 t,temp;
  u8 enshow=0;
  for(t=0;t<len;t++)
  {
    temp=(num/LCD_Pow(10,len-t-1))%10;
    if(enshow==0&&t<(len-1))
    {
      if(temp==0)
      {
        if(mode&0X80)LCD_ShowChar(x+(size/2)*t,y,'0',size,mode&0X01);
        else LCD_ShowChar(x+(size/2)*t,y,' ',size,mode&0X01);
        continue;
      }
      else enshow=1;
    }
    LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,mode&0X01);
  }
}

//函数工能:显示一个字符串,12/16字体
//x,y:起点坐标
//width,height:区域大小
//size:字体大小
//*p:字符串起始地址
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)
{
  u8 x0=x;
  width+=x;
  height+=y;
  while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
  {
    if(x>=width){x=x0;y+=size;}
    if(y>=height)break;//退出
    LCD_ShowChar(x,y,*p,size,0);
    x+=size/2;
    p++;
  }
}
//

lcd.h程序

#ifndef __LCD_H
#define __LCD_H		
#include "sys.h"
//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
//使能s8,u8,s16,u16,s32,u32
//使能常数数据类型sc8,uc8,sc16,uc16,sc32,uc32
//使能vs8,vu8,vs16,vu16,vs32,vu32
//使能vsc8,vuc8,vsc16,vuc16,vsc32,vuc32

用户使用到的变量开始//
//LCD重要参数集 
extern u32  POINT_COLOR;//LCD的画笔颜色,默认红色    
extern u32  BACK_COLOR; //LCD的背景颜色.默认为白色
typedef struct
{
  u16 width;//LCD 宽度
	u16 height;//LCD 高度
	u16 id;//LCD ID
	u8  dir;//横屏还是竖屏控制:0,竖屏;1,横屏。	
	u16	wramcmd;//开始写gram指令
	u16 setxcmd;//设置x坐标指令
	u16 setycmd;//设置y坐标指令
}_lcd_dev;
extern _lcd_dev lcddev;	//管理LCD重要参数 
用户使用到的变量结束//

//画笔颜色
#define WHITE    0xFFFF
#define BLACK    0x0000
#define BLUE     0x001F
#define BRED     0XF81F
#define GRED     0XFFE0
#define GBLUE    0X07FF
#define RED      0xF800
#define MAGENTA  0xF81F
#define GREEN    0x07E0
#define CYAN     0x7FFF
#define YELLOW   0xFFE0
#define BROWN    0XBC40 //棕色
#define BRRED    0XFC07 //棕红色
#define GRAY     0X8430 //灰色
//GUI颜色

#define DARKBLUE   0X01CF  //深蓝色
#define LIGHTBLUE  0X7D7C  //浅蓝色
#define GRAYBLUE   0X5458  //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN     0X841F //浅绿色
//#define LIGHTGRAY    0XEF5B //浅灰色(PANNEL)
#define LGRAY 			   0XC618 //浅灰色(PANNEL),窗体背景色

#define LGRAYBLUE  0XA651  //浅灰蓝色(中间层颜色)
#define LBBLUE     0X2B12  //浅棕蓝色(选择条目的反色)

extern void LCD_Clear(u32 color);
extern void LCD_Display_Dir(u8 dir);
extern void LCD_Init(void);
extern void LCD_DisplayOn(void);
extern void LCD_DisplayOff(void);
extern void LCD_DrawPoint(u16 x,u16 y);
extern void LCD_Fast_DrawPoint(u16 x,u16 y,u32 color);
extern u32 LCD_ReadPoint(u16 x,u16 y);
extern void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u32 color);
extern void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color);
extern void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);
extern void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);
extern void LCD_Draw_Circle(u16 x0,u16 y0,u8 r);
extern void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);
extern void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);
extern void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);
extern void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p);
#endif

小结:

MCU屏通常较贵,使用CPU直接驱动RGB屏不仅提升了动画效果,而且成本较低,值得推荐使用。但是,大部分CPU是没有LTDC控制器,所以MCU屏还是很有市场需求的。


网站公告

今日签到

点亮在社区的每一天
去签到