声明:本篇文章只是个人知识盲区、知识弱点、重点部分的归纳总结,望各位大佬不喜勿喷。梳理顺序是按照正点原子的视频和文档的实际顺序梳理,转载请注明出处。
作者:sumjess
适用:这个视频我已经看过3遍了,总会有忘记的,所以来写这本书的随手笔记,记录重点、易忘点。该博客可以当做字典,也可以当做笔记。
目前内容:LCD的介绍和使用方法
 

一、TFTLCD驱动原理-TFTLCD简介:
(1)介绍TFTLCD:
TFTLCD即薄膜晶体管液晶显示器。它与无源TN-LCD、STN-LCD的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。
TFTLCD具有:亮度好、对比度高、层次感强、颜色鲜艳等特点。是目前最主流的LCD显示器。广泛应用于电视、手机、电脑、平板等各种电子产品。
(2)2.8寸 TFTLCD接口说明(16位80并口):

(3)TFTLCD 16位80并口驱动简介:

(4)ILI9341 驱动时序:

重点时序:

读ID低电平脉宽(trdl)

读ID高电平脉宽(trdh)

读FM低电平脉宽(trdlfm)

读FM高电平脉宽(trdhfm)

写控制低电平脉宽(twrl)

写控制高电平脉宽(twrh)

注意:ID指LCD的ID号

FM指帧缓存,即:GRAM

(5)TFTLCD驱动原理-驱动流程:

(6)TFTLCD驱动原理-指令简介:

三、FSMC简介-FSMC介绍:

      FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和16PC存储器卡连接,STM32FSMC接口支持包括SRAMNAND FLASHNOR FLASHPSRAM等存储器。FSMC的框图如下图所示:

四、FSMC简介-FSMC驱动LCD原理:
      FSMC驱动外部SRAM时,外部SRAM的控制一般有:地址线(如A0~A25)、数据线(如D0~D15)、写信号(WE,即WR)、读信号(OE,即RD)、片选信号(CS),如果SRAM支持字节控制,那么还有UB/LB信号。

      而TFTLCD的信号我们在前面介绍过,包括:RS、D0~D15、WR、RD、CS、RST和BL等,其中真正在操作LCD的时候需要用到的就只有:RS、D0~D15、WR、RD和CS。其操作时序和SRAM的控制完全类似,唯一不同就是TFTLCD有RS信号,但是没有地址信号。

      TFTLCD通过RS信号来决定传送的数据是数据还是命令,本质上可以理解为一个地址信号,比如我们把RS接在A0上面,那么当FSMC控制器写地址0的时候,会使得A0变为0,对TFTLCD来说,就是写命令。而FSMC写地址1的时候,A0将会变为1,对TFTLCD来说,就是写数据了。这样,就把数据和命令区分开了,他们其实就是对应SRAM操作的两个连续地址。当然RS也可以接在其他地址线上,战舰V3和精英板开发板都是把RS连接在A10上面,而探索者STM32F4把RS接在A6上面。

      因此,可以把TFTLCD当成一个SRAM来用,只不过这个SRAM有2个地址,这就是FSMC可以驱动LCD的原理。

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

六、FSMC简介-存储块1 操作简介:
    STM32的FSMC存储块1(Bank1)用于驱动NOR FLASH/SRAM/PSRAM,被分为4个区,每个区管理64M字节空间,每个区都有独立的寄存器对所连接的存储器进行配置。Bank1的256M字节空间由28根地址线(HADDR[27:0])寻址。

    这里HADDR,是内部AHB地址总线,其中,HADDR[25:0]来自外部存储器地址FSMC_A[25:0],而HADDR[26:27]对4个区进行寻址。如下表所示:

当Bank1接的是16位宽度存储器的时候:HADDR[25:1]à FSMC_A[24:0]

当Bank1接的是8位宽度存储器的时候:HADDR[25:0]à FSMC_A[25:0]

不论外部接8位/16位宽设备,FSMC_A[0]永远接在外部设备地址A[0]

 

    STM32的FSMC存储块1 支持的异步突发访问模式包括:模式1、模式A~D等多种时序模型,驱动SRAM时一般使用模式1或者模式 A,这里我们使用模式A来驱动LCD(当SRAM用),其他模式说明详见:STM32中文参考手册-FSMC章节。

            模式A读时序图                                                                        模式A写时序图

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

(1)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

(2)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位,则读写共用这个时序寄存器!

(3)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。

(4)寄存器组合说明:
在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]保留,没有用到。

八、代码思路:
(1)代码内容:
1.硬件连接

2.LCD&lcddev结构体讲解

3.底层接口函数讲解

4.初始化函数讲解

5.坐标设置函数讲解

6.画点函数讲解

7.读点函数讲解

8.字符显示函数讲解

(2)LCD结构体:

//LCD重要参数集
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函数完成

(3)底层接口函数:
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)

(4)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);			//清屏
}
复制

(5)LCD坐标设置函数:

//设置光标位置
//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型号,执行不同的代码
     { 
	 ……//省略部分代码
     }
}
复制

(6)LCD画点函数:

(7)LCD读点函数:

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

(8)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;
                     }
              }  	 
        }  	    	   	 	  
} 
复制

(9)LCD字符显示函数:字符码表:

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*/
}
复制