一看就会LCD显示——利用FMSC模拟8080时序(内含代码)


LCD在日常生活中应用广泛,本章节直接介绍如何利用FMSC模拟8080时序去点亮LCD。本章用到野火的霸道开发板。

一、8080通讯接口

信号线

ILI9341对应的信号线

说明

LCD_DB[15:0]

D[15:0]

数据信号

LCD_RD

RDX

读数据信号,低电平有效

LCD_RS

D/CX

数据/命令信号,高电平时,D[15:0]表示的是数据(RGB像素数据或命令数据),低电平时D[15:0]表示控制命令

LCD_RESET

RESX

复位信号,低电平有效

LCD_WR

WRX

写数据信号,低电平有效

LCD_CS

CSX

片选信号,低电平有效

LCD_BK

-

背光信号,低电平点亮

GPIO[5:1]

-

触摸屏的控制信号线,下一章再介绍

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

 二、STM32向ILI9341(LCD)写命令的时序图

一看就会LCD显示——利用FMSC模拟8080时序(内含代码)_第1张图片

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

 三、使用STM32的FSMC模拟8080接口时序

ILI9341的8080通讯接口时序可以由STM32使用普通I/O接口进行模拟,但这样效率太低,STM32提供了一种特别的控制方法——使用FSMC接口实现8080时序。

FSMC简介

一看就会LCD显示——利用FMSC模拟8080时序(内含代码)_第2张图片

控制LCD时,适合使用FSMC的NOR\PSRAM模式,如下:

FSMC信号名称

信号方向

功能

CLK

输出

时钟(同步突发模式使用)

A[25:0]

输出

地址总线

D[15:0]

输入/输出

双向数据总线

NE[x]

输出

片选,x = 1...4

NOE

输出

输出使能

NWE

输出

写使能

NWAIT

输入

NOR闪存要求FSMC等待的信号

NADV

输出

地址、数据线复用时作锁存信号

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

FSMC NOR/PSRAM中的模式B的写时序如下图:

一看就会LCD显示——利用FMSC模拟8080时序(内含代码)_第3张图片

相对比8080写时序来看:

一看就会LCD显示——利用FMSC模拟8080时序(内含代码)_第4张图片

对比FSMC NOR/PSRAM中的模式B时序与ILI9341液晶控制器芯片使用的8080时序可发现,这两个时序是十分相似的(除了FSMC的地址线A和8080的D/CX线,可以说是完全一样)。       所以我们可以FSMC上的地址线的电平高低来模拟命令(电平为低)和数据(电平为高)

FSMC-NOR信号线

功能

8080信号线

功能

NEx

片选信号

CSX

片选信号

NWR

写使能

WRX

写使能

NOE

读使能

RDX

读使能

D[15:0]

数据信号

D[15:0]

数据信号

A[25:0]

地址信号

D/CX

数据/命令选择

对于FSMC和8080接口,前四种信号线都是完全一样的,仅仅是FSMC的地址信号线A[25:0]与8080的数据/命令选择线D/CX有区别。而对于D/CX线,它为高电平的时候表示数值,为低电平的时候表示命令,如果能使用FSMC的A地址线根据不同的情况产生对应的电平,那么就完全可以使用FSMC来产生8080接口需要的时序了。     
为了模拟出8080时序,我们可以把FSMC的A0地址线(也可以使用其它A1/A2等地址线)与ILI9341芯片8080接口的D/CX信号线连接,那么当A0为高电平时(即D/CX为高电平),数据线D[15:0]的信号会被ILI9341理解为数值,若A0为低电平时(即D/CX为低电平),传输的信号则会被理解为命令。

 四、代码解析

        main主函数

/**
  * @brief  主函数
  * @param  无  
  * @retval 无
  */
int main ( void )
{

	ILI9341_Init ();         //LCD 初始化

	USART_Config();		
	
	printf("\r\n ********** 液晶屏英文显示程序*********** \r\n"); 

	
 //其中0、3、5、6 模式适合从左至右显示文字,
 //不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果			
 //其中 6 模式为大部分液晶例程的默认显示方向  

  ILI9341_GramScan ( 6 );
	while ( 1 )
	{
		LCD_Test();
	}
	
	
}




/*用于测试各种液晶的函数*/
void LCD_Test(void)
{
	/*演示显示变量*/
	static uint8_t testCNT = 0;	
	char dispBuff[100];
	
	testCNT++;	
	
	LCD_SetFont(&Font8x16);
	LCD_SetColors(RED,BLACK);

  ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH);	/* 清屏,显示全黑 */
	/********显示字符串示例*******/
  ILI9341_DispStringLine_EN(LINE(0),"BH 3.2 inch LCD para:");
  ILI9341_DispStringLine_EN(LINE(1),"Image resolution:240x320 px");
  ILI9341_DispStringLine_EN(LINE(2),"ILI9341 LCD driver");
  ILI9341_DispStringLine_EN(LINE(3),"XPT2046 Touch Pad driver");
  
	/********显示变量示例*******/
	LCD_SetFont(&Font16x24);
	LCD_SetTextColor(GREEN);

	/*使用c标准库把变量转化成字符串*/
	sprintf(dispBuff,"Count : %d ",testCNT);
  LCD_ClearLine(LINE(4));	/* 清除单行文字 */
	
	/*然后显示该字符串即可,其它变量也是这样处理*/
	ILI9341_DispStringLine_EN(LINE(4),dispBuff);

 
  ILI9341_Clear(0,16*8,LCD_X_LENGTH,LCD_Y_LENGTH-16*8);	/* 清屏,显示全黑 */

}

ILI9341初始化

/**
 * @brief  ILI9341初始化函数,如果要用到lcd,一定要调用这个函数
 * @param  无
 * @retval 无
 */
void ILI9341_Init ( void )
{
	ILI9341_GPIO_Config ();
	ILI9341_FSMC_Config ();
	
	ILI9341_BackLed_Control ( ENABLE );      //点亮LCD背光灯
	ILI9341_Rst ();
	ILI9341_REG_Config ();
	
	//设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向  
	ILI9341_GramScan(LCD_SCAN_MODE);
}

显示字符串函数

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

以上代码仅供思路参考,需要完整代码的小伙伴可以私信我拿,谢谢

你可能感兴趣的:(单片机,嵌入式硬件)