stm32——lcd液晶显示

news2024/11/24 5:28:46

一.液晶屏介绍

        液晶显示屏是由液晶显示面板,电容触摸屏,pcb底板构成。在液晶显示屏里我们有带控制芯片的还有不带控制芯片的。对于低端的微控制器它不能直接控制液晶面板,所以需要给液晶控制面板而外增加一个液晶控制芯片。对于单片机stm32f429系列的芯片就不需要额外的液晶控制器,该芯片将液晶控制的功能集成到该芯片的内部了,对于f1系列就需要液晶屏带液晶控制芯片。

二.液晶面板的信号控制 

        液晶面板的控制信号线(不带液晶控制器):

· RGB信号线 :RGB信号线各有8根,分别用于表示液晶屏一个像素点的红、绿、蓝颜色分量。

· 同步时钟信号CLK:液晶屏与外部使用同步通讯方式,以CLK信号作为同步时钟,在同步时钟的驱动下,每个时钟传输一个像素点数据。

· 水平同步信号HSYNC:水平同步信号HSYNC(Horizontal Sync)用于表示液晶屏一行像素数据的传输结束,每传输完成液晶屏的一行像素数据时,HSYNC会发生电平跳变,如分辨率为800x480的显示屏(800列,480行),传输一帧的图像HSYNC的电平会跳变480次。

· 垂直同步信号VSYNC: 垂直同步信号VSYNC(Vertical Sync)用于表示液晶屏一帧像素数据的传输结束,每传输完成一帧像素数据时,VSYNC会发生电平跳变。其中“帧”是图像的单位,一幅图像称为一帧,在液晶屏中,一帧指一个完整屏液晶像素点。人们常常用“帧/秒”来表示液晶屏的刷新特性,即液晶屏每秒可以显示多少帧图像,如液晶屏以60帧/秒的速率运行时,VSYNC每秒钟电平会跳变60次。

· 数据使能信号DE: 数据使能信号DE(Data Enable)用于表示数据的有效性,当DE信号线为高电平时,RGB信号线表示的数据有效。

传输一帧图像数据的时序:


        液晶屏显示的图像可看作一个矩形,液晶屏有一个显示指针,它指向将要显示的像素。显示指针的扫描方向方向从左到右、从上到下,一个像素点一个像素点地描绘图形。这些像素点的数据通过RGB数据线传输至液晶屏,它们在同步时钟CLK的驱动下一个一个地传输到液晶屏中,交给显示指针,传输完成一行时,水平同步信号HSYNC电平跳变一次,而传输完一帧时VSYNC电平跳变一次。 

液晶显示指针在行与行之间,帧与帧之间切换时需要延时,而且HSYNC及VSYNC信号本身也有宽度,这些时间参数说明见下表:

 在这些时间参数控制的区域,数据使能信号线“DE”都为低电平,RGB数据线的信号无效,当“DE”为高电平时,表示的数据有效,传输的数据会直接影响液晶屏的显示区域。


显存

        液晶屏中的每个像素点都是数据,在实际应用中需要把每个像素点的数据缓存起来,再传输给液晶屏,这种存储显示数据的存储器被称为显存。显存一般至少要能存储液晶屏的一帧显示数据,如分辨率为800x480的液晶屏,使用RGB888格式显示,它的一帧显示数据大小为:3x800x480=1152000字节;若使用RGB565格式显示,一帧显示数据大小为:2x800x480=768000字节。

三.液晶控制原理

        在这里我们要讲解的是LCD电阻屏,在这个屏幕里面集成了ili9341液晶控制芯片,该控制器芯片中存在显存。使用的是8080接口与单片机通讯,液晶面板引出的FPC信号线即8080接口(RGB接口已在内部直接与ILI9341相连)。PCB底板,它主要包含了一个电阻触摸屏的控制器XPT2046,电阻触摸屏控制器实质上是一个ADC芯片,通过检测电压值来计算触摸坐标。PCB底板与液晶触摸面板通过FPC排线座连接,然后引出到排针,方便与实验板的排母连接。

 四.ili9341液晶显示控制器

ili9341内部结构图:

液晶显示屏原理图:


液晶屏的信号线及8080时序:

        LI9341控制器根据自身的IM[3:0]信号线电平决定它与MCU的通讯方式,其支持SPI及8080通讯方式,本示例中液晶屏被配置为8080接口通讯,用16根数据线的RGB565格式。内部硬件电路连接完,剩下的其它信号线被引出到FPC排线,最后该排线由PCB底板引出到排针,排针再与实验板上的STM32芯片连接,引出的排针信号线如下图:

        这些引出的信号线即8080通讯接口,带X的表示低电平有效。通讯的内容主要包括命令和显存数据,显存数据即各个像素点的RGB565内容;命令是指对ILI9341的控制指令,MCU可通过8080接口发送命令编码控制ILI9341的工作方式,例如复位指令、设置光标指令、睡眠模式指令等等,具体的指令在《ILI9341.pdf》数据手册均有详细说明。 


向ILI9341写命令的时序图: 

    写命令时序由片选信号CSX拉低开始,对数据/命令选择信号线D/CX也置低电平表示写入的是命令地址(可理解为命令编码,如软件复位命令:0x01),以写信号WRX为低,读信号RDX为高表示数据传输方向为写入,同时,在数据线D[17:0](或D[15:0])输出命令地址,在第二个传输阶段传送的是命令的参数,所以D/CX要置高电平,表示写入的是命令数据,命令数据是某些指令带有的参数,如复位指令编码为0x01,它后面可以带一个参数,该参数表示多少秒后复位(实际的复位命令不含参数,此处只是为了讲解指令编码与参数的区别)。 当需要把像素数据写入GRAM时,过程很类似,把片选信号CSX拉低后,再把数据/命令选择信号线D/CX置为高电平,这时由D[17:0]传输的数据则会被ILI9341保存至它的GRAM中。 

五.FSMC模拟8080时序

        8080时序可以用i/o口翻转电平进行通讯模拟实现,但是这种方法的效率太低。在stm32中我们有FSMC功能我们可以用这个外设实现8080时序。(FSMC外设可以用于控制扩展的外部存储器)


FSMC功能框图

控制LCD时,适合使用FSMC的NOR\PSRAM模式,它与前面使用FSMC控制SRAM的稍有不同,控制SRAM时使用的是模式A,而控制LCD时使用的是与NOR FLASH一样的模式B,所以我们重点分析框图中NOR FLASH控制信号线部分。

在控制LCD时,使用的是类似异步、地址与数据线独立的NOR FLASH控制方式,所以实际上CLK、NWAIT、NADV引脚并没有使用到。 


FSMC NOR 中的模式B的时序图:

用FSMC模拟8080时序:

 对比FSMC NOR/PSRAM中的模式B时序与ILI9341液晶控制器芯片使用的8080时序可发现,这两个时序是十分相似的(除了FSMC的地址线A和8080的D/CX线,可以说是完全一样)


