MCU屏和RGB屏

一、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 BANKSDRAM_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个BANKSDRAM_Handler.Init.CASLatency=FMC_SDRAM_CAS_LATENCY_3;               //CAS为3SDRAM_Handler.Init.WriteProtection=FMC_SDRAM_WRITE_PROTECTION_DISABLE;//失能写保护SDRAM_Handler.Init.SDClockPeriod=FMC_SDRAM_CLOCK_PERIOD_2;           //SDRAM时钟为HCLK/2=180M/2=90M=11.1nsSDRAM_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=683HAL_SDRAM_ProgramRefreshRate(&SDRAM_Handler,683);//设置刷新频率
}//函数功能;发送SDRAM初始化序列
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram)
{u32 temp=0;//SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAMSDRAM_Send_Cmd(0,FMC_SDRAM_CMD_CLK_ENABLE,1,0); //时钟配置使能delay_us(500);                                  //至少延时200usSDRAM_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_SDCKE0PD0---FMC_D2
PD1---FMC_D3
PD8---FMC_D13
PD9---FMC_D14
PD10---FMC_D15
PD14---FMC_D0
PD15---FMC_D1PE0---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_D12PF0---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_A9PG0---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_SDCKE0GPIO_InitStructure.Pin=GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3;//选择第0脚,第2脚和第3脚GPIO_InitStructure.Mode=GPIO_MODE_AF_PP;    //外设复用使用GPIO_MODE_AF_PPGPIO_InitStructure.Pull=GPIO_PULLUP;        //设置引脚pull状态为上拉GPIO_InitStructure.Speed=GPIO_SPEED_HIGH;   //设置引脚的工作速率为高速GPIO_InitStructure.Alternate=GPIO_AF12_FMC; //在STM32F429xx复用功能映射表中,映射到FMC接口都是AF12,所以使用GPIO_AF12_FMCHAL_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_D1GPIO_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_D12GPIO_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_A9GPIO_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_SDNCASGPIO_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,vuc32extern 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_RGB888u32 ltdc_lcd_framebuf[1280][800] __attribute__( (at(LCD_FRAME_BUF_ADDR)) );
//定义最大屏分辨率时,LCD所需的帧缓存数组大小
//定义LCD帧缓存数组ltdc_lcd_framebuf[][],共1280*800*4个字节空间,其首地址为0XC0000000,位于外部SDRAM中
#elseu16 ltdc_lcd_framebuf[1280][800] __attribute__((at(LCD_FRAME_BUF_ADDR)));
//定义最大屏分辨率时,LCD所需的帧缓存数组大小
//定义LCD帧缓存数组ltdc_lcd_framebuf[][],共1280*800*4个字节空间,其首地址为0XC0000000,位于外部SDRAM中
#endifu32 *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 LTDCelse 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 Layerelse 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_RGB888if(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表示竖屏的宽度}
#elseif(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_RGB888if(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表示竖屏的宽度}
#elseif(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);             //先停止DMA2DDMA2D->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;                //启动DMA2Dwhile((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);            //先停止DMA2DDMA2D->CR=DMA2D_M2M;                     //存储器到存储器模式DMA2D->FGPFCCR=LCD_PIXFORMAT;            //设置颜色格式DMA2D->FGOR=0;                           //前景层行偏移为0DMA2D->OOR=offline;                      //设置行偏移DMA2D->FGMAR=(u32)color;                 //源地址DMA2D->OMAR=addr;                        //输出存储器地址DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16);//设定行数寄存器DMA2D->CR|=DMA2D_CR_START;               //启动DMA2Dwhile((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 positionHAL_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;AL88pLayerCfg.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脚PG6GPIO_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和PI7HAL_GPIO_Init(GPIOI,&GPIO_InitStructure);//根据GPIO_InitStructure结构变量指定的参数初始化GPIOI的外设寄存器idx=(u8)HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_6);     //读取PG6,即为M0idx|=(u8)HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_2)<<1; //读取PI2,即为M1idx|=(u8)HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_7)<<2; //读取PI7,即为M2switch(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=1DCLKlcdltdc.vsw=1;        //垂直同步宽度,VYSNC pulse width:Tvwh=1DCLKlcdltdc.hbp=40;       //水平后廊,HSD back porch:Thbp=40DCLKlcdltdc.vbp=8;        //垂直后廊,VSD back porch:Tvbp=8DCLKlcdltdc.hfp=5;        //水平前廊,HSD front porch:Thfp=5DCLKlcdltdc.vfp=8;        //垂直前廊,VSD front porch:Tvfp=8DCLKLTDC_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=1DCLKlcdltdc.vsw=1;        //垂直同步宽度,VYSNC pulse width:Tvwh=1DCLKlcdltdc.hbp=46;       //水平后廊,HSD back porch:Thbp=46DCLKlcdltdc.vbp=23;       //垂直后廊,VSD back porch:Tvbp=23DCLKlcdltdc.hfp=210;      //水平前廊,HSD front porch:Thfp=210DCLKlcdltdc.vfp=22;       //垂直前廊,VSD front porch:Tvfp=22DCLKLTDC_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=20DCLKlcdltdc.vsw=3;       //垂直同步宽度,VYSNC pulse width:Tvwh=3DCLKlcdltdc.hbp=140;     //水平后廊,HSD back porch:Thbp=140DCLKlcdltdc.vbp=20;      //垂直后廊,VSD back porch:Tvbp=20DCLKlcdltdc.hfp=160;     //水平前廊,HSD front porch:Thfp=160DCLKlcdltdc.vfp=12;      //垂直前廊,VSD front porch:Tvfp=12DCLKLTDC_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=48DCLKlcdltdc.vsw=3;        //垂直同步宽度,VYSNC pulse width:Tvwh=3DCLKlcdltdc.hbp=88;       //水平后廊,HSD back porch:Thbp=88DCLKlcdltdc.vbp=32;       //垂直后廊,VSD back porch:Tvbp=32DCLKlcdltdc.hfp=40;       //水平前廊,HSD front porch:Thfp=40DCLKlcdltdc.vfp=13;       //垂直前廊,VSD front porch:Tvfp=13DCLKLTDC_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=10DCLKlcdltdc.vsw=3;        //垂直同步宽度,VYSNC pulse width:Tvwh=3DCLKlcdltdc.hbp=140;      //水平后廊,HSD back porch:Thbp=140DCLKlcdltdc.vbp=10;       //垂直后廊,VSD back porch:Tvbp=10DCLKlcdltdc.hfp=10;       //水平前廊,HSD front porch:Thfp=10DCLKlcdltdc.vfp=10;       //垂直前廊,VSD front porch:Tvfp=10DCLKLTDC_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_RGB888lcdltdc.pixsize=4;                         //ARGB8888和RGB888格式,则每个像素占4个字节ltdc_framebuf[0]=(u32*)&ltdc_lcd_framebuf; //装载帧缓存数组
#elselcdltdc.pixsize=2;                          //RGB565格式,则每个像素占2个字节ltdc_framebuf[0]=(u32*)&ltdc_lcd_framebuf;  //装载帧缓存数组
#endif//LTDC配置LTDC_Handler.Instance=LTDC;  //设置LTDC寄存器的基地址,LTDC Register base addressLTDC_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;//像素时钟极性elseLTDC_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_DEGPIO_InitStructure.Pin=GPIO_PIN_10;          //选择第10脚 GPIO_InitStructure.Mode=GPIO_MODE_AF_PP;     //外设复用使用GPIO_MODE_AF_PPGPIO_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_DEHAL_GPIO_Init(GPIOF,&GPIO_InitStructure);//根据GPIO_InitStructure结构变量指定的参数初始化GPIOF的外设寄存器//初始化PG6,PG7,PG11
//PG6映射到LCD_R7,PG7映射到LCD_CLK,PG11映射到LCD_B3GPIO_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_G4GPIO_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_HSYNCGPIO_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 IDu8  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);//5LCD_DrawPoint(x0+b,y0-a);//0LCD_DrawPoint(x0+b,y0+a);//4LCD_DrawPoint(x0+a,y0+b);//6LCD_DrawPoint(x0-a,y0+b);//1LCD_DrawPoint(x0-b,y0+a);LCD_DrawPoint(x0-a,y0-b);//2LCD_DrawPoint(x0-b,y0-a);//7a++;//使用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 IDu8  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屏还是很有市场需求的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7.5 使用MobileNet v3进行图像的区分

MobileNet v3是Google在2019年提出的轻量级卷积神经网络结构,旨在提高在移动设备上的速度和准确性,广泛的用于轻量级网络。 MobileNet v3-Small的网络结构如下,它的输入是224x224的3通道彩色图片。 使用过程如下: 1.创建模型、修改最终分类数量 #1.创建mobilenet_v3_small…

构建面向大模型训练与部署的一体化架构:从文档解析到智能调度

作者&#xff1a;汪玉珠&#xff5c;算法架构师 标签&#xff1a;大模型训练、数据集构建、GRPO、自监督聚类、指令调度系统、Qwen、LLaMA3 &#x1f9ed; 背景与挑战 随着 Qwen、LLaMA3 等开源大模型不断进化&#xff0c;行业逐渐从“能跑通”迈向“如何高效训练与部署”的阶…

PostgreSQL技术大讲堂 - 第86讲:数据安全之--data_checksums天使与魔鬼

PostgreSQL技术大讲堂 - 第86讲&#xff0c;主题&#xff1a;数据安全之--data_checksums天使与魔鬼 1、data_checksums特性 2、避开DML规则&#xff0c;嫁接非法数据并合法化 3、避开约束规则&#xff0c;嫁接非法数据到表中 4、避开数据检查&#xff0c;读取坏块中的数据…

【机器学习】机器学习笔记

1 机器学习定义 计算机程序从经验E中学习&#xff0c;解决某一任务T&#xff0c;进行某一性能P&#xff0c;通过P测定在T上的表现因经验E而提高。 eg&#xff1a;跳棋程序 E&#xff1a; 程序自身下的上万盘棋局 T&#xff1a; 下跳棋 P&#xff1a; 与新对手下跳棋时赢的概率…

Ubuntu20.04 设置开机自启

参考&#xff1a; Ubuntu20.04 设置开机自启_ubuntu进bos系统-CSDN博客

数据库中存储过程的流程语句讲解

一、流程语句讲解 二、总结 一、流程语句讲解 1.1 if语句讲解 语法&#xff1a; IF condition THENstatements; ELSEIF condition THENstatements; ELSEstatements; END IF; 题目示例&#xff1a; # 判断成绩等级 # 输入学生的编号,取出学生的第一门课&#xff0c;然后判断…

kubernetes》》k8s》》ConfigMap 、Secret

configmap官网 ConfigMap是一种 API 对象&#xff0c;使用时&#xff0c; Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。ConfigMap将配置和Pod解耦&#xff0c;更易于配置文件的更改和管理。ConfigMap 并不提供保密或者加密功能。 如果你想存储的数据是机密的…

git在IDEA中使用技巧

git在IDEA中使用技巧 merge和rebase 参考&#xff1a;IDEA小技巧-Git的使用 git回滚、强推、代码找回 参考&#xff1a;https://www.bilibili.com/video/BV1Wa411a7Ek?spm_id_from333.788.videopod.sections&vd_source2f73252e51731cad48853e9c70337d8e cherry pick …

Spring 事务失效的原因及解决方案全解析,来复习了

Spring 事务失效是指在使用 Spring 声明式事务管理时&#xff0c;预期的事务行为&#xff08;如事务的开启、提交、回滚等&#xff09;未按预期执行&#xff0c;导致数据操作未满足 ACID 特性&#xff08;原子性、一致性、隔离性、持久性&#xff09;&#xff0c;从而引发数据不…

「出海匠」借助CloudPilot AI实现AWS降本60%,支撑AI电商高速增长

&#x1f50e;公司简介 「出海匠」&#xff08;chuhaijiang.com&#xff09;是「数绘星云」公司打造的社交内容电商服务平台&#xff0c;专注于为跨境生态参与者提供数据支持与智能化工作流。平台基于大数据与 AI 技术&#xff0c;帮助商家精准分析市场趋势、优化运营策略&…

python每日一练

题目一 输入10个整数,输出其中不同的数,即如果一个数出现了多次,只输出一次(要求按照每一个不同的数第一次出现的顺序输出)。 解题 错误题解 a list(map(int,input().split())) b [] b.append(a[i]) for i in range(2,11):if a[i] not in b:b.append(a[i]) print(b)但是会…

Docker实战:从零构建高可用的MySQL主从集群与Redis集群

在分布式系统架构中&#xff0c;数据库集群是保障数据高可用和性能的关键组件。本文将通过Docker技术&#xff0c;手把手教你搭建MySQL主从集群和Redis Cluster&#xff0c;并分享独创的优化技巧与运维实战经验。 一、为什么选择Docker部署集群&#xff1f; 传统数据库集群搭…

STM32电机库 电机控制特性

ST MC FW库提供FOC和六步法两种电机控制方式。这使得它能够驱动永磁同步电机 (PMSM) 和无刷直流电机 (BLDC)。FOC 更适合 PMSM,而六步法更适合 BLDC 电机。该固件可以驱动内嵌式PMSM 和标贴式PMSM。 ST Motor Control 固件库提供以下功能: FOC SVPWM 生成: 可配置的 PW…

Go:方法

方法声明 type point struct { X, Y float64 }// 普通函数 func Distance(p, q Point) float64 {return math.Hypot(q.x - p.x, q.y - p.Y) }// Point类型的方法 func (p Point) Distance(q Point) float64 {return math.Hypot(q.x - p.x, q.y - p.Y) }方法声明与普通函数声…

前端基础之《Vue(4)—响应式原理》

一、什么是响应式 1、响应式英文reactive 当你get/set一个变量时&#xff0c;你有办法可以“捕获到”这种行为。 2、一个普通对象和一个响应式对象对比 &#xff08;1&#xff09;普通对象 <script>// 这种普通对象不具备响应式var obj1 {a: 1,b: 2} </script>…

【技术派部署篇】Windows本地部署技术派

一、技术派简介 技术派是一个采用 Spring Boot、MyBatis-Plus、MySQL、Redis、ElasticSearch、MongoDB、Docker、RabbitMQ 等技术栈的社区系统&#xff0c;其 1.0 版已正式上线。该项目的技术栈按阶段集成引入&#xff0c;开发者可根据自身需求选择不同版本进行学习。 二、环…

DeepSeek和ChatGPT的全面对比

DeepSeek和ChatGPT作为当前领先的大语言模型&#xff0c;代表了AI发展的不同技术路径和应用理念。以下从技术架构到用户体验的全面对比分析&#xff0c;将揭示两者在AI竞赛中的独特定位。 一、模型架构与原理 1. DeepSeek 架构特点&#xff1a;采用混合专家系统&#xff08;…

Python星球日记 - 第20天:数据分析入门

🌟引言: 欢迎来到Python星球🪐的第20天!今天我们将踏入数据分析的世界,学习如何使用pandas处理数据并提取有价值的信息。无论你是想分析商业销售数据、股票市场趋势还是科学实验结果,pandas都是你必不可少的工具! 上一篇:Python星球日记 - 第19天:Web开发基础 名人…

算力云平台部署—SadTalker的AI数字人视频

选择算力 部署选择 选择镜像 机器管理 控制台 通过平台工具进入服务器 认识管理系统 打开命令行 进入目录 stable-diffusion-webui# cd 增加执行权限 chmod x ./webui.sh 运行命令 bash ./webui.sh sudo apt install -y python3 python3-venv git 安装软件 Creating the …

Linux目录结构:核心目录功能与用途解析

引言 Linux的目录结构就像一棵精心设计的大树&#x1f333;&#xff0c;每个分支都有其特定的用途和规范&#xff01;与Windows不同&#xff0c;Linux采用单一的目录层次结构&#xff0c;所有设备、分区和网络资源都挂载在这个统一的目录树下。本文将带你深入探索Linux目录结构…