VxWorks开发板驱动程序学习之LCD(2.2寸)

来源:互联网 发布:域名怎么注册? 编辑:程序博客网 时间:2024/04/30 10:49

  • TFT22
    • TFT简介
    • 2寸LCD实物图
    • 原理图
  • TFTLCD 驱动芯片
    • ILI9320
    • 驱动程序学习
      • VxWorks API open read
        • open
        • read

今天继续学习VxWorks开发板上的2.2寸LCD屏驱动程序。

先来介绍一下开发板上用的2.2寸的LCD屏。

TFT2.2

这里介绍的TFT2.2相关资料,部分来自网页,部分来自@正点原子 的STM32不完全手册寄存器版本_V3.0 文档,在此一并表示感谢。

TFT简介

TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为: Thin Film Transistor-Liquid Crystal Display。 TFT-LCD 与无源 TN-LCD、 STN-LCD 的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管( TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。 TFT-LCD 也被叫做真彩液晶显示器。

2.2寸LCD实物图

先来看看开发板上所带LCD屏外观,上实物图:

这里写图片描述

然而并没有多美观。此2.2寸TFT液晶屏,分辨率为240*320,高320像素,宽240像素。

原理图

再来看看这个LCD显示屏和开发板接口的原理图:
这里写图片描述
其中 有
DB0-DB15 共16位数据线
X+,X-,Y+,Y- 四个坐标信号线
RESEST
RD
WR
CS
RS
VDD、GND 电源

再看看这些引脚与S3C2410的连接:(与2410的连接,地址线中,实际用到的,只有A0-A3, A7)
这里写图片描述

这是这块开发板上的系统总线通过JB2的32针接口扩展。总共包含16条数据线(D0-D15),8条地址线(A0-A7)、还有一些控制信号线(片选、读写、复位等),同时JB2可以向外提供3.3V电压输出。而实际应用中很少有用户通过总线扩展外设。

此开发板通过这个总线外接2.2寸TFT液晶屏。可以看到LCD2.2显示屏总共引出32根口线,其中

  • D0-D15 共16位数据线
  • A0-A7 共8位地址线
  • RD 读信号
  • WR 写信号
  • EXCS2 外部片选2
  • EXCS3 外部片选3
  • VDD33 x 2
  • GND x 2

除去4位电源线,现在再来看看,剩下的信号线的实际连接,先来看看 数据线:

这里写图片描述

很清晰,16位数据线直接通过排阻拉到了S3C2410的DATA[15:0]数据总线上。

再看看地址线:
这里写图片描述

也是很直接地通过排阻接到S3C2410的 ADDR[7:0]地址总线上。

再来看 EXCS2和EXCS3 两个外部片选信号的连接:
这里写图片描述
这里写图片描述

可以看到,此处的两个外部片选信号通过两个电阻直接接到了 S3C2410的 GPA13、GPA14两个位上,这两个口线均可复用作片选信号:nGCS2 、nGCS3

由上可知 nGCS2 接了LCD2.2的片选信号,nGCS3接了LCD2.2的复位信号。

最后来看看,读写控制信号:
这里写图片描述
这里写图片描述

读写信号直接通过电阻接到 S3C2410的 nOE、nWE 上了。

TFTLCD 驱动芯片

有了TFTLCD硬件模块,还得有相应的驱动芯片才行。常见的型号众多:ILI9341/ILI9325/RM68042/RM68021/ILI9320/ILI9328/LGDP4531/LGDP4535/SPFD5408/SSD1289/1505/B505/C505/NT35310/NT35510 等。

本开发板上用的是 ILI9320DS,接下来先简单了解一下 ILI9320DS。

ILI9320

这里写图片描述


{关于ILI9320的操作,请直接学习datasheet和相关的网站,@正点原子 讲STM32的教程里面也对ILI系列的操作非常清晰,可查阅相关资料,此处暂时略去,回头再来学习}

驱动程序学习

此处再来回顾一下开发板的2410与TFT驱动ILI9320的连接:

S3C2410 ILI9320 nGCS2/GPA13 CS nGCS3/GPA14 RESET nOE RD nWR WR ADDR7 RS ADDR0/GPA0 X+ ADDR1 Y+ ADDR2 X- ADDR3 Y- DATA0-DATA15 DB0-DB15

这是一种典型的总线扩展方式,具体不会的,此处暂以红色标记,回头再来学习。

下面开始来学习驱动代码:

#define LCD22_COM_BASE  (0X10000000) //CS2#define LCD22_DATA_BASE (0X10000080)

先看看S3C2410复位后的存储器映射:
这里写图片描述


可以看到, nGCS2 所指向的SROM区首地址为 0x1000_0000,也就是总线扩展出来驱动LCD驱动芯片的基地址了。但 LCD22_DATA_BASE 宏为什么是在此基础上偏移8*16 = 128个字节,此时还不懂,待学习。

接下来几个函数简单,边看边注释,

void delayms(int count)     // X1ms{    int i,j;    for(i=0;i<count;i++)        for(j=0;j<100;j++);           }void Lcd_Write_Com(UINT16 com)       // 发送命令{           *(volatile UINT16 *)LCD22_COM_BASE = com;    // 直接往 0x1000_0000 写数据}void Lcd_Write_Data(UINT16 data)     // 发送数据{    *(volatile UINT16 *)LCD22_DATA_BASE = data;  // 0x1000_0080 **地址是为何?}void Lcd_Write_Com_Data(UINT16 com,UINT16 val)   // 发送数据命令{    Lcd_Write_Com(com);    Lcd_Write_Data(val);}

此处的 Lcd_Write_Com_Data 函数先写命令,再写数据,实现S3C2410向ILI9320发送命令字控制显示的作用。

// 函数名称:Address_set// 函数功能:设置起始点位置,可以理解为绘图开窗void Address_set(UINT16 x1,UINT16 y1,UINT16 x2,UINT16 y2){    Lcd_Write_Com(0x0020);Lcd_Write_Data(x1);   // 设置X坐标位置    Lcd_Write_Com(0x0021);Lcd_Write_Data(y1);   // 设置Y坐标位置    Lcd_Write_Com(0x0050);Lcd_Write_Data(x1);   // 设置X开始    Lcd_Write_Com(0x0052);Lcd_Write_Data(y1);   // 设置Y开始    Lcd_Write_Com(0x0051);Lcd_Write_Data(x2);   // 设置X结束    Lcd_Write_Com(0x0053);Lcd_Write_Data(y2);   // 设置Y结束    Lcd_Write_Com(0x0022);                           }

这个 Address_set 函数设置相关位置,就不得不提一下ILI9320的指令描述了。直接看datasheet 上的表格:
这里写图片描述
这里写图片描述
具体到每条指令的值如何设置,作用是何就要自己查阅datasheet了。此节只对用到的几条指令做简单讲解。

指令 作用 20h Horizontal GRAM Address Set 21h Vertical GRAM Address Set 50h Horizontal Address Start Position 52h Vertical Address Start Position 51h Horizontal Address End Position 53h Vertical Address End Position 22h Write Data to GRAM

从表格来看和上面代码中的中文注释是一致的。

接下来是LCD2.2初始化函数,这个函数非常重要:

void Lcd22_Init(void){    UINT32  dwValReg;    // GPA[14]:  0=Output, 1=nGCS3    SNGS3C_REG_READ(  rGPACON,  dwValReg );    dwValReg = dwValReg & (~(1<<14));    SNGS3C_REG_WRITE( rGPACON,  dwValReg );      // 配置GPA[14]为普通输出,控制TFT2.2的复位信号    // 连续复位TFT2.2两次, ILI9320需要上电复位,低电平复位    *(volatile UINT32 *)rGPADAT |=(1<<14);      taskDelay(1);    *(volatile UINT32 *)rGPADAT &=~(1<<14);      // 复位    taskDelay(1);    *(volatile UINT32 *)rGPADAT |=(1<<14);     taskDelay(1);    Lcd_Write_Com_Data(0x0000, 0x0001); // Start internal OSC.    Lcd_Write_Com_Data(0x0001, 0x0100); // set SS and SM bit    Lcd_Write_Com_Data(0x0002, 0x0700); // set 1 line inversion    Lcd_Write_Com_Data(0x0003, 0x0030); // set GRAM write direction and BGR=1.    Lcd_Write_Com_Data(0x0004, 0x0000); // Resize register    Lcd_Write_Com_Data(0x0008, 0x0207); // set the back porch and front porch    Lcd_Write_Com_Data(0x0009, 0x0000); // set non-display area refresh cycle ISC[3:0]    Lcd_Write_Com_Data(0x000A, 0x0000); // FMARK function    Lcd_Write_Com_Data(0x000C, 0x0000); // RGB interface setting    Lcd_Write_Com_Data(0x000D, 0x0000); // Frame marker Position    Lcd_Write_Com_Data(0x000F, 0x0000); // RGB interface polarity    // power on sequence    Lcd_Write_Com_Data(0x0010, 0x0000); // SAP, BT[3:0], AP, DSTB, SLP, STB    Lcd_Write_Com_Data(0x0011, 0x0000); // DC1[2:0], DC0[2:0], VC[2:0]    Lcd_Write_Com_Data(0x0012, 0x0000); // VREG1OUT voltage    Lcd_Write_Com_Data(0x0013, 0x0000); // VDV[4:0] for VCOM amplitude    delayms(20); // Dis-charge capacitor power voltage    Lcd_Write_Com_Data(0x0010, 0x1590); // SAP, BT[3:0], AP, DSTB, SLP, STB    Lcd_Write_Com_Data(0x0011, 0x0007); // DC1[2:0], DC0[2:0], VC[2:0]    delayms(20); // Delay 50ms    Lcd_Write_Com_Data(0x0012, 0x013A); // VREG1OUT voltage    delayms(20); // Delay 50ms    Lcd_Write_Com_Data(0x0013, 0x1500); // VDV[4:0] for VCOM amplitude    Lcd_Write_Com_Data(0x0029, 0x0000); // VCM[4:0] for VCOMH    delayms(20);    Lcd_Write_Com_Data(0x0020, 0x0000); // GRAM horizontal Address    Lcd_Write_Com_Data(0x0021, 0x0000); // GRAM Vertical Address    // Adjust the Gamma Curve    Lcd_Write_Com_Data(0x0030, 0x0000);    Lcd_Write_Com_Data(0x0031, 0x0707);    Lcd_Write_Com_Data(0x0032, 0x0700);    Lcd_Write_Com_Data(0x0035, 0x0004);    Lcd_Write_Com_Data(0x0036, 0x1f00);    Lcd_Write_Com_Data(0x0037, 0x0004);    Lcd_Write_Com_Data(0x0039, 0x0007);    Lcd_Write_Com_Data(0x003C, 0x0400);    Lcd_Write_Com_Data(0x003D, 0x1f00);    Lcd_Write_Com_Data(0x0050, 0x0000); // Horizontal GRAM Start Address    Lcd_Write_Com_Data(0x0051, 0x00EF); // Horizontal GRAM End Address    Lcd_Write_Com_Data(0x0052, 0x0000); // Vertical GRAM Start Address    Lcd_Write_Com_Data(0x0053, 0x013F); // Vertical GRAM Start Address    Lcd_Write_Com_Data(0x0060, 0x2700); // Gate Scan Line    Lcd_Write_Com_Data(0x0061, 0x0001); // NDL,VLE, REV    Lcd_Write_Com_Data(0x006A, 0x0000); // set scrolling line    // Partial Display Control    Lcd_Write_Com_Data(0x0080, 0x0000);    Lcd_Write_Com_Data(0x0081, 0x0000);    Lcd_Write_Com_Data(0x0082, 0x0000);    Lcd_Write_Com_Data(0x0083, 0x0000);    Lcd_Write_Com_Data(0x0084, 0x0000);    Lcd_Write_Com_Data(0x0085, 0x0000);    // Panel Control    Lcd_Write_Com_Data(0x0090, 0x0013);    Lcd_Write_Com_Data(0x0092, 0x0000);    Lcd_Write_Com_Data(0x0093, 0x0003);    Lcd_Write_Com_Data(0x0095, 0x0110);    Lcd_Write_Com_Data(0x0097, 0x0000);    Lcd_Write_Com_Data(0x0098, 0x0000);    Lcd_Write_Com_Data(0x0007, 0x0173); }

这个函数就不再具体分析,全部是对TFT2.2写指令数据,可以参照@正点原子的TFTLCD模块使用流程图:
这里写图片描述

接下来看看再个非常实际的驱动函数:

// 函数名称: Pant// 函数功能: 整屏写入数据void Pant(UINT16 data){    int i,j;    Address_set(0,0,240,320);    // 设置开始和结束点的位置,以像素为单位,宽240p,高320p    for(i=0; i<320; i++)    {        for(j=0; j<240; j++)        {            Lcd_Write_Data(data);  // 挨个像素点,整屏全部写入参数数据,初始化的时候设置好参数,可以自动扫描(地址指针自动加)        }    }}
// 函数名称:Lcd22DispPicture// 函数功能:在TFT2.2寸显示屏指定开窗区域显示图片void Lcd22DispPicture(UINT16 x1,UINT16 y1,UINT16 x2,UINT16 y2,char* DispBuf){       int      j;    UINT16   dispdata;    Address_set(x1,y1,x2,y2);           // 开窗左上角(x1,y1),右下角(x2,y2)    for (j=0;j<(x2-x1)*(y2-y1);j++)    {        dispdata=DispBuf[j*2+1];        dispdata=(dispdata<<8)|DispBuf[j*2];        Lcd_Write_Data(dispdata);       // 挨个像素点写入16位的图像数据    }}// 其中,图片数据 DispBuf 组织格式为:两个char元素为一个像素点数据// 其中前一个字节为高8位,后一个字节为低8位

有了上面这个开窗绘图函数 Lcd22DispPicture,还需要为其组织图片数据,也就是下面这个函数了:

// 函数名称:WriteLcd22Buf// 函数功能:由文件名读出图片数据组织为可以直接写入到ILI9320的格式数组buf里面int WriteLcd22Buf(char* FileName,char* DispBuf){    int iFileSize;    int fd;    iFileSize = 0;    fd = open(FileName, O_RDONLY, 0);    // VxWorks 系统API函数    if(ERROR == fd)    {        return ERROR;    }    iFileSize = read(fd, (DispBuf + iFileSize), 200*1024);    if(ERROR == iFileSize)    {        printf("Failed to read the file! return!\n");        close(fd);        return ERROR;    }    close(fd);    return iFileSize;}

这个函数也没有什么好说的,直接打开文件并读取文件数据到 DispBuf中,这可以证明原始的图片文件就已经是满足要求格式(两个字节一个像素点,前一个字节高8位,后一个字节低8位)的文件。

但此处要讲的是VxWorks的两个API函数 open, read.

VxWorks API — open, read

打开VxWorks的API参考手册(file:///D:/Tornado2.2/docs/VxWorks_API_Reference.htm),进入到 OS Library中,找到I字母打头的库,可以看到I/O interface library库—— ioLib,该IO接口库包含了以下函数:

ROUTINES:
creat( ) - create a file
open( ) - open a file
unlink( ) - delete a file (POSIX)
remove( ) - remove a file (ANSI)
close( ) - close a file
rename( ) - change the name of a file
read( ) - read bytes from a file or device
write( ) - write bytes to a file
ioctl( ) - perform an I/O control function
lseek( ) - set a file read/write pointer
ioDefPathSet( ) - set the current default path
ioDefPathGet( ) - get the current default path
chdir( ) - set the current default path
getcwd( ) - get the current default path (POSIX)
getwd( ) - get the current default path
ioGlobalStdSet( ) - set the file descriptor for global standard input/output/error
ioGlobalStdGet( ) - get the file descriptor for global standard input/output/error
ioTaskStdSet( ) - set the file descriptor for task standard input/output/error
ioTaskStdGet( ) - get the file descriptor for task standard input/output/error
isatty( ) - return whether the underlying driver is a tty device

ioLib总共包含20个函数,可以分为以下四类:

  • 7个基本驱动:create(), remove(), open(), close(), read(), write(), ioctl()
  • 文件系统功能:rename(), lseek()
  • 设置或获取当前工作目录:ioDefPathSet(), ioDefPathGet(), chdir(), getcwd(), getwd()
  • 指派任务和全局标准文件描述符:ioTaskStdSet(), ioTaskStdGet(), ioGlobalStdSet(), ioGlobalStdGet()
  • isatty():判断基础驱动是否是一个tty设备

在VxWorks中,基本的IO层,用一个小的整数(文件描述符)来描述文件,通常是一个由open()或create()返回的值。

三个保留的文件描述符:

  • 0 (STD_IN)
  • 1 (STD_OUT)
  • 2 (STD_ERR)

用到IO库里面的文件(比如此处的 open(), read())时均需要包含头文件 ioLib.h

open

SYNOPSIS

int open    (        const char* name,        int         flags,  /* O_RDONLY, O_WRONLY, O_RDWR, or O_CREAT */        int         mode    /* mode of file to create(UNIT chmod style) */    )

通常,open()只能打开存在的设备或文件,然而NFS网络设备也可以通过open()函数来创建,例子:

    fd = open("/usr/myFile", O_CREAT | O_RDWR, 0644);

当然也只有NFS驱动使用这个mode参数。
RETURNS
A file descriptor number, or ERROR if a file name is not specified, the device does not exist, no file descriptors are available, or the driver returns ERROR.
ERRNO
ELOOP

read

NAME
read() - - - read bytes from a file or device
SYNOPSIS

int read    (        int    fd,       /* file descriptor from which to read */        char * buffer,   /* pointer to buffer to receive bytes */        size_t maxbytes  /* max no. of bytes to read into buffer */    )

read函数从fd读取最多maxbytes的数据到buffer,实际会调用底层设备驱动来工作。

RETURNS
The number of bytes read (between 1 and maxbytes, 0 if end of file), or ERROR if the file descriptor does not exist, the driver does not have a read routines, or the driver returns ERROR. If the driver does not have a read routine, errno is set to ENOTSUP.
函数返回读取到的实际数据字节数(1<= returns <= maxbytes),遇到文件结束就返回0;若底层驱动没有读取函数,则置错误号 errno 为 ENOTSUP.

TFTLCD2.2寸液晶屏的驱动程序到此基本结束,再来看看向外部提供的任务接口函数:

//2*240*320=153600, 定义一个全局的缓存用来存储整屏数据,按前分析,大小应两倍于像素点个数unsigned char Lcd22DispBuf[153600];// 函数名称:Lcd_Main// 函数功能:界面显示处理主函数int Lcd_Main(void){    UINT16   dispdata;    int      i, j, k;    Lcd22_Init();          // TFT LCD 初始化    taskDelay(1);    Pant(0xffff);           // 清屏    Pant(0xf800);           // 红屏    taskDelay(120);    Pant(0x07E0);           // 绿屏    taskDelay(120);    Pant(0x001f);           // 蓝屏    taskDelay(120);    i = 0;    while(1)    {        if(4 == MenuDisp)   // 一个全局变量指示进入图片测试界面        {            i++;            switch(i)            {            case 1: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            case 2: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            case 3: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            case 4: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            case 5: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            case 6: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            case 7: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            case 8: WriteLcd22Buf("/C/butterfly240.bin", Lcd22DispBuf); break;            }            i %= 9;            Lcd22DispPicture(0,0,240,240,Lcd22DispBuf);   // 开窗绘图            taskDelay(120);                               // 延时让出CPU        }        else // 退出图片界面        {            i = 0;            WriteLcd22Buf("/C/RK2410-240.bin", Lcd22DispBuf);            Lcd22DispPicture(0,0,240,240,Lcd22DispBuf);   // 显示标准LOGO界面            taskDelay(10);        }    }}

好了,开发板的LCD2.2寸液晶屏驱动程序就学习到这里,可以看到,除了LCD2.2屏驱动芯片ILI9320的驱动程序还需要仔细学习外,应用上驱动一个LCD屏还是比较容易的。

以上代码几乎全部来自Rock,若有侵权,请联系我删除,谢谢!

1 0