欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

程序员文章站 2022-07-14 10:16:54
...

参考:http://www.cnblogs.com/lifexy/p/7604011.html

 

在上一节LCD层次分析时,得出写个LCD驱动入口函数,需要以下4步:

1)分配一个fb_info结构体:framebuffer_alloc();

2)设置fb_info;

3)设置硬件相关的操作;

4)使能LCD,并注册fb_info:register_framebuffer();

 

本节需要用到的函数:

函数dma_alloc_writecombine():(分配显存)

void * dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp);//分配DMA缓存区给现存
//返回值为:申请到的DMA缓冲区的虚拟地址,若为NULL,表示分配失败,则需要使用dma_free_writecombine()释放内存,避免内存泄漏
//参数如下:

//*dev:指针,这里填0,表示这个申请的缓冲区里没有内容

//size:分配的地址大小(字节单位)

//*handle:申请到的物理起始地址

//gfp:分配出来的内存参数,标志定义在<linux/gfp.h>,常用标志如下:
	//GFP_ATOMIC	用来从中断处理和进程上下文之外的其它代码中分配内存.从不睡眠
	//GFP_KERNEL	内核内存的正常分配,可能睡眠
	//GFP_USER	用来为用户空间页来分配内存;它可能睡眠

分配一段DMA缓存区,分配出来的内存会禁止cache缓存(因为DMA传输不需要CPU)

它和dm_alloc_coherent()函数相似,不过dma_alloc_writecombine()函数是分配出来的内存会禁止cache缓存以及禁止写入缓冲区

---------------------------------------------------------------------------------------

函数dma_free_writecombine():(释放显存)

void dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);
//cpu_addr:虚拟地址
//handle:物理地址

释放DMA缓冲区,dev和size参数和上面的函数一样

---------------------------------------------------------------------------------------

函数frambuffer_alloc():(申请fb_info结构体)

struct fb_info *framebuffer_alloc(size_t size, struct device *dev)//申请一个fb_info结构体
//size:额外的内存(里面放私有数据private)
//*dev:指针,这里填写NULL,表示这个申请的结构体没有内容

---------------------------------------------------------------------------------------

函数register_frambuffer()和函数frambuffer_release():

int register_framebuffer(struct fb_info *fb_info)
	//向内核中注册fb_info结构体,若内存不够,注册失败会返回负数
void framebuffer_release(struct fb_info *info)
	//注销内核中fb_info结构体

---------------------------------------------------------------------------------------

本节需要用到的结构体:

fb_info结构体如下:

struct fb_info {
	int node;
	int flags;
	struct fb_var_screeninfo var;	//可变的参数
	struct fb_fix_screeninfo fix;	//固定的参数
… …
	struct fb_ops *fbops;	//操作函数
… …
	char __iomem *screen_base;	//显存虚拟起始地址
	unsigned long screen_size;	//显存虚拟地址长度
	void *pseudo_palette;		/* Fake palette of 16 colors */ 
	//假的16色调色板,里面存放了16色的数据,可以通过8bpp数据来找到调色板里面的16色颜色索引值,模拟出16色颜色来,节省内存,不需要的话就指向一个不用的数组即可
};

其中操作函数fb_info->fbops结构体写法如下:

static struct fb_ops s3c_lcdfb_ops = {
	.owner		= THIS_MODULE,

	//可以参考\drivers\video\Atmel_lcdfb.c
	.fb_setcolreg	= s3c_lcdfb_setcolreg,	//设置调色板fb_info->pseudo_palette,自己构造该函数
	
	//下面三个函数是对显存的操作
	.fb_fillrect	= cfb_fillrect,	//填充一个矩形,用/driver/video/cfbfillrect.c里的函数即可
	.fb_copyarea	= cfb_copyarea,	//拷贝一个区域,用/driver/video/cfbcopyarea.c里的函数即可
	.fb_imageblit	= cfb_imageblit,	//图形填充,用/driver/video/imageblit.c里的函数即可
};

固定的参数fb_info->fix 结构体如下:

 

struct fb_fix_screeninfo {
	char id[16];			//id名字
	unsigned long smem_start;	//framebuffer物理起始地址
					
	__u32 smem_len;			//framebuffer长度,字节为单位
	__u32 type;			//lcd类型,默认值为0即可
	__u32 type_aux;			//附加类型,为0
	__u32 visual;			//画面设备,常见参数如下
// FB_VISUAL_MONO01		0	单色,0:白色,1:黑色
// FB_VISUAL_MONO10		1	单色,1:黑色,1:白色
// FB_VISUAL_TRUECOLOR		2	真彩(TFT:真彩)
// FB_VISUAL_PSEUDOCOLOR	3	伪彩
// FB_VISUAL_DIRECTCOLOR	4	直彩

 
	__u16 xpanstep;			/* 如果没有硬件panning就赋值为0 */
	__u16 ypanstep;			/* 如果没有硬件panning就赋值为0 */
	__u16 ywrapstep;		/* 如果没有硬件panning就赋值为0 */

