1.Linux帧缓冲子系统

  帧缓冲(FrameBuffer)是Linux为显示设备提供的一个接口,用户可以将帧缓冲看成是显示内存的一种映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反映到屏幕上,这种操作是抽象和统一的,用户不必关心显存的位置、换页机制等具体细节,这些都是由FrameBuffer设备驱动来实现,帧缓冲把显示设备描述成一个缓冲区,允许应用程序通过帧缓冲定义好的接口访问这些图形设备,从而不用关心具体的硬件细节。个人感觉,更抽象一点,帧缓冲从本质上是图形设备的硬件抽象,对于我们开发者而言,帧缓冲是一种显示缓存,向显示缓存写入特定格式的数据就意味着向屏幕输出内容,通过不断向帧缓存中写入数据,显示控制器会自动从帧缓冲中取数据并显示出来。FrameBuffer的设备文件一般是dev/fb0、dev/fb1等,最多支持32个设备,FrameBuffer是个字符设备,主设备号是29,对应于/dev/fb%d设备文件,对于我们驱动工程师而言,FrameBuffer设备和其他的文件没有区别,可以通过配置对FrameBuffer设备文件完成对硬件的参数设置,Framebuffer对应的源文件在linux/drivers/video/目录下。总的抽象设备文件为fbcon.c,在这个目录下还有与各种显卡驱动相关的源文件。

  在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用,比如下面的程序就打开 /dev/fb0 设备,并通过 mmap 系统调用进行地址映射,随后用 memset 将屏幕清空(这里假设显示模式是 1024x768-8 位色模式,线性内存模式):

  int fb;

  unsigned char* fb_mem;

  fb = open ("/dev/fb0", O_RDWR);

  fb_mem = mmap (NULL, 1024*768, PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);

  memset (fb_mem, 0, 1024*768); //这个命令应该只有在root可以执行

  Framebuffer驱动实现:

  一般来说,应用程序通过内核对Framebuffer的控制,主要有以下3种方式:

  1,读/写  /dev/fb相当于读/写屏幕缓冲区;

  2,通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图;

  3,I/O控制对于帧缓冲设备,设备文件的ioctl()函数可读取/设置显示设备及屏幕的参数,如分辨率、显示颜色数、屏幕大小等,ioctl()函数由底层的驱动程序完成;

  Linux帧缓冲子系统的层次结构图如下所示,由帧缓冲设备层和控制器驱动组成,帧缓冲设备层在drivers/video/fbmem.c中实现,向上给应用程序提供设备文件结构操作接口,向下提供硬件操作接口,硬件操作接口需要根据具体的LCD控制器硬件来实现,这也是控制器驱动要完成的工作,对比IIC和SPI的软件层次结构,相对较为简单,帧缓冲驱动程序主要依靠四个数据结构,分别是fb_info、fb_var_screeninfo、fb_fix_screeninfo和fb_monospecs,后3个数据结构可以在用户空间访问,数据结构fb_info只能在内核空间访问。

2.LCD工作时序分析:

(1)显示指针从矩形左上角的第一行第一个点开始,一个点一个点的在LCD上显示,在上面的时序图上用时间线表示就为VCLK,我们称之为像素时钟信号; 
(2) 当显示指针一直显示到矩形的右边就结束这一行,那么这一行的动作在上面的时序图中就称之为1 Line; 
(3)接下来显示指针又回到矩形的左边从第二行开始显示,注意,显示指针在从第一行的右边回到第二行的左边是需要一定的时间的,我们称之为行切换; 
(4)如此类推,显示指针就这样一行一行的显示至矩形的右下角才把一副图显示完成。因此,这一行一行的显示在时间线上看,就是时序图上的HSYNC; 
(5)然而,LCD的显示并不是对一副图像快速的显示一下,为了持续和稳定的在LCD上显示,就需要切换到另一幅图上(另一幅图可以和上一副图一样或者不一样,目的只是为了将图像持续的显示在LCD上)。那么这一副一副的图像就称之为帧,在时序图上就表示为1 Frame,因此从时序图上可以看出1 Line只是1 Frame中的一行; 
(6)同样的,在帧与帧切换之间也是需要一定的时间的,我们称之为帧切换,那么LCD整个显示的过程在时间线上看,就可表示为时序图上的VSYNC。

参照下图:VSYNC/VFRAME/STV:垂直同步信号(TFT)/帧同步信号(STN)/SEC TFT信号; HSYNC/VLINE/CPV:水平同步信号(TFT)/行同步脉冲信号(STN)/SEC TFT信号; VCLK/LCD_HCLK:象素时钟信号(TFT/STN)/SEC 参照TFT信号; VD[23:0]:LCD像素数据输出端口(TFT/STN/SEC TFT); VDEN/VM/TP:数据使能信号(TFT)/LCD驱动交流偏置信号(STN)/SEC TFT 信号; LEND/STH:行结束信号(TFT)/SEC TFT信号; LCD_LPCOE:SEC TFT OE信号; LCD_LPCREV:SEC TFT REV信号; LCD_LPCREVB:SEC TFT REVB信号;

上面时序图中上各时钟延时参数的含义如下:(这些参数的值,LCD产生厂商会提供相应的数据手册):

