STM32开发板配备2.8寸TFT LCD显示屏

一,硬件部分

 

 二,16位80并口驱动简介

模块的8080并口读/写的过程为:

先根据要写入/读取的数据的类型,设置RS为高(数据)/低(命令),然后拉低片选,选中ILI9341,接着我们根据是读数据,还是要写数据置RD/WR为低,然后:

1.读数据:在RD的上升沿, 读取数据线上的数据(D[15:0]);

2.写数据:在WR的上升沿,使数据写入到ILI9341里面

 三,驱动流程

 实验版中RST与MCU相通,即按下复位即可。(硬复位)

初始化序列:厂家提供。

四,RGB565

模块对外接口采用16位并口,颜色深度为16位,格式为RGB565,关系如下图:

 例如0xf800,为R全1,其他全0,为纯红色。

五,ILI9341指令格式

ILI9341所有的指令都是8位的(高8位无效),且参数除了读写GRAM的时候是16位,其他操作参数,都是8位的。

0XD3指令(读34,)

该指令为读ID4指令,用于读取LCD控制器的ID 。因此,同一个代码,可以根据ID的不同,执行不同的LCD驱动初始化,以兼容不同的LCD屏幕。

例:得到9341

 0X36指令(逐行、列设置坐标)

该指令为存储访问控制指令,可以控制ILI9341存储器的读写方向,简单的说,就是在连续写GRAM的时候,可以控制GRAM指针的增长方向,从而控制显示方式(读GRAM也是一样)。

 

 0X2A指令

该指令是列地址设置指令,在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置横坐标(x坐标)

在默认扫描方式时,该指令用于设置x坐标,该指令带有4个参数,实际上是2个坐标值:SC和EC,即列地址的起始值和结束值,SC必须小于等于EC,且0≤SC/EC≤239。一般在设置x坐标的时候,我们只需要带2个参数即可,也就是设置SC即可,因为如果EC没有变化,我们只需要设置一次即可(在初始化ILI9341的时候设置),从而提高速度。 

0X2B指令

该指令是页地址设置指令,在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置纵坐标(y坐标)

在默认扫描方式时,该指令用于设置y坐标,该指令带有4个参数,实际上是2个坐标值:SP和EP,即页地址的起始值和结束值,SP必须小于等于EP,且0≤SP/EP≤319。一般在设置y坐标的时候,我们只需要带2个参数即可,也就是设置SP即可,因为如果EP没有变化,我们只需要设置一次即可(在初始化ILI9341的时候设置),从而提高速度。 

0X2C指令

该指令是写GRAM指令,在发送该指令之后,我们便可以往LCD的GRAM里面写入颜色数据了,该指令支持连续写 (地址自动递增)

在收到指令0X2C之后,数据有效位宽变为16位,我们可以连续写入LCD GRAM值,而GRAM的地址将根据MY/MX/MV设置的扫描方向进行自增。例如:假设设置的是从左到右,从上到下的扫描方式,那么设置好起始坐标(通过SC,SP设置)后,每写入一个颜色值,GRAM地址将会自动自增1(SC++),如果碰到EC,则回到SC,同时SP++,一直到坐标:EC,EP结束,其间无需再次设置的坐标,从而大大提高写入速度。 

0X2E指令

该指令是读GRAM指令,用于读取ILI9341的显存(GRAM),同0X2C指令,该指令支持连续读 (地址自动递增)

ILI9341在收到该指令后,第一次输出的是dummy数据(无效),第二次开始,读取到的才是有效的GRAM数据(从坐标:SC,SP开始),输出规律为:每个颜色分量占8个位,一次输出2个颜色分量。比如:第一次输出是R1G1,随后的规律为:B1R2àG2B2àR3G3àB3R4àG4B4àR5G5… 以此类推 

六,FSMC寄存器

FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和16位PC存储器卡连接,STM32的FSMC接口支持包括SRAM、NAND FLASH、NOR FLASH和PSRAM等存储器。

 STM32的FSMC支持8/16/32位数据宽度,我们这里用到的LCD是16位宽度的,所以在设置的时候,选择16位宽就OK了。FSMC的外部设备地址映像,STM32的FSMC将外部存储器划分为固定大小为256M字节的四个存储块

 对于NOR FLASH/PSRAM控制器(存储块1),通过FSMC_BCRx、FSMC_BTRx和FSMC_BWTRx寄存器设置(其中x=1~4,对应4个区)。通过这3个寄存器,可以设置FSMC访问外部存储器的时序参数,拓宽了可选用的外部存储器的速度范围。  

SRAM/NOR闪存片选控制寄存器(FSMC_BCRx)

EXTMOD:扩展模式使能位,控制是否允许读写不同的时序,需设置为1

WREN:写使能位。我们需要向TFTLCD写数据,故该位必须设置为1

MWID[1:0]:存储器数据总线宽度。00,表示8位数据模式;01表示16位数据模式;10和11保留。我们的TFTLCD是16位数据线,所以设置WMID[1:0]=01。

MTYP[1:0]:存储器类型。00表示SRAM、ROM;01表示PSRAM;10表示NOR FLASH;11保留。我们把LCD当成SRAM用,所以需要设置MTYP[1:0]=00。

MBKEN:存储块使能位。需设置为1

 SRAM/NOR闪存片选时序寄存器(FSMC_BTRx)

ACCMOD[1:0]:访问模式。00:模式A;01:模式B;10:模式C;11:模式D。

DATAST[7:0]:数据保持时间,等于: DATAST(+1)个HCLK时钟周期,DATAST最大为255。对ILI9341来说,其实就是RD低电平持续时间,最大为355ns。对STM32F1,一个HCLK=13.8ns (1/72M),设置为15;对STM32F4,一个HCLK=6ns(1/168M) ,设置为60。

ADDSET[3:0]:地址建立时间。表示:ADDSET (+1)个HCLK周期,ADDSET最大为15。对ILI9341来说,这里相当于RD高电平持续时间,为90ns。STM32F1的FSMC性能存在问题,即便设置为0,RD也有190ns的高电平,我们这里设置为1。而对STM32F4,则设置为15。

 如果未设置EXTMOD位,则读写共用这个时序寄存器!

SRAM/NOR闪存写时序寄存器(FSMC_BWTRx)

 

ACCMOD[1:0]:访问模式。00:模式A;01:模式B;10:模式C;11:模式D。

DATAST[7:0]:数据保持时间,等于: DATAST(+1)个HCLK时钟周期,DATAST最大为255。对ILI9341来说,其实就是WR低电平持续时间,为15ns,不过ILI9320等则需要50ns。考虑兼容性,对STM32F1,一个HCLK=13.8ns (1/72M),设置为3;对STM32F4,一个HCLK=6ns(1/168M) ,设置为9。

ADDSET[3:0]:地址建立时间。表示:ADDSET+1个HCLK周期,ADDSET最大为15。对ILI9341来说,这里相当于WR高电平持续时间,为15ns。同样考虑兼容ILI9320,对STM32F1,这里即便设置为1,WR也有100ns的高电平,我们这里设置为1。而对STM32F4,则设置为8。

寄存器组合

在ST官方库提供的的寄存器定义里面,并没有定义FSMC_BCRx、FSMC_BTRx、FSMC_BWTRx等这个单独的寄存器,而是将他们进行了一些组合。规律如下:

 

FSMC_BCRx和FSMC_BTRx,组合成BTCR[8]寄存器组,他们的对应关系如下:

BTCR[0]对应FSMC_BCR1,BTCR[1]对应FSMC_BTR1

BTCR[2]对应FSMC_BCR2,BTCR[3]对应FSMC_BTR2

BTCR[4]对应FSMC_BCR3,BTCR[5]对应FSMC_BTR3

BTCR[6]对应FSMC_BCR4,BTCR[7]对应FSMC_BTR4

FSMC_BWTRx则组合成BWTR[7],他们的对应关系如下:

BWTR[0]对应FSMC_BWTR1,BWTR[2]对应FSMC_BWTR2,

BWTR[4]对应FSMC_BWTR3,BWTR[6]对应FSMC_BWTR4,

BWTR[1]、BWTR[3]和BWTR[5]保留,没有用到。

七,软件部分

结构体

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; 	
//LCD参数
extern _lcd_dev lcddev;	//管理LCD重要参数

lcddev结构体参数的赋值,基本上都是在LCD_Display_Dir函数完成

7个底层接口函数

1,写寄存器值函数 :void LCD_WR_REG(u16 regval)

2,写数据函数:void LCD_WR_DATA(u16 data)

3,读数据函数:u16 LCD_RD_DATA(void)

4,写寄存器内容函数: void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)

5,读寄存器内容函数: u16 LCD_ReadReg(u16 LCD_Reg)

6,开始写GRAM函数: void LCD_WriteRAM_Prepare(void)

7,写GRAM函数: void LCD_WriteRAM(u16 RGB_Code)

lcd伪代码 

LCD初始化函数伪代码:

//LCD初始化
void LCD_Init(void)
{
    初始化GPIO;
    初始化FSMC;		             	//Mini板不需要
    读取LCD ID;	
    printf(“LCD ID:%x\r\n”,lcddev.id);//打印LCD ID,用到了串口1
                                        //所以必须初始化串口1,否则黑屏	
    根据不同的ID执行LCD初始化代码;
    LCD_Display_Dir(0);		 	//默认为竖屏
    LCD_LED=1;			 	//点亮背光
    LCD_Clear(WHITE);			//清屏
}

坐标设置函数

//设置光标位置
//Xpos:横坐标
//Ypos:纵坐标
void LCD_SetCursor(u16 Xpos, u16 Ypos)
{	 
     if(lcddev.id==0X9341||lcddev.id==0X5310)
     {		    
          LCD_WR_REG(lcddev.setxcmd); 
          LCD_WR_DATA(Xpos>>8);
          LCD_WR_DATA(Xpos&0XFF);  
          LCD_WR_REG(lcddev.setycmd); 
	 LCD_WR_DATA(Ypos>>8);
	 LCD_WR_DATA(Ypos&0XFF); 
     }else if(lcddev.id==XXXX)	//根据不同的LCD型号,执行不同的代码
     { 
	 ……//省略部分代码
     }
}
		 

画点函数

//画点
//x,y:坐标
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(u16 x,u16 y)
{
    LCD_SetCursor(x,y);		//设置光标位置 
    LCD_WriteRAM_Prepare();	//开始写入GRAM
    LCD_WR_DATA(POINT_COLOR); 

}

读点函数

LCD读点函数:u16 LCD_ReadPoint(u16 x,u16 y)

1,非Mini板的读点函数代码(FSMC方式,适合战舰、精英、探索者F4板)

2,Mini板的读点函数代码(GPIO方式,适合Mini板)

lcd字符显示函数

//在指定位置显示一个字符
//x,y:起始坐标
//num:要显示的字符:" "--->"~"
//size:字体大小 12/16/24
//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 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;
                     }
              }  	 
        }  	    	   	 	  
} 

 字符码表

const unsigned char oled_asc2_1206[95][12]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
……
{0x40,0x00,0x80,0x00,0x40,0x00,0x20,0x00,0x20,0x00,0x40,0x00},/*"~",94*/
};
const unsigned char oled_asc2_1608[95][16]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
……
{0x00,0x00,0x60,0x00,0x80,0x00,0x80,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x20,0x00},/*"~",94*/
}
const unsigned char oled_asc2_2412[95][36]={ 
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x80,0x38,0x0F,0xFE,0x38,0x0F,0x80,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
……
{0x00,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x0C,0x00,0x00,0x10,0x00,0x00},/*"~",94*/
}

作者:可怜楚楚先生

物联沃分享整理
物联沃-IOTWORD物联网 » STM32开发板配备2.8寸TFT LCD显示屏

发表评论