FSMC时序结构体介绍

        与控制SRAM时一样,控制FSMC使用NOR FLASH存储器时主要是配置时序寄存器以及控制寄存器,利用ST标准库的时序结构体以及初始化结构体可以很方便地写入参数。

· FSMC_AddressSetupTime   本成员设置地址建立时间,它可以被设置为0-0xF个HCLK周期数,按STM32标准库的默认配置,HCLK的时钟频率为72MHz,即一个HCLK周期为1/72微秒。

· FSMC_AddressHoldTime    本成员设置地址保持时间,它可以被设置为0-0xF个HCLK周期数。

· FSMC_DataSetupTime   本成员设置数据建立时间,它可以被设置为0-0xF个HCLK周期数。 

· FSMC_BusTurnAroundDuration 本成员设置总线转换周期,在NOR FLASH存储器中,地址线与数据线可以分时复用,总线转换周期就是指总线在这两种状态间切换需要的延时,防止冲突。控制其它存储器时这个参数无效,配置为0即可。 

· FSMC_CLKDivision 本成员用于设置时钟分频,它以HCLK时钟作为输入,经过FSMC_CLKDivision分频后输出到FSMC_CLK引脚作为通讯使用的同步时钟。控制其它异步通讯的存储器时这个参数无效,配置为0即可。

· FSMC_DataLatency 本成员设置数据保持时间,它表示在读取第一个数据之前要等待的周期数,该周期指同步时钟的周期,本参数仅用于同步NOR FLASH类型的存储器,控制其它类型的存储器时,本参数无效。

· FSMC_AccessMode 本成员设置存储器访问模式,不同的模式下FSMC访问存储器地址时引脚输出的时序不一样,可选FSMC_AccessMode_A/B/C/D模式。一般来说控制异步NOR FLASH时使用B模式。


FSMC的NOR FLASH初始化结构体介绍

· FSMC_Bank 本成员用于选择FSMC映射的存储区域,它的可选参数以及相应的内核地址映射范围见上面的表格。

· FSMC_DataAddressMux     本成员用于设置地址总线与数据总线是否复用。(FSMC_DataAddressMux_Enable /Disable),在控制NOR FLASH时,可以地址总线与数据总线可以分时复用,以减少使用STM32信号线的数量。

· FSMC_MemoryType     本成员用于设置要控制的存储器类型,它支持控制的存储器类型为SRAM、PSRAM以及NOR FLASH(FSMC_MemoryType_SRAM/PSRAM/NOR)。

· FSMC_MemoryDataWidth     本成员用于设置要控制的存储器的数据宽度,可选择设置成8或16位(FSMC_MemoryDataWidth_8b /16b)。

· FSMC_BurstAccessMode     本成员用于设置是否使用突发访问模式。(FSMC_BurstAccessMode_Enable/Disable),突发访问模式是指发送一个地址后连续访问多个数据,非突发模式下每访问一个数据都需要输入一个地址,仅在控制同步类型的存储器时才能使用突发模式。

· FSMC_AsynchronousWait     本成员用于设置是否使能在同步传输时使用的等待信号。(FSMC_AsynchronousWait_Enable/Disable),在控制同步类型的NOR或PSRAM时,存储器可以使用FSMC_NWAIT引脚通知STM32需要等待。

· FSMC_WaitSignalPolarity     本成员用于设置等待信号的有效极性,即要求等待时,使用高电平还是低电平(FSMC_WaitSignalPolarity_High/Low)。

· FSMC_WrapMode     本成员用于设置是否支持把非对齐的AHB突发操作分割成2次线性操作(FSMC_WrapMode_Enable/Disable),该配置仅在突发模式下有效。

· FSMC_WaitSignalActive     本成员用于配置在突发传输模式时,决定存储器是在等待状态之前的一个数据周期有效还是在等待状态期间有效(FSMC_WaitSignalActive_BeforeWaitState/DuringWaitState)。

· FSMC_WriteOperation     这个成员用于设置是否写使能(FSMC_WriteOperation_ Enable /Disable),禁止写使能的话FSMC只能从存储器中读取数据,不能写入。

· FSMC_WaitSignal     本成员用于设置当存储器牌突发传输模式时,是否允许通过NWAIT信号插入等待状态(FSMC_WaitSignal_Enable/Disable)。

· FSMC_ExtendedMode     本成员用于设置是否使用扩展模式(FSMC_ExtendedMode_Enable/Disable),在非扩展模式下,对存储器读写的时序都只使用FSMC_BCR寄存器中的配置,即下面的FSMC_ReadWriteTimingStruct结构体成员;在扩展模式下,对存储器的读写时序可以分开配置,读时序使用FSMC_BCR寄存器,写时序使用FSMC_BWTR寄存器的配置,即下面的FSMC_WriteTimingStruct结构体。

· FSMC_ReadWriteTimingStruct     本成员是一个指针,赋值时使用上一小节中讲解的时序结构体FSMC_NORSRAMInitTypeDef设置,当不使用扩展模式时,读写时序都使用本成员的参数配置。

· FSMC_WriteTimingStruct     同样地,本成员也是一个时序结构体的指针,只有当使用扩展模式时,本配置才有效,它是写操作使用的时序。 


FSMC模拟8080时序及地址的计算可以看下面连接文章

FSMC模拟8080时序及地址的计算-CSDN博客


六.LCD液晶显示屏的驱动程序

液晶屏硬件原理图


对引脚进行宏定义

/*LCD控制信号****************************/
#ifdef USE_ZNZ

	#define     FSMC_BANK_NORSRAMx				FSMC_Bank1_NORSRAM1
	
	#define 		ILI9341_CMD_ADDR  		(__IO uint16_t*)(0x60000000)
    #define 		ILI9341_DATA_ADDR  		(__IO uint16_t*)(0x60020000)
 
	#define      ILI9341_CS_CLK           RCC_APB2Periph_GPIOD
	#define      ILI9341_CS_PORT          GPIOD
	#define      ILI9341_CS_PIN           GPIO_Pin_7

	#define      ILI9341_RST_CLK           RCC_APB2Periph_GPIOE
	#define      ILI9341_RST_PORT          GPIOE
	#define      ILI9341_RST_PIN           GPIO_Pin_1

	#define      ILI9341_BK_CLK           RCC_APB2Periph_GPIOD
	#define      ILI9341_BK_PORT          GPIOD
	#define      ILI9341_BK_PIN           GPIO_Pin_12

	#define      ILI9341_RD_CLK           RCC_APB2Periph_GPIOD
	#define      ILI9341_RD_PORT          GPIOD
	#define      ILI9341_RD_PIN           GPIO_Pin_4

	#define      ILI9341_WR_CLK           RCC_APB2Periph_GPIOD
	#define      ILI9341_WR_PORT          GPIOD
	#define      ILI9341_WR_PIN           GPIO_Pin_5

	#define      ILI9341_DC_CLK           RCC_APB2Periph_GPIOD
	#define      ILI9341_DC_PORT          GPIOD
	#define      ILI9341_DC_PIN           GPIO_Pin_11