VBPD(vertical back porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数,对应驱动中的upper_margin;
VFBD(vertical front porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数,对应驱动中的lower_margin;
VSPW(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算,对应驱动中的vsync_len;
HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCLK的个数,对应驱动中的left_margin;
HFPD(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK的个数,对应驱动中的right_margin;
HSPW(horizontal sync pulse width):表示水平同步信号的宽度,用VCLK计算,对应驱动中的hsync_len;

注意以下细节:(1)VSYNC信号有效时,表示一帧数据的开始 
(2)VSPW表示VSYNC信号的脉冲宽度为(VSPW+1)个HSYNC信号周期,即(VSPW+1)行,这(VSPW+1)行的数据无效。 
(3)VSYNC信号脉冲之后,还要经过(VBPD+1)个HSYNC信号周期,有效的行数据才出现。所以,在VSYNC信号有效后要经过(VSPW+1+VBPD+1)个无效的行,第一个有效行才出现,对应上边框。 
(4)随后即连续发出(LINEVAL+1)行的有效数据。 
(5)最后是(VFPD+1)个无效的行,它对应下边框,完整的一帧结束,紧接着就是下一帧数据了。

下面我们深入到一行中像素数据的传输过程,它与上面行数据的传输相似: 
(1)HSYNC信号有效时,表示一行数据的开始 
(2)HSPW表示HSYNC信号的脉冲宽度为(HSPW+1)个VCLK信号周期,即(HSPW+1)个像素,这(HSPW+1)个像素的数据无效。 
(3)HSYNC信号脉冲之后,还要经过(HBPD+1)个VCLK信号周期,有效的像素数据才出现。所以,在HSYNC有效之后,总共要经过(HSPW+1+HBPD+1)个无效的像素,它对应左边框,第一个有效的像素才出现。 
(4)随后即连续发出(HOZVAL+1)个像素的有效数据。 
(5)最后是(HFPD+1)个无效的像素,它对应右边框,完整的一行结束

3.s3c2440和Exynos4412硬件资源分析

  s3c2440 LCD驱动采用Platform设备驱动模型实现,内核自带的LCD驱动模型代码路径是drivers/vide0/s3c2410fb.c,Exynos 4412的驱动代码里,framebuffer主要代码在driver/video/,文件名是s3c-fb.c Exynos 4412显示控制器可以控制0~5个windows,代码中分给它们分别编号win0, win1,win2......,这里一个window就对应一个独立的驱动控制个体, 每个framebuffer有自己的一个FBI(fb_info)结构,显示控制器对应的抽象结构是s3c_fb结构体,window对应的抽象结构是s3c_fb_win结构体。s3c2440内部LCD控制器结构图如下,根据数据手册:

       1、LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组成;

    2、REGBANK由17个可编程的寄存器组和一块256*16的调色板内存组成,它们用来配置LCD控制器的;
    3、LCDCDMA是一个专用的DMA,它能自动地把在侦内存中的视频数据传送到LCD驱动器,通过使用这个DMA通道,视频数据在不需要CPU的干预的情况下显示在LCD屏上;
    4、VIDPRCS接收来自LCDCDMA的数据,将数据转换为合适的数据格式,比如说4/8位单扫,4位双扫显示模式,然后通过数据端口VD[23:0]传送视频数据到LCD驱动器;
    5、TIMEGEN由可编程的逻辑组成,他生成LCD驱动器需要的控制信号,比如VSYNC、HSYNC、VCLK和LEND等等,而这些控制信号又与REGBANK寄存器组中的LCDCON1/2/3/4/5的配置密切相关,通过不同的配置,TIMEGEN就能产生这些信 号的不同形态,从而支持不同的LCD驱动器(即不同的STN/TFT屏)。

  Exynos4412内部集成了一个FIMD(Fully Interactive Mobile Display),在FS4412开发板上使用的是rgb接口连接外部的LCD屏,相关的电路原理图如下,

3.帧缓冲子系统中数据结构分析

  Fb_info:该结构体重要是用来描述帧缓冲设备的属性和操作的完整描述,包括了设备的设置参数,状态以及操作函数指针,每个缓冲设备都必须对应一个fb_info

/* struct fb_info 结构体 */
struct fb_info {
int node;
int flags;
struct mutex lock; /* 用于 open/release/ioctl的锁 */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* 显示器标准 */
struct work_struct queue; /* Framebuffer event queue 帧缓冲事件队列 */
struct fb_pixmap pixmap; /* Image hardware mapper 图像硬件mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper 光标硬件mapper */
struct fb_cmap cmap; /* Current cmap 目前颜色表 */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current 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; /* 帧缓冲操作 */
struct device *device; /* This is the parent 父设备 */
struct device *dev; /* This is this fb device fb设备 */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend */
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similiar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
resource_size_t aperture_base;
resource_size_t aperture_size;
};

  Fb_ops:该结构体是fb_info中的成员变量,主要是用来为指向底层操作的函数的指针,fb_ops结构体中的成员函数fb_check_var是用来检查可以修改的屏幕参数并调整到最合适的值;成员函数fb_set_par是用来使得用户设置的屏幕参数可以在硬件上生效。

/* 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);
/* 对于非线性布局的/常规内存映射无法工作的帧缓冲设备*/
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);
/* 调整可变参数,并调整到支持的值 */
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
/* 根据info->var设置video模式 */
int (*fb_set_par)(struct fb_info *info);
/* set color register */
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info);
/* 批量设置color寄存器,设置颜色表 */
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
/* 显示空白 */
int (*fb_blank)(int blank, struct fb_info *info);
/* pan display */
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* 矩形填充 */
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* 数据复制 */
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* 图形填充 */
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* 绘制光标 */
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
/* 旋转显示 */
void (*fb_rotate)(struct fb_info *info, int angle);
/* 等待blit空闲(可选) */
int (*fb_sync)(struct fb_info *info);
/* fb特定的ioctl(可选) */
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,unsigned long arg);
/* 处理32位的compat ioctl(可选) */
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,unsigned long arg);
/* fb特定的mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* get capability given var */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,struct fb_var_screeninfo *var);
/* teardown any resources to do with this framebuffer */
void (*fb_destroy)(struct fb_info *info);
};

  Fb_var_screeninfo:该结构体是用来记录用户可以修改的显示控制器参数,包括屏幕分辨率和每个像素点的比特数。该结构体中的xres定义屏幕一行中有多少个点,yres是用来定义一列中有几个点,bit_per_pixel定义每个点用多少字节表示;

