STM32之LCD液晶屏(ILI9341)模拟8080时序

配置8080时序的LCD液晶屏

8080的控制线有四根:RD 写使能,WR 读使能,DC数据/命令,CS 片选

8080总线存在许多接口:8/9/16/18位接口

8080通用时序

读时序

在这里插入图片描述

LCD_CS = 0; 		//片选选中 
LCD_DC = 1; 		//读数据 
LCD_WR = 1; 		//禁止写 
LCD_RD = 0; 		//开始读
data = DATAIN();	//读取数据 
LCD_RD = 1; 		//结束读
LCD_CS = 1; 		//结束片选

在这里插入图片描述

写时序

DC = 1:写数据;DC = 0:写命令

写数据:

LCD_CS = 0; 	//片选选中 
LCD_RD = 1; 	//禁止读 
LCD_DC = 1; 	//写数据 
DATAOUT(Data); 	//输出
LCD_WR = 0; 	//写入开始 
LCD_WR = 1; 	//写入结束 
LCD_CS = 1; 	//结束片选

写命令:

LCD_CS = 0; 	//片选选中 
LCD_RD = 1; 	//禁止读 
LCD_DC = 0; 	//写命令 
DATAOUT(Data); 	//输出
LCD_WR = 0; 	//写入开始 
LCD_WR = 1; 	//写入结束 
LCD_CS = 1; 	//结束片选

针对ILI9341修改后的时序

读时序:

在这里插入图片描述
注意:在 STM32F1 系列产品中,当 GPIO 引脚工作于开漏输出模式,读取输入数据寄存器 IDR 的值时,IDR 表示的值就是 GPIO 引脚当前的实际电平,而当 GPIO 引脚工作于推挽输出模式时,读取输入 IDR 的值并不能获取到引脚的实际电平。读取数据引脚的电平时,必须把GPIO从推挽输出模式切换到浮空输入(或者开漏输出),读取数据完毕后再切换回控制液晶屏大部分时间使用的推挽输出模式。

uint16_t ILI9341_Read_Data ( void )
{
	uint16_t data;
	//配置为输入模式
 	ILI9341_DATA_PORT->CRL=0X88888888; //上拉输入
	ILI9341_DATA_PORT->CRH=0X88888888; //上拉输入
    
    //ILI9341_DATA_PORT->CRL=0X44444444; //浮空输入
    //ILI9341_DATA_PORT->CRH=0X44444444; //浮空输入
    
	ILI9341_DATA_PORT->ODR=0X0000;     //全部输出0

	ILI9341_DC_SET;
	ILI9341_WR_SET;

	ILI9341_CS_CLR;
	//读取数据
	ILI9341_RD_CLR;    
    
	data=DATAIN;  
	ILI9341_RD_SET;
	ILI9341_CS_SET; 

	ILI9341_DATA_PORT->CRL=0X33333333; // 推挽输出
	ILI9341_DATA_PORT->CRH=0X33333333; // 推挽输出
	ILI9341_DATA_PORT->ODR=0XFFFF;     //全部输出1
	return data;  
}

注意:一般来说,ILI9341 控制器是在收到命令后才会输出数据,如查询控制器 ID 的命令或读取液晶屏像素值时。当控制器接收到命令后,它通过 8080 时序输出的第 1 个是无效数据,即图中的“Data(invalid)”表示的内容,从第 2 个数据开始才是有效的输出,即图中的“Data(valid)”。

写时序:

在这里插入图片描述
写命令:

__inline void ILI9341_Write_Cmd ( uint16_t usCmd )
{
	ILI9341_CS_CLR;		//开始片选      
	ILI9341_DC_CLR;		//写命令
	ILI9341_RD_SET;		//禁止读
	DATAOUT(usCmd);		//输出命令
	ILI9341_WR_CLR;		//写入开始
	ILI9341_WR_SET;		//写入结束
	ILI9341_CS_SET;		//结束片选   	
}

写数据:

__inline void ILI9341_Write_Data ( uint16_t usData )
{
	ILI9341_CS_CLR;		//开始片选      
	ILI9341_DC_SET;		//写数据
	ILI9341_RD_SET;		//禁止读
	DATAOUT(usData);	//输出数据
	ILI9341_WR_CLR;		//写入开始
	ILI9341_WR_SET;		//写入结束
	ILI9341_CS_SET;		//结束片选
}