	__u32 line_length;		/* 一行的字节数,例:(RGB565)240*320,那么这里就等于240*16/8 */
	
	/* 以下成员都可以不需要 */
	unsigned long mmio_start;	/*内存映射IO的起始地址,用于应用层直接访问寄存器,可以不需要 */
				
	__u32 mmio_len;			/* 内存映射IO的长度,可以不需要 */
	__u32 accel;			/* Indicate to driver which	*/
					/*  specific chip/card we have	*/
	__u16 reserved[3];		/* Reserved for future compatibility */
};

可变的参数fb_info->var结构体如下:

 

struct fb_var_screeninfo {
	__u32 xres;			/* 可见屏幕一行有多少个像素点 */
	__u32 yres;			/* 可见屏幕一列有多少个像素点 */
	__u32 xres_virtual;		/* 虚拟屏幕一行有多少个像素点 */
	__u32 yres_virtual;		/* 虚拟屏幕一列有多少个像素点 */
	__u32 xoffset;			/* 虚拟到可见屏幕之间的行偏移,若可见和虚拟的分频率一样,就直接设为0 */
	__u32 yoffset;			/* 虚拟到可见屏幕之间的列偏移 */

	__u32 bits_per_pixel;		/* 每个像素的位数即BPP,比如:RGB565,则填入16 */
	__u32 grayscale;		/* 非0时,指的是灰度,真彩直接填0即可 */

	struct fb_bitfield red;		//fb缓存的R位域,fb_bitfield结构体成员如下:
//	__u32 offset;			区域偏移值,比如RGB565中的R,就在第11位
//	__u32 length;			区域长度,比如RGB565的R,共有5位
//	__u32 msb_right;		msb_right==0,表示数据左边最大,msb_right!=0,表示数据右边最大
};

	struct fb_bitfield green;	/* fb缓存的G位域 */
	struct fb_bitfield blue;	/* fb缓存的B位域 */

	/* 以下参数都可以不填,默认为0 */
	struct fb_bitfield transp;	/* 透明度,不需要填0即可 */	

	__u32 nonstd;			/* !=0表示非标准像素格式 */

	__u32 activate;			/* 设为0即可 */

	__u32 height;			/* 外设高度(单位mm),一般不需要填 */
	__u32 width;			/* 外设宽度(单位mm),一般不需要填 */

	__u32 accel_flags;		/* 过时的参数,不需要填 */

	/* 除了pixclock本身外,其它的都以像素时钟为单位 */
	__u32 pixclock;			/* 像素时钟(皮秒) */
	__u32 left_margin;		/* 行切换,从同步到绘图之间的延迟 */
	__u32 right_margin;		/* 行切换,从绘图到同步之间的延迟 */
	__u32 upper_margin;		/* 帧切换,从同步到绘图之间的延迟 */
	__u32 lower_margin;		/* 帧切换,从绘图到同步之间的延迟 */
	__u32 hsync_len;		/* 水平同步的长度 */
	__u32 vsync_len;		/* 垂直同步的长度 */
	__u32 sync;			/* see FB_SYNC_*		*/
	__u32 vmode;			/* see FB_VMODE_*		*/
	__u32 rotate;			/* angle we rotate counter clockwise */
	__u32 reserved[5];		/* 保留 */
};

1、写驱动程序:
(写驱动可以参考自带的LCD平台驱动drivers/video/s3c2410fb.c)
(LCD控制寄存器设置:参考之前的LCD裸机驱动:(硬件设置)https://blog.csdn.net/xiaodingqq/article/details/80724190

 

1.1 步骤如下:
在驱动init入口函数中:

 

(1)分配一个fb_info结构体

(2)设置fb_info

    (2.1)设置固定的参数fb_info->fix

    (2.2)设置可变的参数fb_info->var

    (2.3)设置操作函数fb_info->fbops

    (2.4)设置fb_info其它的成员

(3)设置硬件相关的操作

    (3.1)配置LCD引脚

    (3.2)根据LCD手册设置LCD控制器

    (3.3)分配显存(framebuffer),把地址告诉LCD控制器和fb_info

(4)开启LCD,并注册fb_info:register_framebuffer()

    (4.1)直接在init函数中开启LCD(后面讲到电源管理,再来优化)

                控制LCDCON5允许PWREN信号,

                然后控制LCDCON1输出PWREN信号,

                输出GPB0高电平来开背光,

    (4.2)注册fb_info

 

在驱动exit出口函数中:

(1)卸载内核中的fb_info

(2)控制LCDCON1关闭PWREN信号,关背光灯,iounmap注销地址

(3)释放DMA缓存地址dma_free_writecombine()

(4)释放注册的fb_inifo

1.2具体的代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>

#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h>

static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,	//设置调色板
			     unsigned int green, unsigned int blue,
			     unsigned int transp, struct fb_info *info);