#else

	#define     FSMC_BANK_NORSRAMx				FSMC_Bank1_NORSRAM4
	
	#define 		ILI9341_CMD_ADDR  		(__IO uint16_t*)(0x6c000000)
    #define 		ILI9341_DATA_ADDR  		(__IO uint16_t*)(0x6d000000)

	#define      ILI9341_CS_CLK           RCC_APB2Periph_GPIOG
	#define      ILI9341_CS_PORT          GPIOG
	#define      ILI9341_CS_PIN           GPIO_Pin_12

	#define      ILI9341_RST_CLK           RCC_APB2Periph_GPIOG
	#define      ILI9341_RST_PORT          GPIOG
	#define      ILI9341_RST_PIN           GPIO_Pin_11


	#define      ILI9341_BK_CLK           RCC_APB2Periph_GPIOG
	#define      ILI9341_BK_PORT          GPIOG
	#define      ILI9341_BK_PIN           GPIO_Pin_6


	#define      ILI9341_RD_CLK           RCC_APB2Periph_GPIOD
	#define      ILI9341_RD_PORT          GPIOD
	#define      ILI9341_RD_PIN           GPIO_Pin_4

	#define      ILI9341_WR_CLK           RCC_APB2Periph_GPIOD
	#define      ILI9341_WR_PORT          GPIOD
	#define      ILI9341_WR_PIN           GPIO_Pin_5

	#define      ILI9341_DC_CLK           RCC_APB2Periph_GPIOE
	#define      ILI9341_DC_PORT          GPIOE
	#define      ILI9341_DC_PIN           GPIO_Pin_2
	
#endif

#define      ILI9341_D0_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D0_PORT               GPIOD
#define      ILI9341_D0_PIN                GPIO_Pin_14

#define      ILI9341_D1_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D1_PORT               GPIOD
#define      ILI9341_D1_PIN                GPIO_Pin_15

#define      ILI9341_D2_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D2_PORT               GPIOD
#define      ILI9341_D2_PIN                GPIO_Pin_0

#define      ILI9341_D3_CLK                RCC_APB2Periph_GPIOD  
#define      ILI9341_D3_PORT               GPIOD
#define      ILI9341_D3_PIN                GPIO_Pin_1

#define      ILI9341_D4_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D4_PORT               GPIOE
#define      ILI9341_D4_PIN                GPIO_Pin_7

#define      ILI9341_D5_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D5_PORT               GPIOE
#define      ILI9341_D5_PIN                GPIO_Pin_8

#define      ILI9341_D6_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D6_PORT               GPIOE
#define      ILI9341_D6_PIN                GPIO_Pin_9

#define      ILI9341_D7_CLK                RCC_APB2Periph_GPIOE  
#define      ILI9341_D7_PORT               GPIOE
#define      ILI9341_D7_PIN                GPIO_Pin_10

#define      ILI9341_D8_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D8_PORT               GPIOE
#define      ILI9341_D8_PIN                GPIO_Pin_11

#define      ILI9341_D9_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D9_PORT               GPIOE
#define      ILI9341_D9_PIN                GPIO_Pin_12

#define      ILI9341_D10_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D10_PORT               GPIOE
#define      ILI9341_D10_PIN                GPIO_Pin_13

#define      ILI9341_D11_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D11_PORT               GPIOE
#define      ILI9341_D11_PIN                GPIO_Pin_14

#define      ILI9341_D12_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D12_PORT               GPIOE
#define      ILI9341_D12_PIN                GPIO_Pin_15

#define      ILI9341_D13_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D13_PORT               GPIOD
#define      ILI9341_D13_PIN                GPIO_Pin_8

#define      ILI9341_D14_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D14_PORT               GPIOD
#define      ILI9341_D14_PIN                GPIO_Pin_9

#define      ILI9341_D15_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D15_PORT               GPIOD
#define      ILI9341_D15_PIN                GPIO_Pin_10

注意:

就是让编译器不要优化代码。 


引脚初始化配置(数据引脚和FSMC外设)