这个函数定义使用了“__inline”内联函数关键字修饰,可以把它的作用理解成“#define”一样的宏,程序编译时会直接在调用到这个函数的地方加入内联函数中的代码内容,这种特性可以节省函数调用时的消耗。

在此处把 ILI9341_Write_Cmd 定义成内联函数就是希望程序能更快地执行。

利用GPIO模拟8080时序的数据线

控制信号线配置

//片选
#define      ILI9341_CS_CLK                RCC_APB2Periph_GPIOC   
#define      ILI9341_CS_PORT               GPIOC
#define      ILI9341_CS_PIN                GPIO_Pin_4

//DC引脚
#define      ILI9341_DC_CLK                RCC_APB2Periph_GPIOC   
#define      ILI9341_DC_PORT               GPIOC
#define      ILI9341_DC_PIN                GPIO_Pin_7

//写使能
#define      ILI9341_WR_CLK                RCC_APB2Periph_GPIOC   
#define      ILI9341_WR_PORT               GPIOC
#define      ILI9341_WR_PIN                GPIO_Pin_6

//读使能
#define      ILI9341_RD_CLK                RCC_APB2Periph_GPIOC
#define      ILI9341_RD_PORT               GPIOC
#define      ILI9341_RD_PIN                GPIO_Pin_5

//背光引脚
#define      ILI9341_BK_CLK                RCC_APB2Periph_GPIOD    
#define      ILI9341_BK_PORT               GPIOD
#define      ILI9341_BK_PIN                GPIO_Pin_2

//复位引脚直接使用NRST,开发板复位的时候会使液晶复位

//信号线写1
#define	ILI9341_CS_SET  	ILI9341_CS_PORT->BSRR=ILI9341_CS_PIN    //片选端口  		
#define	ILI9341_DC_SET		ILI9341_DC_PORT->BSRR=ILI9341_DC_PIN    //数据/命令 	  
#define	ILI9341_WR_SET		ILI9341_WR_PORT->BSRR=ILI9341_WR_PIN    //写数据			
#define	ILI9341_RD_SET		ILI9341_RD_PORT->BSRR=ILI9341_RD_PIN    //读数据			
					
//信号线写0			    
#define	ILI9341_CS_CLR  	ILI9341_CS_PORT->BRR=ILI9341_CS_PIN     //片选端口  		
#define	ILI9341_DC_CLR		ILI9341_DC_PORT->BRR=ILI9341_DC_PIN     //数据/命令		     
#define	ILI9341_WR_CLR		ILI9341_WR_PORT->BRR=ILI9341_WR_PIN     //写数据			
#define	ILI9341_RD_CLR		ILI9341_RD_PORT->BRR=ILI9341_RD_PIN     //读数据	

数据信号线配置

#define      ILI9341_DATA_CLK                RCC_APB2Periph_GPIOB   
#define      ILI9341_DATA_PORT               GPIOB
#define      ILI9341_DATA_PIN                GPIO_Pin_All

#define DATAOUT(x) 	ILI9341_DATA_PORT->ODR=x; //数据输出 
#define DATAIN     	ILI9341_DATA_PORT->IDR;   //数据输入	

信号线对应GPIO配置

static void ILI9341_GPIO_Config ( void )
{
	GPIO_InitTypeDef GPIO_InitStructure;
	/* 使能复用IO时钟*/
	RCC_APB2PeriphClockCmd ( RCC_APB2Periph_AFIO, ENABLE );
	/* 使能FSMC对应相应管脚时钟*/
	RCC_APB2PeriphClockCmd (ILI9341_CS_CLK|
                            ILI9341_DC_CLK|
                            ILI9341_WR_CLK|
                            ILI9341_RD_CLK|
                            ILI9341_BK_CLK|
                            ILI9341_DATA_CLK, 
                      ENABLE );
	
    //开启SWD,失能JTAG (部分PB引脚用在了jtag接口,改成SWD接口就不会有干扰)
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);	
	
    /* 配置液晶相对应的数据线,PORT-D0~D15 */	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_Out_PP;
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_DATA_PIN;
	GPIO_Init ( ILI9341_DATA_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_CS_PIN; 
	GPIO_Init ( ILI9341_CS_PORT, & GPIO_InitStructure );  
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_DC_PIN; 
	GPIO_Init ( ILI9341_DC_PORT, & GPIO_InitStructure );
			
	/* 配置LCD背光控制引脚BLK*/	
	GPIO_InitStructure.GPIO_Pin = ILI9341_BK_PIN; 
	GPIO_Init ( ILI9341_BK_PORT, &GPIO_InitStructure );
}

