嵌入式驱动程序Day12
Top
1.LCD驱动设计开发
1 LCD驱动设计开发
1.1 问题
通过lcd驱动开发掌握linux内核framebuffer驱动开发通用方法。
1.2 方案
一、帧缓冲(Framebuffer)。
帧缓冲(Framebuffer)是Linux为显示设备提供的一个接口,Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过 Framebuffer的读写直接对显存进行操作。用户
可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由 Framebuffer设备驱动
来完成的。
Framebuffer本身不具备任何运算数据的能力,就只好比是一个暂时存放水的水池。CPU将运算后的结果放到这个水池,水池再将结果流到显示器,中间不会对数据做处
理。应用程序也可以直接读写这个水池的内容。在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用,比如下面的程序就打开 /dev/fb0 设备,并通过mmap 系统调用进行地址映射。
FrameBuffer 设备还提供了若干 ioctl 命令,通过这些命令,可以获得显示设备的一些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分辨率、象素结构、每扫描线的字节宽度),以及伪彩色模式下的调色板信息等等。
二、FrameBuffer在Linux中的实现和机制。
Framebuffer对应的源文件在linux/drivers/video/目录下。总的抽象设备文件为fbcon.c,在这个目录下还有与各种显卡驱动相关的源文件。
1.分析Framebuffer设备驱动。
FrameBuffer设备驱动基于如下两个文件:
(1)linux/include/linux/fb.h
(2)linux/drivers/video/fbmem.c
2.分析这两个文件。
(1)fb.h。
几乎主要的结构都是在这个中文件定义的。这些结构包括:
1)fb_var_screeninfo结构体。
这个结构描述了显示卡的特性:
__u32 是表示 unsigned 不带符号的 32 bits 的数据类型,其余类推。这是 Linux 内核中所用到的数据类型,如果是开发用户空间(user-space)的程序,可以根据具体计算机平台的情况,用 unsigned long 等等来代替。
struct fb_var_screeninfo
{
__u32 xres; //可视区域
__u32 yres;
__u32 xres_virtual; //可视区域
__u32 yres_virtual;
__u32 xoffset; //可视区域的偏移
__u32 yoffset;
__u32 bits_per_pixel; //每一象素的bit数
__u32 grayscale; //等于零就成黑白
struct fb_bitfield red;
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; //透明
__u32 nonstd; //不是标准格式
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; //内存中的图像高度
__u32 width; //内存中的图像宽度
__u32 accel_flags; //加速标志
//时序-_-这些部分就是显示器的显示方法
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */ 水平可视区域
__u32 vsync_len; /* length of vertical sync */ 垂直可视区域
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 reserved[6]; // 备用-以后开发
};
2) fb_fix_screeninfon结构体。
这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如FrameBuffer内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */ID
unsigned long smem_start; /* Start of frame buffer mem */ 内存起始
/* (physical address) */ 物理地址
__u32 smem_len; /* Length of frame buffer mem */ 内存大小
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */插入区域?
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; //没有硬件设备就为零
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; // 一行的字节表示
unsigned long mmio_start; //内存映射的I/O起始
/* (physical address) */
__u32 mmio_len; // I/O的大小
__u32 accel; /* Type of acceleration available */ 可用的加速类型
__u16 reserved[3]; /* Reserved for future compatibility */
};
3) fb_cmap结构体。
描述设备无关的颜色映射信息。可以通过FBIOGETCMAP 和 FBIOPUTCMAP 对应的
ioctl操作设定或获取颜色映射信息。
struct fb_cmap {
__u32 start; /* First entry */ 第一个入口
__u32 len; /* Number of entries */ 入口的数字
__u16 *red; /* Red values */ 红
__u16 *green;
__u16 *blue;
__u16 *transp; /* transparency, can be NULL */ 透明,可以为零
};
4) fb_info结构体。
定义当显卡的当前状态;fb_info结构仅在内核中可见,在这个结构中有一个fb_ops 指针,指向驱动设备工作所需的函数集。
struct fb_info {
char modename[40]; // 默认的视频卡类型
kdev_t node;
int flags;
int open; // 被打开过么?
#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
struct fb_var_screeninfo var; // 现在的视频信息
struct fb_fix_screeninfo fix; // 修正的信息
struct fb_monspecs monspecs; // 现在的显示器模式
struct fb_cmap cmap; // 当前优先级
struct fb_ops *fbops;
char *screen_base; // 物理基址
struct display *disp; //初始化
struct vc_data *display_fg; /* Console visible on this display */
char fontname[40]; //默认的字体
devfs_handle_t devfs_handle; /* Devfs handle for new name */
devfs_handle_t devfs_lhandle; //兼容
int (*changevar)(int); // 告诉console变量修改了
int (*switch_con)(int, struct fb_info*);
// 告诉fb选择consoles
int (*updatevar)(int, struct fb_info*);
/* tell fb to update the vars */ 告诉fb更新变量
void (*blank)(int, struct fb_info*); /* tell fb to (un)blank the screen */告诉fb使用黑白模式(或者不黑)
/* arg = 0: unblank */arg=0的时候黑白模式
/* arg > 0: VESA level (arg-1) */ arg>0时候选择VESA模式
void *pseudo_palette; /* Fake palette of 16 colors and
the cursor's color for non
palette mode */ 修正调色板
/* From here on everything is device dependent */ 现在就可以使用了
void *par;
};
5) struct fb_ops结构体。
用户应用可以使用ioctl()系统调用来操作设备,这个结构就是用一支持ioctl()的这些操作的。
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);
/* get non settable parameters */
int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con,
struct fb_info *info);
/* get settable parameters */
int (*fb_get_var)(struct fb_var_screeninfo *var, int con,
struct fb_info *info);
/* set settable parameters */
int (*fb_set_var)(struct fb_var_screeninfo *var, int con,
struct fb_info *info);
/* get colormap */
int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info);
/* set colormap */
int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info);
/* pan display (optional) */
int (*fb_pan_display)(struct fb_var_screeninfo *var, int con,
struct fb_info *info);
/* perform fb specific ioctl (optional) */
int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, int con, struct fb_info *info);
/* perform fb specific mmap */
int (*fb_mmap)(struct fb_info *info, struct file *file, struct
vm_area_struct *vma);
/* switch to/from raster image mode */
int (*fb_rasterimg)(struct fb_info *info, int start);
};
(2)fbmem.c。
fbmem.c 处于Framebuffer设备驱动技术的中心位置。它为上层应用程序提供系统调用也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己。fbmem.c 为所有支持FrameBuffer的设备驱动提供了通用的接口,避免重复工作。
1) 全局变量。
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb;
这两变量记录了所有fb_info 结构的实例,fb_info 结构描述显卡的当前状态,所有设备对应的fb_info 结构都保存在这个数组中,当一个FrameBuffer设备驱动向系统注册自己时,其对应的fb_info 结构就会添加到这个结构中,同时num_registered_fb 为自动加1。
static struct {
const char *name;
int (*init)(void);
int (*setup)(void);
} fb_drivers[] __initdata= { ....};
如果FrameBuffer设备被静态链接到内核,其对应的入口就会添加到这个表中;如果是动态加载的,即使用insmod/rmmod,就不需要关心这个表。
static struct file_operations fb_ops ={
owner: THIS_MODULE,
read: fb_read,
write: fb_write,
ioctl: fb_ioctl,
mmap: fb_mmap,
open: fb_open,
release: fb_release
};
这是一个提供给应用程序的接口。
2)fbmem.c 实现了如下函数。
Register_framebuffer(struct fb_info *fb_info);
unregister_framebuffer(struct fb_info *fb_info);
这两个是提供给下层FrameBuffer设备驱动的接口,设备驱动通过这两函数向系统注册或注销自己。几乎底层设备驱动所要做的所有事情就是填充fb_info结构然后向系统注册或注销它。
三、本案例的操作步骤如下。
1. 修改mach-cw210.c源文件,实现LCD设备的初始化。
2.重新编译内核zImage,并将zImage拷贝到/tftpboot下。
3.编写应用测试程序lcd_test.c。
(1)定义宏和变量;
1.#define COLOR_RED 0x000000FF
2.#define COLOR_GREEN 0x0000FF00
3.#define COLOR_BLUE 0x00FF0000
4.
5.int fdfb = -1;
6.
7.struct fb_fix_screeninfo fbfix = { 0 };
8.struct fb_var_screeninfo fbvar = { 0 };
9.long screensize = 0;
10.
11.int *fb32 = NULL;
12.
13.int x = 0;
14.int y = 0;
15.long location = 0;
(2)编写main()函数;
1)打开Framebuffer设备文件;
2)使用ioctl()系统调用获取帧缓冲设备中设备无关的固定参数信息;
3)使用ioctl()系统调用获取帧缓冲设备中设备无关的可变参数信息和特定的显示模式;
4)计算显示屏幕上所有点需要的字节数,字节数等于分辨率*每个点占据的字节数;
5)使用函数mmap()将Framebuffer的显存映射到用户空间,使得用户空间可以直接进行读写操作;
6)根据色深,操作显存,在LCD屏上绘制红色、绿色、蓝色三个区域;
7)使用函数munmap()解除对显存的映射;
8)关闭Framebuffer设备。
4.编写Makefile文件。
5. 编译LCD驱动模块,并将生成的lcd_test文件拷贝到指定目录下。
6.在TPAD的运行终端上运行应用测试程序,测试驱动程序。
1.3 实现
1. 修改mach-cw210.c源文件,实现LCD设备的初始化。
修改/home/tarena/workdir/tools/cw210_kernel_2.6.35.7/arch/arm/ mach-
s5pv210/目录下的mach-cw210.c源文件。
1.$ cd /home/tarena/workdir/tools/cw210_kernel_
2.6.35.7/arch/arm/ mach-
s5pv210/
2.$ vi mach-cw210.c
添加源代码如下:
1.#elif defined(CONFIG_FB_AT070TN92)
2.static struct s3cfb_lcd lte480wv = {
3..width = 800,
4..height = 480,
5..bpp = 24,
6..freq = 28,
7..clkval_f = 6,//3
8.// 27,//HBPD
9.// 14,//HFPD
10.// 20,//10,//HSPW
11..timing = {
12..h_fp = 14,//210,
13..h_bp = 27,//16,
14..h_sw = 20,//30,
15..v_fp = 22,
16..v_fpe = 1,
17..v_bp = 10,
18..v_bpe = 1,
19..v_sw = 13,
20.},
21.
22..polarity = {
23..rise_vclk = 0,
24..inv_hsync = 1,
25..inv_vsync = 1,
26..inv_vden = 0,
27.},
28.};
29.
30.static void lte480wv_cfg_gpio(struct platform_device *pdev)
31.{
32.int i;
33.
34.for (i = 0; i < 8; i++) {
35.s3c_gpio_cfgpin(S5PV210_GPF0(i), S3C_GPIO_SFN(2));
36.s3c_gpio_setpull(S5PV210_GPF0(i), S3C_GPIO_PULL_NONE);
37.}
38.
39.for (i = 0; i < 8; i++) {
40.s3c_gpio_cfgpin(S5PV210_GPF1(i), S3C_GPIO_SFN(2));
41.s3c_gpio_setpull(S5PV210_GPF1(i), S3C_GPIO_PULL_NONE);
42.}
43.
44.for (i = 0; i < 8; i++) {
45.s3c_gpio_cfgpin(S5PV210_GPF2(i), S3C_GPIO_SFN(2));
46.s3c_gpio_setpull(S5PV210_GPF2(i), S3C_GPIO_PULL_NONE);
47.}
48.
49.for (i = 0; i < 4; i++) {
50.s3c_gpio_cfgpin(S5PV210_GPF3(i), S3C_GPIO_SFN(2));
51.s3c_gpio_setpull(S5PV210_GPF3(i), S3C_GPIO_PULL_NONE);
52.}
53.
54./* mDNIe SEL: why we shall write 0x2 ? */
55.writel(0x2, S5P_MDNIE_SEL);
56.
57./* drive strength to max */
58.writel(0xffffffff, S5PV210_GPF0_BASE + 0xc);
59.writel(0xffffffff, S5PV210_GPF1_BASE + 0xc);
60.writel(0xffffffff, S5PV210_GPF2_BASE + 0xc);
61.writel(0x000000ff, S5PV210_GPF3_BASE + 0xc);
62.}
63.
64.static int lte480wv_backlight_on(struct platform_device *pdev)
65.{
66.int err;
67.
68.err = gpio_request(S5PV210_GPD0(0), "GPD0");
69.
70.if (err) {
71.printk(KERN_ERR "failed to request GPD0 for "
72."lcd backlight control\n");
73.return err;
74.}
75.gpio_direction_output(S5PV210_GPD0(0), 1);
76.#if defined(CONFIG_FB_S3C_VGA640480) || defined(CONFIG_FB_AT070TN92)
|| defined(CONFIG_FB_S3C_VGA800600)
77.s3c_gpio_cfgpin(S5PV210_GPD0(0), /*S5PV210_GPD_0_0_TOUT_0*/1);
78.#else
79.s3c_gpio_cfgpin(S5PV210_GPD0(0), S5PV210_GPD_0_0_TOUT_0);
80.#endif
81.gpio_free(S5PV210_GPD0(0));
82.
83.return 0;
84.}
85.
86.static int lte480wv_backlight_off(struct platform_device *pdev, int
onoff)
87.{
88.int err;
89.
90.err = gpio_request(S5PV210_GPD0(0), "GPD0");
91.if (err) {
92.printk(KERN_ERR "failed to request GPD0 for "
93."lcd backlight control\n");
94.return err;
95.}
96.gpio_direction_output(S5PV210_GPD0(0), 1);
97.#if defined(CONFIG_FB_S3C_VGA640480) || defined(CONFIG_FB_AT070TN92)
|| defined(CONFIG_FB_S3C_VGA800600)
98.s3c_gpio_cfgpin(S5PV210_GPD0(0), /*S5PV210_GPD_0_0_TOUT_0*/1);
99.#else
100.s3c_gpio_cfgpin(S5PV210_GPD0(0), S5PV210_GPD_0_0_TOUT_0);
101.#endif
102.gpio_free(S5PV210_GPD0(0));
103.
104.return 0;
105.}
106.
107.static int lte480wv_backlight_off(struct platform_device *pdev, int onoff)
108.{
109.int err;
110.err = gpio_request(S5PV210_GPD0(0), "GPD0");
111.if (err) {
112.printk(KERN_ERR "failed to request GPD0 for "
113."lcd backlight control\n");
114.return err;
115.}
116.
117.gpio_direction_output(S5PV210_GPD0(0), 0);
118.
119.gpio_free(S5PV210_GPD0(0));
120.return 0;
121.}
122.
123.static int lte480wv_reset_lcd(struct platform_device *pdev)
124.{
125.return 0;
126.}
127.
128.static struct s3c_platform_fb lte480wv_fb_data __initdata = {
129..hw_ver = 0x62,
130..nr_wins = 5,
131..default_win = CONFIG_FB_S3C_DEFAULT_WINDOW,
132..swap = FB_SWAP_WORD | FB_SWAP_HWORD,
133..lcd = <e480wv,
134..cfg_gpio = lte480wv_cfg_gpio,
135..backlight_on = lte480wv_backlight_on,
136..backlight_onoff = lte480wv_backlight_off,
137..reset_lcd = lte480wv_reset_lcd,
138.};
139.
140.static void __init smdkc110_machine_init(void)
141.{
142.……
143.s3cfb_set_platdata(<e480wv_fb_data);
144.……
145.}
隐藏
2.重新编译内核zImage,并将zImage拷贝到/tftpboot下。
1.$ cd /home/tarena/workdir/tools/cw210_kernel_
2.6.35.7/
2.$ make zImage
3.$ cp arch/arm/boot/zImage /tftpboot
3.编写测试程序lcd_test.c
编辑lcd_test.c源文件。
1.$ vi lcd_test.c
lcd_test.c源代码如下:
1.#include
2.#include
3.
4.#include
5.#include
6.#include
7.
8.
9.#define COLOR_RED 0x000000FF
10.#define COLOR_GREEN 0x0000FF00
11.#define COLOR_BLUE 0x00FF0000
12.
13.int fdfb = -1;
14.
15.struct fb_fix_screeninfo fbfix = { 0 };
16.struct fb_var_screeninfo fbvar = { 0 };
17.long screensize = 0;
18.
19.int *fb32 = NULL;
20.
21.int x = 0;
22.int y = 0;
23.long location = 0;
24.
25.int main(int argc, char **argv)
26.{
27.// open framebuffer device
28.fdfb = open( argv[1], O_RDWR );
29.if( 0 > fdfb ) {
30.printf("Failure to open framebuffer device:
/dev/fb0 !\n");
31.exit( -1 );
32.}
33.printf("Success to open framebuffer device: /dev/fb0 !\n");
34.
35.// get fixed screen information
36.if( ioctl( fdfb, FBIOGET_FSCREENINFO, &fbfix ) ) {
37.printf("Failure to get fixed screen information !\n");
38.exit( -2 );
39.}
40.printf("Success to get fixed screen information !\n");
41.
42.// get varible screen information
43.if( ioctl( fdfb, FBIOGET_VSCREENINFO, &fbvar ) ) {
44.printf("Failure to get varible screen
information !\n");
45.exit( -3 );
46.}
47.printf("Success to get varible screen information !\n");
48.
49.// calculate the number of bytes for screen size
50.screensize = fbvar.xres * fbvar.yres * ( fbvar.bits_per_pixel
/ 8 );
51.printf("sceeninfo.xres = %d, screeninfo.yres = %d,
screeninfo.bits_per_pixel = %dbpp, screensize = %d !\n", fbvar.xres, fbvar.yres, fbvar.bits_per_pixel, screensize);
52.
53.// mmap framebuffer to process memory space
54.fb32 = (int *)mmap( 0, screensize, PROT_READ | PROT_WRITE,
MAP_SHARED, fdfb, 0 );
55.if( NULL == fb32 ) {
56.printf("Failure to map framebuffer device memory to
process's memory !\n");
57.exit( -4 );
58.}
59.printf("Success to map framebuffer device memory to process's
memory !\n");
60.
61.// draw pixel
62.if( 8 == fbvar.bits_per_pixel ) {
63.printf("Starting 8 bpp framebuffer test...\n");
64.}
65.else if( 16 == fbvar.bits_per_pixel ) {
66.printf("Starting 16 bpp framebuffer test...\n");
67.
68.}
69.else if( 24 == fbvar.bits_per_pixel ) {
70.printf("Starting 24 bpp framebuffer test...\n");
71.}
72.else {
73.printf("Supporting 32 bpp !\n");
74.// draw red color area
75.printf("Starting to show RED area !\n");
76.for( y = 0; y < fbvar.yres / 3; y++ ) {
77.for( x = 0; x < fbvar.xres; x++ ) {
78.*( fb32 + y * fbvar.xres + x ) =
COLOR_RED;
79.}
80.}
81.
82.// draw green color area
83.printf("Starting to show GREEN area !\n");
84.for( y = ( fbvar.yres / 3 ); y < ( fbvar.yres * 2 / 3);
y++ ) {
85.for( x = 0; x < fbvar.xres; x++ ) {
86.*( fb32 + y * fbvar.xres + x ) =
COLOR_GREEN;
87.}
88.}
89.
90.// draw blue color area
91.printf("Starting to show BLUE area !\n");
92.for( y = ( fbvar.yres * 2 / 3 ); y < fbvar.yres; y++ )
{
93.for( x = 0; x < fbvar.xres; x++ ) {
94.*( fb32 + y * fbvar.xres + x ) =
COLOR_BLUE;
95.}
96.}
97.}
98.
99.// unmap framebuffer memory
100.munmap( fb32, screensize );
101.
102.printf("Finished to demo to operate framebuffer !\n");
103.
104.// close device handle
105.close( fdfb );
106.
107.return 0;
108.}
隐藏
4.编写Makefile文件。
1.# makefile for kernel module
2.
3.MODNAME = lcd_drv
4.CROSS_COMPILE = arm-linux-
5.TESTPRE = lcd_test
6.
7.ifeq ($(PLATFORM), PC)
8.KERNELDIR ?= /lib/modules/$(shell uname -r)/build
9.else
https://www.doczj.com/doc/ba8854896.html, = $(CROSS_COMPILE)gcc
11.KERNELDIR ?=/home/tarena/workdir/tools/cw210_kernel_2.6.35.7
12.
13.endif
14.
15.obj-m := $(MODNAME).o
16.
17.default: test driver
18.
19.test:
20.@echo "Compiling test program..."
21.$(CC) $(TESTPRE).c -o $(TESTPRE)
22.@echo
23.
24.driver:
25.@echo "Compiling device driver..."
26.$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
27.@echo
28.
29.clean:
30.@echo "Cleanup test program..."
31.@rm -f $(TESTPRE) $(MODNAME)
32.@echo
33.@echo "Cleanup device driver..."
34.@rm -rf *.o *.ko *.mod.* .$(MODNAME).* .tmp* module* Module*
35.@echo
5.编译lcd驱动模块,并将生成的lcd_test文件拷贝到指定目录下,执行如下命令:
1.$ make
2.$ cp lcd_test /home/tarena/workdir/rootfs/rootfs/
6.在TPAD的运行终端上运行应用测试程序,测试驱动程序。
Linux设备驱动程序设计实例2007-03-03 23:09 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,在与硬件设备之间 建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设 备进行打开(open)、关闭(close)、读写(read/write)等操作。通过分析和设计设 备驱动程序,可以深入理解Linux系统和进行系统开发。本文通过一个简单的例子 来说明设备驱动程序的设计。 1、程序清单 //MyDev.c 2000年2月7日编写 #ifndef __KERNEL__ #define __KERNEL__//按内核模块编译 #endif #ifndef MODULE #define MODULE//设备驱动程序模块编译 #endif #define DEVICE_NAME "MyDev" #define OPENSPK 1 #define CLOSESPK 2 //必要的头文件 #include
实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {
int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;
Linux的LCD驱动源码分析及移植(三部曲) 第一部分: 基于ARM9处理器的linux-2.6.32.2操作系统内核移植手记part5.1(LCD驱动源码分析及移植之platform device) 1.与LCD控制器硬件相关的寄存器内容请参照三星S3C2440A技术手册中的第15章。 2. LCD Controller的平台设备定义如下(文件位于linux/arch/arm/plat-s3c24xx/devs.c):
1./* LCD Controller */ 2. 3.static struct resource s3c_lcd_resource[] = { 4. [0] = { 5. .start = S3C24XX_PA_LCD, 6. .end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1, 7. .flags = IORESOURCE_MEM, 8. }, 9. [1] = { 10. .start = IRQ_LCD, 11. .end = IRQ_LCD, 12. .flags = IORESOURCE_IRQ, 13. } 14. 15.}; 16. 17.static u64 s3c_device_lcd_dmamask = 0xffffffffUL; 18. 19.struct platform_device s3c_device_lcd = { 20. .name = "s3c2410-lcd", 21. .id = -1, 22. .num_resources = ARRAY_SIZE(s3c_lcd_resource), 23. .resource = s3c_lcd_resource, 24. .dev = { 25. .dma_mask = &s3c_device_lcd_dmamask, 26. .coherent_dma_mask = 0xffffffffUL 27. } 28.}; 29. 30.EXPORT_SYMBOL(s3c_device_lcd); 平台设备的结构体定义为s3c_device_lcd,该设备在平台总线中的名字取为s3c2410-lcd,该平台设备申请的两个板级资源为以S3C24XX_PA_LCD为起始的IORESOURCE_MEM资源和一个定义为IRQ_LCD的IORESOURCE_IRQ资源。 其中, 1.#define S3C24XX_PA_LCD S3C2410_PA_LCD 1./* LCD controller */ 2.#define S3C2410_PA_LCD (0x4D000000) 3.#define S3C24XX_SZ_LCD SZ_1M 0x4D000000为LCDCON1寄存器的地址。
linux驱动开发的经典书籍 结构、操作系统、体系结构、编译原理、计算机网络你全修过 我想大概可以分为4个阶段,水平从低到高 从安装使用=>linux常用命令=>linux系统编程=>内核开发阅读内核源码 其中学习linux常用命令时就要学会自己编译内核,优化系统,调整参数 安装和常用命令书太多了,找本稍微详细点的就ok,其间需要学会正则表达式 系统编程推荐《高级unix环境编程》,黑话叫APUE 还有《unix网络编程》 这时候大概还需要看资料理解elf文件格式,连接器和加载器,cmu的一本教材中文名为《深入理解计算机系统》比较好 内核开发阅读内核源码阶段,从写驱动入手逐渐深入linux内核开发 参考书如下《linux device drivers》,黑话叫ldd 《linux kernel development》,黑话叫lkd 《understading the linux kernel》,黑话叫utlk 《linux源码情景分析》 这四本书为搞内核的必读书籍 最后,第三阶段和第四阶段最重动手,空言无益,光看书也不罩,不动手那些东西理解不了 学习linux/unix编程方法的建议 建议学习路径: 首先先学学编辑器,vim, emacs什么的都行。 然后学make file文件,只要知道一点就行,这样就可以准备编程序了。 然后看看《C程序设计语言》K&R,这样呢,基本上就可以进行一般的编程了,顺便找本数据结构的书来看。 如果想学习UNIX/LINUX的编程,《APUE》绝对经典的教材,加深一下功底,学习《UNP》的第二卷。这样基本上系统方面的就可以掌握了。 然后再看Douglus E. Comer的《用TCP/IP进行网际互连》第一卷,学习一下网络的知识,再看《UNP》的第一卷,不仅学习网络编程,而且对系统编程的一些常用的技巧就很熟悉了,如果继续网络编程,建议看《TCP/IP进行网际互连》的第三卷,里面有很多关于应用
Linux设备驱动程序学习(18)-USB 驱动程序(三) (2009-07-14 11:45) 分类:Linux设备驱动程序 USB urb (USB request block) 内核使用2.6.29.4 USB 设备驱动代码通过urb和所有的 USB 设备通讯。urb用 struct urb 结构描述(include/linux/usb.h )。 urb以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。 一个 urb 的典型生命循环如下: (1)被创建; (2)被分配给一个特定 USB 设备的特定端点; (3)被提交给 USB 核心; (4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动; (5)被 USB 主机控制器驱动处理, 并传送到设备; (6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。 urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB 核心取消。urb 被动态创建并包含一个内部引用计数,使它们可以在最后一个用户释放它们时被自动释放。 struct urb
struct list_head urb_list;/* list head for use by the urb's * current owner */ struct list_head anchor_list;/* the URB may be anchored */ struct usb_anchor *anchor; struct usb_device *dev;/* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被USB 驱动初始化.*/ struct usb_host_endpoint *ep;/* (internal) pointer to endpoint */ unsigned int pipe;/* 这个 urb 所要发送到的特定struct usb_device 的端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.必须由下面的函数生成*/ int status;/*当 urb开始由 USB 核心处理或处理结束, 这个变量被设置为 urb 的当前状态. USB 驱动可安全访问这个变量的唯一时间是在 urb 结束处理例程函数中. 这个限制是为防止竞态. 对于等时 urb, 在这个变量中成功值(0)只表示这个 urb 是否已被去链. 为获得等时 urb 的详细状态, 应当检查 iso_frame_desc 变量. */ unsigned int transfer_flags;/* 传输设置*/ void*transfer_buffer;/* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。对控制端点, 这个缓冲区用于数据中转*/ dma_addr_t transfer_dma;/* 用于以 DMA 方式传送数据到 USB 设备的缓冲区*/ int transfer_buffer_length;/* transfer_buffer 或者 transfer_dma 变量指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用。对于一个 OUT 端点, 如果这个端点大小比这个变量指定的值小, 对这个USB 设备的传输将被分成更小的块,以正确地传送数据。这种大的传送以连续的 USB 帧进行。在一个 urb 中提交一个大块数据, 并且使 USB 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/
LCD驱动分析_LCD控制器设置及代码详解 1. LCD工作的硬件需求: 要使一块LCD正常的显示文字或图像,不仅需要LCD驱动器,而且还需要相应的LCD 控制器。在通常情况下,生产厂商把LCD驱动器会以COF/COG的形式与LCD玻璃基板制作在一起,而LCD控制器则是由外部的电路来实现,现在很多的MCU内部都集成了LCD控制器,如S3C2410/2440等。通过LCD控制器就可以产生LCD驱动器所需要的控制信号来控制STN/TFT屏了。 2. S3C2440内部LCD控制器结构图: 我们根据数据手册来描述一下这个集成在S3C2440内部的LCD控制器: a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器组成; b:REGBANK由17个可编程的寄存器组和一块256*16的调色板内存组成,它们用来配置LCD控制器的; c:LCDCDMA是一个专用的DMA,它能自动地把在侦内存中的视频数据传送到LCD驱动器,通过使用这个DMA通道,视频数据在不需要CPU的干预的情况下显示在LCD屏上; d:VIDPRCS接收来自LCDCDMA的数据,将数据转换为合适的数据格式,比如说4/8位单扫,4位双扫显示模式,然后通过数据端口VD[23:0]传送视频数据到LCD驱动器;e:TIMEGEN由可编程的逻辑组成,他生成LCD驱动器需要的控制信号,比如VSYNC、HSYNC、VCLK和LEND等等,而这些控制信号又与REGBANK寄存器组中的LCDCON1/2/3/4/5的配置密切相关,通过不同的配置,TIMEGEN就能产生这些信号的不同形态,从而支持不同的LCD驱动器(即不同的STN/TFT屏)。 3. 常见TFT屏工作时序分析: LCD提供的外部接口信号:
Linux2.6设备驱动常用的接口函数(一) ----字符设备 刚开始,学习linux驱动,觉得linux驱动很难,有字符设备,块设备,网络设备,针对每一种设备其接口函数,驱动的架构都不一样。这么多函数,要每一个的熟悉,那可多难啦!可后来发现linux驱动有很多规律可循,驱动的基本框架都差不多,再就是一些通用的模块。 基本的架构里包括:加载,卸载,常用的读写,打开,关闭,这是那种那基本的咯。利用这些基本的功能,当然无法实现一个系统。比方说:当多个执行单元对资源进行访问时,会引发竞态;当执行单元获取不到资源时,它是阻塞还是非阻塞?当突然间来了中断,该怎么办?还有内存管理,异步通知。而linux 针对这些问题提供了一系列的接口函数和模板框架。这样,在实际驱动设计中,根据具体的要求,选择不同的模块来实现其功能需求。 觉得能熟练理解,运用这些函数,是写号linux设备驱动的第一步。因为是设备驱动,是与最底层的设备打交道,就必须要熟悉底层设备的一些特性,例如字符设备,块设备等。系统提供的接口函数,功能模块就像是工具,能够根据不同的底层设备的的一些特性,选择不同的工具,方能在linux驱动中游刃有余。 最后就是调试,这可是最头疼的事。在调试过程中,总会遇到这样,那样的问题。怎样能更快,更好的发现并解决这些问题,就是一个人的道行咯!我个人觉得: 发现问题比解决问题更难! 时好时坏的东西,最纠结! 看得见的错误比看不见的错误好解决! 一:Fops结构体中函数: ①ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以-EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ②ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数 ③loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ④int (*open) (struct inode *, struct file *);
linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。
本人此刻还不是什么驱动工程师,连入门都谈不上,但我坚信在未来的3-5年我肯定能成为我想像中的人,因为我马上就要进入这一行工作了。写下这个日志来记录我是怎么最后成为我想像中的人才的,呵呵。 《Linux驱动工程师》这个东西是我在大二的时候看到有一篇讲如何学习嵌入式的,点击这里下载PDF,里面讲到嵌入式分为四层:硬件,驱动,系统,应用程序;还说linux驱动最难然后工资也最高就冲着他这句话我就决定我大学毕业的时候要去做这个linux驱动工程师,随后我就先后买了51单片机,ARM7,ARM9还有一大堆的视频教程准备来进行学习。我还跟我旁边那个哈工大哥们说:“我们学校像我这样的人很少,你们学校呢?”他说:“太少了,不过我们学校都是做这种板子卖的人比较多!”。行,你们牛!即使是买了这些东西,从大二到现在都快毕业了但感觉还是没有入门。回想一下我都学过什么啊:1:自己在ARM9上写bootloader(主要锻炼了三方面的知识:C语言应该写了有近万行的代码,ARM9的外设的基本操作方法如UART,LCD,TOUCH,SD,USB,ETHERNET...,makefile);2:移植和学习linux驱动。下面我说一下我学习Linux驱动的一个思路这也是我在面试的时候自我介绍中最重要的部分;1:硬件知识学习Linux驱动首先得了解这个驱动对应的硬件的一些基本原理和操作方法比如LCD你得了解它的场同步,行同步,像素时钟,一个像素的表示模式,还有就是这个LCD是怎么把图像显示在屏幕上的。如果是USB,SD卡就得了解相关协议。可以通过spec(协议)、datasheet来了解,这就是传说中的Linux驱动开发三件宝之二,还有一个就是linux相关源码。2:了解linux驱动框架linux下的每一类驱动差不多都是一个比较完善的子系统,比如FLASH的驱动它就属于MTD子系统从上到下分为四层:设备节点层,设备层,原始设备层,最下面的与具体硬件相关的硬件驱动层,通常要我们自己来实现就是最下面这个与具体硬件相关那部分代码。3:了解这个驱动的数据流。这个过程与第二个过程紧密相关,如果了解了驱动的框架差不多这个过程也算了解了。比如flash.在/dev/目录下有对应flash的字符设备文件和块设备文件,用户对这些文件进行读、写、ioctl操作,其间通过层层的函数调用最终将调用到最下面的硬件驱动层对硬件进行操作。了解这个过程我相信在调试驱动的时候是很有帮助。3:分析与硬件相关通常需要我们实现的那部分源代码。4:三板子上将驱动调试出来。每次调试都会出问题,但我买的板子提供的资料比较全调试过程中遇到的问题都比较浅显,即使是浅显的问题也要把它记录下来。(这个是我上次在华为面试的时候,那个人问我你调试驱动遇到过什么问题吗?你是如何解决的。当时我学习还没有到调试驱动这一步,所以那次面试也惨败收场)。 好像说了这么多,还没有进入正题《工作的选择》。在年前去了龙芯,实习2.8K,转正3.5k,环境还是不错,经理很好,头儿也很帅都是中科院的硕士。不过去了两周我就没去了身边的人都不太理解,我也一度有过后悔的时候,从龙芯出来应该是1月6号,也就是从那个时候开始我就没有再找工作,转而学习linux驱动。一直到上周日。上周日的晚上我就开始投简历一开始要找linux驱动,在智联里面输入linux驱动出来500来个职位,点开一看没有一个自己符合要求的,差不多都要3-5年经验本科,有时候好不容易有个实习的关键字在里面,一看要求硕士,严重打击了我的信心,哎不管了随便投,最后又投了一下嵌入式关键字的职位。最后就瞎申请,看看职位要求差不多就申请。周一来了,这周一共来了6个面试,创下了我求职以来的历史新高。周一下午面了一家感觉还不错不过到现在也没有给我一个通知,估计当时我要了4500把他给要跑了,这家是做测量的不是Linux驱动,差不多是把ARM当单片机用。周二上午一家也是要招linux驱动面了估计不到二分钟,他
Linux设备驱动程序学习(20)-内存映射和DMA-基本概念 (2011-09-25 15:47) 标签: 虚拟内存设备驱动程序Linux技术分类:Linux设备驱动程序 这部分主要研究 Linux 内存管理的基础知识, 重点在于对设备驱动有用的技术. 因为许多驱动编程需要一些对于虚拟内存(VM)子系统原理的理解。 而这些知识主要分为三个部分: 1、 mmap系统调用的实现原理:它允许设备内存直接映射到一个用户进程地址 空间. 这样做对一些设备来说可显著地提高性能. 2、与mmap的功能相反的应用原理:内核态代码如何跨过边界直接存取用户空间的内存页. 虽然较少驱动需要这个能力. 但是了解如何映射用户空间内存到内 核(使用 get_user_pages)会有用. 3、直接内存存取( DMA ) I/O 操作, 它提供给外设对系统内存的直接存取. 但所有这些技术需要理解 Linux 内存管理的基本原理, 因此我将先学习VM子 系统的基本原理. 一、Linux的内存管理 这里重点是 Linux 内存管理实现的主要特点,而不是描述操作系统的内存管理理论。Linux虚拟内存管理非常的复杂,要写可以写一本书:《深入理解Linux 虚拟内存管理》。学习驱动无须如此深入, 但是对它的工作原理的基本了解是必要的. 解了必要的背景知识后,才可以学习内核管理内存的数据结构. Linux是一个虚拟内存系统(但是在没有MMU的CPU中跑的ucLinux除外), 意味着在内核启动了MMU 之后所有使用的地址不直接对应于硬件使用的物理地址,这些地址(称之为虚拟地址)都经过了MMU转换为物理地址之后再从CPU的内存总线中发出,读取/写入数据. 这样 VM 就引入了一个间接层, 它是许多操作成为可能: 1、系统中运行的程序可以分配远多于物理内存的内存空间,即便单个进程都可拥有一个大于系统的物理内存的虚拟地址空间. 2、虚拟内存也允许程序对进程的地址空间运用多种技巧, 包括映射程序的内存到设备内存.等等~~~ 1、地址类型 Linux 系统处理几种类型的地址, 每个有它自己的含义: 用户虚拟地址:User virtual addresses,用户程序见到的常规地址. 用户地址在长度上是 32 位或者 64 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.
一.接口 LCD1602是很多单片机爱好者较早接触的字符型液晶显示器,它的主控芯片是HD44780或者其它兼容芯片。刚开始接触它的大多是单片机的初学者。由于对它的不了解,不能随心所欲地对它进行驱动。经过一段时间的学习,我对它的驱动有了一点点心得,今天把它记录在这里,以备以后查阅。与此相仿的是LCD12864液晶显示器,它是一种图形点阵显示器,能显示的内容比LCD1602要丰富得多,除了普通字符外,还可以显示点阵图案,带有汉字库的还可以显示汉字,它的并行驱动方式与LCD1602相差无几,所以,在这里花点时间是值得的。 一般来说,LCD1602有16条引脚,据说还有14条引脚的,与16脚的相比缺少了背光电源A(15脚)和地线K(16脚)。我手里这块LCD16 02的型号是HJ1602A,是绘晶科技公司的产品,它有16条引脚。如图1所示:
图1 再来一张它的背面的,如图2所示:
图2它的16条引脚定义如下:
3. VO是液晶显示的偏压信号,可接10K的3296精密电位器。或同样阻值的RM065/RM063蓝白可调电阻。见图3。 图3 4. RS是命令/数据选择引脚,接单片机的一个I/O,当RS为低电 平时,选择命令;当RS为高电平时,选择数据。 5. RW是读/写选择引脚,接单片机的一个I/O,当RW为低电平时,向LCD1602写入命令或数据;当RW为高电平时,从LCD1602读取状态 或数据。如果不需要进行读取操作,可以直接将其接VSS。 6. E,执行命令的使能引脚,接单片机的一个I/O。 7. D0—D7,并行数据输入/输出引脚,可接单片机的P0—P3任意 的8个I/O口。如果接P0口,P0口应该接4.7K—10K的上拉电阻。如果是4线并行驱动,只须接4个I/O口。 8. A背光正极,可接一个10—47欧的限流电阻到VDD。 9. K背光负极,接VSS。见图4所示。
参考: 韦东山视频第10课第一节内核启动流程分析之编译体验 第11课第三节构建根文件系统之busybox 第11课第四节构建根文件系统之构建根文件系统韦东山书籍《嵌入式linux应用开发完全手册》 其他《linux设备驱动程序》第三版 平台: JZ2440、mini2440或TQ2440 交叉网线和miniUSB PC机(windows系统和Vmware下的ubuntu12.04) 一、交叉编译环境的选型 具体的安装交叉编译工具,网上很多资料都有,我的那篇《arm-linux- gcc交叉环境相关知识》也有介绍,这里我只是想提示大家:构建跟文件系统中所用到的lib库一定要是本系统Ubuntu中的交叉编译环境arm-linux- gcc中的。即如果电脑ubuntu中的交叉编译环境为arm-linux-
二、主机、开发板和虚拟机要三者互通 w IP v2.0》一文中有详细的操作步骤,不再赘述。 linux 2.6.22.6_jz2440.patch组合而来,具体操作: 1. 解压缩内核和其补丁包 tar xjvf linux-2.6.22.6.tar.bz2 # 解压内核 tar xjvf linux-2.6.22.6_jz2440.tar.bz2 # 解压补丁
cd linux_2.6.22.6 patch –p1 < ../linux-2.6.22.6_jz2440.patch 3. 配置 在内核目录下执行make 2410_defconfig生成配置菜单,至于怎么配置,《嵌入式linux应用开发完全手册》有详细介绍。 4. 生成uImage make uImage 四、移植busybox 在我们的根文件系统中的/bin和/sbin目录下有各种命令的应用程序,而这些程序在嵌入式系统中都是通过busybox来构建的,每一个命令实际上都是一个指向bu sybox的链接,busybox通过传入的参数来决定进行何种命令操作。 1)配置busybox 解压busybox-1.7.0,然后进入该目录,使用make menuconfig进行配置。这里我们这配置两项 一是在编译选项选择动态库编译,当然你也可以选择静态,不过那样构建的根文件系统会比动态编译的的大。 ->Busybox Settings ->Build Options
LCD Driver(液晶驱动器) 在单片机的应用中,人机界面占据相当重要的地位。人机界面主要包括事件输入和结果指示,事件输入包括键盘输入,通讯接口,事件中断等,结果指示包括LED/LCD显示、通讯接口、外围设备操作等。而在这些人机界面当中,LCD 显示技术由于其具有界面友好,成本较低等特点而在很多应用场合得以广泛应用。 1.LCD的显示原理 在讲解LCD driver之前,我们先就LCD的显示原理作一简单的介绍。 LCD(Liquid Crystal Display)是利用液晶分子的物理结构和光学特性进行显示的一种技术。液晶分子的特性: 液晶分子是介于固体和液体之间的一种棒状结构的大分子物质; 在自然形态,具有光学各向异性的特点,在电(磁)场作用下,呈各向同性特点; 下面以直视型简单多路TN/STN LCD Panel(液晶显示面板)的基本结构介绍LCD的基本显示原理,示意图如图-1: 图-1 LCD的基本显示原理
整个LCD Panel 由上下玻璃基板和偏振片组成,在上下玻璃之间,按照螺旋结构将液晶分子有规律的进行涂层。液晶面板的电极是通过一种ITO 的金属化合物蚀刻在上下玻璃基板上。如图所示,液晶分子的排列为螺旋结构,对光线具有旋旋光性,上下偏振片的偏振角度相互垂直。在上下基板间的电压为0时,自然光通过偏振片后,只有与偏振片方向相同的光线得以进入液晶分子的螺旋结构的涂层中,由于螺旋结构的的旋旋光性,将入射光线的方向旋转90度后照射到另一端的偏振片上,由于上下偏振片的偏振角度相互垂直,这样入射光线通过另一端的偏振片完全的射出,光线完全进入观察者的眼中,看到的效果就为白色。而在上下基板间的电压为一交流电压时,液晶分子的螺旋结构在电(磁)场的作用下,变成了同向排列结构,对光线的方向没有作任何旋转,而上下偏振片的偏振角度相互垂直,这样入射光线就无法通过另一端的偏振片射出,光线无法进入观察者的眼中,看到的效果就为黑色。这样通过在上下玻璃基板电极间施加不同的交流电压,即可实现液晶显示的两种基本状态亮(On)和暗(Off)。 在实际的液晶模以驱动电压中,有几个参数非常关键: 交流电压,液晶分子是需要交流信号来驱动的,长时间的直流电压加在液晶分子两端,会影响液晶分子的电气化学特性,引起显示模糊,寿命的减少,其破坏性为不可恢复; 扫描频率,直接驱动液晶分子的交流电压的频率一般在60~100Hz 之间,具体是依据LCD Panel 的面积和设计而定,频率过高,会导致驱动功耗的增加,频率过低,会导致显示闪烁,同时如果扫描频率同光源的频率之间有倍数关系,则显示也会有闪烁现象出现。 图-2 帧频(Frame)示意图 液晶分子是一种电压积分型材料,它的扭曲程度(透光性)仅仅和极板间电压的有效值有关,和充电波形无关。电压的有效值用COM/SEG 之间的电压差值的均方根VRMS 表示: []dt t V T RMS V T 2 )(1 )(∫= LCD 显示黑白(透光和不透光)的电压有效值的分界电压称为开启电压Vth,当电压有效值超过Vth,螺旋结构的旋光角度加大,透光率急剧变化,透明度急剧上升。反之,则透明度急剧下降。光线的透射率与交流电压的有效值的关系如图-3:
第一章Linux设备驱动程序简介 Linux Kernel 系统架构图 一、驱动程序的特点 ?是应用和硬件设备之间的一个软件层。 ?这个软件层一般在内核中实现 ?设备驱动程序的作用在于提供机制,而不是提供策略,编写访问硬件的内核代码时不要给用户强加任何策略 o机制:驱动程序能实现什么功能。 o策略:用户如何使用这些功能。 二、设备驱动分类和内核模块 ?设备驱动类型。Linux 系统将设备驱动分成三种类型 o字符设备 o块设备 o网络设备 ?内核模块:内核模块是内核提供的一种可以动态加载功能单元来扩展内核功能的机制,类似于软件中的插件机制。这种功能单元叫内核模块。 ?通常为每个驱动创建一个不同的模块,而不在一个模块中实现多个设备驱动,从而实现良好的伸缩性和扩展性。 三、字符设备 ?字符设备是个能够象字节流<比如文件)一样访问的设备,由字符设备驱动程序来实现这种特性。通过/dev下的字符设备文件来访问。字符设备驱动程序通常至少需要实现 open、close、read 和 write 等系统调用 所对应的对该硬件进行操作的功能函数。 ?应用程序调用system call<系统调用),例如:read、write,将会导致操作系统执行上层功能组件的代码,这些代码会处理内核的一些内部 事务,为操作硬件做好准备,然后就会调用驱动程序中实现的对硬件进 行物理操作的函数,从而完成对硬件的驱动,然后返回操作系统上层功 能组件的代码,做好内核内部的善后事务,最后返回应用程序。 ?由于应用程序必须使用/dev目录下的设备文件<参见open调用的第1个参数),所以该设备文件必须事先创建。谁创建设备文件呢? ?大多数字符设备是个只能顺序访问的数据通道,不能前后移动访问指针,这点和文件不同。比如串口驱动,只能顺序的读写设备。然而,也 存在和数据区或者文件特性类似的字符设备,访问它们时可前后移动访
文库资料?2017 Guangzhou ZHIYUAN Electronics Stock Co., Ltd. 如何实现Linux 设备驱动模型 设备驱动模型,对系统的所有设备和驱动进行了抽象,形成了复杂的设备树型结构,采用面向对象的方法,抽象出了device 设备、driver 驱动、bus 总线和class 类等概念,所有已经注册的设备和驱动都挂在总线上,总线来完成设备和驱动之间的匹配。总线、设备、驱动以及类之间的关系错综复杂,在Linux 内核中通过kobject 、kset 和subsys 来进行管理,驱动编写可以忽略这些管理机制的具体实现。 设备驱动模型的内部结构还在不停的发生改变,如device 、driver 、bus 等数据结构在不同版本都有差异,但是基于设备驱动模型编程的结构基本还是统一的。 Linux 设备驱动模型是Linux 驱动编程的高级内容,这一节只对device 、driver 等这些基本概念作介绍,便于阅读和理解内核中的代码。实际上,具体驱动也不会孤立的使用这些概念,这些概念都融合在更高层的驱动子系统中。对于大多数读者可以忽略这一节内容。 1.1.1 设备 在Linux 设备驱动模型中,底层用device 结构来描述所管理的设备。device 结构在文件
Linux设备驱动程序学习(10)-时间、延迟及延缓操作 Linux设备驱动程序学习(10) -时间、延迟及延缓操作 度量时间差 时钟中断由系统定时硬件以周期性的间隔产生,这个间隔由内核根据HZ 值来设定,HZ 是一个体系依赖的值,在
的.config文件中定义,并没有在make menuconfig的配置选项中出现。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定义为: 所以正常情况下s3c24x0的HZ为200。这一数值在后面的实验中可以证实。 每次发生一个时钟中断,内核内部计数器的值就加一。这个计数器在系统启动时初始化为0,因此它代表本次系统启动以来的时钟嘀哒数。这个计数器是一个64-位变量( 即便在32-位的体系上)并且称为“jiffies_64”。但是驱动通常访问jiffies 变量(unsigned long)(根据体系结构的不同:可能是jiffies_64 ,可能是jiffies_64 的低32位)。使用jiffies 是首选,因为它访问更快,且无需在所有的体系上实现原子地访问64-位的jiffies_64 值。 使用jiffies 计数器 这个计数器和用来读取它的工具函数包含在
Linux设备驱动 操作系统的目的之一就是将系统硬件设备细节从用户视线中隐藏起来。例如虚拟文件系统对各种类型已安装的文件系统提供了统一的视图而屏蔽了具体底层细节。本章将描叙Linux核心对系统中物理设备的管理。 CPU并不是系统中唯一的智能设备,每个物理设备都拥有自己的控制器。键盘、鼠标和串行口由一个高级I/O芯片统一管理,IDE控制器控制IDE硬盘而SCSI控制器控制SCSI硬盘等等。每个硬件控制器都有各自的控制和状态寄存器(CSR)并且各不相同。例如Adaptec 2940 SCSI控制器的CSR与NCR 810 SCSI控制器完全不一样。这些CSR被用来启动和停止,初始化设备及对设备进行诊断。在Linux中管理硬件设备控制器的代码并没有放置在每个应用程序中而是由内核统一管理。这些处理和管理硬件控制器的软件就是设备驱动。Linux 核心设备驱动是一组运行在特权级上的内存驻留底层硬件处理共享库。正是它们负责管理各个设备。 设备驱动的一个基本特征是设备处理的抽象概念。所有硬件设备都被看成普通文件;可以通过和操纵普通文件相同的标准系统调用来打开、关闭、读取和写入设备。系统中每个设备都用一种特殊的设备相关文件来表示(device special file),例如系统中第一个IDE硬盘被表示成/dev/hda。块(磁盘)设备和字符设备的设备相关文件可以通过mknod命令来创建,并使用主从设备号来描叙此设备。网络设备也用设备相关文件来表示,但Linux寻找和初始化网络设备时才建立这种文件。由同一个设备驱动控制的所有设备具有相同的主设备号。从设备号则被用来区分具有相同主设备号且由相同设备驱动控制的不同设备。例如主IDE硬盘的每个分区的从设备号都不相同。如/dev/hda2表示主IDE 硬盘的主设备号为3而从设备号为2。Linux通过使用主从设备号将包含在系统调用中的(如将一个文件系统mount到一个块设备)设备相关文件映射到设备的设备驱动以及大量系统表格中,如字符设备表,chrdevs。 Linux支持三类硬件设备:字符、块及网络设备。字符设备指那些无需缓冲直接读写的设备,如系统的串口设备/dev/cua0和/dev/cua1。块设备则仅能以块为单位读写,典型的块大小为512或1024字节。块设备的存取是通过