void ILI9341_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	// 打开GPIO的时钟
	RCC_APB2PeriphClockCmd(ILI9341_CS_CLK|ILI9341_RST_CLK|ILI9341_BK_CLK|ILI9341_RD_CLK|
						ILI9341_WR_CLK|ILI9341_DC_CLK|ILI9341_D0_CLK|ILI9341_D1_CLK|ILI9341_D2_CLK|
						ILI9341_D3_CLK|ILI9341_D4_CLK|ILI9341_D5_CLK|ILI9341_D6_CLK|ILI9341_D7_CLK|
						ILI9341_D8_CLK|ILI9341_D9_CLK|ILI9341_D10_CLK|ILI9341_D11_CLK|ILI9341_D12_CLK|
						ILI9341_D13_CLK|ILI9341_D14_CLK|ILI9341_D15_CLK	, ENABLE);
	
	// 将的GPIO配置为推挽模式
	GPIO_InitStructure.GPIO_Pin = ILI9341_BK_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ILI9341_BK_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_RST_PIN;
	GPIO_Init(ILI9341_RST_PORT, &GPIO_InitStructure);
		
	// 将的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = ILI9341_CS_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ILI9341_CS_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_RD_PIN;
	GPIO_Init(ILI9341_RD_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_WR_PIN;
	GPIO_Init(ILI9341_WR_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_DC_PIN;
	GPIO_Init(ILI9341_DC_PORT, &GPIO_InitStructure);
		
		/* 配置FSMC相对应的数据线,FSMC-D0~D15 */	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_AF_PP;
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D0_PIN;
	GPIO_Init ( ILI9341_D0_PORT, & GPIO_InitStructure );

	GPIO_InitStructure.GPIO_Pin = ILI9341_D1_PIN;
	GPIO_Init ( ILI9341_D1_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D2_PIN;
	GPIO_Init ( ILI9341_D2_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D3_PIN;
	GPIO_Init ( ILI9341_D3_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D4_PIN;
	GPIO_Init ( ILI9341_D4_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D5_PIN;
	GPIO_Init ( ILI9341_D5_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D6_PIN;
	GPIO_Init ( ILI9341_D6_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D7_PIN;
	GPIO_Init ( ILI9341_D7_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D8_PIN;
	GPIO_Init ( ILI9341_D8_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D9_PIN;
	GPIO_Init ( ILI9341_D9_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D10_PIN;
	GPIO_Init ( ILI9341_D10_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D11_PIN;
	GPIO_Init ( ILI9341_D11_PORT, & GPIO_InitStructure );

	GPIO_InitStructure.GPIO_Pin = ILI9341_D12_PIN;
	GPIO_Init ( ILI9341_D12_PORT, & GPIO_InitStructure );	
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D13_PIN;
	GPIO_Init ( ILI9341_D13_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D14_PIN;
	GPIO_Init ( ILI9341_D14_PORT, & GPIO_InitStructure );
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_D15_PIN;
	GPIO_Init ( ILI9341_D15_PORT, & GPIO_InitStructure );
}
void ILI9341_FSMC_Config(void)
{
	FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
	FSMC_NORSRAMTimingInitTypeDef  readWriteTiming;
		
	/*使能FSMC外设时钟*/
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);

	//地址建立时间(ADDSET)
	readWriteTiming.FSMC_AddressSetupTime = 0x01;	
	//数据保持时间(DATAST)
	readWriteTiming.FSMC_DataSetupTime = 0x04;		 
	
	//地址保持时间(ADDHLD)模式B未用到
	readWriteTiming.FSMC_AddressHoldTime = 0x00;	 
	
	//设置总线转换周期,仅用于复用模式的NOR操作
	readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
	
	//设置时钟分频,仅用于同步类型的存储器
	readWriteTiming.FSMC_CLKDivision = 0x00;	

	//数据保持时间,仅用于NOR
	readWriteTiming.FSMC_DataLatency = 0x00;		
	
	//选择匹配SRAM的模式
	readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_B;	 
    
	// 选择FSMC映射的存储区域: Bank1 NORSRAMx
	FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_BANK_NORSRAMx; 
	
	//设置地址总线与数据总线是否复用,仅用于NOR
	FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; 
	
	//设置要控制的存储器类型:NOR类型
	FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_NOR;   
	
	//存储器数据宽度:16位
	FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; 
	
	//设置是否使用突发访问模式,仅用于同步类型的存储器
	FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;
	
	//设置是否使能等待信号,仅用于同步类型的存储器
	FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
	
	//设置等待信号的有效极性,仅用于同步类型的存储器
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
	
	//设置是否支持把非对齐的突发操作,仅用于同步类型的存储器
	FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; 
	
	//设置等待信号插入的时间,仅用于同步类型的存储器
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive =         
    FSMC_WaitSignalActive_BeforeWaitState;
	
	//存储器写使能 
	FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
	
	//不使用等待信号
	FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;  		
	
	// 不使用扩展模式,读写使用相同的时序
	FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; 
	
	//突发写操作
	FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;  
	
	//读写时序配置
	FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;
	
	//读写同样时序,使用扩展模式时这个配置才有效
	FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &readWriteTiming; 

	FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);  //初始化FSMC配置

	FSMC_NORSRAMCmd(FSMC_BANK_NORSRAMx, ENABLE);  // 使能BANK										  
					
}

向液晶屏读写及命令操作

/**
  * @brief  向ILI9341写入命令
  * @param  usCmd :要写入的命令(表寄存器地址)
  * @retval 无
  */	
__inline void ILI9341_Write_Cmd ( uint16_t usCmd )
{
	 *ILI9341_CMD_ADDR = usCmd;	
}


/**
  * @brief  向ILI9341写入数据
  * @param  usData :要写入的数据
  * @retval 无
  */	
__inline void ILI9341_Write_Data ( uint16_t usData )
{
	* ILI9341_DATA_ADDR = usData;
	
}


/**
  * @brief  从ILI9341读取数据
  * @param  无
  * @retval 读取到的数据
  */	
__inline uint16_t ILI9341_Read_Data ( void )
{
	return ( *ILI9341_DATA_ADDR );
	
}

uint16_t Read_Pixel_Format(void)
{

	ILI9341_Write_Cmd(0x0C);
	ILI9341_Read_Data();
	
	return ILI9341_Read_Data();

}

ili9341数据手册中的常用命令ili9341数据手册中的常用命令-CSDN博客 


背光控制函数

/**
 * @brief  ILI9341背光LED控制
 * @param  enumState :决定是否使能背光LED
  *   该参数为以下值之一:
  *     @arg ENABLE :使能背光LED
  *     @arg DISABLE :禁用背光LED
 * @retval 无
 */
void ILI9341_BackLed_Control ( FunctionalState enumState )
{
	if ( enumState )
		GPIO_ResetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );	
	else
		GPIO_SetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );
		
}


初始化ili9341寄存器

/**
 * @brief  初始化ILI9341寄存器
 * @param  无
 * @retval 无
 */
static void ILI9341_REG_Config ( void )
{
	/*  Power control B (CFh)  */
	DEBUG_DELAY  ();
	ILI9341_Write_Cmd ( 0xCF  );
	ILI9341_Write_Data ( 0x00  );
	ILI9341_Write_Data ( 0x81  );
	ILI9341_Write_Data ( 0x30  );
	
	/*  Power on sequence control (EDh) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xED );
	ILI9341_Write_Data ( 0x64 );
	ILI9341_Write_Data ( 0x03 );
	ILI9341_Write_Data ( 0x12 );
	ILI9341_Write_Data ( 0x81 );
	
	/*  Driver timing control A (E8h) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xE8 );
	ILI9341_Write_Data ( 0x85 );
	ILI9341_Write_Data ( 0x10 );
	ILI9341_Write_Data ( 0x78 );
	
	/*  Power control A (CBh) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xCB );
	ILI9341_Write_Data ( 0x39 );
	ILI9341_Write_Data ( 0x2C );
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x34 );
	ILI9341_Write_Data ( 0x02 );
	
	/* Pump ratio control (F7h) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xF7 );
	ILI9341_Write_Data ( 0x20 );
	
	/* Driver timing control B */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xEA );
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x00 );
	
	/* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xB1 );
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x1B );
	
	/*  Display Function Control (B6h) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xB6 );
	ILI9341_Write_Data ( 0x0A );
	ILI9341_Write_Data ( 0xA2 );
	
	/* Power Control 1 (C0h) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xC0 );
	ILI9341_Write_Data ( 0x35 );
	
	/* Power Control 2 (C1h) */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0xC1 );
	ILI9341_Write_Data ( 0x11 );
	
	/* VCOM Control 1 (C5h) */
	ILI9341_Write_Cmd ( 0xC5 );
	ILI9341_Write_Data ( 0x45 );
	ILI9341_Write_Data ( 0x45 );
	
	/*  VCOM Control 2 (C7h)  */
	ILI9341_Write_Cmd ( 0xC7 );
	ILI9341_Write_Data ( 0xA2 );
	
	/* Enable 3G (F2h) */
	ILI9341_Write_Cmd ( 0xF2 );
	ILI9341_Write_Data ( 0x00 );
	
	/* Gamma Set (26h) */
	ILI9341_Write_Cmd ( 0x26 );
	ILI9341_Write_Data ( 0x01 );
	DEBUG_DELAY ();
	
	/* Positive Gamma Correction */
	ILI9341_Write_Cmd ( 0xE0 ); //Set Gamma
	ILI9341_Write_Data ( 0x0F );
	ILI9341_Write_Data ( 0x26 );
	ILI9341_Write_Data ( 0x24 );
	ILI9341_Write_Data ( 0x0B );
	ILI9341_Write_Data ( 0x0E );
	ILI9341_Write_Data ( 0x09 );
	ILI9341_Write_Data ( 0x54 );
	ILI9341_Write_Data ( 0xA8 );
	ILI9341_Write_Data ( 0x46 );
	ILI9341_Write_Data ( 0x0C );
	ILI9341_Write_Data ( 0x17 );
	ILI9341_Write_Data ( 0x09 );
	ILI9341_Write_Data ( 0x0F );
	ILI9341_Write_Data ( 0x07 );
	ILI9341_Write_Data ( 0x00 );
	
	/* Negative Gamma Correction (E1h) */
	ILI9341_Write_Cmd ( 0XE1 ); //Set Gamma
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x19 );
	ILI9341_Write_Data ( 0x1B );
	ILI9341_Write_Data ( 0x04 );
	ILI9341_Write_Data ( 0x10 );
	ILI9341_Write_Data ( 0x07 );
	ILI9341_Write_Data ( 0x2A );
	ILI9341_Write_Data ( 0x47 );
	ILI9341_Write_Data ( 0x39 );
	ILI9341_Write_Data ( 0x03 );
	ILI9341_Write_Data ( 0x06 );
	ILI9341_Write_Data ( 0x06 );
	ILI9341_Write_Data ( 0x30 );
	ILI9341_Write_Data ( 0x38 );
	ILI9341_Write_Data ( 0x0F );
	
	/* memory access control set */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0x36 ); 	
	ILI9341_Write_Data ( 0xC8 );    /*竖屏  左上角到 (起点)到右下角 (终点)扫描方式*/
	DEBUG_DELAY ();
	
	/* column address control set */
	ILI9341_Write_Cmd ( CMD_SetCoordinateX );  
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0xEF );
	
	/* page address control set */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( CMD_SetCoordinateY ); 
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x00 );
	ILI9341_Write_Data ( 0x01 );
	ILI9341_Write_Data ( 0x3F );
	
	/*  Pixel Format Set (3Ah)  */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0x3a ); 
	ILI9341_Write_Data ( 0x55 );
	
	/* Sleep Out (11h)  */
	ILI9341_Write_Cmd ( 0x11 );	
	ILI9341_Delay ( 0xAFFf<<2 );
	DEBUG_DELAY ();
	
	/* Display ON (29h) */
	ILI9341_Write_Cmd ( 0x29 ); 
	
	
}

 在这里我们要特别注意这个设置窗口坐标的函数。