/* fb_var_screeninfo 结构体 */
struct fb_var_screeninfo {
__u32 xres; /* visible resolution 可见度解析 */
__u32 yres;
__u32 xres_virtual; /* virtual resolution 虚拟度解析 */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible 虚拟到可见之间的偏移 */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* 非0时指灰度 */
/* fb缓存的R/G/B位域 */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green;
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency 透明度 */
__u32 nonstd; /* != 0 非标准像素格式 */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.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; /* 顺时针旋转的角度 */
__u32 reserved[]; /* Reserved for future compatibility */
};

  Fb_fix_screeninfo:该结构体是用来记录用户不能修改的显示控制器的参数,如果屏幕缓冲区的物理地址,长度。当对帧缓冲区进行映射操作时,就是从fb_fix_screeninfo中取得帧缓冲区的物理地址。

/* fb_fix_screeninfo 结构体*/
struct fb_fix_screeninfo {
char id[]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* fb缓存的开始位置(物理地址) */
__u32 smem_len; /* fb缓存的长度 */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
/* visual:记录屏幕使用的色彩模式 ①Monochrome(FB_VISUAL_MON01、FB_VISUAL_MON10) 每个像素都是黑或白
②Pseudo color(FB_VISUAL_PSEDOCOLOR、FB_VISUAL_STATIC_PSEUDOCOLOR), 即伪彩色,采用索引颜色显示
③True color(FB_VISUAL_TRUECOLOR),即彩色,分成红绿蓝三基色
④Direct color(FB_VISUAL_DIRECTCOLOR)每个像素偃师市也是由红绿蓝组成,不过每个颜色值是个索引,需要查表
⑤Grayscale displays,灰度显示,红、绿、蓝的值都一样
*/
__u32 visual; /* see FB_VISUAL_* 记录屏幕使用的色彩模式 */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* 1行的字节数 */
unsigned long mmio_start; /* 内存映射IO长度 */
__u32 mmio_len; /* 内存映射IO长度 */
__u32 accel; /* Indicate to driver which */
__u16 reserved[]; /* Reserved for future compatibility */
};

  Fb_bitfield:该结构体是fb_var_screeninfo中的成员函数,主要是用来记录藐视每一像素显示缓冲区的组织方式,包括位域偏移,位域长度,和MSB(最高有效位)指示。

/* fb_bitfield 结构体 */
struct fb_bitfield {
__u32 offset; /* beginning of bitfield */
__u32 length; /* length of bitfield */
__u32 msb_right; /* != 0 : Most significant bit MSB在右边 */
};

  Fb_cmap:该结构体重要是用来记录设备无关的颜色值信息,用户空间通过ioctl()的FBIOGETMAP和FBIOSETMAP命令来读取或设定颜色值(FBI代表fb_info)

/* fb_cmap 结构体 */
struct fb_cmap {
__u32 start; /* First entry 第一个元素入口 */
__u32 len; /* Number of entries 原色数量 */
__u16 *red; /* Red values RGB透明度 */
__u16 *green;
__u16 *blue;
__u16 *transp; /* transparency, can be NULL */
};

3.帧缓冲字符设备接口

  操作帧缓冲设备的字符设备接口在fbmem.c中实现,下面主要分析fb_write、fb_mmap和fb_ioctl方法,帧缓冲设备的mmap()操作函数比较重要,顾名思义其任务是完成设备到系统内存(虚拟地址)之间的映射,在多数情况下,访问帧缓冲设备不是通过其读写方法,而是通过mmap()系统调用将帧缓冲映射到用户空间直接访问,这样做不仅可以省去一次用户空间与内和空间的数据复制,而且操作起来也更加方便快捷,这里的映射行为发生在驱动程序初始化阶段。请注意,这些行为是否被囊括在一个名为mmap的函数中并不是问题的关键,我们在开发中真正应该关心的是映射所需要的前提条件如设备物理地址的提取,映射长度的确认以及实际的映射操作。只要在驱动程序的初始化中完成了上述动作,那就算是成功了。因此,不少显卡的驱动程序里是找不到mmap这个函数的,但它们一样工作得很好,原因就是它们已经完成了实际的映射操作。