struct lcd_regs {
	unsigned long lcdcon1;
	unsigned long lcdcon2;
	unsigned long lcdcon3;
	unsigned long lcdcon4;
	unsigned long lcdcon5;
	unsigned long lcdsaddr1;
	unsigned long lcdsaddr2;
	unsigned long lcdsaddr3;
	unsigned long redlut;
	unsigned long greenlut;
	unsigned long bluelut;
	unsigned long reserved[9];	//保留9个
	unsigned long dithmode;
	unsigned long tpal;
	unsigned long lcdintpnd;
	unsigned long lcdsrcpnd;
	unsigned long lcdintmsk;
	unsigned long lpcsel;
};

static struct fb_ops s3c_lcdfb_ops = {
	.owner		= THIS_MODULE,
	.fb_setcolreg	= s3c_lcdfb_setcolreg,	//设置调色板
	//下面三个函数是对显存的操作
	.fb_fillrect	= cfb_fillrect,		//填充一个矩形
	.fb_copyarea	= cfb_copyarea,		//拷贝一个区域
	.fb_imageblit	= cfb_imageblit,	//图形填充
};

static struct fb_info *s3c_lcd;
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;
static volatile struct lcd_regs* lcd_regs;
static u32 pseudo_palette[16];	//假的调色板,调色板数组,被fb_info->pseudo_palette调用

/* from pxafb.c */
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
	chan &= 0xffff;
	chan >>= 16 - bf->length;
	return chan << bf->offset;
}

static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,	//设置调色板
			     unsigned int green, unsigned int blue,
			     unsigned int transp, struct fb_info *info)
{
	unsigned int val;
	
	if (regno > 16)	//regno哪一个调色板
		return 1;
	

	/* 用read,green,blue三原色构造出val */
	val = chan_to_field(red,   &info->var.red);
	val |= chan_to_field(green, &info->var.green);
	val |= chan_to_field(blue,  &info->var.blue);

	//((u32 *)(info->pseudo_palette))[regno] = val;
	pseudo_palette[regno] = val;	//放到调色板数组中
	return 0;
}