/******************************* 定义 ILI934 常用命令 ********************************/
#define      CMD_SetCoordinateX		 		    0x2A	     //设置X坐标
#define      CMD_SetCoordinateY		 		    0x2B	     //设置Y坐标
#define      CMD_SetPixel		 		          0x2C	     //填充像素

由这两个命令我们就可以在液晶屏上面确认像素的显示区域。


总的初始化函数

void ILI9341_Init(void)
{
    ILI9341_GPIO_Config();				// FSMC_GPIO的配置
    ILI9341_FSMC_Config();				// FSMC相关配置
 
    ILI9341_BackLed_Control(ENABLE);	// 点亮LCD背光灯
    ILI9341_Rst();						// 复位
    ILI9341_REG_Config();				// 初始化ILI9341寄存器
    
    // 其中0、3、5、6 模式适合从左至右显示文字,
    // 不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果
    // 其中 6 模式为大部分液晶例程的默认显示方向
    ILI9341_GramScan(LCD_SCAN_MODE);
}

设置液晶显示窗口和光标位置

/**
 * @brief  在ILI9341显示器上开辟一个窗口
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @retval 无
 */
void ILI9341_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{
	/* 设置X坐标,设置起始点和结束点 */
    ILI9341_Write_Cmd(CMD_SetCoordinateX); 				
    ILI9341_Write_Data(usX >> 8);
    ILI9341_Write_Data(usX & 0xff);
    ILI9341_Write_Data((usX + usWidth - 1) >> 8);
    ILI9341_Write_Data((usX + usWidth - 1) & 0xff);
 
	/* 设置Y坐标,设置起始点和结束点 */
    ILI9341_Write_Cmd(CMD_SetCoordinateY);
    ILI9341_Write_Data(usY >> 8);
    ILI9341_Write_Data(usY & 0xff);
    ILI9341_Write_Data((usY + usHeight - 1) >> 8);
    ILI9341_Write_Data((usY + usHeight - 1) & 0xff);
}
 
/**
 * @brief  设定ILI9341的光标坐标
 * @param  usX :在特定扫描方向下光标的X坐标
 * @param  usY :在特定扫描方向下光标的Y坐标
 * @retval 无
 */
static void ILI9341_SetCursor(uint16_t usX, uint16_t usY)
{
    ILI9341_OpenWindow(usX, usY, 1, 1);
}

填充像素点和清窗口屏

/**
 * @brief  在ILI9341显示器上以某一颜色填充像素点
 * @param  ulAmout_Point :要填充颜色的像素点的总数目
 * @param  usColor :颜色
 * @retval 无
 */
static __inline void ILI9341_FillColor(uint32_t ulAmout_Point, uint16_t usColor)
{
    uint32_t i = 0;
 
    /* memory write */
    ILI9341_Write_Cmd(CMD_SetPixel);
 
    for (i = 0; i < ulAmout_Point; i ++)
    {
        ILI9341_Write_Data(usColor);
    }
}
 
static uint16_t CurrentTextColor = BLACK;	// 前景色
static uint16_t CurrentBackColor = WHITE;	// 背景色
 
/**
 * @brief  对ILI9341显示器的某一窗口以某种颜色进行清屏
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_Clear(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{
    ILI9341_OpenWindow(usX, usY, usWidth, usHeight);
    ILI9341_FillColor(usWidth * usHeight, CurrentBackColor);
}
 
/**
 * @brief  对ILI9341显示器的某一点以某种颜色进行填充
 * @param  usX :在特定扫描方向下该点的X坐标
 * @param  usY :在特定扫描方向下该点的Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_SetPointPixel(uint16_t usX, uint16_t usY)
{
    if ((usX < LCD_X_LENGTH) && (usY < LCD_Y_LENGTH))
    {
        ILI9341_SetCursor(usX, usY);
        ILI9341_FillColor(1, CurrentTextColor);
    }
}

获取某一个坐标点的像素数据

/**
 * @brief  读取 GRAM 的一个像素数据
 * @param  无
 * @retval 像素数据
 */