4.帧缓冲驱动实现

  针对platform驱动模型,帧缓冲驱动需要完成的核心任务包括两点,首先实现镇缓冲设备操作集合fb_ops,然后分配fb_info结构、填充其成员,并调用register_framebuffer()向系统注册;帧缓冲设备作为平台设备: 在S3C2440中,LCD控制器是片上资源,采用了platform驱动模型,在drivers/video/s3c2410fb.c中,模块初始化函数s3c2410fb_init()试图对两个platform驱动注册,名称分别时“s3c2410-lcd”和“s3c2412-lcd”,相应的平台设备在/arch/arm/plat-s3c24xx/devs.c中定义,初始名称是"s3c2410-lcd",由于S3C2440和S3C2410的LCD驱动器相同,因此只要在机器配置文件中将相应的平台设备注册进内核,适合于S3C2440 LCD控制器的平台驱动“s3c2410-lcd”就能顺利加载。除此之外,Linux还在/arch/arm/mach-s3c2410/include/mach/fb.h中为LCD平台设备定义了一个 s3c2410fb_mach_info结构体,该结构体主要是记录LCD的硬件参数信息(比如该结构体的s3c2410fb_display成员结构中就用于记录LCD的屏幕尺寸、屏幕信息、可变的屏幕参数、LCD配置寄存器等),这样在写驱动的时候就直接使用这个结构体,针对EXYNOS4412, Linux,在/arch/arm/mach-exynos/include/mach/s3cfb.h中为LCD平台设备定义了一个s3cfb_lcd结构体,该结构体主要是记录LCD的硬件参数信息(比如LCD的屏幕尺寸、 屏幕信息、 可变的屏幕参数、 LCD配置寄存器等),这样在写驱动的时候就直接使用这个结构体,对比可知,两者的驱动架构是相同的。

  具体移植步骤,首先在板级初始化文件中添加对帧缓冲的支持,需要在板级初始化文件中为帧缓冲的平台设备类型为s3c2410fb_mach_info的平台数据,并向系统中添加该平台设备,而针对EXYNOS4412通过设备树来加载初始化硬件信息,以后汇详细讲解,下面第一图是开发板原理图的LCD控制器部分,第二图是S3c2440数据手册中IO端口C和IO端口D控制器部分。原理图中使用了 GPC8-15和GPD0-15来用做LCD控制器VD0-VD23的数据端口,又分别使用GPC0、GPC1端口用做LCD控制器的LEND和VCLK 信号,对于GPC2-7则是用做STN屏或者三星专业TFT屏的相关信号。然而,S3C2440的各个IO口并不是单一的功能,都是复用端口,要使用他们 首先要对他们进行配置。所以代码中加粗部分的参数就是把GPC和GPD的部分端口配置成LCD控制功能模式。因此需要在mach-smdk2410.c中添加如下内容:

/* LCD driver info */
#if defined(CONFIG_FB_S3C2410_N240320)
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_PIXCLOCK 100000
#define LCD_RIGHT_MARGIN 36
#define LCD_LEFT_MARGIN 19
#define LCD_HSYNC_LEN 5
#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN 1
#elif defined(CONFIG_FB_S3C2410_W320240)
#define LCD_WIDTH 320
#define LCD_HEIGHT 240
#define LCD_PIXCLOCK 170000
#define LCD_RIGHT_MARGIN 0x44
#define LCD_LEFT_MARGIN 0x04
#define LCD_HSYNC_LEN 0x01
#define LCD_UPPER_MARGIN 10
#define LCD_LOWER_MARGIN 4
#define LCD_VSYNC_LEN 1
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVFRAME | \
S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_HWSWP )
#elif defined(CONFIG_FB_S3C2410_N480272)
#define LCD_WIDTH 480
#define LCD_HEIGHT 272
#define LCD_PIXCLOCK 100000
#define LCD_RIGHT_MARGIN 36
#define LCD_LEFT_MARGIN 19
#define LCD_HSYNC_LEN 5
#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN 1
#elif defined(CONFIG_FB_S3C2410_TFT640480)
#define LCD_WIDTH 640
#define LCD_HEIGHT 480
#define LCD_PIXCLOCK 40000
#define LCD_RIGHT_MARGIN 67
#define LCD_LEFT_MARGIN 40
#define LCD_HSYNC_LEN 31
#define LCD_UPPER_MARGIN 5
#define LCD_LOWER_MARGIN 25
#define LCD_VSYNC_LEN 1
#elif defined(CONFIG_FB_S3C2410_T240320)
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_PIXCLOCK 170000
#define LCD_RIGHT_MARGIN 25
#define LCD_LEFT_MARGIN 0
#define LCD_HSYNC_LEN 4
#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 4
#define LCD_VSYNC_LEN 1
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVDEN | \
S3C2410_LCDCON5_INVVFRAME | S3C2410_LCDCON5_INVVLINE | \
S3C2410_LCDCON5_INVVCLK | S3C2410_LCDCON5_HWSWP )
#elif defined(CONFIG_FB_S3C2410_X240320)
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_PIXCLOCK 170000
#define LCD_RIGHT_MARGIN 25
#define LCD_LEFT_MARGIN 0
#define LCD_HSYNC_LEN 4
#define LCD_UPPER_MARGIN 0
#define LCD_LOWER_MARGIN 4
#define LCD_VSYNC_LEN 9
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVDEN | \
S3C2410_LCDCON5_INVVFRAME | S3C2410_LCDCON5_INVVLINE | \
S3C2410_LCDCON5_INVVCLK | S3C2410_LCDCON5_HWSWP )
#elif defined(CONFIG_FB_S3C2410_TFT800480)
#define LCD_WIDTH 800
#define LCD_HEIGHT 480
#define LCD_PIXCLOCK 40000
#define LCD_RIGHT_MARGIN 67
#define LCD_LEFT_MARGIN 40
#define LCD_HSYNC_LEN 31
#define LCD_UPPER_MARGIN 25
#define LCD_LOWER_MARGIN 5
#define LCD_VSYNC_LEN 1
#elif defined(CONFIG_FB_S3C2410_VGA1024768)
#define LCD_WIDTH 1024
#define LCD_HEIGHT 768
#define LCD_PIXCLOCK 80000
#define LCD_RIGHT_MARGIN 15
#define LCD_LEFT_MARGIN 199
#define LCD_HSYNC_LEN 15
#define LCD_UPPER_MARGIN 1
#define LCD_LOWER_MARGIN 1
#define LCD_VSYNC_LEN 1
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_HWSWP)
#endif
#if defined (LCD_WIDTH)
static struct s3c2410fb_display My2440_lcd_cfg __initdata = {
#if !defined (LCD_CON5)
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
#else
.lcdcon5 = LCD_CON5,
#endif
.type = S3C2410_LCDCON1_TFT,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
.pixclock = LCD_PIXCLOCK, /* HCLK 60 MHz, divisor 10 */
.xres = LCD_WIDTH,
.yres = LCD_HEIGHT,
.bpp = ,
.left_margin = LCD_LEFT_MARGIN + ,
.right_margin = LCD_RIGHT_MARGIN + ,
.hsync_len = LCD_HSYNC_LEN + ,
.upper_margin = LCD_UPPER_MARGIN + ,
.lower_margin = LCD_LOWER_MARGIN + ,
.vsync_len = LCD_VSYNC_LEN + ,
};
#define S3C2410_GPCCON_MASK(x) (3 << ((x) * 2))
#define S3C2410_GPDCON_MASK(x) (3 << ((x) * 2))
static struct s3c2410fb_mach_info My2440_fb_info __initdata = {
.displays = &My2440_lcd_cfg,
.num_displays = ,
.default_display = ,
.gpcup = (0xf << ) | (0x3f << ),
.gpccon = (S3C2410_GPC1_VCLK | S3C2410_GPC2_VLINE |
S3C2410_GPC3_VFRAME | S3C2410_GPC4_VM |
S3C2410_GPC10_VD2 | S3C2410_GPC11_VD3 |
S3C2410_GPC12_VD4 | S3C2410_GPC13_VD5 |
S3C2410_GPC14_VD6 | S3C2410_GPC15_VD7),
.gpccon_mask = (S3C2410_GPCCON_MASK() | S3C2410_GPCCON_MASK() |
S3C2410_GPCCON_MASK() | S3C2410_GPCCON_MASK() |
S3C2410_GPCCON_MASK() | S3C2410_GPCCON_MASK() |
S3C2410_GPCCON_MASK() | S3C2410_GPCCON_MASK() |
S3C2410_GPCCON_MASK() | S3C2410_GPCCON_MASK()),
.gpdup = (0x3f << ) | (0x3f << ),
.gpdcon = (S3C2410_GPD2_VD10 | S3C2410_GPD3_VD11 |
S3C2410_GPD4_VD12 | S3C2410_GPD5_VD13 |
S3C2410_GPD6_VD14 | S3C2410_GPD7_VD15 |
S3C2410_GPD10_VD18 | S3C2410_GPD11_VD19 |
S3C2410_GPD12_VD20 | S3C2410_GPD13_VD21 |
S3C2410_GPD14_VD22 | S3C2410_GPD15_VD23),
.gpdcon_mask = (S3C2410_GPDCON_MASK() | S3C2410_GPDCON_MASK() |
S3C2410_GPDCON_MASK() | S3C2410_GPDCON_MASK() |
S3C2410_GPDCON_MASK() | S3C2410_GPDCON_MASK() |
S3C2410_GPDCON_MASK() | S3C2410_GPDCON_MASK()|
S3C2410_GPDCON_MASK() | S3C2410_GPDCON_MASK()|
S3C2410_GPDCON_MASK() | S3C2410_GPDCON_MASK(
)),
}; #endif

