屏幕这一块没什么搞头,直接用迪文屏 文章只起个记录作用
显示器分类
LCD简介
Liquid Crystal Display,即液晶显示器,由:玻璃基板、背光、驱动IC等组成。全彩LCD,是一种全彩显示屏(RGB565、RGB888),可以显示各种颜色。
LCD基本组成:
一个像素点有RGB三种色来表示。
LCD接口分类:
MCU屏接口由于自带SRAM,驱动简单,大部分单片机都能驱动!
一眼看过去排针多的基本上都是MCU接口的屏
LCD驱动原理
LCD屏(MCU接口)驱动的核心是:LCD驱动芯片 --- ILI9341
LCD驱动基本知识:
1,8080时序,LCD驱动芯片一般使用8080时序控制,实现数据写入/读取
2,初始化序列(数组),屏厂提供,用于初始化特定屏幕,不同屏幕厂家不完全相同!
3,画点函数、读点函数(非必需),基于这两个函数可以实现各种绘图功能!
LCD驱动的一般过程:
8080时序简介:
并口总线时序,常用于MCU屏驱动IC的访问,由Intel提出,也叫英特尔总线
8080时序信号说明
8080写时序
8080读时序
cpp
/*8080时序简化写代码*/
void lcd_wr_data(uint16_t data)
{
LCD_RS(0); /* 操作数据 */
LCD_CS(0); /* 选中 */
LCD_DATA_OUT(data); /* 数据 */
LCD_WR(0); /* WR低电平 */
LCD_WR(1); /* WR高电平 */
LCD_CS(1); /* 释放片选 */
}
cpp
/*8080简化读代码*/
uint16_t lcd_rd_data(void)
{
uint16_t ram; /* 定义变量 */
LCD_RS(1); /* 操作数据 */
LCD_CS(0); /* 选中 */
LCD_RD(0); /* RD低电平 */
ram = LCD_DATA_IN; /* 读取数据 */ 这个宏其实就是操作IDR寄存器
LCD_RD(1); /* RD高电平 */
LCD_CS(1); /* 释放片选 */
return ram; /* 返回读数 */
}
LCD驱动芯片简介
LCD驱动芯片用于控制LCD的各种显示功能和效果,整体功能复杂。常见型号:ILI9341/ST7789等
一般我们只需要:6条指令即可完成对LCD的基本使用(以9341为例)
|------------------|--------|---------------------------------|
| 指令 (HEX) | 名称 | 作用 |
| 0XD3 | 读ID | 用于读取LCD控制器的ID,区分型号用 |
| 0X36 | 访问控制 | 设置GRAM读写方向,控制显示方向(从左往右上到下还是右往左) |
| 0X2A | 列地址 | 一般用于设置X坐标 |
| 0X2B | 页地址 | 一般用于设置Y坐标 |
| 0X2C | 写GRAM | 用于往LCD写GRAM数据 |
| 0X2E | 读GRAM | 用于读取LCD的GRAM数据 |
读ID指令(0XD3)
读取LCD控制器型号,通过型号可以执行不同LCD初始化序列,以兼容不同LCD
|--------------|----|----|----|---------|----|----|----|----|----|----|----|----|---------|
| 顺序 | 控制 ||| 各位描述 ||||||||| HEX |
| 顺序 | RS | RD | WR | D15~D8 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | HEX |
| 指令 | 0 | 1 | ↑ | XX | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | D3H |
| 参数 1 | 1 | ↑ | 1 | XX | X | X | X | X | X | X | X | X | X |
| 参数 2 | 1 | ↑ | 1 | XX | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 00H |
| 参数 3 | 1 | ↑ | 1 | XX | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 93H |
| 参数 4 | 1 | ↑ | 1 | XX | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 41H |
参数一是一个假读,不需要关心读到的是什么
访问控制指令(0x36)
实现GRAM读写方向控制,即:控制GRAM自增方向,从而控制显示方向
MX,MY,MV:共同控制GRAM自增方向(扫描方向) BGR位:可以控制RGB、BGR顺序
MX、MY、MV扫描方向控制关系:
|--------|--------|--------|---------------------------------------------|
| 控制位 ||| 效果 LCD 扫描方向( GRAM 自增方式) |
| MY | MX | MV | 效果 LCD 扫描方向( GRAM 自增方式) |
| 0 | 0 | 0 | 从左到右, 从上到下 |
| 1 | 0 | 0 | 从左到右, 从下到上 |
| 0 | 1 | 0 | 从右到左, 从上到下 |
| 1 | 1 | 0 | 从右到左, 从下到上 |
| 0 | 0 | 1 | 从上到下, 从左到右 |
| 0 | 1 | 1 | 从上到下, 从右到左 |
| 1 | 0 | 1 | 从下到上, 从左到右 |
| 1 | 1 | 1 | 从下到上, 从右到左 |
X坐标设置指令(0X2A)
列地址设置指令,一般用于设置X坐标(其实就是窗口大小)
SC:起始坐标 设置关系:0≤SC≤EC≤239(LCD像素宽度)
EC:结束坐标
Y坐标设置指令(0X2B)
页地址设置指令,一般用于设置Y坐标(其实就是窗口大小)
SP:起始坐标 设置关系:0≤SP≤EP≤319(LCD像素高度)
EP:结束坐标
写GRAM指令(0X2C)
发送该指令后,数据线变成16位,可以开始写入GRAM数据,支持地址自增
|--------------|----|----|----|---------|----|----|----|----|----|----|----|----|---------|
| 顺序 | 控制 ||| 各位描述 ||||||||| HEX |
| 顺序 | RS | RD | WR | D15~D8 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | HEX |
| 指令 | 0 | 1 | ↑ | XX | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 2CH |
| 参数 1 | 1 | 1 | ↑ | D1[15:0] ||||||||| XX |
| ...... | 1 | 1 | ↑ | D2[15:0] ||||||||| XX |
| 参数 N | 1 | 1 | ↑ | Dn[15:0] ||||||||| XX |
每次写入1个像素点的颜色值(RGB565),地址自增方向由MX/MY/MV控制 无需重新设置坐标,可实现连续写入,大大提高写入速度
读GRAM指令(0X2E)
发送该指令后,数据线变成16位,可以开始读取GRAM数据,支持地址自增
读1个点的颜色,要读3次
1,dummy 假读
2,R1G1 //读该点的R1和G1
3,B1R2 //读该点的B1和下个点的R2 这样设计连读时一个点3次,两个点4次,效率就高了
cpp
uint16_t r, g, b;
r = lcd_rd_data(); //读r1
g = lcd_rd_data(); //读g1
b = lcd_rd_data();//读b1
g = r & 0XFF;
return (((r >> 11) << 11) | ((g >> 2) << 5) | (b >> 11)); //获取包含RGB值的16位
cpp
/*驱动芯片读数据操作*/
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); /* 设置坐标 其实就是2a/2b指令*/
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));
//移位得到含RGB565三色的16位值
}
LCD基本驱动步骤
目标:用最简单代码,点亮开发板LCD屏,实现任意位置画点和读点
1,确定IO连接关系 LCD模块原理图、开发板液晶接口原理图
2,初始化IO口 初始化连接LCD的各个IO口
初始化FSMC外设 可选,某些芯片是没有FSMC外设,MINI板是没有的
3,编写读写接口函数 lcd_wr_data、lcd_wr_regno、lcd_write_reg、lcd_rd_data
4,编写LCD初始化函数 编写lcd_init函数,完成初始化序列配置,点亮背光等
5,编写LCD画和读点函数 编写lcd_draw_point函数,实现LCD任意位置画点
IO硬件连接关系查表看原理图
LCD控制结构体
cpp
/* LCD重要参数集 */
typedef struct
{
uint16_t width; /* LCD 宽度 */
uint16_t height; /* LCD 高度 */
uint16_t id; /* LCD ID */
uint8_t dir; /* 横屏还是竖屏控制:0,竖屏;1,横屏 */
uint16_t wramcmd; /* 开始写gram指令 */
uint16_t setxcmd; /* 设置x坐标指令 */
uint16_t setycmd; /* 设置y坐标指令 */
} _lcd_dev;
编程实战1
1,使用8080时序在LCD上实现任意位置画点和读点
2,在LCD屏幕上能支持12*12、16*16、24*24、32*32大小的ASCII字符显示
任意字符显示的关键是要制作字库,有了字库就能实现任意字符显示
1,字库制作
根据字体大小(12/16/24/32),制作对应的字库
2,编写任意字符显示函数
根据字库生成方式,编写对应的字符显示函数