static uint16_t ILI9341_Read_PixelData(void)
{
    uint16_t usRG = 0, usB = 0 ;
 
    ILI9341_Write_Cmd(0x2E); /* 读数据 */
    // 去掉前一次读取结果
    ILI9341_Read_Data(); 	 /* FIRST READ OUT DUMMY DATA */
 
    // 获取红色通道与绿色通道的值
    usRG = ILI9341_Read_Data();  	/*READ OUT RED AND GREEN DATA  */
    usB = ILI9341_Read_Data();  	/*READ OUT BLUE DATA*/
 
    return ((usRG & 0xF800) | ((usRG << 3) & 0x7E0) | (usB >> 11));
}
 
/**
 * @brief  获取 ILI9341 显示器上某一个坐标点的像素数据
 * @param  usX :在特定扫描方向下该点的X坐标
 * @param  usY :在特定扫描方向下该点的Y坐标
 * @retval 像素数据
 */
uint16_t ILI9341_GetPointPixel(uint16_t usX, uint16_t usY)
{
    uint16_t usPixelData;
	
    ILI9341_SetCursor(usX, usY);
    usPixelData = ILI9341_Read_PixelData();
 
    return usPixelData;
}

画线

/**
 * @brief  在 ILI9341 显示器上使用 Bresenham 算法画线段
 * @param  usX1 :在特定扫描方向下线段的一个端点X坐标
 * @param  usY1 :在特定扫描方向下线段的一个端点Y坐标
 * @param  usX2 :在特定扫描方向下线段的另一个端点X坐标
 * @param  usY2 :在特定扫描方向下线段的另一个端点Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DrawLine(uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2)
{
    uint16_t us;
    uint16_t usX_Current, usY_Current;
 
    int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance;
    int32_t lIncrease_X, lIncrease_Y;
 
    //计算坐标增量
    lDelta_X = usX2 - usX1;
    lDelta_Y = usY2 - usY1;
 
    usX_Current = usX1;
    usY_Current = usY1;
 
    if (lDelta_X > 0)
    {
        lIncrease_X = 1;    //设置单步方向
    }
    else if (lDelta_X == 0)
    {
        lIncrease_X = 0;    //垂直线
    }
    else
    {
        lIncrease_X = -1;
        lDelta_X = - lDelta_X;
    }
 
    if (lDelta_Y > 0)
    {
        lIncrease_Y = 1;
    }
    else if (lDelta_Y == 0)
    {
        lIncrease_Y = 0;    //水平线
    }
    else
    {
        lIncrease_Y = -1;
        lDelta_Y = - lDelta_Y;
    }
 
    if (lDelta_X > lDelta_Y)
    {
        lDistance = lDelta_X;    //选取基本增量坐标轴
    }
    else
    {
        lDistance = lDelta_Y;
    }
 
    for (us = 0; us <= lDistance + 1; us ++)  //画线输出
    {
        ILI9341_SetPointPixel(usX_Current, usY_Current);   //画点
 
        lError_X += lDelta_X ;
        lError_Y += lDelta_Y ;
 
        if (lError_X > lDistance)
        {
            lError_X -= lDistance;
            usX_Current += lIncrease_X;
        }
 
        if (lError_Y > lDistance)
        {
            lError_Y -= lDistance;
            usY_Current += lIncrease_Y;
        }
    }
}

画矩形

/**
 * @brief  在 ILI9341 显示器上画一个矩形
 * @param  usX_Start :在特定扫描方向下矩形的起始点X坐标
 * @param  usY_Start :在特定扫描方向下矩形的起始点Y坐标
 * @param  usWidth:矩形的宽度(单位:像素)
 * @param  usHeight:矩形的高度(单位:像素)
 * @param  ucFilled :选择是否填充该矩形
  *   该参数为以下值之一:
  *     @arg 0 :空心矩形
  *     @arg 1 :实心矩形
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DrawRectangle(uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight,
                           uint8_t ucFilled)
{
    if (ucFilled)
    {
        ILI9341_OpenWindow(usX_Start, usY_Start, usWidth, usHeight);
        ILI9341_FillColor(usWidth * usHeight, CurrentTextColor);
    }
    else
    {
        ILI9341_DrawLine(usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start);
        ILI9341_DrawLine(usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1);
        ILI9341_DrawLine(usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1);
        ILI9341_DrawLine(usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1);
    }
}

画圆形

/**
 * @brief  在 ILI9341 显示器上使用 Bresenham 算法画圆
 * @param  usX_Center :在特定扫描方向下圆心的X坐标
 * @param  usY_Center :在特定扫描方向下圆心的Y坐标
 * @param  usRadius:圆的半径(单位:像素)
 * @param  ucFilled :选择是否填充该圆
  *   该参数为以下值之一:
  *     @arg 0 :空心圆
  *     @arg 1 :实心圆
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DrawCircle(uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled)
{
    int16_t sCurrentX, sCurrentY;
    int16_t sError;
 
    sCurrentX = 0;
    sCurrentY = usRadius;
 
    sError = 3 - (usRadius << 1);       //判断下个点位置的标志
 
    while (sCurrentX <= sCurrentY)
    {
        int16_t sCountY;
 
        if (ucFilled)
		{
            for (sCountY = sCurrentX; sCountY <= sCurrentY; sCountY ++)
            {
                ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center + sCountY);              //1,研究对象
                ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center + sCountY);              //2
                ILI9341_SetPointPixel(usX_Center - sCountY,   usY_Center + sCurrentX);            //3
                ILI9341_SetPointPixel(usX_Center - sCountY,   usY_Center - sCurrentX);            //4
                ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center - sCountY);              //5
                ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center - sCountY);              //6
                ILI9341_SetPointPixel(usX_Center + sCountY,   usY_Center - sCurrentX);            //7
                ILI9341_SetPointPixel(usX_Center + sCountY,   usY_Center + sCurrentX);            //0
            }
		}
        else
        {
            ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center + sCurrentY);                //1,研究对象
            ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center + sCurrentY);                //2
            ILI9341_SetPointPixel(usX_Center - sCurrentY, usY_Center + sCurrentX);                //3
            ILI9341_SetPointPixel(usX_Center - sCurrentY, usY_Center - sCurrentX);                //4
            ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center - sCurrentY);                //5
            ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center - sCurrentY);                //6
            ILI9341_SetPointPixel(usX_Center + sCurrentY, usY_Center - sCurrentX);                //7
            ILI9341_SetPointPixel(usX_Center + sCurrentY, usY_Center + sCurrentX);                //0
        }
 
        sCurrentX ++;
 
        if (sError < 0)
        {
            sError += 4 * sCurrentX + 6;
        }
        else
        {
            sError += 10 + 4 * (sCurrentX - sCurrentY);
            sCurrentY --;
        }
    }
}

显示字符和字符串

/**
 * @brief  在 ILI9341 显示器上显示一个英文字符
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下该点的起始Y坐标
 * @param  cChar :要显示的英文字符
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DispChar_EN(uint16_t usX, uint16_t usY, const char cChar)
{
    uint8_t  byteCount, bitCount, fontLength;
    uint16_t ucRelativePositon;
    uint8_t *Pfont;
 
    //对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号)
    ucRelativePositon = cChar - ' ';
 
    //每个字模的字节数
    fontLength = (LCD_Currentfonts->Width * LCD_Currentfonts->Height) / 8;
 
    //字模首地址
    /*ascii码表偏移值乘以每个字模的字节数,求出字模的偏移位置*/
    Pfont = (uint8_t *)&LCD_Currentfonts->table[ucRelativePositon * fontLength];
 
    //设置显示窗口
    ILI9341_OpenWindow(usX, usY, LCD_Currentfonts->Width, LCD_Currentfonts->Height);
 
    ILI9341_Write_Cmd(CMD_SetPixel);
 
    //按字节读取字模数据
    //由于前面直接设置了显示窗口,显示数据会自动换行
    for (byteCount = 0; byteCount < fontLength; byteCount++)
    {
        //一位一位处理要显示的颜色
        for (bitCount = 0; bitCount < 8; bitCount++)
        {
            if (Pfont[byteCount] & (0x80 >> bitCount))
            {
                ILI9341_Write_Data(CurrentTextColor);
            }
            else
            {
                ILI9341_Write_Data(CurrentBackColor);
            }
        }
    }
}
 
