精华内容
下载资源
问答
  • 人们使用的电脑、手机、车载系统、各种测量设备、显示设备等等,都把LCD作为重要的人机接口,它把使用者需要的信息及时反应出来。目前市场上存在着各种LCD和相对应的驱动模块,驱动模块所带的驱动程序都是针对本身...
  • 人们使用的电脑、手机、车载系统、各种测量设备、显示设备等等,都把LCD作为重要的人机接口,它把使用者需要的信息及时反应出来。目前市场上存在着各种LCD和相对应的驱动模块,驱动模块所带的驱动程序都是针对本身...
  • 其次描述了嵌入式Linux系统在目标板ARM221上移植过程及界面相关硬件驱动程序的开发;最后移植了开源嵌入式界面系统MiniGUI到目标板ARM221。  1 引言  嵌入式产品如 PDA、机顶盒、WAP 手机等迅速地普及,给广大的...
  • PS:用虚拟机启动系统要加载到软盘驱动器,运行内存不能小于512M 经测试安卓手机用limbo模拟器可以完美运行 简介:MenuetOS是一款为个人计算机开发的操作系统,完全由32/64位汇编语言编写。Menuet64在遵循License...
  • LCD 设备驱动

    千次阅读 2013-04-09 14:58:15
    在多媒体应用的推动下,彩色 LCD 越来越多地应用到了嵌入式系统中,掌上电脑(PDA),手机等多采用 TFT 显示器件,支持彩色图形界面,能显示图片并进行视频媒体播放。帧缓冲(Framebuffer)是 Linux 为显示设备提供的一个...

    在多媒体应用的推动下,彩色 LCD 越来越多地应用到了嵌入式系统中,掌上电脑(PDA),手机等多采用 TFT 显示器件,支持彩色图形界面,能显示图片并进行视频媒体播放。帧缓冲(Framebuffer)是 Linux 为显示设备提供的一个接口,它允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。

    LCD 硬件原理

    利用液晶制成的显示器称为 LCD,依据驱动方式可分为静态驱动、简单矩阵驱动以及主动矩阵驱动 3 种。其中,简单矩阵型又可再细分扭转向列型(TN)和超扭转式向列型(STN)两种,而主动矩阵型则以薄膜式晶体管型(TFT)为主流。下表列出了 TN、STN 和 TFT 显示器的区别。


    TN 型液晶显示技术是 LCD 中最基本的,其他种类的 LCD 都以 TN 型为基础改进而得。TN 型 LCD 显示质量很差,色彩单一,对比度低,反映速度很慢,故主要用于简单的数字符与文字的显示,如电子表及电子计算器等。

    STN LCD 的显示原理与 TN 类似,区别在于 TN 型的液晶分子将入射光旋转 90°,而 STN 则可将入射光旋转 180°~270°。STN 改善了 TN 视角狭小的缺点,并提高了对比度,显示品质较 TN 高。

    STN 搭配彩色滤光片,将单色显示矩阵的任一像素分成 3 个子像素,分别透过彩色滤光片显示红、绿、蓝三原色,再经由三原色按比例调和,显示出逼近全彩模式的色彩。STN 显示的画面色彩对比度仍较小,反应速度也较慢,可以作为一般的操作显示接口。

    随后出现的 DSTN 通过双扫描方式来显示,显示效果相对 STN 而言有了较大幅度的提高。DSTN 的反应速度可达到 100ms,但是在电场反复改变电压的过程中,每一像素的恢复过程较慢。因此,当在屏幕画面快速变化时,会产生“拖尾”现象。

    TN 与 STN 型液晶显示器都是使用场电压驱动方式,如果显示尺寸加大,中心部位对电极变化的反应时间就会拉长,显示器的速度跟不上。为了解决这个问题,主动式矩阵驱动被提出,主动式 TFT 型的液晶显示器的结构较为复杂,它包括背光管、导光板、偏光板、滤光板、玻璃基板、配向膜、液晶材料和薄膜式晶体管等。

    在 TFT 型 LCD 中,晶体管矩阵依显示信号开启或关闭液晶分子的电压,使液晶分子轴转向而成“亮”或“暗”的对比,避免了显示器对电场效应的依靠。因此,TFTLCD 的显示质量较 TN/STN 更佳,画面显示对比度可达 150:1 以上,反应速度逼近 30ms甚至更快,适用于 PDA、笔记本电脑、数码相机、MP4 等。

    一块 LCD 屏显示图像不但需要 LCD 驱动器,还需要有相应的 LCD 控制器。通常 LCD 驱动器会以 COF/COG 的形式与 LCD 玻璃基板制作在一起, LCD 控制器则由外部电路来实现。许多 MCU 内部直接集成了 LCD 控制器,通过 LCD控制器可以方便地控制 STN 和 TFT 屏。

    TFT 屏是目前嵌入式系统应用的主流,下图给出了 TFT 屏的典型时序。时序图中的 VCLK、HSYNC 和 VSYNC 分别为像素时钟信号(用于锁存图像数据的像素时钟)、行同步信号和帧同步信号,VDEN 为数据有效标志信号,VD 为图像的数据信号。


    作为帧同步信号的 VSYNC,每发出一个脉冲,都意味着新的一屏图像数据开始发送。而作为行同步信号的 HSYNC,每发出一个脉冲都表明新的一行图像资料开始发送。在帧同步以及行同步的头尾都必须留有回扫时间。这样的时序安排起源于 CRT显示器电子枪偏转所需要的时间,但后来成为实际上的工业标准,因此 TFT 屏也包含了回扫时间。

    下图给出了 LCD 控制器中应该设置的 TFT 屏的参数,其中的上边界和下边界即为帧切换的回扫时间,左边界和右边界即为行切换的回扫时间,水平同步和垂直同步分别是行和帧同步本身需要的时间。xres 和 yres 则分别是屏幕的水平和垂直分辨率,常见的嵌入式设备的 LCD 分辨率主要为 320*240、640*480 等。



    帧缓冲

    帧缓冲的概念
    帧缓冲(framebuffer)是 Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示,下面将讲解显示缓冲区与显示点的对应关系


    显示缓冲区与显示点
    在帧缓冲设备中,对屏幕显示点的操作通过读写显示缓冲区来完成,在不同的色彩模式下,显示缓冲区和屏幕上的显示点有不同的对应关系, 下表分别给出了 16 级灰度、8 位色和 16 位情况下显示缓冲区与显示点的对应关系。


    问题:上面这幅图的用意是什么???

    Linux 帧缓冲相关数据结构与函数
    1.fb_info 结构体
    帧缓冲设备最关键的一个数据结构体是 fb_info 结构体(为了便于记忆,我们把它简称为“FBI”),FBI 中包括了关于帧缓冲设备属性和操作的完整描述,这个结构体的定义如下所示:

    struct fb_info {
    	int node;
    	int flags;
    	struct fb_var_screeninfo var;	/*可变参数 */
    	struct fb_fix_screeninfo fix;	/*固定参数 */
    	struct fb_monspecs monspecs;	/*显示器标准 */
    	struct work_struct queue;	/* 帧缓冲事件队列 */
    	struct fb_pixmap pixmap;	/* 图像硬件 mapper */
    	struct fb_pixmap sprite;	/* 光标硬件 mapper */
    	struct fb_cmap cmap;		/* 目前的颜色表*/
    	struct list_head modelist;      /* mode list */
    	struct fb_videomode *mode;	/* 目前的 video 模式 */
    
    #ifdef CONFIG_FB_BACKLIGHT
    	/* assigned backlight device */
    	/* set before framebuffer registration, 
    	   remove after unregister */
    	struct backlight_device *bl_dev;/* 对应的背光设备 */
    
    	/* Backlight level curve */
    	struct mutex bl_curve_mutex;	//背光的互斥锁
    	u8 bl_curve[FB_BACKLIGHT_LEVELS];/* 背光调整 */
    #endif
    #ifdef CONFIG_FB_DEFERRED_IO
    	struct delayed_work deferred_work;
    	struct fb_deferred_io *fbdefio;
    #endif
    
    	struct fb_ops *fbops;           /* fb_ops,帧缓冲操作 */
    	struct device *device;		/* This is the parent */
    	struct device *dev;		/* This is this fb device */
    	int class_flag;                    /* 私有 sysfs 标志 */
    #ifdef CONFIG_FB_TILEBLITTING
    	struct fb_tile_ops *tileops;    /* 图块 Blitting */
    #endif
    	char __iomem *screen_base;	/* 虚拟基地址 */
    	unsigned long screen_size;	/* ioremapped 的虚拟内存大小 */ 
    	void *pseudo_palette;		/* 伪 16 色颜色表 */
    #define FBINFO_STATE_RUNNING	0
    #define FBINFO_STATE_SUSPENDED	1
    	u32 state;			/* 硬件状态,如挂起 */
    	void *fbcon_par;                /* fbcon use-only private area */
    	/* From here on everything is device dependent */
    	void *par;	
    };
    FBI 中记录了帧缓冲设备的全部信息,包括设备的设置参数、状态以及操作函数指针。每一个帧缓冲设备都必须对应一个 FBI。

    fb_ops 结构体
    FBI 的成员变量 fb_ops 为指向底层操作的函数的指针,这些函数是需要驱动程序开发人员编写的,其定义如下所示。

    struct fb_ops {
    	/* open/release and usage marking */
    	struct module *owner;
    	int (*fb_open)(struct fb_info *info, int user);/* 打开/释放 */
    	int (*fb_release)(struct fb_info *info, int user);
    
    	/* For framebuffers with strange non linear layouts or that do not
    	 * work with normal memory mapped access
    	 *//* 对于非线性布局的/常规内存映射无法工作的帧缓冲设备需要 */
    	ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
    			   size_t count, loff_t *ppos);
    	ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
    			    size_t count, loff_t *ppos);
    
    	/* checks var and eventually tweaks it to something supported,
    	 * DO NOT MODIFY PAR *//* 检测可变参数,并调整到支持的值*/
    	int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
    
    	/* set the video mode according to info->var *//* 根据 info->var 设置 video 模式 */
    	int (*fb_set_par)(struct fb_info *info);
    
    	/* set color register *//* 设置 color 寄存器 */
    	int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
    			    unsigned blue, unsigned transp, struct fb_info *info);
    
    	/* set color registers in batch *//* 批量设置 color 寄存器,设置颜色表 */
    	int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
    
    	/* blank display *//*显示空白 */
    	int (*fb_blank)(int blank, struct fb_info *info);
    
    	/* pan display *//* pan 显示 */
    	int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
    
    	/* Draws a rectangle *//* 矩形填充 */
    	void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
    	/* Copy data from area to another *//* 数据复制 */
    	void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
    	/* Draws a image to the display *//* 图形填充 */
    	void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
    
    	/* Draws cursor *//* 绘制光标 */
    	int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
    
    	/* Rotates the display *//* 旋转显示 */
    	void (*fb_rotate)(struct fb_info *info, int angle);
    
    	/* wait for blit idle, optional *//* 等待 blit 空闲 (可选) */
    	int (*fb_sync)(struct fb_info *info);
    
    	/* perform fb specific ioctl (optional) */
    	int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
    			unsigned long arg);
    
    	/* Handle 32bit compat ioctl (optional) *//* 处理 32 位的 compat ioctl (可选) */
    	int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
    			unsigned long arg);
    
    	/* perform fb specific mmap *//* fb 特定的 mmap */
    	int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
    
    	/* save current hardware state *//* 保存目前的硬件状态 */
    	void (*fb_save_state)(struct fb_info *info);
    
    	/* restore saved state *//* 恢复被保存的硬件状态 */
    	void (*fb_restore_state)(struct fb_info *info);
    
    	/* get capability given var */
    	void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
    			    struct fb_var_screeninfo *var);
    };
    fb_ops 的 fb_check_var()成员函数用于检查可以修改的屏幕参数并调整到合适的值,而 fb_set_par()则使得用户设置的屏幕参数在硬件上有效
    fb_var_screeninfo(可变参数) 和 fb_fix_screeninfo(固定参数) 结构体
    fb_var_screeninfo记录用户可修改的显示控制器参数,包括屏幕分辨率和每个像素点的比特数。
    fb_var_screeninfo 中的 xres 定义屏幕一行有多少个点,yres 定义屏幕一列有多少个点,bits_per_pixel 定义每个点用多少个字节表示。

    fb_fix_screeninfo 中记录用户不能修改的显示控制器的参数,如屏幕缓冲区的物理地址、长度。当对帧缓冲设备进行映射操作的时候,就是从 fb_fix_screeninfo 中取得缓冲区物理地址的。上述数据成员都需要在驱动程序中初始化和设置。

    fb_var_screeninfo fb_fix_screeninfo 结构体的定义分别如下代码:

    struct fb_var_screeninfo {//可变参数
    	__u32 xres;			/* 可见分辨率		*/
    	__u32 yres;
    	__u32 xres_virtual;		/* 虚拟分辨率		*/
    	__u32 yres_virtual;
    	__u32 xoffset;			/* 虚拟分辨率到可见分辨率的偏移 */
    	__u32 yoffset;			/* resolution			*/
    
    	__u32 bits_per_pixel;		/* 每像素位数,BPP */
    	__u32 grayscale;		/非 0 时指灰度 ,问题:什么是灰度?*/
            /* fb 缓存的 R\G\B 位域 */
    	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
    	struct fb_bitfield green;	/* else only length is significant */
    	struct fb_bitfield blue;
    	struct fb_bitfield transp;	/* 透明度 */
    
    	__u32 nonstd;			/* != 0 非标准像素格式 */
    
    	__u32 activate;			/* see FB_ACTIVATE_*		*/
    
    	__u32 height;			/*高度 mm*/
    	__u32 width;			/*宽度 mm*/
    
    	__u32 accel_flags;		/* 看 fb_info.flags */
    
    	/* 定时: 除了 pixclock 本身外,其他的都以像素时钟为单位 */
    	__u32 pixclock;			/* pixel clock in ps (pico seconds) */
    	__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;			/* 顺时钟旋转的角度 */
    	__u32 reserved[5];		/* Reserved for future compatibility */
    };
    
    struct fb_fix_screeninfo {//不可变参数
    	char id[16];			/* 字符串形式的标识符 */
    	unsigned long smem_start;	/* fb 缓存的开始位置 */
    					/* (physical address) */
    	__u32 smem_len;			/* fb 缓存的长度 */
    	__u32 type;			/* see FB_TYPE_*		*/
    	__u32 type_aux;			/* 分界 */
    	__u32 visual;			/* see FB_VISUAL_*		*/ 
    	__u16 xpanstep;			/* 如果没有硬件 panning ,赋 0 */
    	__u16 ypanstep;			/* zero if no hardware panning  */
    	__u16 ywrapstep;		/* zero if no hardware ywrap    */
    	__u32 line_length;		/* 1 行的字节数 */
    	unsigned long mmio_start;	/* 内存映射 I/O 的开始位置 */
    					/* (physical address) */
    	__u32 mmio_len;			/* 内存映射 I/O 的长度 */
    	__u32 accel;			/* Indicate to driver which	*/
    					/*  specific chip/card we have	*/
    	__u16 reserved[3];		/* 保留*/
    };
     上面定义的visual成员记录屏幕使用的色彩模式,在 Linux 系统中,支持的色彩模式包括如下几种。
    1.Monochrome(FB_VISUAL_MONO01、FB_VISUAL_MONO10),每个像素是黑或白
    2.Pseudocolor(FB_VISUAL_PSEUDOCOLOR、FB_VISUAL_STATIC_PSEUDOCOLOR),即伪彩色,采用索引颜色显示
    3.True color(FB_VISUAL_TRUECOLOR),真彩色,分成红、绿、蓝三基色。
    4.Direct color(FB_VISUAL_DIRECTCOLOR),每个像素颜色也是有红、绿、蓝组成,不过每个颜色值是个索引,需要查表
    5.Grayscale displays,灰度显示,红、绿、蓝的值都一样。


    fb_bitfield 结构体

    在可变参数的成员中分别记录 R、G、B 的位域,fb_bitfield 结构体描述每一像素显示缓冲区的组织方式,包含位域偏移、位域长度和 MSB 指示,如下所示:

    struct fb_bitfield {
    	__u32 offset;			/* 位域偏移	*/
    	__u32 length;			/* 位域长度	*/
    	__u32 msb_right;		/* != 0 : Most significant bit is */ 
    					/* right */ 
    };


    fb_cmap结构体:

    fb_cmap 结 构 体 记 录 设 备 无 关 的 颜 色表 信 息 , 用户 空 间 可 以 通 过 ioctl() 的FBIOGETCMAP 和 FBIOPUTCMAP 命令读取或设定颜色表。

    struct fb_cmap {
    	__u32 start;			/* 第 1 个元素入口	*/
    	__u32 len;			/* 元素数量 */
    	__u16 *red;			/* Red values	*/
    	__u16 *green;
    	__u16 *blue;
    	__u16 *transp;			/* transparency, can be NULL */
    };
    下面代码所示为用户空间获取颜色表的例程,若 BPP 为 8 位,则颜色表长度为 256;若 BPP 为 4 位,则颜色表长度为 16;否则,颜色表长度为 0,这是因为,对于 BPP 大于等于 16 的情况,使用颜色表是不划算的。(问题:颜色表的作用是什么???

    // 读入颜色表
    if ((vinfo.bits_per_pixel == 8) || (vinfo.bits_per_pixel == 4))
    {
       screencols = (vinfo.bits_per_pixel == 8) ? 256 : 16;//颜色表大小
       int loopc;
       startcmap = new fb_cmap;
       startcmap->start = 0;//第一个颜色的入口
       startcmap->len = screencols;//颜色的数目
       //分配颜色表的内存
       startcmap->red = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);
       startcmap->green = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);
       startcmap->blue = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);
       startcmap->transp = (unsigned short int*)malloc(sizeof(unsigned short int) * screencols);
       //获取颜色表
       ioctl(fd, FBIOGETCMAP, startcmap);//通过FBIOGETMAP获取颜色表
       for (loopc = 0; loopc < screencols; loopc++)
       {
         screenclut[loopc] = qRgb(startcmap->red[loopc] >> 8,startcmap->green[loopc] >> 8, startcmap->blue[loopc] >> 8);
       }
    }
    else
    {
       screencols = 0;
    }
    
    对于一个 256 色(BPP=8)的 800*600 分辨率的图像而言,若红、绿、蓝分别用一个字节描述,则需要 800*600*3=1440000Byte 的空间,而若使用颜色表,则只需要 800*600*1+256*3= 480768Byte 的空间。(什么意思???)

    文件操作结构体
    作 为 一 种 字 符 设 备 , 帧 缓 冲设 备 的 文件 操 作 结 构 体 定 义 于/linux/drivers/video/fbmem.c 文件中,代码如下所示。

    static const struct file_operations fb_fops = {
    	.owner =	THIS_MODULE,
    	.read =		fb_read,//读函数
    	.write =	fb_write,//写函数
    	.ioctl =	fb_ioctl,//I/O 控制函数
    #ifdef CONFIG_COMPAT
    	.compat_ioctl = fb_compat_ioctl,
    #endif
    	.mmap =		fb_mmap,//内存映射函数
    	.open =		fb_open,//打开函数
    	.release =	fb_release,//释放函数
    #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
    	.get_unmapped_area = get_fb_unmapped_area,
    #endif
    #ifdef CONFIG_FB_DEFERRED_IO
    	.fsync =	fb_deferred_io_fsync,
    #endif
    };
    帧缓冲设备驱动的文件操作接口函数已经在 fbmem.c 中被统一实现,一般不需要由驱动工程师再编写。
    注册与注销帧缓冲设备
    Linux 内核提供了 register_framebuffer()和 unregister_framebuffer()函数分别注册和注销帧缓冲设备,这两个函数都接受 FBI 指针为参数,原型为:

    int register_framebuffer(struct fb_info *fb_info);
    int unregister_framebuffer(struct fb_info *fb_info);
    
    对于 register_framebuffer()函数而言,如果注册的帧缓冲设备数超过了 FB_MAX(目前定义为 32),则函数返回-ENXIO,注册成功则返回 0。
    Linux 帧缓冲设备驱动结构

    下图所示为 Linux 帧缓冲设备驱动的主要结构,帧缓冲设备提供给用户空间的ile_operations 结构体由 fbmem.c 中的 file_operations 提供,而特定帧缓冲设备 fb_info结构体的注册、注销以及其中成员的维护,尤其是 fb_ops 中成员函数的实现则由对应的 xxxfb.c 文件实现,fb_ops 中的成员函数最终会操作 LCD 控制器硬件寄存器。

    帧缓冲设备驱动的模块加载与卸载函数

    在帧缓冲设备驱动的模块加载函数中,应该完成如下 4 个工作。
    1.申请 FBI 结构体的内存空间,初始化 FBI 结构体中固定和可变的屏幕参数,即填充 FBI 中 fb_var_screeninfo var 和 struct fb_fix_screeninfo fix 成员。
    2.根据具体 LCD 屏幕的特点,完成 LCD 控制器硬件的初始化。
    3.申请帧缓冲设备的显示缓冲区空间。
    4.注册帧缓冲设备。

    在帧缓冲设备驱动的模块卸载函数中,应该完成相反的工作,包括释放 FBI 结构体内存、关闭 LCD、释放显示缓冲区以及注销帧缓冲设备。
    由于 LCD 控制器经常被集成在 SoC 上作为一个独立的硬件模块而存在(成为platform_device),因此,LCD 驱动中也经常包含平台驱动,这样,在帧缓冲设备驱动的模块加载函数中完成的工作只是注册平台驱动,而初始化 FBI 结构体中的固定和可变参数、LCD 控制器硬件的初始化、申请帧缓冲设备的显示缓冲区空间和注册帧缓冲设备的工作则移交到平台驱动的探测函数(probe)中完成。

    同样地,在使用平台驱动的情况下,释放 FBI 结构体内存、关闭 LCD、释放显示缓冲区以及注销帧缓冲设备的工作也移交到平台驱动的移除函数中完成。

    下面代码所示为帧缓冲设备驱动的模块加载和卸载以及平台驱动的探测和移除函数中的模板:

    /* 平台驱动结构体 */
    static struct platform_driver xxxfb_driver =
    {
       .probe = xxxfb_probe,//平台驱动探测函数
       .remove = xxxfb_remove,//平台驱动移除函数
       .suspend = xxxfb_suspend, 
       .resume = xxxfb_resume, 
       .driver = {
           .name = "xxx-lcd", //驱动名
           .owner = THIS_MODULE,
       }
    };
    /* 平台驱动探测函数 */
    static int _ _init xxxfb_probe(...)
    {
       struct fb_info *info;
       /*分配 fb_info 结构体*/
       info = framebuffer_alloc(...);
       info->screen_base = framebuffer_virtual_memory;
       info->var = xxxfb_var; //可变参数
       info->fix = xxxfb_fix; //固定参数
       /*分配显示缓冲区*/
       alloc_dis_buffer(...);
       /*初始化 LCD 控制器*/
       lcd_init(...);
       /*检查可变参数*/
       xxxfb_check_var(&info->var, info);
       /*注册 fb_info*/
       if (register_framebuffer(info) < 0)
         return - EINVAL;
       return 0;
    }
    /* 平台驱动移除函数 */
    static void _ _exit xxxfb_remove(...)
    {
        struct fb_info *info = dev_get_drv_data(dev);
        if (info)
        {
          unregister_framebuffer(info); //注销 fb_info
          dealloc_dis_buffer(...); //释放显示缓冲区
          framebuffer_release(info); //注销 fb_info
        }
    
        return 0;
    }
    /* 帧缓冲设备驱动模块加载与卸载函数 */
    int __devinit xxxfb_init(void)
    {
       return platform_driver_register(&xxxfb_driver); //注册平台设备
    }
    static void __exit xxxfb_cleanup(void)
    {
       platform_driver_unregister(&xxxfb_driver); //注销平台设备
    }
    module_init(xxxfb_init);
    module_exit(xxxfb_cleanup);
    


    帧缓冲设备显示缓冲区的申请与释放
    在嵌入式系统中,一种常见的方式是直接在 RAM 空间中分配一段显示缓冲区,典型结构如下图所示。


    在分配显示缓冲区时一定要考虑 cache 的一致性问题,因为系统往往会通过DMA 方式搬移显示数据。合适的方式是使用 dma_alloc_writecombine()函数分配一 段writecombining区 域 , 对 应 的writecombining区 域 由dma_free_writecombine()函数释放,如代码清单如下所示。

    static int __init xxxfb_map_video_memory(struct xxxfb_info *fbi)
    {
      fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);
      fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,&fbi->map_dma,GFP_KERNEL); //分配内存
      fbi->map_size = fbi->fb->fix.smem_len; //显示缓冲区大小
      if (fbi->map_cpu)
      {
        memset(fbi->map_cpu, 0xf0, fbi->map_size);
        fbi->screen_dma = fbi->map_dma;
        fbi->fb->screen_base = fbi->map_cpu;
        fbi->fb->fix.smem_start = fbi->screen_dma; // 赋 值 fix 的 smem_start
      }
    
      return fbi->map_cpu ? 0 : - ENOMEM;
    }
    
    static inline void xxxfb_unmap_video_memory(struct s3c2410fb_info *fbi)
    {
      //释放显示缓冲区
      dma_free_writecombine(fbi->dev,fbi->map_size,fbi->map_cpu,fbi->map_dma);
    }
    


    帧缓冲设备的参数设置

    定时参数
    FBI 结 构 体 可 变 参 数 var 中 的 left_margin 、 right_margin 、 upper_margin 、lower_margin、hsync_len 和 vsync_len 直接查 LCD 的数据手册就可以得到, 下所示为某 LCD 数据手册中直接抓图获得的定时信息。由下图可知对该 LCD 而言,var 中各参数的较合适值分别为:left_margin = 104,right_margin =8,upper_margin = 2,lower_margin = 2,hsync_len = 2,vsync_len = 2。
    像素时钟
    FBI 可变参数 var 中的 pixclock 意味着像素时钟,例如,如果为 28.37516 MHz,那么画 1 个像素需要 35242 ps(皮秒):

    1/(28.37516E6 Hz) = 35.242E-9 s
    如果屏幕的分辨率是 640×480,显示一行需要的时间是:
    640*35.242E-9 s = 22.555E-6 s
    每条扫描线是 640,但是水平回扫和水平同步也需要时间,假设水平回扫和同步需要 272 个像素时钟,因此,画一条扫描线完整的时间是:
    (640+272)*35.242E-9 s = 32.141E-6 s
    可以计算出水平扫描率大约是 31kHz:
    1/(32.141E-6 s) = 31.113E3 Hz
    完整的屏幕有 480 线,但是垂直回扫和垂直同步也需要时间,假设垂直回扫和垂直同步需要 49 个象素时钟,因此,画一个完整的屏幕的时间是:
    (480+49)*32.141E-6 s = 17.002E-3 s
    可以计算出垂直扫描率大约是 59kHz:
    1/(17.002E-3 s) = 58.815 Hz
    这意味着屏幕数据每秒钟大约刷新 59 次。


    颜色位域
    FBI 可变参数 var 中的 red、green 和 blue 位域的设置直接由显示缓冲区与显示点的对应关系决定,例如,对于 RGB565 模式,查表表可知,red 占据 5 位,偏移为 11 位;green 占据 6 位,偏移为 5 位;blue 占据 5 位,偏移为 0 位,即:

    fbinfo->var.red.offset = 11;
    fbinfo->var.green.offset = 5;
    fbinfo->var.blue.offset = 0;
    fbinfo->var.transp.offset = 0;
    fbinfo->var.red.length = 5;
    fbinfo->var.green.length = 6;
    fbinfo->var.blue.length = 5;
    
    固定参数
    FBI 固定参数 fix 中的 smem_start 指示帧缓冲设备显示缓冲区的首地址,smem_len为帧缓冲设备显示缓冲区的大小,计算公式为:
    smem_len = max_xres * max_yres * max_bpp
    即:
    帧缓冲设备显示缓冲区的大小 = 最大的 x 解析度 * 最大的 y 解析度 * 最大的 BPP

    帧缓冲设备驱动的 fb_ops 成员函数

    FBI 中的 fp_ops 是使得帧缓冲设备工作所需函数的集合,它们最终与 LCD 控制器硬件打交道。

    fb_check_var()用于调整可变参数,并修正为硬件所支持的值;fb_set_par()则根据屏幕参数设置具体读写 LCD 控制器的寄存器以使得 LCD 控制器进入相应的工作状态。
    对于 fb_ops 中的 fb_fillrect()、fb_copyarea()和 fb_imageblit()成员函数,通常直接使用对应的通用的 cfb_fillrect()、cfb_copyarea()和 cfb_imageblit()函数即可。
    cfb_fillrect()函 数 定 义 在 drivers/video/cfbfillrect.c 文 件 中 , cfb_copyarea() 定 义 在drivers/video/cfbcopyarea.c 文件中,cfb_imageblit()定义在 drivers/video/cfbimgblt.c 文件中。

    fb_ops 中 的 fb_setcolreg() 成 员 函 数 实 现 伪 颜 色 表 ( 针 对FB_VISUAL_TRUECOLOR、FB_ VISUAL_DIRECTCOLOR 模式)和颜色表的填充,
    其模板如下所示。

    static int xxxfb_setcolreg(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info)
    {
      struct xxxfb_info *fbi = info->par;
      unsigned int val;
      
      switch (fbi->fb->fix.visual)8
      {
        case FB_VISUAL_TRUECOLOR:
           /* 真彩色,设置伪颜色表 */
          if (regno < 16)
         {
            u32 *pal = fbi->fb->pseudo_palette;
            val = chan_to_field(red, &fbi->fb->var.red); 
            val |= chan_to_field(green, &fbi->fb->var.green);
            val |= chan_to_field(blue, &fbi->fb->var.blue); 
            pal[regno] = val;
         }
         break;
        case FB_VISUAL_PSEUDOCOLOR:
          if (regno < 256)
          {
            /* RGB565 模式 */
            val = ((red >> 0) &0xf800);
            val |= ((green >> 5) &0x07e0);
            val |= ((blue >> 11) &0x001f);
            writel(val, XXX_TFTPAL(regno));
            schedule_palette_update(fbi, regno, val);
         }
         break;
    ...
       }
       return 0;
    }
    
    问题:这段代码的作用是什么????有什么意义??

    LCD 设备驱动的读写、mmap 和 ioctl 函数

    虽然帧缓冲设备的 file_operations 中的成员函数,即文件操作函数已经由内核在fbmem.c 文件中实现,一般不再需要驱动工程师修改,但分析这些函数对于巩固字符设备驱动的知识以及加深对帧缓冲设备驱动的理解是大有裨益的。

    如下所示为 LCD 设备驱动的文件操作读写函数的源代码,

    fb_read

    static ssize_t
    fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    {
    	unsigned long p = *ppos;
    	struct inode *inode = file->f_path.dentry->d_inode;
    	int fbidx = iminor(inode);
    	struct fb_info *info = registered_fb[fbidx];//获得 FBI
    	u32 *buffer, *dst;
    	u32 __iomem *src;
    	int c, i, cnt = 0, err = 0;
    	unsigned long total_size;
    
    	if (!info || ! info->screen_base)
    		return -ENODEV;
    
    	if (info->state != FBINFO_STATE_RUNNING)
    		return -EPERM;
    
    	if (info->fbops->fb_read)//如果 fb_ops 中定义了特定的读函数
    		return info->fbops->fb_read(info, buf, count, ppos);
    	
    	total_size = info->screen_size;/*获得显示缓冲区总的大小*/
    
    	if (total_size == 0)
    		total_size = info->fix.smem_len;
    
    	if (p >= total_size)
    		return 0;
    
    	if (count >= total_size)/*获得有效的读长度*/
    		count = total_size;
    
    	if (count + p > total_size)
    		count = total_size - p;
    
    	buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    			 GFP_KERNEL);/*分配用于临时存放显示缓冲区数据的 buffer*/
    	if (!buffer)
    		return -ENOMEM;
    
    	src = (u32 __iomem *) (info->screen_base + p);//获得源地址
    
    	if (info->fbops->fb_sync)
    		info->fbops->fb_sync(info);
    
    	while (count) {/*读取显示缓冲区中的数据并复制到分配的 buffer*/
    		c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    		dst = buffer;
    		for (i = c >> 2; i--; )
    			*dst++ = fb_readl(src++);
    		if (c & 3) {
    			u8 *dst8 = (u8 *) dst;
    			u8 __iomem *src8 = (u8 __iomem *) src;
    
    			for (i = c & 3; i--;)
    				*dst8++ = fb_readb(src8++);
    
    			src = (u32 __iomem *) src8;
    		}
    
    		if (copy_to_user(buf, buffer, c)) {//复制到用户空间
    			err = -EFAULT;
    			break;
    		}
    		*ppos += c;
    		buf += c;
    		cnt += c;
    		count -= c;
    	}
    
    	kfree(buffer);
    
    	return (err) ? err : cnt;
    }
    
    fb_write

    static ssize_t
    fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    {
    	unsigned long p = *ppos;
    	struct inode *inode = file->f_path.dentry->d_inode;
    	int fbidx = iminor(inode);
    	struct fb_info *info = registered_fb[fbidx];//获取到fb_info
    	u32 *buffer, *src;
    	u32 __iomem *dst;
    	int c, i, cnt = 0, err = 0;
    	unsigned long total_size;
    
    	if (!info || !info->screen_base)
    		return -ENODEV;
    
    	if (info->state != FBINFO_STATE_RUNNING)
    		return -EPERM;
    
    	if (info->fbops->fb_write)//如果 fb_ops 中定义了特定的写函数
    		return info->fbops->fb_write(info, buf, count, ppos);
    	
    	total_size = info->screen_size;/*获得显示缓冲区总的大小*/
    
    	if (total_size == 0)
    		total_size = info->fix.smem_len;
    
    	if (p > total_size)
    		return -EFBIG;
    
    	if (count > total_size) {/*获得有效的写长度*/
    		err = -EFBIG;
    		count = total_size;
    	}
    
    	if (count + p > total_size) {
    		if (!err)
    			err = -ENOSPC;
    
    		count = total_size - p;
    	}
    
    	buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    			 GFP_KERNEL);/*分配用于存放用户空间传过来的显示缓冲区数据的 buffer*/
    	if (!buffer)
    		return -ENOMEM;
    
    	dst = (u32 __iomem *) (info->screen_base + p);//要写的显示缓冲区基地址
    
    	if (info->fbops->fb_sync)
    		info->fbops->fb_sync(info);
    
    	while (count) {
    		c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    		src = buffer;
    
    		if (copy_from_user(src, buf, c)) {/*读取用户空间数据并复制到显示缓冲区*/
    			err = -EFAULT;
    			break;
    		}
    
    		for (i = c >> 2; i--; )
    			fb_writel(*src++, dst++);
    
    		if (c & 3) {
    			u8 *src8 = (u8 *) src;
    			u8 __iomem *dst8 = (u8 __iomem *) dst;
    
    			for (i = c & 3; i--; )
    				fb_writeb(*src8++, dst8++);
    
    			dst = (u32 __iomem *) dst8;
    		}
    
    		*ppos += c;
    		buf += c;
    		cnt += c;
    		count -= c;
    	}
    
    	kfree(buffer);
    
    	return (cnt) ? cnt : err;
    }
    file_operations 中的 mmap()函数非常关键,它将显示缓冲区映射到用户空间,从而使得用户空间可以直接操作显示缓冲区而省去一次用户空间到内核空间的内存复制过程,提高效率,其源代码如下所示。
    static int 
    fb_mmap(struct file *file, struct vm_area_struct * vma)
    {
    	int fbidx = iminor(file->f_path.dentry->d_inode);
    	struct fb_info *info = registered_fb[fbidx];
    	struct fb_ops *fb = info->fbops;
    	unsigned long off;
    #if !defined(__sparc__) || defined(__sparc_v9__)
    	unsigned long start;
    	u32 len;
    #endif
    
    	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
    		return -EINVAL;
    	off = vma->vm_pgoff << PAGE_SHIFT;
    	if (!fb)
    		return -ENODEV;
    	if (fb->fb_mmap) {//FBI 中实现了 mmap,则调用 FBI 的 mmap
    		int res;
    		lock_kernel();
    		res = fb->fb_mmap(info, vma);
    		unlock_kernel();
    		return res;
    	}
    
    #if defined(__sparc__) && !defined(__sparc_v9__)
    	/* Should never get here, all fb drivers should have their own
    	   mmap routines */
    	return -EINVAL;
    #else
    	/* !sparc32... */
    	lock_kernel();
    
    	/* frame buffer memory *//* 映射帧缓冲设备的显示缓冲区 */
    	start = info->fix.smem_start;//开始地址
    	len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);//长度
    	if (off >= len) {
    		/* /* 内存映射的 I/O */ */
    		off -= len;
    		if (info->var.accel_flags) {
    			unlock_kernel();
    			return -EINVAL;
    		}
    		start = info->fix.mmio_start;
    		len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
    	}
    	unlock_kernel();
    	start &= PAGE_MASK;
    	if ((vma->vm_end - vma->vm_start + off) > len)
    		return -EINVAL;
    	off += start;
    	vma->vm_pgoff = off >> PAGE_SHIFT;
    	/* This is an IO map - tell maydump to skip this VMA */
    	vma->vm_flags |= VM_IO | VM_RESERVED;
    #if defined(__mc68000__)
    #if defined(CONFIG_SUN3)
    	pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE;
    #elif defined(CONFIG_MMU)
    	if (CPU_IS_020_OR_030)
    		pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030;
    	if (CPU_IS_040_OR_060) {
    		pgprot_val(vma->vm_page_prot) &= _CACHEMASK040;
    		/* Use no-cache mode, serialized */
    		pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S;
    	}
    #endif
    #elif defined(__powerpc__)
    	vma->vm_page_prot = phys_mem_access_prot(file, off >> PAGE_SHIFT,
    						 vma->vm_end - vma->vm_start,
    						 vma->vm_page_prot);
    #elif defined(__alpha__)
    	/* Caching is off in the I/O space quadrant by design.  */
    #elif defined(__i386__) || defined(__x86_64__)
    	if (boot_cpu_data.x86 > 3)
    		pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
    #elif defined(__mips__) || defined(__sparc_v9__)
    	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    #elif defined(__hppa__)
    	pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
    #elif defined(__arm__) || defined(__sh__) || defined(__m32r__)
    	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
    #elif defined(__avr32__)
    	vma->vm_page_prot = __pgprot((pgprot_val(vma->vm_page_prot)
    				      & ~_PAGE_CACHABLE)
    				     | (_PAGE_BUFFER | _PAGE_DIRTY));
    #elif defined(__ia64__)
    	if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start))
    		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
    	else
    		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    #else
    #warning What do we have to do here??
    #endif
    	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
    			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
    		return -EAGAIN;
    	return 0;
    #endif /* !sparc32 */
    }
    fb_ioctl() 函 数 最 终 实 现 对 用 户 I/O 控 制 命 令 的 执 行 , 这 些 命 令 包 括FBIOGET_VSCREENINFO(获得可变的屏幕参数)
    、FBIOPUT_VSCREENINFO(设置可变的屏幕参数)、FBIOGET _FSCREENINFO(获得固定的屏幕参数设置,注意,固定的屏幕参数不能由用户设置)、FBIOPUTCMAP(设置颜色表)、FBIOGETCMAP(获得颜色表)等。下面代码所示为帧缓冲设备 ioctl()函数的源代码。
    static int 
    fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
    	 unsigned long arg)
    {
    	int fbidx = iminor(inode);
    	struct fb_info *info = registered_fb[fbidx];
    	struct fb_ops *fb = info->fbops;
    	struct fb_var_screeninfo var;
    	struct fb_fix_screeninfo fix;
    	struct fb_con2fbmap con2fb;
    	struct fb_cmap_user cmap;
    	struct fb_event event;
    	void __user *argp = (void __user *)arg;
    	int i;
    	
    	if (!fb)
    		return -ENODEV;
    	switch (cmd) {
    	case FBIOGET_VSCREENINFO:// 获得可变的屏幕参数
    		return copy_to_user(argp, &info->var,
    				    sizeof(var)) ? -EFAULT : 0;
    	case FBIOPUT_VSCREENINFO://设置可变的屏幕参数
    		if (copy_from_user(&var, argp, sizeof(var)))
    			return -EFAULT;
    		acquire_console_sem();
    		info->flags |= FBINFO_MISC_USEREVENT;
    		i = fb_set_var(info, &var);
    		info->flags &= ~FBINFO_MISC_USEREVENT;
    		release_console_sem();
    		if (i) return i;
    		if (copy_to_user(argp, &var, sizeof(var)))
    			return -EFAULT;
    		return 0;
    	case FBIOGET_FSCREENINFO://获得固定的屏幕参数设置
    		return copy_to_user(argp, &info->fix,
    				    sizeof(fix)) ? -EFAULT : 0;
    	case FBIOPUTCMAP://设置颜色表
    		if (copy_from_user(&cmap, argp, sizeof(cmap)))
    			return -EFAULT;
    		return (fb_set_user_cmap(&cmap, info));
    	case FBIOGETCMAP://获得颜色表
    		if (copy_from_user(&cmap, argp, sizeof(cmap)))
    			return -EFAULT;
    		return fb_cmap_to_user(&info->cmap, &cmap);
    	case FBIOPAN_DISPLAY:
    		if (copy_from_user(&var, argp, sizeof(var)))
    			return -EFAULT;
    		acquire_console_sem();
    		i = fb_pan_display(info, &var);
    		release_console_sem();
    		if (i)
    			return i;
    		if (copy_to_user(argp, &var, sizeof(var)))
    			return -EFAULT;
    		return 0;
    	case FBIO_CURSOR:
    		return -EINVAL;
    	case FBIOGET_CON2FBMAP:
    		if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
    			return -EFAULT;
    		if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
    		    return -EINVAL;
    		con2fb.framebuffer = -1;
    		event.info = info;
    		event.data = &con2fb;
    		fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
    		return copy_to_user(argp, &con2fb,
    				    sizeof(con2fb)) ? -EFAULT : 0;
    	case FBIOPUT_CON2FBMAP:
    		if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
    			return - EFAULT;
    		if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES)
    		    return -EINVAL;
    		if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
    		    return -EINVAL;
    #ifdef CONFIG_KMOD
    		if (!registered_fb[con2fb.framebuffer])
    		    try_to_load(con2fb.framebuffer);
    #endif /* CONFIG_KMOD */
    		if (!registered_fb[con2fb.framebuffer])
    		    return -EINVAL;
    		event.info = info;
    		event.data = &con2fb;
    		return fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP,
    					      &event);
    	case FBIOBLANK:
    		acquire_console_sem();
    		info->flags |= FBINFO_MISC_USEREVENT;
    		i = fb_blank(info, arg);
    		info->flags &= ~FBINFO_MISC_USEREVENT;
    		release_console_sem();
    		return i;
    	default:
    		if (fb->fb_ioctl == NULL)
    			return -EINVAL;
    		return fb->fb_ioctl(info, cmd, arg);
    	}
    }



    展开全文
  • 在多媒体应用的推动下,彩色LCD越来越多地应用到了嵌入式系统中,掌上电脑(PDA)、手机等多采用TFT显示器件,支持彩色图形界面,能显示图片并进行视频媒体播放。帧缓冲(Framebuffer)是Linux为显示设备提供的一个...
  • 关键词:嵌入式系统 驱动程序 快速捕捉 摄像头随着嵌入式处理器的普及和硬件成本的不断降低,具有拍照和摄像功能的手机逐步走进了人们的生活。但由于嵌入式处理器的速度有限,在处理图形和多媒体数据方面显得...
  • 一 对驱动程序的几种操作方法 现在安卓手机都有GPU,Linux也跟着进步,再加上人工智能,需要GPU做硬件加速。目前Linux的显示系统基本都是基于drm驱动,这点在linux 4.0 5.0以后都非常常见。 我们知道,Linux对驱动的...

    linux系统GPU编程之DRM编程

    背景:

    framebuffer大家都知道哈,打开/dev/fb0设备节点,然后开辟一段内存(即显存),再mmap,在吧图形数据填充到这段显存里面。怎么填,比如QT,有一套图形系统,他帮你填,或者ubuntu等系统。

    现在安卓手机都有GPU,Linux也跟着进步,再加上人工智能,需要GPU做硬件加速。目前Linux的显示系统基本都是基于drm驱动,这点在linux 4.0 5.0以后都非常常见。

    DRM驱动原理,看这篇就够了:

    DRM (Direct Rendering Manager)

    一 对驱动程序的几种操作方法

    我们知道,Linux对驱动的操作有以下几种:
    1 一种是/dev下的设备节点文件,
    2 proc文件系统
    3 sys文件系统。

    这里讲第一种,dev下的设备节点。Linux显卡下的设备节点是:
    /dev/dri/card0

    二 modeset工具使用

    2.1 对于DRM显卡的测试,目前比较流行的是modeset,这个源码在我的git。

    https://gitee.com/caledonian_study/modeset.git

    2.2 modeset其实调用了libdrm这个库,流程是这样的。
    在这里插入图片描述

    2.3 为了适配ST的交叉工具链,我对makefile做了修改,另外我对modeset.c做了修改,为的是显示4个矩形框。
    docs是makefile进行编译的,
    modeset-qmake是可以用qtcreak打开进行编译的。
    modeset-cmake目前还没有搞定,留着以后有时间了再搞吧。
    在这里插入图片描述

    三 编译过程

    https://gitee.com/caledonian_study/modeset/blob/master/README.md

    四 最终效果

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  •  第10章“Android的电话部分”,介绍Android系统在电话方面的功能,Android是智能手机系统,电话是其核心部分。  第11章“Android的连接部分”,主要包括WiFi、蓝牙及定位系统等,这些Android的连接部分也具有从...
  • Windows子系统(GUI)

    2020-12-15 11:42:17
    Windows子系统 1 Windows子系统结构 Windows子系统结构,如图: Windows子系统有用户模式和内核模式组件。列出这些组件的职责: ...a....图形设备驱动程序。 c.Windows环境子系统进程(csrss.e..

     Windows子系统

    1  Windows子系统结构

    Windows子系统结构,如图:

    Windows子系统有用户模式和内核模式组件。列出这些组件的职责:

    a. 内核模块win32k.sys。是Windows内核的扩展。包含两大功能组成部分:

    1. 窗口管理器(window manager): 负责控制窗口显示、管理屏幕输出、手机来自键盘鼠标和其他设备的输入,以及将用户信息传递给应用程序。
    2. GDI:图形输出设备的函数库。

    b.图形设备驱动程序。

    c.Windows环境子系统进程(csrss.exe),它包含以下支持:

    1. 控制台窗口
    2. 创建和删除进程和线程
    3. 支持16位虚拟DOS 机(VDM,Virtual DOS Machine)进程
    4. 其他一些函数,如GetTempFile,DefinedDosDevice、ExitWindowsEx等。

    d.子系统DLL,如user32.dll、advapi32.dll、gdi32.dll、kernel32.dll。

    Win32k.sys 注册了一个SDT(系统服务描述符表),将所提供的功能直接以系统服务的形式暴露给用户模式程序。

    2  Windows子系统初始化与GUI线程

    Windows的会话管理器(smss.exe) 是系统中第一个被创建的用户模式进程,是在Windows执行体和内核初始化完成以后被启动的。Smss 进程的任务之一是,启动Windows环境子系统进程csrss.exe 和Windows登录进程winlogon.exe 。smss 还通过系统服务

    NtSetSystemInformation ,指示内核加载win32.sys 模块。它想NtSetSystemInformation 的

    SystemInformationClass 参数传递的值位SystemExtendServiceTableInformation。NtSetSystemInformation 调用MmLoadSystemImage 加载指定的

    “\SystemRoot\System32\win32k.sys”文件。

    Win32k.sys 主要做了以下的初始化工作:

    1. 调用KeAddSystemServiceTable ,加入一个新的SDT(服务描述表)。
    2. 调用PsEstablishWin32Callouts ,向内核注册一组出掉函数,以便内核在适当时候调用这些出调函数。
    3. 调用MmPageEntireDriver,使得win32k.sys 的所有代码页面和数据页面可被换出内存(pageable)。
    4. 调用InitializeGre ,初始化windows 子系统的图形引擎。
    5. 调用Win32UserInitialize ,初始化Windows 子系统的窗口管理器。

    Win32k.sys 在入口例程中,调用KeAddSystemServiceTable ,将win32k.sys 的系统服务表加入到SDT数组KeServiceDescriptorTableShadow。

    Win32k.sys 提供的系统服务的名称分类(windows server 2003)

    名称

    功能说明

    NtUser<xxx>

    窗口管理服务,如NtUserFindWindowsEx

    NtGdi<xxx>

    GDI服务,如NtGdiDcObject

    NtGdiEng<xxx>

    图形引擎服务,如NtGdiEngBitBlt

    NtGdiDd<xxx>

    DirectDraw服务,如NtGdiDdGetDxHandle

    NtGdiDvp<xxx>

    与视频端口打交道的DirectDraw服务,如NtGdiDvpCreateVideoPort

    NtGdiD3d<xxx>

    Direct3D服务,如NtGdiD3dCreateSurfaceEx

    Win32k.sys 在初始化阶段通过PsEstablishWin32Callouts 向内核注册一组出调函数,数据结构WIN32_CALLOUTS_FPNS 定义如下:

    typedef struct _WIN32_CALLOUTS_FPNS {

        PKWIN32_PROCESS_CALLOUT ProcessCallout; //创建或删除进程时调用

        PKWIN32_THREAD_CALLOUT ThreadCallout;;//转成GUI线程或删除时被调用

        PKWIN32_GLOBALATOMTABLE_CALLOUT GlobalAtomTableCallout;;//内核的全局原子对象管理器调用该函数来获得当前线程的全局原子对象表的地址

        PKWIN32_POWEREVENT_CALLOUT PowerEventCallout;//子系统通过它来接收电源事件

        PKWIN32_POWERSTATE_CALLOUT PowerStateCallout;//子系统通过它来接收电源状态

        PKWIN32_JOB_CALLOUT JobCallout;//win32k.sys 通过它参与作业管理

        PVOID BatchFlushRoutine;//刷新GDI批任务处理

        PKWIN32_OBJECT_CALLOUT DesktopOpenProcedure;//打开桌面

        PKWIN32_OBJECT_CALLOUT DesktopOkToCloseProcedure;//是否可以关闭桌面

        PKWIN32_OBJECT_CALLOUT DesktopCloseProcedure;//解除桌面映射

        PKWIN32_OBJECT_CALLOUT DesktopDeleteProcedure;//释放桌面

        PKWIN32_OBJECT_CALLOUT WindowStationOkToCloseProcedure;//是否可以关闭窗口站

        PKWIN32_OBJECT_CALLOUT WindowStationCloseProcedure;//从全局列表中去除窗口站

        PKWIN32_OBJECT_CALLOUT WindowStationDeleteProcedure;//删除窗口站,释放资源

        PKWIN32_OBJECT_CALLOUT WindowStationParseProcedure;//解析一个窗口站路径

        PKWIN32_OBJECT_CALLOUT WindowStationOpenProcedure;//打开一个窗口站路径

    } WIN32_CALLOUTS_FPNS, *PKWIN32_CALLOUTS_FPNS;

    Win32k.sys 每个会话被加载一次,驱动程序对象为“\Driver\win32k”,其他模块通过ObReferenceObjectByname 获得此驱动程序对象。

    当一个非GUI线程调用的任何一个子系统系统服务(服务号为0x1000~-x1fff)时,线程的ServiceTable 指向KeServiceDescriptorTable,而KeServiceDescriptorTable[1]不包含任务系统服务,此调用导致PsConvertToGuiThread 被调用,从而转为GUI线程。

    PsConvertToGuiThread 将一个线程转换为GUI线程,主要工作包括:

    1. 将线程的内核栈转换为一个大内核栈。先调用MmCreateKernelStack 创建一个新的内核栈,然后调用KeSwitchKernelStack 替换内核栈。
    2. 调用win32k.sys 注册的出调函数PspW32ProcessCallout.
    3. 将线程的ServiceTable 切换到KeServiceDescriptorTableShadow,从而使该线程可以使用win32k.sys 提供的系统服务。
    4. 调用win32k.sys 的PspW32ThreadCallout。

    3  窗口管理

    当用户登录到Windows系统时,winlogon 进程会创建一个交互式窗口站(interactive window station) 和三个桌面。应用程序所创建的窗口,一定属于某一个桌面。Windows中窗口站和桌面的关系,如图:

     

    窗口站和桌面与进程和线程之间的关系:

    1. 窗口站可通过Windows子系统的系统服务NtUserCreateWindowStation 来创建,当一个进程调用该系统服务来创建一个窗口站时,此窗口站与该调度进程相关联,并且属于该进程所在的会话。
    2. 桌面可通过Windows子系统的系统服务NtUserCreateDesktop来创建,当一个进程调用该系统服务来创建一个桌面时,所创建的桌面属于该进程所属的窗口站,并且与调用线程相关联。
    3. 当一个进程第一次调用Windows子系统的系统服务(非窗口站和桌面的系统服务)时,它自动与一个窗口站和桌面建立连接。连接哪个窗口站,则依据以下规则:
    • a. 该进程已调用过SetProcessWindowStation ,指定了窗口站。
    • b. 从父进程继承一个窗口站。
    • c. 如果该进程没有调用过SetProcessWindowStation,也不能从父进程获得窗口站,那么Windows子系统试图按以下顺序来选择窗口站:
    1. 进程创建函数CreateProcess 的STARTUPINFO参数指定的窗口站名称。
    2. 若进程运行在交互式用户的登录会话中,则连接到交互式窗口站。
    3. 若运行在非交互式的登录会话中,则根据登录会话的标识信息,构造一个窗口站名称,并试图打开该窗口站。如果打开操作不成功,则创建此名称的窗口站已经一个默认桌面。
    4. 当进程连接到一个窗口站以后,Windows子系统给调用线程分配一个桌面。分配桌面规则如下:
    • a . 该线程已调用过SetThreadDesktop指定了桌面。
    • b.  从父进程继承一个桌面。
    • c.  如果该线程没有调用SetThreadDesktop,也不能从父进程获得桌面,那么,Windows子系统按以下顺序选择桌面:
    1. 进程创建函数CreateProcess 的STARTUPINFO参数指定的桌面名称。
    2. 否则,连接到该进程所连接的窗口站的默认桌面。

    窗口站被注册到对象管理器名字空间的\\Windows\WindowStations目录下或“\Sessions\<X>\Windows\WindowStations”目录下,X表示会话ID。如Windows Server 2003 sp1 ,”Windows\WindowStations”目录包含以下5 个窗口站:

    WinSta0

    Service-0x0-3e4$ //NETWORK SERVICE 账户进程所对应的窗口站

    Service-0x0-3e5$ //LOCAL SERVICE 账户

    Service-0x0-3e7$  //SYSTEM 账户下的

    SAWubSta       //由任务调度器(Task Scheduler,一个svchost)创建的

    WinSta0是登录用户的默认窗口站,Service-0x0-<xxx>$是非交互式窗口站。

    任何一个进程都属于一个会话。在Windows中,会话是按编号来区分的。在系统控制台登录的用户会话为Session 0,远程桌面或终端服务登录到系统中的会话可以是Session 1、Session 2等。每个会话包含一个或多个窗口站,每个窗口站包含一个或多个桌面。

    EnumWindowStations 列举当前会话的窗口站

    EnumDesktops 列举指定窗口站的桌面

    EnumDesktopWindows 列举指定桌面的所有顶级窗口

    窗口是Windows子系统中的对象,由win32k.sys 来管理的内核对象,以句柄(HWND)的方式暴露给应用程序代码。每个会话实力都维护一个句柄表,该句柄表由两部分组成:一是该对象在句柄表中的索引,二是确保句柄唯一性的部分。

    Windows子系统支持5种窗口:

    1. 可重叠窗口(overlapped window),是桌面上的顶级窗口,通常有标题、边框和客户区域。
    2. 弹出式窗口(pop-up window),是一种特殊的可重叠窗口,往往用做对话框、消息框或者临时跳出应用程序主窗口的窗口。
    3. 子窗口(child window),他们有父窗口,并且受限于父窗口的客户区域。
    4. 层次窗口(layered window),用于实现不规则形状的窗口,或者窗口的形状有动画效果,或者窗口有半透明效果。
    5. 消息窗口(message-only window),仅仅用于发送和接收消息,不可见,不参与桌面上窗口的层次交叠,也不会被列举到。

    Windows子系统为桌面中的所有窗口定义了一个z- 序(z-order),即深度顺序。在z-序中,最上面的窗口用户可以看到。

    Windows子系统内置了7种窗口类:按钮(Button)、组合框(ComboBox)、编辑框(Edit)、列表框(ListBox)、多文档界面中的子窗口(MDIClient)、滚动条(ScrollBar)和静态文本(Static)。Windows 内部使用的窗口类: 组合框内涵列表框(ComboLBox)、DDE管理库事件(DDEMLEvent)、消息窗口(Message)、菜单(#32768)、桌面窗口(#32769)、对话框(#32770)、任务切换窗口(#32771)和图标(#32772)

    CreateWindow或CreateWindowEx 创建一个窗口,对应的win32k.sys系统服务为NtUserCreateWindowEx.;

    EnumThreadWindows 列举一个线程创建的窗口;

    EnumChildWindows 列举某一指定窗口的所有子窗口;

    FindWindow 查找指定类名称或窗口名称的窗口;

    FindWindowEx 查找子窗口对象

    SetProp、GetProp 设置或获取一个命名属性;

    EnumProps 列举一个窗口的命名属性。

    Windows 为应用程序提供了消息驱动的编程模型,负责处理用户的线程的主题逻辑通常是一个消息循环,代码如下:

    for(;;)

    {

           if(bRet = GetMessage(&msg,NULL,0,0))//从消息队列中获得一个消息

           {

                  if(bRet =-1) goto ErrorExit; //严重错误处理

                  TranslateMessage(&msg) ; //处理按键消息,将虚拟键消息转译为字符消息

                  DispatchMessage(&); //将消息分发到负责处理该消息的窗口

           }else

           {

                  Break;  //线程接收到WM_QUIT 消息,退出循环

           }

    }

    消息结构定义如下:

    typedef struct tagMSG {

    HWND hwnd;  //接收该消息的窗口的句柄

    UINT message; //消息标识符,WM_USER(0x400)以下的消息为系统保留

    WPARAM wParam; //该消息的参数,其含义取决于message的值

    LPARAM lParam;  //该消息的参数,其含义取决于message的值

    DWORD time; //该消息被寄到队列中的时间

    POINT pt;    //记录了当该消息被寄到队列中时的光标位置(按屏幕坐标)

    } MSG,*PMSG;

    Windows子系统为GUI线程维护了一个消息队列。消息循环不断地获取消息并交给消息的目标窗口的窗口过程来处理。

    Windows子系统的消息流,如图:

    在每个会话中,Windows子系统进程(csrss.exe)都会创建一个RIT(Raw Input Thread),该线程负责从设备驱动程序获得原始的输入,然后将消息寄到正确的队列中。鼠标的输入是由桌面线程(Desktop thread)的线程来接收的,然后交给RIT线程分发到应用线程中。键盘事件或其他的HID(Human Input Device)事件则直接由RIT线程从设备驱动中获得。

    RIT或桌面线程获得输入事件的做法:

    1. 向设备发起一个异步操作,在读操作中指定一个APC例程,当设备驱动程序由数据可提供时,I/O管理器在完成时将此APC例程插入到原线程中。
    2. 此APC例程在获得了当前的输入事件后,再次发起一个异步读操作,然后返回。
    3. 当设备驱动程序由输入数据时,会再次出发APC例程。
    4. 以此类推,可通过APC例程不停地接收输入设备的数据。

    在一个GUI线程的消息循环中,当该线程要获取一个消息时,win32k.sys 中的系统服务NtUserGetMessage必须检查两个队列:首先检查系统队列,即输入消息队列;如果该队列中没有满足条件的消息,则在检查应用队列,即寄入消息队列。

    应用程序调用DispatchMessage处理一个消息时,有两种情况是必须要进入内核的:

    1. 这是一个系统定时器消息
    2. 消息的窗口类著名了这是一个内核窗口类型。

    Windows 子系统的消息钩子(hook)机制,SetWindowsHookEx 来安装钩子,UnhookWindowsHookEx 卸载钩子。每个线程可以有多个钩子,按照不同的类型形成一个或多个钩子链(hook chain)。如下表:

    钩子类型

    说明

    WM_CALLWNDPROC

    在系统发送一个消息到目标窗口过程之前调用的钩子函数

    WM_CALLWNDPROCRET

    在一个消息被目标窗口过程处理之后调用的钩子函数

    WH_CBT

    在接收CBT(Computer-Based Training)事件之前调用的钩子函数,这里CBT事件是指系统在处理窗口重要事件、同步消息队列等时引发的各种消息

    WH_DEBUG

    用于调试其他的钩子函数

    WH_FOREGROUNDIDLE

    当应用程序的前台线程变成空闲时调用的钩子函数

    WH_GETMESSAGE

    当应用程序调用GetMessage或PeekMessage从消息队列种获取消息时调用的钩子函数

    WH_JOURNALPLAYBACK

    应用程序使用此钩子函数回放一序列由WM_JOURNALRECORD钩子记录下来的鼠标和键盘消息

    WH_JOURNALRECORD

    当子系统从系统消息队列中移除消息时,调用此钩子函数,因而该钩子函数可以记录下消息序列

    WH_KEYBOARD

    当应用程序调用GetMessage或PeekMessage获取一个键盘消息时调用的钩子函数

    WH_KEYBOARD_LL

    当一个键盘输入事件被插入到线程的输入队列中时调用的钩子函数

    WM_MOUSE

    当应用程序调用GetMessage或PeekMessage获取一个鼠标消息时调用的钩子函数

    WM_MOUSE_LL

    当一个鼠标输入事件被插入到线程的输入队列中时调用的钩子函数

    WH_MSGFILTER

    当对话框、消息框、菜单或滚动条中的键盘或鼠标操作导致产生的消息被处理时调用的钩子函数

    WH_SHELL

    通过此钩子函数可以接收系统的Shell事件的通知

    WM_SYSMSGFILTER

    当对话框、消息框、菜单或滚动条中的键盘或鼠标操作导致产生的消息被处理时调用的钩子函数,是全局钩子

    所有的钩子都是在内核模式下被激发的,钩子函数是用户模式的。Windows子系统通过KeUserModeCallBack 来调用指定的钩子函数。KeUserModeCallback 和KiCallUserMode 函数机制,他们构造一个陷阱帧,利用系统服务返回机制,指定运行ntdll.dll 的KeUserCallbackDispatcher,由它调用指定的用户钩子函数。待钩子函数返回后,KeUserCallbackDispatcher 通过NtCallbackReturn 系统服务返回内核模式,回到KeUserModeCallback。

    Windows中从内核回调到用户模式在返回内核的执行过程,如图:

     

    展开全文
  • 图形程序开发界面《WAY STUDIO 依路舵机机器人动作开发环境》,仿真模型实时位置显示,时间线组织方式,简单易用,动作设计更加灵活 安卓系统终端控制台软件Way Pocket,支持连接蓝牙透传模块(如HC-05/HC-06),...
  • 第18章LCD设备驱动之LCD 硬件原理

    千次阅读 2018-06-20 15:57:21
    本章重点 在多媒体应用的推动下,彩色 LCD( 液晶显示屏(liquid crystal display) )应用到了嵌入式系统中,掌上电脑(PDA)、手机等多采用 TFT(薄膜式晶体管) 显示器件,支持彩色图形界面,能显示图片并进行...
    本章重点

        在多媒体应用的推动下,彩色 LCD(   液晶显示屏(liquid crystal display) )应用到了嵌入式系统中,掌上电脑(PDA)、手机等多采用 TFT(薄膜式晶体管) 显示器件,支持彩色图形界面,能显示图片并进行视频媒体播放。帧缓冲(Framebuffer)是 Linux 为显示设备提供的一个接口,帧缓冲(Framebuffer)允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。

        本章主要讲解帧缓冲设备 Linux 驱动的架构及编程方法。

    1、 LCD 的底层硬件操作原理。

    2、帧缓冲设备的概念及驱动中的重要数据结构和函数。

    3、帧缓冲设备驱动的整体结构,帧缓冲设备的几个重要函数,整体与部分的关系。

    4、 Linux 帧缓冲设备用户空间的访问方法,并对Android 等 GUI简单的介绍。

    5、 S3C6410 LCD 控制器设备驱动的实例。

    18.1 LCD 硬件原理

            利用液晶制成的显示器称为 LCD(液晶显示器),依据驱动方式分为静态驱动简单矩阵驱动以及主动矩阵驱动 3 种。其中,简单矩阵型又可再细分扭转向列型(TN)和超扭转式向列型(STN)两种,主动矩阵型以薄膜式晶体管型(TFT)为主流。表 18.1 列出TN、STN 和 TFT 显示器的区别。

    表 18.1  TN、STN 和 TFT 显示器的区别


          TN 型液晶显示技术是 LCD 中最基本的,其他种类的 LCD 都以 TN 型为基础改进而得。TN型 LCD 显示质量很差,色彩单一,对比度低,反映速度很慢,主要用于简单的数字符与文字的显示,如电子表及电子计算器等。

          STN LCD 的显示原理与 TN 类似,区别在于 TN 型的液晶分子将入射光旋转 90°,而 STN可将入射光旋转 180°~270°。STN 改善了 TN 视角狭小的缺点,并提高了对比度,显示品质较 TN 高。

          STN 搭配彩色滤光片,将单色显示矩阵的任一像素分成 3 个子像素,分别透过彩色滤光片显示红、绿、蓝三原色,再经由三原色按比例调和,显示出逼近全彩模式的色彩。STN 显示的画面色彩对比度仍较小,反应速度也较慢,可作为一般的操作显示接口。

         DSTN 通过双扫描方式来显示,显示效果相对 STN 有较大幅度的提高。DSTN 的反应速度可达到 100ms,但是在电场反复改变电压的过程中,每一像素的恢复过程较慢。因此,当在屏幕画面快速变化时,会产生“拖尾”现象。

        TN 与 STN 型液晶显示器都是使用场电压驱动方式,如果显示尺寸加大,中心部位对电极变化的反应时间就会拉长,显示器的速度跟不上。为了解决这个问题,主动式矩阵驱动被提出,主动式 TFT 型的液晶显示器的结构较为复杂,包括背光管、导光板、偏光板、滤光板、玻璃基板、配向膜、液晶材料和薄膜式晶体管等。

         TFT 型 LCD,晶体管矩阵依显示信号开启或关闭液晶分子的电压,使液晶分子轴转向而成“亮”或“暗”的对比,避免了显示器对电场效应的依靠。TFT LCD 的显示质量较TN/STN 更佳,画面显示对比度可达 150:1 以上,反应速度逼近 30ms 甚至更快,适用于 PDA、笔记本电脑、数码相机、MP4 等。

        一块 LCD 屏显示图像不但需要 LCD 驱动器,还需要有相应的 LCD 控制器。通常 LCD驱动器以 COF/COG 的形式与 LCD 玻璃基板制作在一起,而 LCD 控制器则由外部电路来实现。许多 MCU 内部直接集成了 LCD 控制器,通过 LCD 控制器可以方便地控制 STN 和TFT 屏。

        TFT 屏是目前嵌入式系统应用的主流,图 18.1 所示给出了 TFT 屏的典型时序。时序图中的VCLK、HSYNC 和 VSYNC 分别为像素时钟信号(用于锁存图像数据的像素时钟)、行同步信号帧同步信号VDEN 为数据有效标志信号VD 为图像的数据信号


    18.1 TFT 屏工作时序

    分析:

        帧同步信号VSYNC,每发出一个脉冲,都意味着新的一屏图像数据开始发送。行同步信号HSYNC,每发出一个脉冲都表明新的一行图像资料开始发送。在帧同步以及行同步的头尾都必须留有回扫时间。这样的时序安排起源于 CRT 显示器电子枪偏转所需要的时间,但后来成为实际上的工业标准,因此 TFT 屏也包含了回扫时间。

        图 18.2 给出LCD 控制器中应该设置的 TFT 屏的参数,其中的上边界和下边界即为帧切换的回扫时间,左边界和右边界即为行切换的回扫时间,水平同步和垂直同步分别是行和帧同步本身需要的时间。xres 和 yres 则分别是屏幕的水平和垂直分辨率,常见的嵌入式设备的 LCD 分辨率主要为 320×240、640×480 等。




    展开全文
  • 物联网操作系统由内核、辅助外围模块(文件系统图形用户界面、通信协议栈、各类常见设备的驱动程序等)、集成开发环境等组成,基于此,可衍生出一系列面向行业的特定应用,下图展示了这个概念: 物联网操作系统与...
  •  第3章“Android的Linux内核与驱动程序”,介绍Android内核的特点、Android中使用的专用驱动程序、Android系统可能使用的标准设备驱动。  第4章“Android的底层库和程序”,介绍Android系统的基础程序,以本地程序...
  • 嵌入式操作系统为用户程序提供了丰富的接口,如设备的驱动接口、通信接口、图形接口等,这就大大的方便了应用程序的开发,使得嵌入式产品的可靠性、实用性、用户体验等都大大提升。以下几款系统是常见的嵌入式操作系...
  •  第3章“Android的Linux内核与驱动程序”,介绍Android内核的特点、Android中使用的专用驱动程序、Android系统可能使用的标准设备驱动。  第4章“Android的底层库和程序”,介绍Android系统的基础程序,以本地程序...
  • 消费者对于处理多媒体音频和视频流、以及运行基于图形的高级应用程序所具备的成熟技术,也提出高于以往的要求,这些驱动着智能手机、平板电脑和其他智能移动消费者产品的演进,促使这些设备不仅要内置复杂程度较高且...
  • C#程序开发范例宝典

    2010-12-15 20:05:16
    实例100 使用FileSystemWatcher组件监视系统日志文件是否被更改...... 140 3.5 HelpProvider组件...... 142 实例101 使用HelpProvider组件调用帮助文件...... 142 3.6 Process组件...... 143 实例102 使用...
  • 嵌入式系统开发圣经

    千次下载 热门讨论 2006-06-09 16:47:16
    5-5 Symbian Platform:典型的智能型手机系统设计 5-5-1 功能结构介绍 5-5-2 GTv6基本组件 5-5-3 Framework 5-5-4 通信与网络 5-5-5 应用层 5-5-6 Java Phone 5-5-7 新一代智能型手机 5-5-8 持续创新的...
  • 其内容涵盖了android应用程序开发和android系统移植构建手机系统两大方面。  本书既适合从事android各个层次开发的工程师阅读,也适合通用嵌入式linux系统的学习者使用。 目录: 第1章 android系统概述 1 1.1 基础...
  • 其内容涵盖了android应用程序开发和android系统移植构建手机系统两大方面。  本书既适合从事android各个层次开发的工程师阅读,也适合通用嵌入式linux系统的学习者使用。 目录: 第1章 android系统概述 1 1.1 基础...
  • 其内容涵盖了android应用程序开发和android系统移植构建手机系统两大方面。  本书既适合从事android各个层次开发的工程师阅读,也适合通用嵌入式linux系统的学习者使用。 目录: 第1章 android系统概述 1 1.1 基础...
  • 其内容涵盖了android应用程序开发和android系统移植构建手机系统两大方面。  本书既适合从事android各个层次开发的工程师阅读,也适合通用嵌入式linux系统的学习者使用。 目录: 第1章 android系统概述 1 1.1 基础...
  • 其内容涵盖了android应用程序开发和android系统移植构建手机系统两大方面。  本书既适合从事android各个层次开发的工程师阅读,也适合通用嵌入式linux系统的学习者使用。 目录: 第1章 android系统概述 1 1.1 基础...
  • 其内容涵盖了android应用程序开发和android系统移植构建手机系统两大方面。  本书既适合从事android各个层次开发的工程师阅读,也适合通用嵌入式linux系统的学习者使用。 目录: 第1章 android系统概述 1 1.1 基础...
  • wince 6.0在cortex-a8系统上的移植与开发,系统地讲述windows embedded ce 6.0的体系结构、系统安装、环境设置,在i.mx51开发系统下的软件包的编译和调试,主要功能模块驱动程序的详细描述与开发指导,以及典型的...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 138
精华内容 55
关键字:

手机系统图形驱动程序