stm32专题二十一:FSMC液晶显示和扫描方向

液晶屏连接原理图:

       其中值得注意的是,LCD_RESET复位引脚和LCD_BL背光,可以选择普通的GPIO来进行控制。而左侧的FSMC数据线,就直接按照编号连接到液晶屏。右侧LCD_CS片选、RD、WE都是FSMC固定占用的,而命令 / 数据线使用的是地址线A16。

       如果程序跑飞了导致下载器无法下载,先按住复位键,再点下载,然后松开复位键就能下载成功。

接下来是代码说明:

       初始化GPIO和FMSC,这里一个值得注意点地方就是,我们模拟8080时序使用的是FSMC模式B,这其中有一些时序参数。在操作SRAM时,这些参数可以通过SRAM提供的数据手册来得到。而在驱动液晶屏时,因为提供的参数并没有严格对应,需要使用实验测试参数。多测试几组参数,来获得一个相对稳定且速度较快的参数值

void ILI9341_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	// 打开 GPIO的时钟
	RCC_APB2PeriphClockCmd(ILI9341_CS_CLK | ILI9341_DC_CLK | ILI9341_WR_CLK | ILI9341_RD_CLK | ILI9341_BK_CLK | 
			ILI9341_RST_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_InitStructure.GPIO_Pin = ILI9341_RST_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ILI9341_RST_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_BK_PIN;
	GPIO_Init(ILI9341_BK_PORT, &GPIO_InitStructure);
	
	// 初始化其他引脚(复用推挽模式)
	/* 控制引脚 */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	
	GPIO_InitStructure.GPIO_Pin = ILI9341_CS_PIN;
	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_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);
	
	/* 数据引脚 */
	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);

	/* 这里地址建立时间和数据保持时间,在ILI9341数据手册中没有明确的给出来,
		 我们实际上是通过实验的方式,来直接测试几个参数,如果可以用就ok
		 如果把数据改大一点,就不容易出错,但数据改小一点的话,速度就会变快 */
	//地址建立时间(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 sram1
	FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;
	
	//设置地址总线与数据总线是否复用,仅用于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_Bank1_NORSRAM1, ENABLE);  // 使能BANK				
}

读写数据的函数:

#define ILI9341_CMD_ADDR					((volatile uint16_t *)(0X60000000))
#define	ILI9341_DATA_ADDR					((volatile uint16_t *)(0X60020000))

/**
  * @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);
}

设置背光的函数(起始就是把背光的IO口置成高低电平):

/**
 * @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 );	
}

同理,复位函数就是把接复位引脚的IO口置高低电平:

/**
 * @brief  ILI9341 软件复位
 * @param  无
 * @retval 无
 */
void ILI9341_Rst ( void )
{			
	GPIO_ResetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN );	 //低电平复位

	ILI9341_Delay ( 0xAFF ); 					   

	GPIO_SetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN );		 	 

	ILI9341_Delay ( 0xAFF ); 	
}

初始化寄存器:

这其中值得关注的有三个地方,其他的配置不用过于纠结。

设置列的开始和结束坐标(0X2A):

设置行的开始和结束坐标(0X2B):

显存数据写入:

举个例子如下:

整个设置范围到写入数据的流程图如下:

看一下代码中的列设置:

	/* 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 );

起始列为0,结束列为00EF,EF转成10进制为239,那么列的范围就是0 ~ 239,共240列。

行设置代码:

	/* 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 );

起始行为0,结束列为013F,EF转成10进制为319,那么行的范围就是0 ~ 319,共320行。

我们使用的屏幕,就正好是 320行 + 240列。

像素格式设置:

	/*  Pixel Format Set (3Ah)  */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0x3a ); 
	ILI9341_Write_Data ( 0x55 );

命令解析:首先发送命令0X3A,这个是用来设置像素格式的(多少位颜色数据),实际上我们希望使用的是RGB565,正好是16位数据对应2字节。对应的正好是0X55。

完整的配置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 ); 
}

