1.ARM系统中LCD体系架构
其中LCD驱动芯片和LCD显示器是连接在LCD模块上的,而LCD控制器则是在ARM处理器上的。
2.液晶工作原理:
液晶分子为长棒子状,通过分子的电流不一样, 他们会发生不同程度的偏转,从而对背光灯的光线产生不同程度的反射和折射,进而使得显示的像素呈现出不同的色彩。每个分子对应一个像素,很多像素构成一幅图像。
3.液晶屏分类:常见的有STN的,就是玩单片机用到的12864还有1602等,以及GF,而我们现在说的大都是TFT的。
4.LCD驱动芯片:为液晶分子的偏转提供电压,从而达到控制LCD的效果。相当于神经网络。
5.LCD控制器:为LCD驱动芯片提供必要的数据和控制信号。这是相当于大脑,LCD显示器就相当于器官。REGBANK里有寄存器组,调色板内存。LCDCDMA则是用来控制帧缓存(内存)里的数据到LCD显示器,不需要CPU干预。VIDPRCS则是像素数据,TIMEGEN则是控制时序的,与REGBANK密切相关。
6.显示过程
7.时序信号
8.各信号含义:
9.简要概括
10.引脚初始化,找到原理图VD对应的引脚,然后在datasheet里面找到相应的GPIO寄存器区写入初值,让这些引脚工作在LCD数据传输模式。
11时序初始化:
(1)找出需要初始化什么时序信号
(2)找到初始化这些信号的寄存器(LCDCON1,LCDCON2,LCDCON3,LCDCON4)
(3)根据datashhet确定初始化的值
12.帧缓冲初始化:
(1)分配空间,分辨率的两倍,每个像素用两个字节来描述。一般静态分配,用二维数组来分配,数组元素类型是描述颜色的位数对应的类型。但不一定是两倍,即不是只能用16位描述一个像素,位数越多颜色越丰富。
(2)告知地址:由DMA来搬运数据,不需要CPU干预。所以DMA首先要知道数据在内存中的地址,即帧缓存。(LCDSADDR1,2,3)起始地址,结束地址,是否间隔,多少半字单位。
13.杂项初始化:
(1)选择什么屏
(2)选择控制像素的位数
(3)是否打开LCD
(4)选择颜色控制格式(LCDCON5)
(5)选择所用LCD的HSYNC以及VSYNC的极性,即是否选择反转。默认三星的是高电平的极性。(LCDCON5)
(6)打开半字转换(LCDCON5),后来我发现打不打开都可以,这里表示有疑问???
(7)关闭临时调色板(TPAL)
(8)打开电源引脚功能(LCD_PWR,引脚复用,LCDCON5打卡电源允许)
(9)使能LCD控制器(LCDCON1)
14.画点函数:
(1)定义坐标,与帧缓存里面的数组的行列注意对应关系。
(2)颜色转换。根据datasheet将24位转换为16为数据,再赋值。
(3)建议大家初始化的时候不要使能LCD的数据输出,不然会花屏,建议大家用这个画点的思想,在最初上电的时候,进行一个清屏操作,根据自己想要的颜色来做。虽然老师说过这种方法比较傻,不如调色板好用,但是我试验了,用调色板清屏以后就不能在画图,具体原因还没弄清楚。所以为了避免一开机就花屏,建议大家用循环的方式不断往帧缓存里面写入相同的颜色值。这里不是纯粹的调用画点函数,那样的话效率很低,每次都要做移位运算。建议这样写,会加速清屏时间。
void lcd_clear_init(U32 color)
{
U32 x,y;
U32 red,green,blue;
U16 temp;
red = ((color >> 19) & 0x1f);
green = ((color >> 10) & 0x3f);
blue = ((color >> 3) & 0x1f);
temp = (U16)((red << 11) | (green < 5) | blue);
for(y = 0;y < 272;y++){
for(x = 0;x < 480;x++)
buf[y][x] = temp;
}
}
15.画线画图:利用画点函数,学会使用取模软件,单色显示要用到调色板(TPAL)。
我想实现一个函数,在任意两点之间画一条直线,但是由于像素是一个个小方格,不是直接连起来的圆点,目前还未实现,希望大家提出想法一起探讨。争取找一个好算法。
16.我用mini2440板子,用的tq4.3的液晶,不是迷你自带的LCD
代码如下:
(1)宏定义和全局变量定义部分
#define VSPW 9
#define VBPD 1
#define LINEVAL 271
#define VFPD 1
#define HSPW 40
#define HBPD 1
#define HOZVAL 479
#define HFPD 1
#define CLKVAL 4
#define U32 unsigned int
#define U16 unsigned short
#define U8 unsigned char
unsigned short buf[272][480];
这些参数是根据这张表和时序信号图以及LCDCON1的描述得到的
(2)引脚初始化
void lcd_port_init()
{
GPCCON = 0xAAAAAAAA;
GPDCON = 0xAAAAAAAA;
}
(3)时序初始化
void lcd_time_init()
{
LCDCON1 = (CLKVAL<<8);
LCDCON2 = (VBPD << 24)| (LINEVAL << 14) | (VFPD << 6) | (VSPW);
LCDCON3 = (HBPD << 19) | (HOZVAL << 8) | (HFPD << 0);
LCDCON4 = (HSPW << 0);
}
(4)帧缓存初始化
void lcd_frame_init()
{
LCDSADDR1 = ((U32)(((U32)buf >> 22)<<21)) | ((U32)(((U32)buf >> 1) & 0x1fffff) << 0);
LCDSADDR2 = (U32)(((((U32)buf + 272*480*2) >> 1) & 0x1fffff) << 0);
LCDSADDR3 = ((U32)(0 <<11)) | ((U32)(480<<0));
}
(5)杂项初始化
void lcd__others_init()
{
LCDCON1 |= (3 << 5) | (0xc << 1) | (0 << 0);
LCDCON5 = (1 << 11) | (1 << 9) | (1 << 8) | (1 << 0);
TPAL = 0;
GPGCON |= (3 << 8);
LCDCON5 |= (1 << 3);
LCDCON1 |= (1 << 0);
}
(6)总的初始化
void lcd_init()
{
lcd_port_init();
lcd_time_init();
lcd_frame_init();
lcd__others_init();
lcd_clear_init(0x0000CD);
Delay(500000);
}
(7)画点函数
void lcd_point(U16 x, U16 y,U32 color)
{
U32 red,green,blue;
red = ((color >> 19) & 0x1f);
green = ((color >> 10) & 0x3f);
blue = ((color >> 3) & 0x1f);
buf[y][x] = (U16)((red << 11) | (green < 5) | blue);
}
(8)用调色板清屏函数
void lcd_clear(U32 color)
{
TPAL = (1<<24)|(color&0xffffff);
}
(9)画”十字架“函数
void lcd_line()
{
U32 x,y;
for(x = 0; x < 480;x++){
lcd_point(x,136,0xFF0000);
}
for(y = 0; y < 272;y++){
lcd_point(240,y,0x54FF9F);
}
}
(10)画一张预存好的图片函数
extern const U8 pic[85928];
void lcd_picture(U16 x_start,U16 y_start,U16 x_size,U16 y_size,const U8* pic)
{
U32 y = 0,x = 0;
U16 temp = 0;
U32 p = 0;
for(y = y_start; y < y_start + y_size;y++){
for(x = x_start; x < x_start + x_size;x++){
temp = ((U16)pic[p]) | (((U16)pic[p + 1]) << 8);
if(x < 480 && y < 272){
buf[y][x] = temp;
}
p += 2;
}
}
}
(11)开机使用的清屏函数
void lcd_clear_init(U32 color)
{
U32 x,y;
U32 red,green,blue;
U16 temp;
red = ((color >> 19) & 0x1f);
green = ((color >> 10) & 0x3f);
blue = ((color >> 3) & 0x1f);
temp = (U16)((red << 11) | (green < 5) | blue);
for(y = 0;y < 272;y++){
for(x = 0;x < 480;x++)
buf[y][x] = temp;
}
}
(12)延时函数
void Delay(U32 time)
{
while(time--);
}
(13)测试函数
void lcd_tes()
{
lcd_line();
Delay(500000);
lcd_picture(0,0,240,179,pic);
Delay(500000);
}
6410代码
/****************************
@File:lcd.c
@
@Tiny6410裸机下学期代码
@LCD配置文件
@Author:小君君
@****************************/
#include "common.h"
void lcd_init()
{
/*1.配置GPIO*/
(vi GPECON) = 0x00011111;
(vi GPEDAT) = 0x00000001;
(vi GPFCON) = 0x96AAAAAA;
(vi GPFDAT) = 0x00002000;
(vi GPICON) = 0xAAAAAAAA;
(vi GPJCON) = 0x00AAAAAA;
/*2.设置MOFPCON为normal mode*/
(vi MIFPCON) &= ~(1<<3);
/*3.设置SPCON,选择RGB/IF style*/
(vi SPCON) &= ~(0x3);
(vi SPCON) |= 1;
/*4.设置VIDCONx,选择接口类型,输出格式,时钟,极性,使能LCD控制器*/
/*ENVID_F = 1,当前帧结束后使能 LCD 控制器;
ENVID = 1,使能 LCD 控制器;
CLKSEL_F = 0, 选择时钟源为 HCLK;
CLKDIR = 1, 选择需要分频;
VCLKFREE = 0,选择 normal mode;
CLKVAL_F = 10, 分频系数为 9,即 VCLK = 133 / (9+1) = 13MHz;
CLKVALUP = 0,总是选择 CLKVAL_F 来更新时序控制;
RGSPSEL = 0,RGB 并行;
VIDOUT=0,使用 RGB 接口;*/
(vi VIDCON0) = (0<<26)|(0<<17)|(0<<16)|(10<<6)|(0<<5)|(1<<4)|(0<<2)|(3<<0);
(vi VIDCON1) |= 1<<5 | 1<<6;
/*5.设置VIDTCONx,选择时序和分辨率*/
(vi VIDTCON0) = VBPD<<16 | VFPD<<8 | VSPW<<0;
(vi VIDTCON1) = HBPD<<16 | HFPD<<8 | HSPW<<0;
(vi VIDTCON2) = (LINEVAL << 11) | (HOZVAL << 0);
/*6.设置WINCON0,设置window0的数据格式*/
/*Enable the video output and the VIDEO control signal.*/
/*1011 = unpacked 24 BPP (non-palletized R:8-G:8-B:8 ) */
(vi WINCON0) |= 1<<0;
(vi WINCON0) &= ~(0xf << 2);
(vi WINCON0) |= 0xB<<2;
/*7.配置VIDOSDOA/B/C,设置window0坐标*/
/*结合LCD的Data Input Format可知宽480,高272像素*/
/*所以左上角坐标(0,0),右下角(479,271)*/
(vi VIDOSD0A) = (LeftTopX<<11) | (LeftTopY << 0);
(vi VIDOSD0B) = (RightBotX<<11) | (RightBotY << 0);
(vi VIDOSD0C) = (LINEVAL + 1) * (HOZVAL + 1);
/*8.设置VIDWOOADD0B0以及VIDWOOADD1B0,设置framebuffer的地址*/
(vi VIDW00ADD0B0) = FRAME_BUFFER;
(vi VIDW00ADD1B0) = (((HOZVAL + 1)*4 + 0) * (LINEVAL + 1)) & (0xffffff);
}
// 描点
void lcd_draw_pixel(int row, int col, int color)
{
unsigned long * pixel = (unsigned long *)FRAME_BUFFER;
*(pixel + row * COL + col) = color;
}
// 清屏
void lcd_clear_screen(int color)
{
int i, j;
for (i = 0; i < ROW; i++)
for (j = 0; j < COL; j++)
lcd_draw_pixel(i, j, color);
}
// 划横线
void lcd_draw_hline(int row, int col1, int col2, int color)
{
int j;
// 描第row行,第j列
for (j = col1; j <= col2; j++)
lcd_draw_pixel(row, j, color);
}
// 划竖线
void lcd_draw_vline(int col, int row1, int row2, int color)
{
int i;
// 描第i行,第col列
for (i = row1; i <= row2; i++)
lcd_draw_pixel(i, col, color);
}
// 划十字
void lcd_draw_cross(int row, int col, int halflen, int color)
{
lcd_draw_hline(row, col-halflen, col+halflen, color);
lcd_draw_vline(col, row-halflen, row+halflen, color);
}
// 绘制同心圆
void lcd_draw_circle(void)
{
int x,y;
int color;
unsigned char red,green,blue,alpha;
int xsize = ROW;
int ysize = COL;
for (y = 0; y < ysize; y++)
for (x = 0; x < xsize; x++)
{
color = ((x-xsize/2)*(x-xsize/2) + (y-ysize/2)*(y-ysize/2))/64;
red = (color/8) % 256;
green = (color/4) % 256;
blue = (color/2) % 256;
alpha = (color*2) % 256;
color |= ((int)alpha << 24);
color |= ((int)red << 16);
color |= ((int)green << 8 );
color |= ((int)blue );
lcd_draw_pixel(x,y,color);
}
}
/****************************
@File:common.h
@
@Tiny6410裸机下学期代码
@常用头文件定义
@Author:小君君
@****************************/
#ifndef __COMMON_H__
#define __COMMON_H__
#define vi *( volatile unsigned long * )
#define ulong unsigned long
/*取消使用mmu*/
//#define MMU_ENABLE 1
/*LED初始化*/
#ifndef MMU_ENABLE
#define LED_CON 0x7F008800
#define LED_DAT 0x7F008808
#else
#define LED_CON 0xA0008800
#define LED_DAT 0xA0008808
#endif
void mmu_init();
void led_init();
void led_on();
void led_off();
void led1_on();
void led2_on();
void led3_on();
void led4_on();
void led5_on();
void led6_on();
void led7_on();
void led8_on();
/*按键相关初始化*/
#define KEYCON 0x7f008830
#define KEYCON1 0x7f008814
#define K1_MSK (3 << 0)
#define K2_MSK (3 << 2)
#define K3_MSK (3 << 4)
#define K4_MSK (3 << 6)
#define K5_MSK (3 << 8)
#define K6_MSK (3 << 10)
#define K7_MSK (0xF << 12)
#define K8_MSK (0xF << 16)
#define K1_OK (2 << 0)
#define K2_OK (2 << 2)
#define K3_OK (2 << 4)
#define K4_OK (2 << 6)
#define K5_OK (2 << 8)
#define K6_OK (2 << 10)
#define K7_OK (0x3 << 12)
#define K8_OK (0x3 << 16)
void button_init();
/*中断控制器相关的寄存器地址*/
#define EXT_INT_0_CON 0x7f008900
#define EXT_INT_1_CON 0x7f008904
#define EXT_INT_0_MASK 0x7f008920
#define EXT_INT_0_PEND 0x7f008924
#define VIC0INTENABLE 0x71200010
#define VIC1INTENABLE 0x71300010
#define EINT0_VECTADDR 0x71200100 /*每个中断源有一个寄存器存放相应的中断处理函数的地址,共32+32 = 64个*/
#define EINT1_VECTADDR 0x71200104
#define EINT2_VECTADDR 0x71200108
#define EINT3_VECTADDR 0x7120010C
#define EINT4_VECTADDR 0x71200110
#define EINT5_VECTADDR 0x71200114/*以上是VIC0,见6410datasheet的P414*/
#define EINT19_VECTADDR 0x71300100
#define EINT20_VECTADDR 0x71300104 /*以上是VIC1*/
#define VIC0ADDRESS 0x71200f00
#define VIC1ADDRESS 0x71300f00
void irq_init();
/*nandflash相关寄存器定义*/
#define NFCONF 0x70200000
#define NFCONT 0x70200004
#define NFCMMD 0x70200008
#define NFADDR 0x7020000C
#define NFDATA 0x70200010
#define NFDATA8 (*(volatile unsigned char *)0x70200010)
#define NFSTAT 0x70200028
int nand_erase(unsigned int block_addr);
int Nand_PageWrite(unsigned long start_addr,char *buf);
/*UART相关寄存器定义*/
#define UARTCON 0x7F008000
#define ULCON0 0x7F005000
#define UCON0 0x7F005004
#define UFCON0 0x7F005008
#define UMCON0 0x7F00500C
#define UTRSTAT0 0x7F005010
#define UFSTAT0 0x7F005018
#define UTXH0 (*((volatile unsigned char *)0x7F005020))//注意是char类型的
#define URXH0 (*((volatile unsigned char *)0x7F005024))
#define UBRDIV0 (*((volatile unsigned short *)0x7F005028))//注意是short类型的
#define UDIVSLOT0 (*((volatile unsigned short *)0x7F00502C))
#define UART_FIFO_ENABLE 1 //最好使用FIFO模式,就是必须有这个宏定义
void uart_init();
#ifdef UART_FIFO_ENABLE
char getchar(void);
void putchar(char c);
void send_string(char* str);
#else
unsigned char getchar(void);
void putchar(unsigned char c);
void send_string(unsigned char* str);
#endif
/*DMA相关定义*/
#define UTXH0_DMA 0x7F005020
#define DMA_BASE 0x75000000
#define DMACC0SrcAddr (DMA_BASE + 0x100)
#define DMACC0DestAddr (DMA_BASE + 0x104)
#define DMACC0Control0 (DMA_BASE + 0x10C)
#define DMACC0Control1 (DMA_BASE + 0x110)
#define DMACC0Configuration (DMA_BASE + 0x114)
#define DMACC0ConfigurationExp (DMA_BASE + 0x118)
#define DMACC0LLI (DMA_BASE + 0x108)
#define DMACEnbldChns (DMA_BASE + 0x01C)
#define DMACConfiguration (DMA_BASE + 0x030)
#define DMACSync (DMA_BASE + 0x034)
#define DMA_SEL 0x7E00F110
void dma_init();
void dma_start();
/*LCD相关寄存器定义*/
#define GPECON 0x7F008080
#define GPEDAT 0x7F008084
#define GPEPUD 0x7F008088
#define GPFCON 0x7F0080A0
#define GPFDAT 0x7F0080A4
#define GPFPUD 0x7F0080A8
#define GPICON 0x7F008100
#define GPIPUD 0x7F008108
#define GPJCON 0x7F008120
#define GPJPUD 0x7F008128
/* display controller */
#define MIFPCON 0x7410800C
#define SPCON 0x7F0081A0
#define VIDCON0 0x77100000
#define VIDCON1 0x77100004
#define VIDTCON0 0x77100010
#define VIDTCON1 0x77100014
#define VIDTCON2 0x77100018
#define WINCON0 0x77100020
#define VIDOSD0A 0x77100040
#define VIDOSD0B 0x77100044
#define VIDOSD0C 0x77100048
#define VIDW00ADD0B0 0x771000A0
#define VIDW00ADD1B0 0x771000D0
#define VIDW00ADD2 0x77100100
#define DITHMODE 0x77100170
#define FRAME_BUFFER 0x54000000
#define ROW 272
#define COL 480
#define HSPW (2)
#define HBPD (40- 1)
#define HFPD (5 - 1)
#define VSPW (2)
#define VBPD (8 - 1)
#define VFPD (9 - 1)
#define LINEVAL (271)
#define HOZVAL (479)
#define LeftTopX 0
#define LeftTopY 0
#define RightBotX 479
#define RightBotY 271
void lcd_init();
void lcd_draw_pixel(int row, int col, int color);
void lcd_clear_screen(int color);
void lcd_draw_hline(int row, int col1, int col2, int color);
void lcd_draw_vline(int col, int row1, int row2, int color);
void lcd_draw_cross(int row, int col, int halflen, int color);
void lcd_draw_circle(void);
#define WIDTHEIGHT 480
#define HEIGHT 272
#endif
/****************************
@File:main.c
@
@Tiny6410裸机下学期代码
@LCD测试文件
@Author:小君君
@****************************/
#include "common.h"
int main(void)
{
int num = 1000;
//mmu_init();//MMU初始化,这里不使用MMU
led_init();//LED的GPIO初始化
button_init();//按键初始化
irq_init();//中断初始化
led_on();//点亮4颗LED
uart_init();//串口初始化
putchar('a');
putchar('\r');
putchar('\n');
putchar('b');
putchar('\r');
putchar('\n');
dma_init();//DMA初始化
dma_start();//启动DMA发送数据到串口
uart_init();//串口再次初始化,使得串口恢复中断或者轮询模式
lcd_init();
lcd_clear_screen(0xFFFFFF);
while(1){
printf("=================================================\n\r");
printf("===================JUN-BOOT======================\n\r");
printf("0.Send the ARP to get yhe host's MAC address\n\r");
printf("1.Download the linux kernel from tftp\n\r");
printf("2.Boot linux OS from SDRAM\n\r");
printf("3.Junjun is a houmorous\n\r");
printf("=================================================\n\r");
printf("===================LCD_TEST======================\n\r");
printf("4.清屏\n\r");
printf("5.画横线\n\r");
printf("6.画竖线\n\r");
printf("7.画十字架\n\r");
printf("8.画同心圆\n\r");
printf(" \n\r");
printf("请输入0-8任意一个数字:\n\r");
scanf("%d",&num);
switch(num){
case 0:
printf("请支持成都国嵌\n\r");
break;
case 1:
printf("国嵌学院=====打造你的嵌入式人生\n\r");
break;
case 2:
printf("学ARM,学Linux,学C++,学安卓,学嵌入式,就到成都国嵌学院\n\r");
break;
case 3:
printf("只要你肯努力,你的明天就会等你!!\n\r");
break;
case 4:
lcd_clear_screen(0x000000);
break;
case 5:
lcd_clear_screen(0x000000);
lcd_draw_hline(HEIGHT/2, 100, WIDTHEIGHT-100, 0xff0000);
break;
case 6:
lcd_clear_screen(0x000000);
lcd_draw_vline(WIDTHEIGHT/2, 50, HEIGHT-50, 0xff0000);
break;
case 7:
lcd_clear_screen(0x000000);
lcd_draw_cross(HEIGHT/2, WIDTHEIGHT/2, 20, 0x777777);
break;
case 8:
lcd_clear_screen(0x000000);
lcd_draw_circle();
break;
default:
printf("只要你肯努力,你的明天就会等你!!\n\r");
break;
}
}
return 0;
}