LCD学习
引入
-
裸屏,也就是最终接口是RGB的信号线,需要MCU支持液晶驱动或者MCU连接液晶驱动芯片
在JZ2440连接的就是裸屏,接口一般如下:
-
带驱动芯片的液晶模块,类似STM32就是这么使用的,比如使用STM32连接
ili9341
或者RA8875
等驱动芯片,STM32通过FSMC
与RA8875
通信,然后RA8875
本身有相应的RGB信号线
硬件设计
所以在这里,并没有所谓的液晶驱动芯片,液晶驱动控制被集成在s3c2440内部已经. 板载的TFT液晶型号是AT043TN24
,像素点是480*272,自带触摸屏
注意:
- HSYNC水平方向信号线,水平换行
- VSYNC垂直方向信号线,也就是从头开始刷新显示
- 板载的lcd是24数据线的,但是这里接了5+6+5,低位连地,因为2440支持16、24bpp显示
时序图
在上图中,行信号和列信号中都有3个时间参数,对应显示区的黑框
- VBPD(vertical back porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数,对应驱动中的upper_margin;
- VFBD(vertical front porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数,对应驱动中的lower_margin;
- VSPW(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算,对应驱动中的vsync_len;
- HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCLK的个数,对应驱动中的left_margin;
- HFPD(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK的个数,对应驱动中的right_margin;
- HSPW(horizontal sync pulse width):表示水平同步信号的宽度,用VCLK计算,对应驱动中的hsync_len;
2440特性
- 支持 TFT 的 1、2、4、8 bpp(位每像素)调色显示-----这里就是调色板
- 支持彩色 TFT 的 16、24 bpp 无调色显示
- 支持 24 位每像素模式下最大 16M 色 TFT
- 典型实际屏幕尺寸:640×480、320×240、160×160
也就是说
- 使用真彩色,也就是在一块内存FB中存着实际的颜色值 1,2,4,8,16,24
- 使用调色板,FB内存单元存放着8位的索引,也就是最大256个索引,索引到实际的颜色值
- 临时调色板,启用之后只能使用这个颜色,用来刷屏等
2440外部GPIO如下
寄存器设置
2440的手册有寄存器设置的向导TFT LCD CONTROLLER OPERATION
//这里选择9M,9=100M /[(CLKVAL+1) x 2], clkval = 4.5 = 5
// 选择支持 8bpp或者16bpp或者24bpp
8bpp=1011=0x0b
16bpp=1100=0x0c
24bpp=1101=0x0d
int clkval = 5;
int bppmode = plcdparams->bpp == 8 ? 0xb :\
plcdparams->bpp == 16 ? 0xc :\
0xd; /* 0xd: 24,32bpp */
LCDCON1 = (clkval<<8) | (3<<5) | (bppmode<<1) ;
注意,这里的时间参数都是+1
才等于需要的值,所以配置寄存器的时候都要减去1
typedef struct time_sequence {
/* 垂直方向 */
int tvp; /* vysnc脉冲宽度 */
int tvb; /* 上边黑框, Vertical Back porch */
int tvf; /* 下边黑框, Vertical Front porch */
/* 水平方向 */
int thp; /* hsync脉冲宽度 */
int thb; /* 左边黑框, Horizontal Back porch */
int thf; /* 右边黑框, Horizontal Front porch */
int vclk;
}time_sequence, *p_time_sequence;
/* [31:24] : VBPD = tvb - 1
* [23:14] : LINEVAL = line - 1
* [13:6] : VFPD = tvf - 1
* [5:0] : VSPW = tvp - 1
*/
LCDCON2 = ((plcdparams->time_seq.tvb - 1)<<24) | \
((plcdparams->yres - 1)<<14) | \
((plcdparams->time_seq.tvf - 1)<<6) | \
((plcdparams->time_seq.tvp - 1)<<0);
/* [25:19] : HBPD = thb - 1
* [18:8] : HOZVAL = 列 - 1
* [7:0] : HFPD = thf - 1
*/
LCDCON3 = ((plcdparams->time_seq.thb - 1)<<19) | \
((plcdparams->xres - 1)<<8) | \
((plcdparams->time_seq.thf - 1)<<0);
/*
* [7:0] : HSPW = thp - 1
*/
LCDCON4 = ((plcdparams->time_seq.thp - 1)<<0);
注意选择[1:0]
来选择bpp存放的格式,文档搜索BSWP
即可看到,我们都选择低位优先存储的
pixelplace = plcdparams->bpp == 32 ? (0) : \
plcdparams->bpp == 16 ? (1) : \
(1<<1); /* 8bpp */
具体的极性按照lcd参数具体设计
LCDCON5 = (plcdparams->pins_pol.vclk<<10) |\
(plcdparams->pins_pol.rgb<<7) |\
(plcdparams->pins_pol.hsync<<9) |\
(plcdparams->pins_pol.vsync<<8) |\
(plcdparams->pins_pol.de<<6) |\
(plcdparams->pins_pol.pwren<<5) |\
(1<<11) | pixelplace;
地址 | 寄存器 | 解释 | 这里的单位都是半字,也就是2个字节 |
---|---|---|---|
ADDR1 | LCDBANK | 内存起始地址,4M对齐 | 实际内存最大允许使用4M也就是说 |
LCDBASEU | 视窗的起始地址,低4M偏移 | 视窗从这个4M的哪个地方开始 | |
ADDR2 | LCDBASEL | 视窗结束地址,低4M的偏移 | 视窗从这个4M的哪个地方结束 |
ADDR3 | OFFSIZE | 视窗与实际内存的偏差 | 通过该值,计算下一行的像素点对应在内存的位置。当前在x,那么下一行的取值在x+PAGEWIDTH+OFFSIZE |
PAGEWIDTH | 视窗的宽度 |
这里的结束地址我觉得按照手册写的它并不是指的是视窗的结束地址,而是指的是视窗结束地址同行的所在内存空间的结束地址,如下图蓝色标识是手册的位置,实际的地址是在红色箭头的位置,但是设置到蓝色的位置应该也没啥关系
/* framebuffer地址 */
/*
* [29:21] : LCDBANK, A[30:22] of fb
* [20:0] : LCDBASEU, A[21:1] of fb
*/
addr = plcdparams->fb_base & ~(1<<31);
LCDSADDR1 = (addr >> 1);
/*
* [20:0] : LCDBASEL, A[21:1] of end addr
*/
addr = plcdparams->fb_base + plcdparams->xres*plcdparams->yres*plcdparams->bpp/8;
addr >>=1;
addr &= 0x1fffff;
LCDSADDR2 = addr;//
上面的这段代码,结束地址是实际的结束地址,也就是上面红色箭头的位置.
调色板
-
1,2,4,8bpp都是指的调色板,8bpp指的意思就是2^8共256个颜色索引,每个索引值16位的颜色值.
-
调色板起始位置在
0x4D000400 为调色板开始地址
-
操作调色板的时候,需要禁止LCD
画点
只需要在内存填充数据,并选择视图位置即可.颜色排序都是RGB的顺序虽然硬件只接了16bpp,但是软件上依然能够使用32bpp,因为硬件上的接法是忽略低位的.
所谓8Bpp就是使用调色板了
画线画圆
字库
所谓字库,其实就是一片小区域的画点,比如在Linux
中搜索font
,打开font_8x16.c
,可以看到A的字库如下,转换为二进制,再把0替换成空白,其实就是在8*16的格子中点亮相应的内存,一个字符占据16个字节
/* 65 0x41 'A' */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x10, /* 00010000 */ 1
0x38, /* 00111000 */ 111
0x6c, /* 01101100 */ 11 11
0xc6, /* 11000110 */ 11 11
0xc6, /* 11000110 */ 11 11
0xfe, /* 11111110 */ 1111111
0xc6, /* 11000110 */ 11 11
0xc6, /* 11000110 */ 11 11
0xc6, /* 11000110 */ 11 11
0xc6, /* 11000110 */ 11 11
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
0x00, /* 00000000 */
点亮字符如下
void fb_print_char(int x, int y, char c, unsigned int color)
{
int i, j;
/* 根据c的ascii码在fontdata_8x16中得到点阵数据 */
// 这里一个字符占据16字节
unsigned char *dots = &fontdata_8x16[c * 16];
unsigned char data;
int bit;
/* 根据点阵来设置对应象素的颜色 */
for (j = y; j < y+16; j++)
{
data = *dots++;
bit = 7;
for (i = x; i < x+8; i++)
{
/* 根据点阵的某位决定是否描颜色 */
if (data & (1<<bit))
fb_put_pixel(i, j, color);
bit--;
}
}
}
程序设计框架
-
底层
- LCD.4.3 提供液晶参数规格
- s3c2440Lcd 提供寄存器操作的库函数
-
中间层1
- LcdController 通过底层的库函数去操作具体的液晶,向上层提供画点算法,提供多个控制器和液晶参数
-
中间层2
- LCD 驱动层,自主选择液晶控制器和液晶,为应用层提供统一接口
-
应用层1
- 画线,画圆,字符
- 其他