基于AT91RM9200的S1D13506控制器驱动程序开发(转)
熊敏 万频 谷刚 詹宜巨 李学聪
(广东工业大学自动化学院,广东 广州,510090)
摘 要:介绍基于Ateml公司的AT91RM9200芯片开发板和S1D13506显示控制器,通过对其嵌入式Linux驱动程序的编写,实现了对12.1英寸TFT彩 {MOD}LCD屏的驱动和显示。侧重对其中的难点问题进行了说明。
关键词:AT91RM9200,S1D13506控制器,LCD,Linux驱动程序
The Development of Driver Based on AT91RM9200 and S1D13506 controller
XIONG Min, WAN Pin, GU Gang, ZHAN Yi-ju, LI Xue-cong
(Faculty of Automation, Guangdong University of Technology, Guangzhou 510090)
Abstract:This article introduce the Ateml Corporation’s development board of AT91RM9200 chip and the S1D13506 display control. Through develop the embedded Linux driver to realize the 12.1’ TFT color LCD screen driving and demonstration. Emphasize demonstrating the difficulty problem in the developing process.
Key words: AT91RM9200,S1D13506 display control,LCD,Linux driver
1 引言
嵌入式系统应用于工控领域越来越普及,对于传统工控产品升级换代发挥重要作用,随着由此带来的工控产品性能的大幅提高,与之对应的较高档次、友好的人机界面需求也不断增大。本文针对一款基于AT91RM9200芯片的工业级嵌入式系统开发板,加上可扩展外围控制器S1D13506,通过编写其驱动程序来添加12.1英寸TFT彩 {MOD}LCD显示功能。
2 硬件介绍
AT91RM9200是一款基于ARM920T内核的高性价比、低功耗、32位的ARM 芯片,拥有独立的16K指令和16K数据cache,写缓存,全功能的MMU虚拟内存管理单元,内部的16KB SDRAM和128KB ROM,在180MHz工作频率下运行速度为200MIPS。AT91RM9200集成了EBI, PMC,I/O,Ethernet,USB,MCI,SSC,UASRT, SPI,RTC,TWI等接口及其控制器。却没有针对LCD显示的控制器,所以本系统添加了S1D13506作为显示控制器,来实现LCD的显示。
S1D13506是EPSON大规模显示控制器家族中较新的一款。它内置RAMDAC硬件X-Y轴转置2维加速器,共有114个寄存器,可以灵活地对各种不同的显示方式进行设置,功能非常强大,可以和目前市场上流行的多种CPU总线兼容。
3设备驱动程序
设备驱动程序在linux内核中,是一个个独立的“黑盒子”,使某个特定的硬件响应一个定义良好的内部编程接口,同时完全隐藏了设备的工作细节。用户操作只需要通过一组标准化的调用完成,把这些调用映射到设备特定的操作上,则是设备驱动程序的任务。而在Linux系统里,每一个设备都可以看作是一个文件,所以打开的设备在内核内部都可以由一个File结构标识,内核使用File_operations结构访问驱动程序的函数。每个文件(设备)都与它自己的函数集相关联。这些操作函数主要负责上面所提到的系统调用的实现,并因此被命名为open,read,fork,ioctl等。
4 linux下LCD驱动程序设计
LCD控制器的功能是显示驱动信号,进而驱动LCD。用户只需要通过读写一系列的寄存器,就可以配置和显示驱动,在配置LCD控制器中最重要的一步则是帧缓冲区(FrameBuffer)的指定。帧缓冲区为图像硬件设备提供了一种抽象化处理,它代表了一些视频硬件设备,允许应用软件通过定义明确的界面来访问图像硬件设备。用户程序只要与帧缓冲驱动程序抽象出来的接口打交道,就可以把要显示的内容从缓冲区中读出,从而显示到屏幕上的。
4.1帧缓冲区驱动程序接口
在FrameBuffer驱动程序里最核心的结构体是struct fb_info,他记录了当前FrameBuffer硬件设备的状态,其定义通常在linux的include/linux/fb.h,其中最主要的有:
1)Struct fb_fix_screeninfo:定义了显示输出设备自身的属性,如屏幕缓冲区的物理地址和长度。
2)Struct fb_var_screeninfo:记录了帧缓冲设备和指定显示模式的可修改信息。它包括显示屏幕的分辨率、每个像素的比特数和一些时序变量。
如要先设定帧缓冲区的物理地址和长度,就要在S1D13506.h里指定,然后在驱动程序里通过对fb_fix_screeninfo赋值来实现:
#define S1D_PHYSICAL_VMEM_ADDR 0x30200000L//设定物理地址
#define S1D_PHYSICAL_VMEM_SIZE 0x200000L//设定长度
static int s1d13xxx_encode_fix(struct fb_fix_screeninfo *fix)
{
…
//赋值实现
fix->smem_start = S1D_PHYSICAL_VMEM_ADDR;
fix->smem_len = S1D_PHYSICAL_VMEM_SIZE;
…
}
而定义当前显示输出状态就要给fb_var_screeninfo结构赋值来实现
#define S1D_DISPLAY_WIDTH 800
#define S1D_DISPLAY_HEIGHT 600
#define S1D_DISPLAY_BPP 16
static int s1d13xxx_encode_var(struct fb_var_screeninfo *var,)
{
memset(var, 0, sizeof(struct fb_var_screeninfo));
var->xres = S1D_DISPLAY_WIDTH; //显示分辨率
var->yres = S1D_DISPLAY_HEIGHT;
var->bits_per_pixel = S1D_DISPLAY_BPP; //定义颜 {MOD}位数
…
}
4.2 LCD初始化
Linux下驱动程序总是先调用module_init(),因此初始化通过调用module_init(s1d13xxxfb_init)来实现。s1d13xxxfb_init初始化函数部分代码如下:
int __init s1d13xxxfb_init(char *dummy)
{
S1D_INDEX s1dReg;
S1D_VALUE s1dValue;
plateform_init_video();
for (i = 0; i < sizeof(aS1DRegs)/sizeof(aS1DRegs[0]); i++)
{
s1dReg = aS1DRegs[i].Index;
s1dValue = aS1DRegs[i].Value;
…
}
首先对LCD的背光灯进行点亮,LCD显示是一种被动的显示模式,它不能发光,只能依靠控制透射或反射周围环境的光来达到显示的目的,因此,必须通过写寄存器,用高电平指示对LCD加3.2伏电压来实现背光灯的点亮。其函数的部分代码如下:
void plateform_init_video(void)
{
AT91_SYS->PIOC_PER |= 0x00004000;
AT91_SYS->PIOC_OER |= 0x00004000;
AT91_SYS->PIOC_SODR |= 0x00004000;
…
}
本文系统采用的12.1寸TFT彩 {MOD}LCD最佳分辨率是800×600,但通过前面对结构Struct fb_var_screeninfo的赋值并不能真正设定其分辨率,因为结构Struct fb_var_screeninfo的值只是作为一个显示记录来用,必须通过设定寄存器的值,才能达到需要的分辨率。本系统在S1D13506.h头文件里用了一个数组对寄存器的设置作了一个预定义,然后在初始化函数里利用s1dReg和s1dValue这两个实参写入,从而设定寄存器的值:
static S1D_REGS aS1DRegs[] =
{
…
{0x0032,0x63}, // 水平象素数目
{0x0038,0x57}, // 垂直象素数目
{0x0039,0x02},
…
}
其中数组里每个元素的第一个值代表寄存器的名称,第二个值代表要设定的数值,是一个16进制的数,如32h寄存器是设定LCD显示的水平象素数目,它的计算方法是把第二个参数的值加1再乘以8就得出水平象素,如0x63换算成十进制为99,加1 乘以8就是800了。38h,39h分别设置成0x57和0x02就可以显示600的垂直分辨率,它的计算方法是以39h寄存器的bit0和bit1位为高位,38h寄存器的bit0-7为低位,组成的一个16进制的数再转换成十进制。除了要改这三个寄存器外,34h和3Ah这两个寄存器也会对显示的分辨率有影响。
4.3 LCD驱动程序“文件层-驱动层”及其成员函数的实现
帧缓冲设备也属于字符设备,要实现“文件层-驱动层”的接口方式来对LCD进行驱动就必须在对File_operationes数据结构fb_ops进行填充,并实现其对应的成员函数。本系统在include/linux/fb.h中定义了帧缓冲区的文件操作,部分代码如下:
struct fb_ops {
struct module *owner;
int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con, struct fb_info *info);
int (*fb_get_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info);
int (*fb_set_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info);
int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
int (*fb_mmap)(struct fb_info *info, struct file *file, struct vm_area_struct *vma);
};
这个结构中的每一个字段都必须指向驱动程序中实现特定操作的函数,对于不支持的操作,对应的字段可以被置为NULL,或留到后续开发时载添加。本系统中实现的特定操作的成员函数的代码如下:
static struct fb_ops s1d13xxxfb_ops =
{
owner: THIS_MODULE,
fb_open: s1d13xxxfb_open,
fb_get_fix: fbgen_get_fix,
fb_get_var: fbgen_get_var,
fb_set_var: fbgen_set_var,
fb_get_cmap: fbgen_get_cmap,
fb_set_cmap: fbgen_set_cmap,
fb_mmap: s1d13xxx_mmap,
};
至此,彩 {MOD}LCD的驱动程序框架基本上已经完成了,接下来就是把一些调用的函数写完整,编写好程序后用arm-linux-gcc编译驱动模块,之后可以对其动态加载或静态将其编译到Linux内核。加载完程序后就可以编写应用程序,进行读/写等操作了。
5 结束语
文中介绍了基于AT91RM9200的S1D13506控制器驱动程序基本原理和框架,并介绍Linux操作系统下帧缓冲设备接口。LCD显示器的型号虽然很多,但其驱动的编写基本上是类似的,可以通过本文介绍的步骤对其它彩 {MOD}LCD进行编写。
参考文献:
[1] Alessandro Rubini & Jonathan Corbet.LINUX设备驱动程序[M].北京:中国电力出版社,2004.
[2]刘淼. 嵌入式系统接口设计与Linux驱动程序开发[M]. 北京:北京航空航天大学出版社,2006
[3]EPSON. S1D13506 datasheet. http://www.epson.com/
[4]Atmel. About AT91RM9200. http://www.at91.com/,2005
[5]杨显强 田远富.嵌入式系统中LCD驱动的实现原理[J].今日电子,2005.8