配置ILI9341相关寄存器

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 ); 
		
}

​ 这个初始化过程中发送的代码及参数主要是配置了液晶屏的上电过程、显示屏的伽玛参数、分辨率、像素格式等内容。

液晶屏初始化

void ILI9341_Init ( void )
{
	ILI9341_GPIO_Config ();					//配置IO口
	ILI9341_BackLed_Control ( ENABLE );     //点亮LCD背光灯
	ILI9341_REG_Config ();					//配置液晶屏相关寄存器
	ILI9341_GramScan(6);					//设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向  
}

获取LCD液晶屏的芯片ID

uint16_t ILI9341_Read_ID(void)
{
	uint16_t id = 0;
	ILI9341_Write_Cmd(0xD3);
	ILI9341_Read_Data();
	ILI9341_Read_Data();
	id = ILI9341_Read_Data();
	id<<=8;
	id|=ILI9341_Read_Data();
	return id;
}

设置扫描方向

在这里插入图片描述

由上图可知,通过配置MY,MX,MV来配置GRAM的扫描方向。

具体流程如下:
在这里插入图片描述

ILI9341_Write_Cmd ( 0x36 ); 
ILI9341_Write_Data ( 0x08 |(ucOption<<5));	//启动BGR,向高位写入MY,MX,MV的值,8种模式

8种模式扫描方式:(其中模式 6 最符合我们的阅读习惯,扫描方向与文字方向一致,都是从左到右,从上到下)

在这里插入图片描述

代码中使能了BGR,即RGB翻转成BGR,效果如下:

在这里插入图片描述

设置液晶屏的扫描方向函数