/**
 * @brief  在 ILI9341 显示器上显示英文字符串
 * @param  line :在特定扫描方向下字符串的起始Y坐标
  *   本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标,
  *   宏LINE(x)会根据当前选择的字体来计算Y坐标值。
	*		显示中文且使用LINE宏时,需要把英文字体设置成Font8x16
 * @param  pStr :要显示的英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DispStringLine_EN(uint16_t line,  char *pStr)
{
    uint16_t usX = 0;
 
    while (* pStr != '\0')
    {
        if ((usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width) > LCD_X_LENGTH)
        {
            usX = ILI9341_DispWindow_X_Star;
            line += LCD_Currentfonts->Height;
        }
 
        if ((line - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height) > LCD_Y_LENGTH)
        {
            usX = ILI9341_DispWindow_X_Star;
            line = ILI9341_DispWindow_Y_Star;
        }
 
        ILI9341_DispChar_EN(usX, line, * pStr);
        pStr ++;
        usX += LCD_Currentfonts->Width;
    }
}
 
/**
 * @brief  在 ILI9341 显示器上显示英文字符串
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DispString_EN(uint16_t usX, uint16_t usY,  char *pStr)
{
    while (* pStr != '\0')
    {
        if ((usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width) > LCD_X_LENGTH)
        {
            usX = ILI9341_DispWindow_X_Star;
            usY += LCD_Currentfonts->Height;
        }
 
        if ((usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height) > LCD_Y_LENGTH)
        {
            usX = ILI9341_DispWindow_X_Star;
            usY = ILI9341_DispWindow_Y_Star;
        }
 
        ILI9341_DispChar_EN(usX, usY, * pStr);
        pStr ++;
        usX += LCD_Currentfonts->Width;
    }
}
 
/**
 * @brief  在 ILI9341 显示器上显示英文字符串(沿Y轴方向)
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DispString_EN_YDir(uint16_t usX, uint16_t usY,  char *pStr)
{
    while (* pStr != '\0')
    {
        if ((usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height) > LCD_Y_LENGTH)
        {
            usY = ILI9341_DispWindow_Y_Star;
            usX += LCD_Currentfonts->Width;
        }
 
        if ((usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width) >  LCD_X_LENGTH)
        {
            usX = ILI9341_DispWindow_X_Star;
            usY = ILI9341_DispWindow_Y_Star;
        }
 
        ILI9341_DispChar_EN(usX, usY, * pStr);
        pStr ++;
        usY += LCD_Currentfonts->Height;
    }
}

设置或获取当前字体类型

/**
  * @brief  设置英文字体类型
  * @param  fonts: 指定要选择的字体
	*		参数为以下值之一
  * 	@arg:Font24x32;
  * 	@arg:Font16x24;
  * 	@arg:Font8x16;
  * @retval None
  */
void LCD_SetFont(sFONT *fonts)
{
    LCD_Currentfonts = fonts;
}
 
/**
  * @brief  获取当前字体类型
  * @param  None.
  * @retval 返回当前字体类型
  */
sFONT *LCD_GetFont(void)
{
    return LCD_Currentfonts;
}

设置或获取LCD前景和背景颜色

/**
  * @brief  设置LCD的前景(字体)及背景颜色,RGB565
  * @param  TextColor: 指定前景(字体)颜色
  * @param  BackColor: 指定背景颜色
  * @retval None
  */
void LCD_SetColors(uint16_t TextColor, uint16_t BackColor)
{
    CurrentTextColor = TextColor;
    CurrentBackColor = BackColor;
}
 
/**
  * @brief  获取LCD的前景(字体)及背景颜色,RGB565
  * @param  TextColor: 用来存储前景(字体)颜色的指针变量
  * @param  BackColor: 用来存储背景颜色的指针变量
  * @retval None
  */
void LCD_GetColors(uint16_t *TextColor, uint16_t *BackColor)
{
    *TextColor = CurrentTextColor;
    *BackColor = CurrentBackColor;
}
 
/**
  * @brief  设置LCD的前景(字体)颜色,RGB565
  * @param  Color: 指定前景(字体)颜色
  * @retval None
  */
void LCD_SetTextColor(uint16_t Color)
{
    CurrentTextColor = Color;
}
 
/**
  * @brief  设置LCD的背景颜色,RGB565
  * @param  Color: 指定背景颜色
  * @retval None
  */
void LCD_SetBackColor(uint16_t Color)
{
    CurrentBackColor = Color;
}

清除某行文字

/**
  * @brief  清除某行文字
  * @param  Line: 指定要删除的行
  *   本参数可使用宏LINE(0)、LINE(1)等方式指定要删除的行,
  *   宏LINE(x)会根据当前选择的字体来计算Y坐标值,并删除当前字体高度的第x行。
  * @retval None
  */
void LCD_ClearLine(uint16_t Line)
{
    ILI9341_Clear(0, Line, LCD_X_LENGTH, ((sFONT *)LCD_GetFont())->Height);	/* 清屏,显示全黑 */
}

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

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

相关文章

Photos框架 - 自定义媒体选择器(UI预览)

引言 在前面的博客中我们已经介绍了使用媒体资源数据的获取&#xff0c;以及自定义的媒体资源选择列表页。在一个功能完整的媒体选择器中&#xff0c;预览自然是必不可少的&#xff0c;本篇博客我们就来实现一个资源的预览功能&#xff0c;并且实现列表和预览的数据联动效果。…

内网横向——远程桌面利用

文章目录 一、远程桌面的确定和开启二、RDP Hijacking 网络拓扑&#xff1a; 攻击机kali IP&#xff1a;192.168.111.0 跳板机win7 IP&#xff1a;192.168.111.128&#xff0c;192.168.52.143 靶机win server 2008 IP&#xff1a;192.168.52.138 一、远程桌面的确定和开启 下面…