初始化LCD液晶屏的代码如下,这里有个非常值得注意的地方,LCD初始化的代码一定要放在串口初始化前,不然会出现莫名其妙的问题(不知道为什么)

/** 
  * @brief 初始化函数
  */
void ILI9341_Init(void)
{
	ILI9341_GPIO_Config();
	ILI9341_FSMC_Config();
	
	ILI9341_BackLed_Control(ENABLE);	// 开启背光
	ILI9341_Rst();			// 复位
	ILI9341_REG_Config();				// 设置显示屏大小,颜色位宽等
}

然后设置一个开窗的函数,并利用这个开窗来填充矩形:

开窗函数:

/**
  * @brief 开窗函数(开一个矩形区域)
  * @param usX 	X起始坐标
	* @param usY	Y起始坐标
	* @param usWidth 		X方向宽度(X方向上有多少个像素点)
	* @param usHeight 	Y方向宽度(Y方向上有多少个像素点)
  */
void ILI9341_Open_Window(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight)
{
	ILI9341_Write_Cmd ( CMD_SetCoordinateX ); 
	ILI9341_Write_Data ( (usX & 0XFF00) >> 8 );
	ILI9341_Write_Data ( usX & 0X00FF );
	ILI9341_Write_Data ( ((usX + usWidth - 1) & 0XFF00) >> 8 );
	ILI9341_Write_Data ( (usX + usWidth - 1) & 0X00FF );
	
	ILI9341_Write_Cmd ( CMD_SetCoordinateY ); 
	ILI9341_Write_Data ( (usY & 0XFF00) >> 8 );
	ILI9341_Write_Data ( usY & 0X00FF );
	ILI9341_Write_Data ( ((usY + usHeight - 1) & 0XFF00) >> 8 );
	ILI9341_Write_Data ( (usY + usHeight - 1) & 0X00FF );
}

画矩形的代码:

/**
  * @brief 画一个实心的矩形
  */
void ILI9341_Darw_Rec(void)
{
	uint32_t i = 0;
	
	ILI9341_Open_Window(10, 20, 50, 100);
	
	ILI9341_Write_Cmd(CMD_SetPixel);	// 填充像素
	
	for (i = 0; i < 50 * 100; i++)
	{
		ILI9341_Write_Data(0XF800);			// 对应红色
	}
}

然后在主函数中进行测试:

int main(void)
{
	/* 液晶屏的初始化一定要放在串口之前,不然会出现莫名其妙的问题 */
	ILI9341_Init();			
	
	LED_GPIO_Config();
	USART_Config();
	
	LED_BLUE;
	
	printf("这是一个LCD显示实验\t\n");
	
	printf("\t0X0C命令返回值测试:%#X\n", Read_Pixel_Format());
	
	ILI9341_Darw_Rec();
	
  
  while (1);
}

液晶屏显示结果如图所示,在我们预设的区域出现了一个红色的矩形块。这里使用的是野火F103指南者的开发板和配套的液晶屏。说明:照片中的摩尔纹和边缘凸弯是手机摄像头拍照太近,光学干涉和畸变造成的,真实屏幕质量很好无此问题。

扫描方向设置

参数说明:

                                   A                                           0

MY = 0,扫描方向为  |      ,MY = 1,扫描方向为  |

                                   0                                           A

MX = 0,扫描方向为 A — 0      ,MX = 1,扫描方向为 0 — A

MY = 0,X Y方向不变,    MY = 1,X Y 方向交换。

 

8个模式的方向设置如下:

​/**
 * @brief  设置ILI9341的GRAM的扫描方向 
 * @param  ucOption :选择GRAM的扫描方向 
 *     @arg 0-7 :参数可选值为0-7这八个方向
 *
 *	!!!其中0、3、5、6 模式适合从左至右显示文字,
 *				不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果			
 *		
 *	其中0、2、4、6 模式的X方向像素为240,Y方向像素为320
 *	其中1、3、5、7 模式下X方向像素为320,Y方向像素为240
 *
 *	其中 6 模式为大部分液晶例程的默认显示方向
 *	其中 3 模式为摄像头例程使用的方向
 *	其中 0 模式为BMP图片显示例程使用的方向
 *
 * @retval 无
 * @note  坐标图例:A表示向上,V表示向下,<表示向左,>表示向右
					X表示X轴,Y表示Y轴

设置扫描方向的函数:

//根据液晶扫描方向而变化的XY像素宽度
//调用ILI9341_GramScan函数设置方向时会自动更改

#define    ILI9341_LESS_PIXEL    240    //液晶屏较短方向的像素宽度
#define    ILI9341_MORE_PIXEL    320    //液晶屏较长方向的像素宽度


uint16_t LCD_X_LENGTH = ILI9341_LESS_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9341_MORE_PIXEL;

//液晶屏扫描模式,本变量主要用于方便选择触摸屏的计算参数
//参数可选值为0-7
//调用ILI9341_GramScan函数设置方向时会自动更改
//LCD刚初始化完成时会使用本默认值
uint8_t LCD_SCAN_MODE = 6;

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 ( CMD_SetCoordinateX ); 
	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 ( CMD_SetCoordinateY ); 
	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 ( CMD_SetPixel );	
}

代码分析:

1 定义了两个宏,保存液晶屏长边和短边的像素数量;

#define    ILI9341_LESS_PIXEL    240    //液晶屏较短方向的像素宽度
#define    ILI9341_MORE_PIXEL    320    //液晶屏较长方向的像素宽度

2 定义两个全局变量,用于保存当前模式下,X 和 Y 方向的像素个数(在设置函数中修改);

uint16_t LCD_X_LENGTH = ILI9341_LESS_PIXEL;
uint16_t LCD_Y_LENGTH = ILI9341_MORE_PIXEL;

3 函数主体:根据输入的模式(0 ~ 7),分别修改X 和 Y 方向的像素值,然后先写入命令参数(36h),然后写入模式数据(左移 5 位,是因为这三个参数分别为 D7 D6 D5),| (0X08)是因为官方默认提供的配置中,数据写入值为0XC8,所以这里| (0X08)确保其他配置不变,只修改扫描模式方向;

/* memory access control set */
	DEBUG_DELAY ();
	ILI9341_Write_Cmd ( 0x36 ); 	
	ILI9341_Write_Data ( 0xC8 );    /*竖屏  左上角到 (起点)到右下角 (终点)扫描方式*/
	DEBUG_DELAY ();

4 扫描方向设置ok后,进行开窗操作;

代码如下:

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 ( CMD_SetCoordinateX ); 
	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 ( CMD_SetCoordinateY ); 
	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 ( CMD_SetPixel );	
}

测试程序

/*用于展示LCD的八种方向模式*/
void LCD_Direction_Show(void)
{
	uint8_t i = 0;	
	char dispBuff[100];
	
	//轮流展示各个方向模式(8种扫描方向)
	for(i=0;i<8;i++)
	{	
		LCD_SetFont(&Font16x24);
		LCD_SetColors(RED,BLACK);

		ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);	/* 清屏,显示全黑 */
		
	 //其中0、3、5、6 模式适合从左至右显示文字,
	 //不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果			
	 //其中 6 模式为大部分液晶例程的默认显示方向  
		ILI9341_GramScan ( i );
			
		sprintf(dispBuff,"o%d. X --->",i);
		ILI9341_DispStringLine_EN(LINE(0),dispBuff);//沿X方向显示文字
			
		sprintf(dispBuff,"o%d.Y|V",i);	
		ILI9341_DispString_EN_YDir(0,0,dispBuff);//沿Y方向显示文字
			
		Delay(0xFFFFFF);		
		
		//显示测试
		// *	!!!其中0、3、5、6 模式适合从左至右显示文字,不推荐使用其它模式显示文字	
		//其它模式显示文字会有镜像效果
		LCD_Test();
	}
}

不同扫描方向的显示效果:

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值