void ILI9341_GramScan ( uint8_t ucOption )
{	
	//参数检查,只可输入0-7
	if(ucOption >7 )
		return;
	//根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数
	LCD_SCAN_MODE = ucOption;
    
	//根据模式更新XY方向的像素宽度
	if(ucOption%2 == 0)	
	{
		//0 2 4 6模式下X方向像素宽度为240,Y方向为320
		LCD_X_LENGTH = ILI9341_LESS_PIXEL;
		LCD_Y_LENGTH =	ILI9341_MORE_PIXEL;
	}
	else				
	{
		//1 3 5 7模式下X方向像素宽度为320,Y方向为240
		LCD_X_LENGTH = ILI9341_MORE_PIXEL;
		LCD_Y_LENGTH =	ILI9341_LESS_PIXEL; 
	}

	//0x36命令参数的高3位可用于设置GRAM扫描方向	
	ILI9341_Write_Cmd ( 0x36 ); 
	ILI9341_Write_Data( 0x08 |(ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
	
    ILI9341_Write_Cmd ( 0x2A ); 
	ILI9341_Write_Data( 0x00 );							/* x 起始坐标高8位 */
	ILI9341_Write_Data( 0x00 );							/* x 起始坐标低8位 */
	ILI9341_Write_Data( ((LCD_X_LENGTH-1)>>8)&0xFF ); 	/* x 结束坐标高8位 */	
	ILI9341_Write_Data( (LCD_X_LENGTH-1)&0xFF );		/* x 结束坐标低8位 */

	ILI9341_Write_Cmd ( 0x2B ); 
	ILI9341_Write_Data ( 0x00 );						/* y 起始坐标高8位 */
	ILI9341_Write_Data ( 0x00 );						/* y 起始坐标低8位 */
	ILI9341_Write_Data ( ((LCD_Y_LENGTH-1)>>8)&0xFF );	/* y 结束坐标高8位 */	 
	ILI9341_Write_Data ( (LCD_Y_LENGTH-1)&0xFF );		/* y 结束坐标低8位 */

	/* write gram start */
	ILI9341_Write_Cmd ( 0x2C );	
}

填充像素点

根据数据手册提供的流程图:

在这里插入图片描述

先写命令0x2A配置起始地址,再写命令0x2B配置结束地址,最后写0x2C填充特定数量且特定颜色的像素点。

void ILI9341_FillColor(uint16_t X0, uint16_t Y0, 
                       uint16_t Width, uint16_t Height,
                       uint32_t ulAmout_Point, uint16_t usColor)
{	
    uint32_t i = 0;
	ILI9341_Write_Cmd (0x2A); 								/* 设置X坐标 */
	ILI9341_Write_Data ( X0 >> 8  );	 					/* 先高8位,然后低8位 */
	ILI9341_Write_Data ( X0 & 0xff  );	 					/* 设置起始点和结束点*/
	ILI9341_Write_Data ( ( X0 + Width - 1 ) >> 8  );
	ILI9341_Write_Data ( ( X0 + Width - 1 ) & 0xff);

	ILI9341_Write_Cmd (0x2B); 			    				/* 设置Y坐标*/
	ILI9341_Write_Data ( Y0 >> 8  );
	ILI9341_Write_Data ( Y0 & 0xff  );
	ILI9341_Write_Data ( ( Y0 + Height - 1 ) >> 8 );
	ILI9341_Write_Data ( ( Y0 + Height - 1) & 0xff);
    
	ILI9341_Write_Cmd (0x2C);								/* memory write */
	for ( i = 0; i < ulAmout_Point; i ++ )
		ILI9341_Write_Data ( usColor );
    
}


//或者如下分开
void ILI9341_OpenWindow ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{	
    ILI9341_Write_Cmd ( CMD_SetCoordinateX ); 	/* 设置X坐标 */
    ILI9341_Write_Data ( usX >> 8  );	 		/* 先高8位,然后低8位 */
    ILI9341_Write_Data ( usX & 0xff  );	 		/* 设置起始点和结束点*/
    ILI9341_Write_Data ( ( usX + usWidth - 1 ) >> 8  );
    ILI9341_Write_Data ( ( usX + usWidth - 1 ) & 0xff  );

    ILI9341_Write_Cmd ( CMD_SetCoordinateY ); 			     /* 设置Y坐标*/
    ILI9341_Write_Data ( usY >> 8  );
    ILI9341_Write_Data ( usY & 0xff  );
    ILI9341_Write_Data ( ( usY + usHeight - 1 ) >> 8 );
    ILI9341_Write_Data ( ( usY + usHeight - 1) & 0xff );
}

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 );
}

举例:

​ 若设置了一个 X0=10,Y0=30,Width=50,Height=20 的窗口,然后再连续填充 50*20 个颜色值为 0XFFFF 的像素数据,即可在 (10,30) 的起始坐标处显示一个宽 50 像素,高 20 像素的白色矩形。

清屏函数

直接开辟一个全屏窗口,写入背景色。

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 );
}

绘制线段

利用前面的绘制像素点的函数就可以实现,绘制各种图形,这里绘制一条线段。

大概原理:

  1. 根据要绘制的线段的起点和终点,确定线段绘制的位置;

  2. 取出起点和终点的x,y坐标,起点(x1,y1),终点(x0,y0);

  3. 判断线段的方向,(x1 - x0 = 0)则为垂线、(y1 - y0 = 0)则为水平线
    若(x1 - x0)>(y1 - y0),说明线段向水平线的倾斜角小于45°, 则在x轴上添加增量;
    若(x1 - x0)<(y1 - y0),说明线段向水平线的倾斜角大于45°, 则在y轴上添加增量;

    x轴增量: y轴增量:
    在这里插入图片描述

  4. 根据(x1 - x0)和(y1 - y0)确定每次增量的大小,比如(x1 - x0)-(y1 - y0)= 1,那么 x轴增量在对应的每个y坐标上增量为2;

  5. 特殊情况:(x1 - x0)=(y1 - y0),则只要像素点在x轴和y轴上每次递增1个像素点就行,如下图:
    在这里插入图片描述