static void __init My2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&My2440_fb_info);
s3c_i2c0_set_platdata(NULL);
i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
s3c_device_spi0.dev.platform_data= &s3c2410_spi0_platdata;
spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board));
s3c_device_spi1.dev.platform_data= &s3c2410_spi1_platdata;
spi_register_board_info(s3c2410_spi1_board, ARRAY_SIZE(s3c2410_spi1_board));
s3c_device_nand.dev.platform_data = &My2440_nand_info;
s3c_device_sdi.dev.platform_data = &My2440_mmc_cfg;

platform_add_devices(My2440_devices, ARRAY_SIZE(My2440_devices));
}

  对于驱动使用platform模型的编写,probe函数是平台设备和平台驱动匹配上后第一个调用的函数,它主要完成工作如下:

  1、分配核心结构体struct fb_info空间和私有数据空间;

  2、获取平台资源和平台私有数据,把获取到的信息保存到struct fb_info结构的fb_var_screeninfo var和struct fb_fix_screeninfo fix结构空间中,并保存一些必要的设备私有数据,对于内存资源,获取后要进行申请-映射为虚拟地址-使用映射后的虚拟地址对硬件寄存器初始化,对于中断资源,则进行对中断资源函数的注册;

  3、struct fb_ops结构是操作硬件的函数,所以这部分是在probe函数外部实现,但是把已经实现好的struct fb_ops *fops结构填充到struct fb_info结构中也是在探测函数中实现的;

  4、在探测函数中使用register_framebuffer函数注册已经填充好的struct fb_info结构,详尽代码如下:

注意,s3c2440调用的是s3c24xxfb_probe(),而Exynos4412通过调用s3cfb_probe()来实现;