static int lcd_init(void)
{
	/* 1. 分配一个fb_info */
	//第一个参数大小:size表示分配额外的空间(里面放私有数据private)
	s3c_lcd = framebuffer_alloc(0, NULL);	//不分配额外的空间
	
	/* 2.  设置 */
	/* 2.1 设备固定的参数fix */
	strcpy(s3c_lcd->fix.id, "mylcd");//名字id
	s3c_lcd->fix.smem_len    = 480*272*16/8;	//显存的长度(需要看LCD手册)分辨率resolution:480*272,RGB(Bit):565,一个像素16位
	s3c_lcd->fix.type        = FB_TYPE_PACKED_PIXELS;	//在fb.h,使用默认值(值为0)
	s3c_lcd->fix.visual      = FB_VISUAL_TRUECOLOR;	/* visual视觉的 TFTLCD:真彩色 */
	s3c_lcd->fix.line_length = 480*2;	//一行480个像素,一个像素16位(2个字节)
	
	/* 2.2 设置可变的参数var */
	s3c_lcd->var.xres           = 480;	//x方向的分辨率
	s3c_lcd->var.yres           = 272;	//y方向的分辨率
	s3c_lcd->var.xres_virtual   = 480;	//电脑桌面点击右键,设置分辨率(虚拟分辨率)
	s3c_lcd->var.yres_virtual   = 272;	//虚拟分辨率
	s3c_lcd->var.bits_per_pixel = 16;	//每个像素用多少位

	/* RGB:565 */
	s3c_lcd->var.red.offset     = 11;	//红色的偏移值从bit11开始
	s3c_lcd->var.red.length     = 5;	//红色的长度为5位

	s3c_lcd->var.green.offset   = 5;	//绿色的偏移值从bit5开始
	s3c_lcd->var.green.length   = 6;	//绿色的长度为6位

	s3c_lcd->var.blue.offset    = 0;	//蓝色的偏移值从bit0开始
	s3c_lcd->var.blue.length    = 5;	//蓝色的长度为5位

	s3c_lcd->var.activate       = FB_ACTIVATE_NOW;	//使用默认值

	/* 2.3 设置操作函数fbops       */
	s3c_lcd->fbops 				= &s3c_lcdfb_ops;
	
	/* 2.4 其他的设置 */
	s3c_lcd->pseudo_palette 	= pseudo_palette;	//调色板
	//s3c_lcd->screen_base = ;			/* 显存的虚拟地址 */
	s3c_lcd->screen_size = 480*272*16/8 ;	/* 显存的大小 */	//虚拟地址长
	
	/* 3.  硬件相关的操作 */
	/* 3.1 配置GPIO用于LCD */
	//映射
	gpbcon = ioremap(0x56000010, 8);	//映射8个字节
	gpbdat = gpbcon+1;	//指针加1相当于加4
	gpccon = ioremap(0x56000020, 4);	//一页一页地映射的,所以size写4或者1024都可以
	gpdcon = ioremap(0x56000030, 4);
	gpgcon = ioremap(0x56000060, 4);

	//参考以前的裸机程序lcddrv.c
    *gpccon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
	*gpdcon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[23:8] */

	*gpbcon &= ~(3);	/* GPB0(KEYBOARD)设置为输出引脚 */
	*gpbcon |= 1;
	*gpbdat &= ~1;	/* 输出低电平,背光先不开启 */

	*gpgcon |= (3<<8);	/* GPG4用作LCD_PWREN */
	
	/* 3.2 根据LCD手册设置LCD控制器,比如VCLK的频率等 */
	//映射所有的lcdconX控制器
	lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));

	/* bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2]  ,具体可看LCD手册
	 *    10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2]
	 *	  CLKVAL = 4
	 * bit[6:5]:0b11,TFT LCD
	 * bit[4:1]:0b1100 16bpp for TFT (每个像素用16位来表示)
	 * bit[0]  :0 = Disable the video output and the LCD control signal. 
	 */
	lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1); 

	/* 垂直方向的时间参数
	 * bit[31:24]:VBPD,VSYNC之后再过多长时间才能发出第1行数据
     *             LCD手册 tvb=2
     *             所以VBPD=1
     * bit[23:14]:多少行,272行,所以LINEVAL=272-1=271
     * bit[13:6] :VFPD,发出最后一行数据之后,再过多长时间才发出VSYNC
     *             LCD手册 tvf=2,所以VFPD=2-1=1
     * bit[5:0]  :VSPW,VSYC信号的脉冲宽度,LCD手册 tvp=10,所以VSPW=10-1=9
	 */
	lcd_regs->lcdcon2 = (1<<24) | (271<<14) | (1<<6) | (9);

	/* 水平方向的时间参数
	 * bit[25:19]:HBPD,HSYNC之后再过多长时间才能发出第1个像素的数据
     *             LCD手册 thb=2
     *             所以HBPD=1
     * bit[18:8]: 多少列,480列,所以LINEVAL=480-1=479
     * bit[7:0] : HFPD,发出最后一行里,最后一个像素之后,再过多长时间才发出HSYNC
     *             LCD手册 thf=2,所以HFPD=2-1=1
	 */
	lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1);

	/* 水平方向的同步信号
     * bit[7:0] : HSPW,HSYC信号的脉冲宽度,LCD手册 thp=41,所以HSPW=41-1=40
	 */
	lcd_regs->lcdcon4 = 40;


	/* 信号的极性 
     * bit[11]:1=565 format
     * bit[10]:0 = The video data is fetched at VCLK falling edge 在下降沿取数据
     * bit[9]:1 = HSYNC信号要反转,即低电平有效(水平方向的同步信号,在2440与LCD手册相反,所以要反转)
     * bit[8]:1 = VSYNC信号要反转,即低电平有效
     * bit[6]:0 = VDEN不用反转(数据引脚VD0~VD23)
     * bit[3]:0 = PWREN输出低电平
     * bit[1]:0 = BSWAP
     * bit[0]:1 = HWSWP 看2440手册 P413
	 */
	lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);
	
	/* 3.3 分配显存(frambuffer,在SDRAM内存划分出来的),并把地址告诉LCD控制器 */
	//第3个参数存放物理地址,返回值为这块内存的虚拟地址
	s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);

	lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);	//第0位和最高位忽略,保存缓冲起始地址A[30:1]
	lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;	//结束地址=开始地址+长度,只关心21位,保存缓冲结束地址A[21:1]
	
