一、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位数据线,用于传输数据或命令。
D9:第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,使用CPU的BANK1的第4区CPU的BANK1的第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屏的内部没有专用的驱动芯片,因此需要CPU有LTDC控制器,显示数据位于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(<DC_Handler); //使能LTDC功能,Enable the LTDC
else if(sw==0)__HAL_LTDC_DISABLE(<DC_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(<DC_Handler,layerx); //使能LTDC的第layerx层,Enable the LTDC Layer
else if(sw==0) __HAL_LTDC_LAYER_DISABLE(<DC_Handler,layerx);//不使能LTDC的第layerx层,Disable the LTDC Layer
__HAL_LTDC_RELOAD_CONFIG(<DC_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(<DC_Handler,sx,sy,layerx); //设置窗口的位置,Set the LTDC window position
HAL_LTDC_SetWindowSize(<DC_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(<DC_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*)<dc_lcd_framebuf; //装载帧缓存数组
#else
lcdltdc.pixsize=2; //RGB565格式,则每个像素占2个字节
ltdc_framebuf[0]=(u32*)<dc_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(<DC_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屏还是很有市场需求的。