static int s3c24xxfb_probe(struct platform_device *pdev,
enum s3c_drv_type drv_type)
{
struct s3c2410fb_info *info;
struct s3c2410fb_display *display; //LCD屏的配置信息结构体,该结构体在mach-s3c2410/include/mach/fb.h中
struct fb_info *fbinfo; //FrameBuffer驱动所对应的fb_info结构体
struct s3c2410fb_mach_info *mach_info;
struct resource *res;
int ret;
int irq;
int i;
int size;
u32 lcdcon1;
mach_info = dev_get_platdata(&pdev->dev); //获取硬件平台信息
if (mach_info == NULL) {
dev_err(&pdev->dev,
"no platform data for lcd, cannot attach\n");
return -EINVAL;
}
if (mach_info->default_display >= mach_info->num_displays) {
dev_err(&pdev->dev, "default is %d but only %d displays\n",
mach_info->default_display, mach_info->num_displays);
return -EINVAL;
}
display = mach_info->displays + mach_info->default_display;
irq = platform_get_irq(pdev, );
if (irq < ) {
dev_err(&pdev->dev, "no irq for device\n");
return -ENOENT;
}
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
if (!fbinfo)
return -ENOMEM;
platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par;
info->dev = &pdev->dev; //把全局的平台设备变量pdev中的dev成员存放到私有数据成员中
info->drv_type = drv_type; res = platform_get_resource(pdev, IORESOURCE_MEM, );
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory registers\n");
ret = -ENXIO;
goto dealloc_fb;
}
size = resource_size(res);
info->mem = request_mem_region(res->start, size, pdev->name);
if (info->mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto dealloc_fb;
}
info->io = ioremap(res->start, size); //映射内存为虚拟地址空间
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}
if (drv_type == DRV_S3C2412)
info->irq_base = info->io + S3C2412_LCDINTBASE;
else
info->irq_base = info->io + S3C2410_LCDINTBASE;
dprintk("devinit\n");
strcpy(fbinfo->fix.id, driver_name);
/* Stop the video */
lcdcon1 = readl(info->io + S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux = ;
fbinfo->fix.xpanstep = ;
fbinfo->fix.ypanstep = ;
fbinfo->fix.ywrapstep = ;
fbinfo->fix.accel = FB_ACCEL_NONE;
fbinfo->var.nonstd = ;
fbinfo->var.activate = FB_ACTIVATE_NOW;
fbinfo->var.accel_flags = ;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
fbinfo->fbops = &s3c2410fb_ops;
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;
for (i = ; i < ; i++)
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
ret = request_irq(irq, s3c2410fb_irq, , pdev->name, info);
if (ret) {
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
ret = -EBUSY;
goto release_regs;
}
info->clk = clk_get(NULL, "lcd");
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get lcd clock source\n");
ret = PTR_ERR(info->clk);
goto release_irq;
}
clk_enable(info->clk);
dprintk("got and enabled clock\n"); usleep_range(, ); info->clk_rate = clk_get_rate(info->clk); /* find maximum required memory size for display */
for (i = ; i < mach_info->num_displays; i++) {
unsigned long smem_len = mach_info->displays[i].xres;
smem_len *= mach_info->displays[i].yres;
smem_len *= mach_info->displays[i].bpp;
smem_len >>= ;
if (fbinfo->fix.smem_len < smem_len)
fbinfo->fix.smem_len = smem_len;
}
/* Initialize video memory */
ret = s3c2410fb_map_video_memory(fbinfo);
if (ret) {
dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_clock;
}
dprintk("got video memory\n");
fbinfo->var.xres = display->xres;
fbinfo->var.yres = display->yres;
fbinfo->var.bits_per_pixel = display->bpp;
s3c2410fb_init_registers(fbinfo);
s3c2410fb_check_var(&fbinfo->var, fbinfo);
ret = s3c2410fb_cpufreq_register(info);
if (ret < ) {
dev_err(&pdev->dev, "Failed to register cpufreq\n");
goto free_video_memory;
}
ret = register_framebuffer(fbinfo);
if (ret < ) {
dev_err(&pdev->dev, "Failed to register framebuffer device: %d\n",
ret);
goto free_cpufreq;
}
/* create device files */
ret = device_create_file(&pdev->dev, &dev_attr_debug);
if (ret)
dev_err(&pdev->dev, "failed to add debug attribute\n");
dev_info(&pdev->dev, "fb%d: %s frame buffer device\n",
fbinfo->node, fbinfo->fix.id);
return ;
free_cpufreq:
s3c2410fb_cpufreq_deregister(info);
free_video_memory:
s3c2410fb_unmap_video_memory(fbinfo);
release_clock:
clk_disable(info->clk);
clk_put(info->clk);
release_irq:
free_irq(irq, info);
release_regs:
iounmap(info->io);
release_mem:
release_mem_region(res->start, size);
dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}

5.修改Makefile和Kconfig,并在内核中添加对帧缓冲驱动的支持

  首先,drivers/video/Kconfig中添加下面配置:

choice
prompt "LCD select"
depends on FB_S3C2410
help
S3C24x0 LCD size select config FB_S3C2410_W320240
boolean "3.5 inch 320X240 TFT Landscape LCD"
depends on FB_S3C2410
help
3.5 inch 320X240 TFT Landscape LCD config FB_S3C2410_T240320
boolean "3.5 inch 240X320 Toppoly LCD"
depends on FB_S3C2410
help
3.5 inch 240X320 Toppoly LCD config FB_S3C2410_X240320
boolean "3.5 inch 240X320 LCD(ACX502BMU)"
depends on FB_S3C2410
help
3.5 inch 240X320 LCD(ACX502BMU) config FB_S3C2410_N240320
boolean "3.5 inch 240X320 NEC LCD"
depends on FB_S3C2410
help
3.5 inch 240x320 NEC LCD config FB_S3C2410_N480272
boolean "4.3 inch 480X272 NEC LCD"
depends on FB_S3C2410
help
4.3 inch 480x272 NEC LCD config FB_S3C2410_TFT640480
boolean "8 inch 640X480 L80 LCD"
depends on FB_S3C2410
help
inch 640X480 LCD config FB_S3C2410_TFT800480
boolean "7 inch 800x480 TFT LCD"
depends on FB_S3C2410
help
inch 800x480 TFT LCD config FB_S3C2410_VGA1024768
boolean "VGA 1024x768"
depends on FB_S3C2410
help
VGA 1024x768 endchoice config BACKLIGHT_MY2440
tristate "Backlight support for My2440"
depends on MACH_MY2440 && FB_S3C2410
help
backlight driver for MY2440

  其次,make menuconfig, 见下图,加进所需要配置的选项:

6.帧缓冲驱动测试

  针对2440,arm-linux-gcc bmplib.c fbtest.c -o test , 然后./test logo.bmp,将在显示屏上看到企鹅图像。

/*bmplib.c*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include "bmplib.h"
int bmp_open(bmp_t *bmp, char *bmpn)
{
bmp_file_header_t fhr;
bmp_info_header_t ihr;
if (- == (bmp->fd=open(bmpn, O_RDONLY))) {
printf("Error: cannot open bmp file.\n");
_exit(EXIT_FAILURE);
}
read(bmp->fd, &fhr, sizeof(bmp_file_header_t));
read(bmp->fd, &ihr, sizeof(bmp_info_header_t));
bmp->width = char_to_int(ihr.width);
bmp->height = char_to_int(ihr.height);
bmp->bitcount = char_to_int(ihr.bitcount);
bmp->siz = (bmp->width * bmp->height * bmp->bitcount)/;
printf("bmp->width = %d\n", bmp->width);
printf("bmp->height = %d\n", bmp->height);
printf("bmp->bitcount = %d\n", bmp->bitcount);
printf("bmp->siz = %d\n", bmp->siz);
bmp->data = malloc(bmp->siz);
int ret;
ret = read(bmp->fd, bmp->data, bmp->siz);
bmp->curp = (rgb_32_t*)bmp->data;
return ;
}
int bmp_close(bmp_t *bmp)
{
close(bmp->fd);
free(bmp->data);
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include "bmplib.h"
#define FB_DEV_NAME "/dev/fb0"
#define RED_COLOR565 0x0F100
#define GREEN_COLOR565 0x007E0
#define BLUE_COLOR565 0x0001F
#define BLACK_COLOR565 0x0
typedef struct fb_dev {
int fd;
void *pfb;
int xres, yres, siz;
int bpp;
} fb_dev_t;
int fb_open(fb_dev_t *fbd, char *fbn)
{
struct fb_var_screeninfo vinfo;
if (- == (fbd->fd=open(fbn, O_RDWR))) {
perror("open fb.");
_exit(EXIT_FAILURE);
}
// Get variable screen information
ioctl(fbd->fd, FBIOGET_VSCREENINFO, &vinfo);
fbd->xres = vinfo.xres;
fbd->yres = vinfo.yres;
fbd->bpp = vinfo.bits_per_pixel;
printf("vinfo.red.offset = %d, vinfo.red.length = %d\n", vinfo.red.offset, vinfo.red.length);
printf("vinfo.green.offset = %d, vinfo.green.length = %d\n", vinfo.green.offset, vinfo.green.length);
printf("vinfo.blue.offset = %d, vinfo.blue.length = %d\n", vinfo.blue.offset, vinfo.blue.length);
if (fbd->bpp != ) {
ioctl(fbd->fd, FBIOPUT_VSCREENINFO, &vinfo);
fbd->bpp = vinfo.bits_per_pixel;
}
// Figure out the size of the screen in bytes
fbd->siz = fbd->xres * fbd->yres * fbd->bpp / ;
printf("%dx%d, %dbpp, screensize = %d\n", fbd->xres, fbd->yres,
fbd->bpp, fbd->siz );
// Map the device to memory
fbd->pfb = mmap(, fbd->siz, PROT_READ | PROT_WRITE, MAP_SHARED, fbd->fd, );
if ((int)fbd->pfb == -) {
printf("Error: failed to map framebuffer device to memory.\n");
_exit(EXIT_FAILURE);
}
printf("fbd->pfb = %p\n", fbd->pfb);
return ;
}
int fb_close(fb_dev_t *fbd)
{
munmap(fbd->pfb, fbd->siz);
close(fbd->fd);
}
int fb_drawrect(fb_dev_t *fbd, int x0, int y0, int w, int h, int color)
{
int x, y;
for(y = y0; y < y0+h; y++) {
for(x = x0; x < x0+w ; x++)
*((short*)(fbd->pfb) + y*fbd->xres + x) = color;
}
return ;
}
int fb_drawbmp(fb_dev_t *fbd, int x0, int y0, char *bmpn)
{
int x, y, x1, y1;
bmp_t bmp; bmp_open(&bmp, bmpn);
if (x0 < )
x0 = (fbd->xres - bmp.width) / ;
if (y0 < )
y0 = (fbd->yres - bmp.height) / ; x1 = x0+bmp.width;
y1 = y0+bmp.height; for(y = y1; y > y0; y--) {
for(x = x0; x < x1; x++) {
if (x > fbd->xres || y > fbd->yres) {
bmp_next_pixel(&bmp);
continue;
}
*((short*)(fbd->pfb) + y*fbd->xres + x) = bmp_get_pixel_16bit(&bmp);
bmp_next_pixel(&bmp);
}
}
bmp_close(&bmp);
return ;
}
int main(int argc, char **argv)
{
fb_dev_t *fbd;
fbd = (fb_dev_t*)malloc(sizeof(fb_dev_t)); fb_open(fbd, FB_DEV_NAME); if(fbd->bpp == ) {
printf("Red/Green/Blue Screen\n");
fb_drawrect(fbd, , , fbd->xres, fbd->yres/, RED_COLOR565);
fb_drawrect(fbd, , fbd->yres/, fbd->xres, fbd->yres/, GREEN_COLOR565);
fb_drawrect(fbd, , fbd->yres*/, fbd->xres, fbd->yres/, BLUE_COLOR565);
//fb_drawrect(fbd, 0, 0, fbd->xres, fbd->yres, BLACK_COLOR565); //fb_drawbmp(fbd, 0, 0, argv[1]);
fb_drawbmp(fbd, -, -, argv[]);
} else
printf("16 bits only, (bpp = %d)!\n", fbd->bpp);
fb_close(fbd);
return ;
}