VMware、Docker - 让虚拟机走主机代理,解决镜像封禁问题

文章目录 虚拟机全局代理配置找到 VMnet8 的 IPv4 地址代理相关配置虚拟机代理配置 Docker 代理配置修改镜像修改 Docker 代理配置 虚拟机全局代理配置 找到 VMnet8 的 IPv4 地址 a&#xff09;打开此电脑&#xff0c;输入 “控制面板”&#xff0c;然后回车. b&#xff09;之…

元注解相关知识总结

Target 这个注解适用于决定注解的适用范围&#xff0c;例如适用于构造器&#xff0c;方法&#xff0c;字段等 Retention 这个注解的作用是确定注解的生命周期一般用得比较多的是RunTime这样就可以在运行环境中使用它&#xff0c;赋值的方式一般是使用value进行赋值 SOURCE 代…

【初阶数据结构篇】顺序表的实现(赋源码)

文章目录 本篇代码位置顺序表和链表1.线性表2.顺序表2.1 概念与结构2.2分类2.2.1 静态顺序表2.2.2 动态顺序表 2.3 动态顺序表的实现2.3.1动态顺序表的初始化和销毁及打印2.3.2动态顺序表的插入动态顺序表的尾插动态顺序表的头插动态顺序表的在指定位置插入数据 2.3.3动态顺序表…

spring常用注解有哪些

Spring框架使用了大量的注解来简化配置和开发&#xff0c;以下是一些常用的Spring注解&#xff1a; 1.Component&#xff1a;通用的构造型注解&#xff0c;用于标记一个类作为Spring管理的组件&#xff0c;通常用于自定义组件。 2.Autowired&#xff1a;用于自动装配Bean&#…

Android Add a Header to the GridLayout

RecyclerView 添加 Header, RecycleView 设置 LayoutManager 为 GridLayoutManger的 时候&#xff1b; Header 要占用3行 val manager GridLayoutManager(activity, 3)binding.sleepList.layoutManager manager// span size for each position, and assign it to manager.spa…

Golang零基础入门课_20240726 课程笔记

视频课程 最近发现越来越多的公司在用Golang了&#xff0c;所以精心整理了一套视频教程给大家&#xff0c;这个只是其中的第一部&#xff0c;后续还会有很多。 视频已经录制完成&#xff0c;完整目录截图如下&#xff1a; 课程目录 01 第一个Go程序.mp402 定义变量.mp403 …

github简单地操作

1.调节字体大小 选择options 选择text 选择select 选择你需要的参数就可以了。 2.配置用户名和邮箱 桌面右键&#xff0c;选择git Bash Here git config --global user.name 用户名 git config --global user.email 邮箱名 3.用git实现代码管理的过程 下载别人的项目 git …

PlatformIO+ESP32S3学习:通过WIFI与和风天气API获取指定地点的天气情况并显示

1. 硬件准备 你只需要有一个ESP32S3开发板。我目前使用的是&#xff1a; 购买地址&#xff1a;立创ESP32S3R8N8 开发板 2. 和风天气API 2.1. 和风天气介绍 和⻛天气是中国领先的气象科技服务商、国家高新技术 企业&#xff0c;致力于运用先进气象模型结合大数据、人工智能 技术…

Halcon 引擎方式调试

1.C# 端添加代码 启动调试模式 public HDevEngine MyEngine new HDevEngine(); // halcon引擎;// 启动调试服务 MyEngine.StartDebugServer();2.Halcon程序添加到进程 打开Halcon程序 【执行】>【附加到进程】 点击【确定】 3.C# 程序执行到相关位置 C# 程序执行调用…

大模型学习笔记十四:Agent模型微调

文章目录 一、大模型需要Agent技术的原因二、Prompt Engineering可以实现Agent吗&#xff1f;&#xff08;1&#xff09;ReAct原理展示和代码&#xff08;2&#xff09;ModelScope&#xff08;3&#xff09;AutoGPT&#xff08;4&#xff09;ToolLLaMA 三、既然AutoGPT可以满足…

Java SE 文件上传和文件下载的底层原理

1. Java SE 文件上传和文件下载的底层原理 文章目录 1. Java SE 文件上传和文件下载的底层原理2. 文件上传2.1 文件上传应用实例2.2 文件上传注意事项和细节 3. 文件下载3.1 文件下载应用实例3.2 文件下载注意事项和细节 4. 总结&#xff1a;5. 最后: 2. 文件上传 文件的上传和…

【C语言】指针基础知识理解

1. 内存和地址 1.1 内存 在讲内存和地址之前&#xff0c;我们想有个⽣活中的案例&#xff1a; 假设你在学校宿舍楼&#xff0c;但是宿舍的房间没有编号&#xff0c;你的⼀个朋友发微信来找你玩&#xff0c;如果想找到你&#xff0c;就得挨个房⼦去找&#xff0c;这样效率会很…

《算法笔记》总结No.11——数字处理(上)欧拉筛选

机试中存在部分涉及到较复杂数字的问题&#xff0c;这是编码的基本功&#xff0c;各位一定要得心应手。 目录 一.最大公约数和最小公倍数 1.最大公约数 2.最小公倍数 二.素数 1.判断指定数 2.输出所有素数 3.精进不休——埃拉托斯特尼筛法 4.达到更优&#xff01;——…

STL-string(使用和部分模拟实现)

1.string basic_string<char> 是 C 标准库中定义的一个模板类型,用于表示一个字符串。这个模板类接收一个字符类型作为模板参数。typedef basic_string<char> string&#xff1a;string类是basic_string类模板的实例化&#xff0c;它使用 char作为其字符类型。 2.…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第六十一章 Linux内核定时器

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

科普文:分布式数据一致性协议Paxos

1 什么是Paxos Paxos协议其实说的就是Paxos算法, Paxos算法是基于消息传递且具有高度容错特性的一致性算 法&#xff0c;是目前公认的解决分布式一致性问题最有效的算法之一。 Paxos由 莱斯利兰伯特(Leslie Lamport)于1998年在《The Part-Time Parliament》论文中首次公 开&…

KDP开源平台升级,推进大数据处理迈向轻量化、智能化

本文由 LeetTools 工具生成 编辑 | June 在当今数字化转型的浪潮中&#xff0c;企业面临着如何高效管理和利用大数据的挑战。智领云推出的Kubernetes Data Platform&#xff08;简称KDP&#xff09;正是为了解决这一问题而设计的。作为一款开源的云原生大数据平台&#xff0c;K…

【前端 08】简单学习js字符串

JavaScript中的String对象详解 在JavaScript中&#xff0c;字符串&#xff08;String&#xff09;是一种非常基础且常用的数据类型&#xff0c;用于表示文本数据。虽然JavaScript中的字符串是原始数据类型&#xff0c;但它们的行为类似于对象&#xff0c;因为JavaScript为字符…