Bootstrap

HAL库源码移植与使用之驱动LCD屏

LCD屏会有烧屏残影的风险,但因其价格便宜他非常适合用于单片机显示

显示屏分为以下几种:

他的组成部包含玻璃基板、背光、驱动IC等

LCD接口的种类

MCU很简单,连51单片机都能驱动,但无法频繁刷新,一般有着20几个引脚引出的就是MCU接口

我们常用的是就是MCU,下面讲的也是LCD屏幕MCU驱动方式

正点原子所用的驱动芯片ILI9341芯片,下面是他的简图

它支持三种驱动接口:MCU接口(8/9/16/18位)     3/4 线SPI接口     RGB接口(6/16/18位)

LCD模组接口由厂家设计的决定

GRAM即指它内部有像之前的OLED一样具有一个缓存区,专门用来接收图片并显示

RGB三原色

他把红 R 绿  G 蓝  B三种基础颜色组合起来形成新的颜色,用一个数组来表示每种颜色的强度大小,再把三种颜色融合成新颜色

RGB565 代表红色有五位绿色有六位蓝色有五位,一共有2^5*2^6*2^5=65536种颜色

RGB888 代表红色有八位绿色有八位蓝色有八位,一共2^8*2^8*2^8种颜色

电脑一般用ARGB888 A透明色占32-8-8-8=8位则共2^8*2^8*2^8种颜色(透明度不算)

驱动原理:

初始化序列(数组),屏厂提供,用于初始化特定屏幕,不同屏幕厂家不完全相同!

ILI9341芯片一般也用8080并口时序,如下(箭头为上升沿下降沿)

8080写时序

示例代码

void lcd_wr_data(uint16_t data)
{
    	LCD_RS(1);		/* 操作数据 */
    	LCD_CS(0);		/* 选中 */
    	LCD_DATA_OUT(data);	/* 数据 */
    	LCD_WR(0);		/* WR低电平 */
    	LCD_WR(1);		/* WR高电平 */
    	LCD_CS(1);		/* 释放片选 */
}

 8080读时序示例代码

注意:经过实验认证,

 LCD_RD(0);            /* RD低电平 */      

 ram = LCD_DATA_IN;        /* 读取数据 */         

LCD_RD(1);            /* RD高电平 */

只能把读取数据放在上升沿中间不然会出错

uint16_t  lcd_rd_data(void)
{
	uint16_t ram;  			/* 定义变量 */
 	LCD_RS(1);              		/* 操作数据 */
    	LCD_CS(0);			/* 选中 */
    	LCD_RD(0);			/* RD低电平 */
   	ram = LCD_DATA_IN;    	/* 读取数据 */
    	LCD_RD(1);			/* RD高电平 */
    	LCD_CS(1);			/* 释放片选 */
	return ram;			/* 返回读数 */
}

用于控制LCD的各种显示功能和效果,整体功能较复杂。常见型号:ILI9341/ST7789等

ST7789和ILI9341都是LCD屏常用的芯片,且驱动方式大差不差,驱动代码也是可以通用的

为了防止意外,一般程序设计时都会验证芯片id看看是不是真的ILI9341芯片驱动,这也就是ILI9341/ST7789驱动的唯一不同,你要想通用把这个步骤删了或者用另一个芯片的id替换,并且她两读取指令还不一样,前者是0xD3后者是0xD4

ILI9341  ID:是9341

ST7789  ID:是7789

具体命令:

发送0xD3执行一次假读(啥也不读)再读取一次全零 再读取ID高四位 再读取ID低四位

LCD是自动实时刷新的,完全不用自己手动刷新,所以只要你设置了GRAM,等一下他就自动刷新

所以只要写好了GRAM就可以自动按你设置的显示方式显示

GRAM显示扫描设置

特别注意:BGR位控制颜色不要写错成RGB放进去

读取/写入点位颜色信息步骤:

先设置X坐标位置,Y坐标位置,他是设置了个区域你就可以在该区域内按左上角第一个点往右再回来第二排第一个....完成连续写入

再发送写入/读取指令,

就可以在该点开始读取/写入点位颜色信息,

而点位信息再写入或读取时可以自动地址自增,不用频繁发送

eg:


/* 设置坐标 */
void lcd_set_cursor(uint16_t x, uint16_t y)
{
    lcd_wr_regno(lcddev.setxcmd);
    lcd_wr_data(x >> 8);
    lcd_wr_data(x & 0xFF);
    lcd_wr_regno(lcddev.setycmd);
    lcd_wr_data(y >> 8);
    lcd_wr_data(y & 0xFF);
}

他只用发送你写入的x起始 y起始位置 因为之前已经初始化,你不发送结束位置它默认结束位置是初始化时结束的位置

那也就是你设置了你设置的点到默认的终止位置的区域位操作区后续在这个区域内执行连续写连续读操作,那你设置只读/写一个点就完成了只写/读一个点

下面用一个写正方形进GRAM的例子

void lcd_clear(uint16_t color)
{
    uint32_t index = 0;
    uint32_t totalpoint = lcddev.width;
    
    totalpoint *= lcddev.height;    /* 得到总点数 */
    lcd_set_cursor(0, 0);   /* 设置光标位置 */
    lcd_write_ram_prepare();        /* 开始写入GRAM */

    LCD_RS(1);                      /* RS=1,表示写数据 */
    LCD_CS(0);
    
    for (index = 0; index < totalpoint; index++)
    {
        LCD_DATA_OUT(color);        /* 写入要写的数据 */
        LCD_WR(0);
        LCD_WR(1);
    }
    
    LCD_CS(1);
}

它初始化结束位置是屏幕最后一个点起始位置是第一个点,他这又发送(0,0),那默认是(0,0)到最后一个点也就是全部屏幕都是可操作GRAM区,那你就可以算算点数长乘以宽得到总共totalpoint个点,那就一直依靠连续写功能直接输入totalpoint个蓝色或其他颜色得全屏一个颜色

这个设置X  Y的步骤是设置能操作的GRAM的区域

刚开始第一次设置X  Y坐标就是初始化,后面才是设置可操作区域

第一次设置X的起始与结束,后面再发送该命令时就只需要写起始地址,可不写结束地址默认为第一次的结束地址

第一次设置Y的起始与结束,后面再发送该命令时就只需要写起始地址,可不写结束地址默认为第一次的结束地址

确定

写入指令支持连续写与地址自增,它的连续写入方向是由MX/MY/MV决定

比如:MX/MY/MV=0/0/0  那就会按

RGB颜色代码  注意写入时要bgr这样写入

来连续写入点位颜色进GRAM

读取点位信息指令:

R1  G1  B1  R2  G2  B2...分别代表第一个点  第二个点....的R G B值

读取点位颜色代码:

读取数据函数

uint16_t  lcd_rd_data(void)
{
	uint16_t ram;  		/* 定义变量 */
	DATA_IN_MODE();	/* 设置数据输入 */
 	LCD_RS(1);              	/* 操作数据 */
    	LCD_CS(0);			/* 选中 */
    	LCD_RD(0);		/* RD低电平 */
   	ram = LCD_DATA_IN;    	/* 读取数据 */
    	LCD_RD(1);		/* RD高电平 */
    	LCD_CS(1);			/* 释放片选 */
	DATA_OUT_MODE();	/* 设置数据输出 */
	return ram;		/* 返回读数 */
}

读取点位颜色 

uint16_t  lcd_read_point (uint16_t x, uint16_t y)
{
	uint16_t r = 0, g = 0, b = 0;	/* 定义变量 */
	lcd_set_cursor(x, y);		/* 设置坐标 */
 	lcd_wr_regno(0X2E);		/* 发读点命令 */
 	r = lcd_rd_data();  		/* 假读 */
	r = lcd_rd_data();  		/* 读rg */
	b = lcd_rd_data(); 		/* 读b */
    	g = r & 0XFF;       			/* 得到g值 */
	return (((r >> 11) << 11) | ((g >> 2) << 5) | (b >> 11));
}

代码解析:

b = lcd_rd_data();         /* 读b */

 g = r & 0XFF;                   /* 得到g值 */

(((r >> 11) << 11) | ((g >> 2) << 5) | (b >> 11))这段是干嘛的呢?

这是一个极其nb的代码思想,因为你从上面lcd返回的数据可以看出,他是混在一个16位数据发回来的,那你把r往右移位11再左移11看似啥都没干,实则把D1给删掉了,真nb,后面的g只提取了后八位所以先往左移2位把空位删掉再左移五位把G对其r

;