lcd_regs->lcdsaddr3 = (480*16/16);	/* 一行的长度(单位:半字,就是2个字节) */
		
	//s3c_lcd->fix.smem_start = xxx;  //显存的物理地址
	/* 启动LCD */
	lcd_regs->lcdcon1 |= (1<<0);	/* 使能LCD控制器 */
	lcd_regs->lcdcon5 |= (1<<3);	/* 使能LCD本身 */
	*gpbdat |= 1;	/* 原来输出低电平,现在输出高电平,使能背光灯 */
	
	/* 4. 注册 */
	register_framebuffer(s3c_lcd);

	return 0;
}

static void lcd_exit(void)
{
	unregister_framebuffer(s3c_lcd);
	
	lcd_regs->lcdcon1 &= ~(1<<0);	/* 关闭LCD本身,省电 */
	*gpbdat &= ~1;	/* 关闭背光灯 */

	dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);//释放显存,第三个参数cpu地址,就是虚拟地址,第四个参数,handle物理地址

	iounmap(lcd_regs);
	iounmap(gpbcon);
	iounmap(gpccon);
	iounmap(gpdcon);
	iounmap(gpgcon);

	framebuffer_release(s3c_lcd);	//释放掉fb_info的sc3_lcd结构体
}

module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");

 

2、重新编译内核,去掉原来的LCD驱动程序

在内核代码目录下,输入命令make menuconfig,进入menu菜单重新设置内核参数:

进入-> Device Drivers
  	-> Graphics support
		<M> S3C2410 LCD framebuffer support

(上面“<*>s3c2410 LCD framebuffer support”是之前自带的驱动程序,这里去掉后以<M>模块方式编译进内核)

(<M>模块方式编译进内核)

然后make uImage编译内核(如果生成的是zImage,而不是uImage,可以点击这里

编译好的uImage 在 arch/arm/boot/uImage

make modules编译模块(编译好的3个模块在drivers/video

cp arch/arm/boot/uImage /work/nfsroot
cp drivers/video cfb*.ko /work/nfsroot

为什么要编译模块?

因为LCD驱动相关的文件也没有编进内核,而fb_ops里的成员fb_fillrect(),fb_copyarea(),fb_imageblit()用的都是drivers/video下面的3个文件所以需要这3个的.ko模块。

 

3、使用新的uImage来启动开发板

    (3.1) 启动开发板,进入uboot的菜单界面,然后输入q退出菜单,进入命令行。

                S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

    (3.2) 打开tftpd32.exe软件,选择需要传输文件的目录,以及PC机(服务器)的ip地址。

                S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

    (3.3) 在uboot的命令行,输入命令tftp 30000000 uImage(把在PC机的uImage传到地址30000000上(SDRAM))。

    (3.4) 输入bootm 30000000,开发板从地址30000000开始启动内核。

                S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

 

4、挂载驱动

    (4.1) 把编译好的LCD驱动模块和drivers/video里的3个.ko模块放入nfs文件系统(/work/nfsroot)中,

    (4.2) 使用nfs网络文件系统挂载,输入命令:

                mount -t nfs -o intr,nolock,rsize=1024,wsize=1024 192.168.1.3:/work/nfsroot /mnt   

                (如果nfs挂载后,不能读取,可以点击这里)           

    (4.3) 先装载3个/drivers/video下编译好的模块(cfbcopyvarea.ko、cfbfillrect.ko、cfbimgblt.ko),再来装载LCD驱动模块(lcd.ko)。

    (4.4) 挂载LCD驱动后,可以通过ls -l /dev/fb*命令查看已挂载的LCD设备节点。

    S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

 

5、测试运行

    echo hello > /dev/tty1

    ls -l > /dev/tty1

    S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

 

6、优化。(使用上节的键盘驱动在LCD终端打印命令行)

    (5.1)vi /etc/inittab        (修改inittab,inittab:配置文件,用于启动init进程时,读取inittab)

        添加tty1::askfirst:-/bin/sh        (将sh进程(命令行)输出到tty1里,也就是LCD输出信息)

        S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

    (5.2)然后重启,insmod装载/drivers/video下编译好的模块,再来insmod装载LCD驱动模块,tty1设备便有了,就能看到信息。

        S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

    (5.3)最后insmod上一节的键盘驱动buttons.ko后,按下"Enter"按键,便能在LCD终端上操作linux了。

        S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

    (5.4)如果按下Enter键,它就启动了一个进程号770的-sh进程,如下图发现这个-sh的描述符指向tty1:

        S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)