//这里的是野火的线段绘制函数
/**
 * @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; 
        } 
    }  
}   

绘制图形

以下是绘制一个矩形,利用坐标和线段长度的关系,绘制空心或实心矩形。绘制其他图形也可以在此基础上进行改造得到。不过绘制圆形则要麻烦一点。

矩形的坐标定义图:

在这里插入图片描述

void ILI9341_DrawRectangle ( uint16_t X_Start, 
                             uint16_t Y_Start, 
                             uint16_t Width, 
                             uint16_t Height, 
                             uint8_t Filled )
{
    if ( Filled )	//实心矩形
    {
        ILI9341_OpenWindow ( X_Start, Y_Start, Width, Height );
        ILI9341_FillColor ( Width * Height ,CurrentTextColor);	
    }
    else			//空心矩形
    {
        ILI9341_DrawLine ( X_Start, Y_Start, X_Start + Width - 1, Y_Start );	//1
       
        ILI9341_DrawLine ( X_Start, 
                           Y_Start + Height - 1, 
                           X_Start + Width - 1, 
                           Y_Start + Height - 1 );		//2
        
        ILI9341_DrawLine ( X_Start, Y_Start, X_Start, Y_Start + Height - 1 );	//3
        
        ILI9341_DrawLine ( X_Start + Width - 1, 
                           Y_Start, 
                           X_Start + Width - 1, 
                           Y_Start + Height - 1 );		//4
    }
}

硬件接口

以下是40pin的LCD硬件接口

在这里插入图片描述

硬件接线图

在这里插入图片描述

总的流程:

1. 液晶屏先配置好数据线和控制线,即GPIO配置;
2. 然后打开LCD的背光灯;
3. 再配置相关的寄存器;
4. 最后确定GRAM的扫描方向;
5. 在main函数中先初始化,清屏;
6. 然后就可以利用各种绘制函数绘制图形。

  • 6
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是使用STM32F4和ILI9341 LCD屏幕输出“张翼鹏”的示例代码: ```c #include "stm32f4xx.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_spi.h" // 定义SPI接口 #define LCD_SPI_PORT SPI5 #define LCD_SPI_RCC RCC_APB2Periph_SPI5 #define LCD_SPI_CLK 45000000 // 定义SPI引脚 #define LCD_SPI_SCK_PIN GPIO_Pin_7 #define LCD_SPI_SCK_PORT GPIOA #define LCD_SPI_MISO_PIN GPIO_Pin_11 #define LCD_SPI_MISO_PORT GPIOC #define LCD_SPI_MOSI_PIN GPIO_Pin_12 #define LCD_SPI_MOSI_PORT GPIOC // 定义LCD控制引脚 #define LCD_CS_PIN GPIO_Pin_2 #define LCD_CS_PORT GPIOB #define LCD_DC_PIN GPIO_Pin_1 #define LCD_DC_PORT GPIOB #define LCD_RST_PIN GPIO_Pin_0 #define LCD_RST_PORT GPIOB // 定义LCD屏幕分辨率 #define LCD_WIDTH 240 #define LCD_HEIGHT 320 // 初始化SPI接口 void LCD_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; // 使能SPI时钟 RCC_APB2PeriphClockCmd(LCD_SPI_RCC, ENABLE); // 配置SPI引脚 GPIO_InitStructure.GPIO_Pin = LCD_SPI_SCK_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(LCD_SPI_SCK_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(LCD_SPI_SCK_PORT, GPIO_PinSource7, GPIO_AF_SPI5); GPIO_InitStructure.GPIO_Pin = LCD_SPI_MISO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(LCD_SPI_MISO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(LCD_SPI_MISO_PORT, GPIO_PinSource11, GPIO_AF_SPI5); GPIO_InitStructure.GPIO_Pin = LCD_SPI_MOSI_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(LCD_SPI_MOSI_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(LCD_SPI_MOSI_PORT, GPIO_PinSource12, GPIO_AF_SPI5); // 配置SPI SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(LCD_SPI_PORT, &SPI_InitStructure); // 使能SPI SPI_Cmd(LCD_SPI_PORT, ENABLE); } // 初始化LCD控制引脚 void LCD_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = LCD_CS_PIN | LCD_DC_PIN | LCD_RST_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStructure); } // 初始化LCD屏幕 void LCD_Init(void) { LCD_GPIO_Init(); LCD_SPI_Init(); // 复位LCD屏幕 GPIO_ResetBits(LCD_RST_PORT, LCD_RST_PIN); DelayMs(10); GPIO_SetBits(LCD_RST_PORT, LCD_RST_PIN); DelayMs(10); // 发送初始化命令 LCD_WriteCommand(0x01); // 软件复位 DelayMs(10); LCD_WriteCommand(0xCF); LCD_WriteData(0x00); LCD_WriteData(0xC1); LCD_WriteData(0x30); DelayMs(10); LCD_WriteCommand(0xED); LCD_WriteData(0x64); LCD_WriteData(0x03); LCD_WriteData(0x12); LCD_WriteData(0x81); DelayMs(10); LCD_WriteCommand(0xE8); LCD_WriteData(0x85); LCD_WriteData(0x10); LCD_WriteData(0x7A); DelayMs(10); LCD_WriteCommand(0xCB); LCD_WriteData(0x39); LCD_WriteData(0x2C); LCD_WriteData(0x00); LCD_WriteData(0x34); LCD_WriteData(0x02); DelayMs(10); LCD_WriteCommand(0xF7); LCD_WriteData(0x20); DelayMs(10); LCD_WriteCommand(0xEA); LCD_WriteData(0x00); LCD_WriteData(0x00); DelayMs(10); LCD_WriteCommand(0xC0); // 电源控制1 LCD_WriteData(0x23); DelayMs(10); LCD_WriteCommand(0xC1); // 电源控制2 LCD_WriteData(0x10); DelayMs(10); LCD_WriteCommand(0xC5); // VCOM控制1 LCD_WriteData(0x3E); LCD_WriteData(0x28); DelayMs(10); LCD_WriteCommand(0xC7); // VCOM控制2 LCD_WriteData(0x86); DelayMs(10); LCD_WriteCommand(0x36); // MADCTL LCD_WriteData(0x68); DelayMs(10); LCD_WriteCommand(0x3A); // COLMOD LCD_WriteData(0x55); DelayMs(10); LCD_WriteCommand(0xB1); // Frame rate control,最大 70 Hz,同时保证低功耗 LCD_WriteData(0x00); LCD_WriteData(0x18); DelayMs(10); LCD_WriteCommand(0xB6); // Display function control,RGB/MCU interface select LCD_WriteData(0x08); LCD_WriteData(0x82); LCD_WriteData(0x27); DelayMs(10); LCD_WriteCommand(0xF2); // 3Gamma control,disable LCD_WriteData(0x00); DelayMs(10); LCD_WriteCommand(0x26); // Gamma curve selected LCD_WriteData(0x01); DelayMs(10); LCD_WriteCommand(0xE0); // Set Gamma,positive gamma correction LCD_WriteData(0x0F); LCD_WriteData(0x31); LCD_WriteData(0x2B); LCD_WriteData(0x0C); LCD_WriteData(0x0E); LCD_WriteData(0x08); LCD_WriteData(0x4E); LCD_WriteData(0xF1); LCD_WriteData(0x37); LCD_WriteData(0x07); LCD_WriteData(0x10); LCD_WriteData(0x03); LCD_WriteData(0x0E); LCD_WriteData(0x09); LCD_WriteData(0x00); DelayMs(10); LCD_WriteCommand(0XE1); // Set Gamma,negative gamma correction LCD_WriteData(0x00); LCD_WriteData(0x0E); LCD_WriteData(0x14); LCD_WriteData(0x03); LCD_WriteData(0x11); LCD_WriteData(0x07); LCD_WriteData(0x31); LCD_WriteData(0xC1); LCD_WriteData(0x48); LCD_WriteData(0x08); LCD_WriteData(0x0F); LCD_WriteData(0x0C); LCD_WriteData(0x31); LCD_WriteData(0x36); LCD_WriteData(0x0F); DelayMs(10); LCD_WriteCommand(0x11); // Sleep out DelayMs(120); LCD_WriteCommand(0x29); // Display on // 清屏 LCD_Clear(0xFFFF); } // 写入数据到LCD屏幕 void LCD_WriteData(uint8_t data) { GPIO_SetBits(LCD_DC_PORT, LCD_DC_PIN); // DC设置为高电平表示写入数据 GPIO_ResetBits(LCD_CS_PORT, LCD_CS_PIN); // 使能片选 while (SPI_I2S_GetFlagStatus(LCD_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET); // 等待发送缓冲区为空 SPI_I2S_SendData(LCD_SPI_PORT, data); // 发送数据 while (SPI_I2S_GetFlagStatus(LCD_SPI_PORT, SPI_I2S_FLAG_RXNE) == RESET); // 等待接收缓冲区非空 SPI_I2S_ReceiveData(LCD_SPI_PORT); // 读取接收到的数据,清除RXNE标志位 GPIO_SetBits(LCD_CS_PORT, LCD_CS_PIN); // 禁止片选 } // 写入命令到LCD屏幕 void LCD_WriteCommand(uint8_t cmd) { GPIO_ResetBits(LCD_DC_PORT, LCD_DC_PIN); // DC设置为低电平表示写入命令 GPIO_ResetBits(LCD_CS_PORT, LCD_CS_PIN); // 使能片选 while (SPI_I2S_GetFlagStatus(LCD_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET); // 等待发送缓冲区为空 SPI_I2S_SendData(LCD_SPI_PORT, cmd); // 发送命令 while (SPI_I2S_GetFlagStatus(LCD_SPI_PORT, SPI_I2S_FLAG_RXNE) == RESET); // 等待接收缓冲区非空 SPI_I2S_ReceiveData(LCD_SPI_PORT); // 读取接收到的数据,清除RXNE标志位 GPIO_SetBits(LCD_CS_PORT, LCD_CS_PIN); // 禁止片选 } // 清屏 void LCD_Clear(uint16_t color) { int i, j; LCD_SetArea(0, 0, LCD_WIDTH-1, LCD_HEIGHT-1); for (i = 0; i < LCD_HEIGHT; i++) { for (j = 0; j < LCD_WIDTH; j++) { LCD_WriteData(color >> 8); LCD_WriteData(color & 0xFF); } } } // 设置绘制区域 void LCD_SetArea(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { LCD_WriteCommand(0x2A); LCD_WriteData(x1 >> 8); LCD_WriteData(x1 & 0xFF); LCD_WriteData(x2 >> 8); LCD_WriteData(x2 & 0xFF); LCD_WriteCommand(0x2B); LCD_WriteData(y1 >> 8); LCD_WriteData(y1 & 0xFF); LCD_WriteData(y2 >> 8); LCD_WriteData(y2 & 0xFF); LCD_WriteCommand(0x2C); } // 在指定位置绘制一个像素点 void LCD_DrawPixel(uint16_t x, uint16_t y, uint16_t color) { LCD_SetArea(x, y, x, y); LCD_WriteData(color >> 8); LCD_WriteData(color & 0xFF); } // 在指定位置绘制一个字符 void LCD_DrawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bgcolor) { int i, j; uint8_t font[16]; for (i = 0; i < 16; i++) { font[i] = Font16x16[c*16+i]; } for (i = 0; i < 16; i++) { for (j = 0; j < 8; j++) { if (font[i] & (1 << (7-j))) { LCD_DrawPixel(x+j, y+i, color); } else { LCD_DrawPixel(x+j, y+i, bgcolor); } } } } // 在指定位置绘制一个字符串 void LCD_DrawString(uint16_t x, uint16_t y, char *str, uint16_t color, uint16_t bgcolor) { while (*str) { LCD_DrawChar(x, y, *str++, color, bgcolor); x += 8; } } int main(void) { LCD_Init(); LCD_DrawString(50, 100, "张翼鹏", 0x0000, 0xFFFF); while (1) {} } ``` 在代码中,需要注意以下几点: 1. 需要定义LCD屏幕的SPI接口、引脚和分辨率; 2. 需要初始化SPI接口和LCD控制引脚; 3. 需要发送初始化命令,具体的命令可以参考LCD屏幕的数据手册; 4. 绘制字符和字符串时需要使用字库,本例中使用了一个16x16的字库,可以根据需要自行替换; 5. 绘制字符和字符串时需要注意字体的大小和颜色。 6. 在main函数中,我们先调用LCD_Init()函数初始化LCD屏幕,然后调用LCD_DrawString()函数在屏幕上绘制字符串“张翼鹏”。 需要注意的是,上述代码中的一些函数调用和数据类型可能与你的程序略有不同,需要根据具体情况进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值