参考文献 1.《深入Linux内核架构》 2.《Linux设备驱动开发详解》

LCD驱动移植在在mini2440(linux2.6.29)和FS4412(linux3.14.78)上实现对比(deep dive)的更多相关文章

  1. DM9000驱动移植在mini2440(linux2.6.29)和FS4412(linux3.14.78)上的实现(deep dive)篇一

    关于dm9000的驱动移植分为两篇,第一篇在mini2440上实现,基于linux2.6.29,也成功在在6410上移植了一遍,和2440非常类似,第二篇在fs4412(Cortex A9)上实现,基 ...

  2. IIC驱动移植在linux3.14.78上的实现和在linux2.6.29上实现对比(deep dive)

    首先说明下为什么写这篇文章,网上有许多博客也是介绍I2C驱动在linux上移植的实现,但是笔者认为他们相当一部分没有分清所写的驱动时的驱动模型,是基于device tree, 还是基于传统的Platf ...

  3. AM335x(TQ335x)学习笔记——LCD驱动移植

    TI的LCD控制器驱动是非常完善的,共通的地方已经由驱动封装好了,与按键一样,我们可以通过DTS配置完成LCD的显示.下面,我们来讨论下使用DTS方式配置内核完成LCD驱动的思路. (1)初步分析 由 ...

  4. SPI在linux3.14.78 FS_S5PC100(Cortex A8)和S3C2440上驱动移植(deep dive)

    由于工作的原因,对SPI的理解最为深刻,也和SPI最有感情了,之前工作都是基于OSEK操作系统上进行实现,也在US/OS3上实现过SPI驱动的实现和测试,但是都是基于基本的寄存器操作,没有一个系统软件 ...

  5. Linux的LCD驱动分析及移植

    测试平台 宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 LCD驱动分析 LCD屏的驱动总体上分成两块,一块是GUI ...

  6. 移植ok6410 LCD驱动

    1.本次移植过程选择 linux-2.6.28 lcd驱动为参考移植到 linux-2.6.34 ok6410 开发板上. 2.移植过程 主要以给内核增加驱动的思想,在/driver/video/ 下 ...

  7. TQ2440平台上LCD驱动的移植

    参考: http://liu1227787871.blog.163.com/blog/static/205363197201242393031250/ http://blog.csdn.net/cum ...

  8. 【Linux驱动】TQ2440 DM9000E网卡驱动移植(Linux-2.6.30.4)

    花了一天的时间研究了一下Linux-2.6.30.4版本号内核下关于TQ2440 DM9000E的网卡驱动移植.总结一下自己的收获. 事实上.在Linux-2.6.30.4版本号内核下有关于网卡驱动, ...

  9. 全志A33移植LCD驱动(ILI9806E)

    0x00 环境说明: 所使用的开发板为锐尔威视的插针版A33_Vstar 屏幕是买的第三方的KD050FWFPA011-C009A,其中LCD驱动IC为ILI9806E,所使用的接口为RGB666 0 ...

随机推荐

  1. perties类的操作

    http://www.cnblogs.com/bakari/p/3562244.html perties类的操作   知识学而不用,就等于没用,到真正用到的时候还得重新再学.最近在看几款开源模拟器的源 ...

  2. sublime text3 Emmet (原zenCoding)安装方法

    1.安装使用Package Control组件安装 (1)打开控制台 (mac)control+`; (win)ctrl+` (2)复制一下代码并回车 import urllib.request,os ...

  3. MySQL引擎简述

    MySQL数 据库引擎取决于MySQL在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外两种类型IN ...

  4. Android线程之异步消息处理机制(一)

    Android不允许在子线程中进行UI操作,但是有些时候,我们必须在子线程里去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件.对于这种情况,Android提供了一套异步消息处理机制,完美 ...

  5. stm32 DMA数据搬运 [操作寄存器+库函数](转)

    源:stm32 DMA数据搬运 [操作寄存器+库函数]        DMA(Direct Memory Access)常译为“存储器直接存取”.早在Intel的8086平台上就有了DMA应用了.   ...

  6. Jquey里的同步请求和异步请求

    1.同步请求 发送了同步请求后  会一直等待 先执行 alert("result:" + d); temp = d;   在执行alert("this is last:& ...

  7. vi的基本操作

    vi的基本操作 a) 进入vi 在系统提示符号输入vi及文件名称后,就进入vi全屏幕编辑画面: $ vi myfile 不过有一点要特别注意,就是您进入vi之后,是处于「命令行模式(command m ...

  8. 谈谈线程同步Lock和unLock

    Lock可以使用Condition进行线程之间的调度,它有更好的灵活性,而且在一个对象里面可以有多个Condition(即对象监视器),则线程可以注册在不同的Condition,从而可以 有选择性的调 ...

  9. oracle系列--级联删除和级联更新

    必须声明:此博客转载于Oracle外键级联删除和级联更新http://www.2cto.com/database/201507/417496.html 鉴于此前收藏的精彩博客无料被删除了,很是痛心,所 ...

  10. php-fpm 相关

    ps aux | grep -c php-fpm 查看php-fpm进程数:ps aux | grep -c php-fpm 查看运行内存/usr/bin/php  -i|grep mem 重启php ...