当前位置:文档之家› 手把手要教你编写Linux设备驱动程序

手把手要教你编写Linux设备驱动程序

手把手要教你编写Linux设备驱动程序
手把手要教你编写Linux设备驱动程序

如何编写Linux设备驱动程序

Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。

以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料。

一、Linux device driver 的概念

系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1。对设备初始化和释放。

2。把数据从内核传送到硬件和从硬件读取数据。

3。读取应用程序传送给设备文件的数据和回送应用程序请求的数据。

4。检测和处理设备出现的错误。

在Linux操作系统下有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。

已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他

们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。

最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。

读/写时,它首先察看缓冲区的内容,如果缓冲区的数据

如何编写Linux操作系统下的设备驱动程序

二、实例剖析

我们来写一个最简单的字符设备(比如蜂鸣器)驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理。把下面的C代码输入机器,你就会获得一个真正的设备驱动程序。

#define __NO_VERSION__

#include

#include

char kernel_version [] = UTS_RELEASE;

这一段定义了一些版本信息,虽然用处不是很大,但也必不可少。Johnsonm 说所有的驱动程序的开头都要包含,一般来讲最好使用。

由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如open,read,write,close…,注意,不是fopen,fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构:

struct file_operations

{

int (*seek) (struct inode * ,struct file *,off_t ,int); //文件定位

int (*read) (struct inode * ,struct file *,char ,int);

int (*write) (struct inode * ,struct file *,off_t ,int);

int (*readdir) (struct inode * ,struct file *,struct dirent * ,int); //读取目录

int (*select) (struct inode * ,struct file *,int ,select_table *);

// I/O端口复用,非阻塞的状态下实现设备的访问

int (*ioctl) (struct inode * ,struct file *,unsined int ,unsigned long);

//对设备的属性修改

int (*mmap) (struct inode * ,struct file *,struct vm_area_struct *);

// 内存映射

int (*open) (struct inode * ,struct file *);

int (*release) (struct inode * ,struct file *);

int (*fsync) (struct inode * ,struct file *); //设备的同步信息

int (*fasync) (struct inode * ,struct file *,int); //异步

int (*check_media_change) (struct inode * ,struct file *); //检测数据是否发生改变int (*revalidate) (dev_t dev); //使设备重新有效

}

这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。

下面就开始写子程序。

#include

#include //文件系统

#include //内存管理

#include

#include //汇编语言编写

unsigned int test_major = 0; //主设备号,自动搜索

static int read_test(struct inode *node,struct file *file,char *buf,int count)

//读测试,inode *node表示哪一个设备,*file表示文件描述符,*buf表读取时的接口,count表期望读的字节数,static int 返回值—实际读到的数量

{

int left;

if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )

// verify_area,验证某一个buf中的数据是否有效

return -EFAULT;

for(left = count ; left > 0 ; left--)

{

__put_user(1,buf,1); //从内核空间将数据拷贝到用户空间去,将”1”依次放到用户空间的buf中,每次放的大小是一个字节

buf++;

}

return count; //返回读的数据数

}

这个函数是为read调用准备的。当调用read时,read_test()被调用,它把用户的缓冲区全部写1。buf 是read调用的一个参数。它是用户进程空间的一个地址。但是在read_test被调用时,系统进入核心态。所以不能使用buf这个地址,必须用__put_user(),这是kernel提供的一个函数,用于向用户传送数据。另外还有很多类似功能的函数。请参考,在向用户空间拷贝数据之前,必须验证buf 是否可用。这就用到函数verify_area。

static int write_tibet(struct inode *inode,struct file *file,const char *buf,int count) {

return count;

}

// *inode表入口节点,*file表设备描述符

static int open_tibet(struct inode *inode,struct file *file )

{

MOD_INC_USE_COUNT; //宏定义,注册一个驱动之后,模块数自动+1

return 0;

}

static void release_tibet(struct inode *inode,struct file *file )

{

MOD_DEC_USE_COUNT; // 模块数自动减1

}

这几个函数都是空操作。实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。

struct file_operations test_fops = {

NULL,

read_test,

write_test,

NULL,/* test_readdir */

NULL,

NULL,/* test_ioctl */

NULL,/* test_mmap */

open_test,

release_test,

NULL,/* test_fsync */

NULL,/* test_fasync */

/* nothing more,fill with NULLs */

};

设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。

int init_module(void) //注册方式

{

int result;

result = register_chrdev(0,"test",&test_fops);

//注册字符型设备到内核中去,“0”表是自动根据设备节点里的主设备号来获取它的设备号;"test"表示你注册的设备名;&test_fops为注册的接口

if (result < 0) {

printk(KERN_INFO "test: can't get major number\n"); //在内核打印信息必须用printk;而print f只能在用户空间用

return result;

}

if (test_major == 0) test_major = result; /* dynamic */

//由内核分配一个设备号给驱动程序,获取设备的主设备号

return 0;

}

在用insmod命令将编译好的模块调入内存时,init_module 函数被调用。在这里,init_module只做了一件事,就是向系统的字符设备表登记了一个字符设备。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一个没有被占用的设备号返回。参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针。

如果登记成功,返回设备的主设备号,不成功,返回一个负值。

void cleanup_module(void)

{

unregister_chrdev(test_major,"test");

}

在用rmmod卸载模块时,cleanup_module函数被调用,它释放字符设备test 在系统字符设备表中占有的表项。

一个极其简单的字符设备可以说写好了,文件名就叫test.c吧。

下面编译(模块方式):

$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c

//-O2表二级优化;-DMODULE表编译成模块;-D__KERNEL__表加载内核某一个块;2.6内核编译出来是test.ko文件

得到文件test.o就是一个设备驱动程序。

如果设备驱动程序有多个文件,把每个文件按上面的命令行编译,然后

ld -r file1.o file2.o -o modulename。

//链接文件,生成相应的模块名

驱动程序已经编译好了,现在把它安装到系统中去。

$ insmod –f test.o //强制加载到内核中去

如果安装成功,在/proc/devices文件中就可以看到设备test,并可以看到它的主设备号。要卸载的话,运行:

$ rmmod test

下一步要创建设备文件。

mknod /dev/test c major minor

c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。

用shell命令

$ cat /proc/devices

就可以获得主设备号,可以把上面的命令行加入你的shell script中去。

minor是从设备号,设置成0就可以了。

我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。

#include

#include

#include

#include

main()

{

int testdev; //返回的设备描述符

int i;

char buf[10];

testdev = open("/dev/test",O_RDWR); //打开设备并获取设备描述符

if ( testdev == -1 )

{

printf("Cann't open file \n");

exit(0);

}

read(testdev,buf,10);

for (i = 0; i < 10;i++)

printf("%d\n",buf[i]);

close(testdev);

}

编译运行,看看是不是打印出全1 ?

以上只是一个简单的演示。真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。这些才是真正的难点。请看下节,实际情况的处理。

如何编写Linux操作系统下的设备驱动程序

三、设备驱动程序中的一些具体问题

1。I/O Port。

和硬件打交道离不开I/O Port,老的ISA设备经常是占用实际的I/O端口,在linux下,操作系统没有对I/O口屏蔽,也就是说,任何驱动程序都可对任意的I/O口操作,这样就很容易引起混乱。每个驱动程序应该自己避免误用端口。

有两个重要的kernel函数可以保证驱动程序做到这一点。

1)check_region(int io_port,int off_set)

这个函数察看系统的I/O表,看是否有别的驱动程序占用某一段I/O口。

参数1:io端口的基地址,

参数2:io端口占用的范围。

返回值:0 没有占用,非0,已经被占用。

2)request_region(int io_port,int off_set,char *devname)

//端口在Linux内核中映射成内存地址,用于串口驱动

如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用。登记后,在/proc/ioports文件中可以看到你登记的io口。

参数1:io端口的基地址。

参数2:io端口占用的范围。

参数3:使用这段io地址的设备名。

在对I/O口登记后,就可以放心地用inb(),outb()之类的函来访问了。

在一些PCI设备中,I/O端口被映射到一段内存中去,要访问这些端口就相当于访问一段内存。经常性的,我们要获得一块内存的物理地址。(复杂)

2。内存操作

在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages直接申请页(Linux里每一页的大小是4K)。释放内存用的是kfree,或free_pages。请注意,kmalloc等函数返回的是物理地址!

注意,kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。

内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用

F0000000(X86体系结构)以上的地址空间。在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址。

另外,很多硬件需要一块比较大的连续内存用作DMA传送。这块程序需要一直驻留在内存,不能被交换到文件中去。但是kmalloc最多只能开辟128k的内存。

这可以通过牺牲一些系统内存的方法来解决。

3。中断处理

同处理I/O端口一样,要使用一个中断,必须先向系统登记。

int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *),unsigned int long flags,const char *device);

irq: 是要申请的中断号。

handle:中断处理函数指针。

flags:SA_INTERRUPT 请求一个快速中断,0 正常中断。

device:设备名。

如果登记成功,返回0,这时在/proc/interrupts文件中可以看你请求的中断。

4。一些常见的问题。

对硬件操作,有时时序很重要。但是如果用C语言写一些低级的硬件操作的话,gcc往往会对你的程序进行优化,这样时序会发生错误。如果用汇编写呢,gcc同样会对汇编代码进行优化,除非用volatile关键字修饰。最保险的办法是禁止优化。这当然只能对一部分你自己编写的代码。如果对所有的代码都不优化,你会发现驱动程序根本无法装载。这是因为在编译驱动程序时要用到gcc的一些扩展特性,而这些扩展特性必须在加了优化选项之后才能体现出来。

cd /dev

ls

mknod test c 254 0 //创建设备节点(设备名+设备类型+主设备号+从设备号)

ls test –l

cd /proc

ls

ps –e //打印进程所有信息

cd 1727(进程)

ls (显示进程的一些信息)

cat stat (查看进程的状态信息)

cd .. //回到proc目录

cat interrups //显示系统所使用的中断

cat iomem //显示内存空间映射(内核代码和内核数据,PCI设备)

cat ioports //显示I/O端口在内存中的映射(serial寄存器地址,Linux静止直接访问寄存器)

//X86体系:内存编址和设备编址,我们采用统一编址的方式

cat filesystems //显示支持的文件系统

//proc(进程)、tmpfs(临时文件)、ext2/3(支持日志)、ramfs(在RAM运行的文件系统)iso9660(光驱支持的文件系统)

cat meminfo //显示内存信息

cat devices //显示当前的设备

// ramdisk(内存块设备)、sd(U盘)

cat modules //系统运行加载了哪些模块(驱动程序)

查找Linux内核源码:

cd /home/s123/

ls

cd uclinux/

ls

cd uClinux-dist/

cd linux-2.4.x/ //Linux内核源码

cd drivers/ //驱动程序

Linux内核启动流程:

BIOS(Bootloader)—>Linux内核的入口点

// linux-2.4.x/arch/armnommu/boot/compresed/head.S(整个Linux内核的入口点,是用汇编语言编写的,open with gedit)

linux-2.4.x/init/main.c(整个程序的入口点,Source Insight 研读)

在开发板上运行内核程序(main.c)

运行SecureCRT软件,通过COM1端口连接开发板:

move 1000(在Flash中内核存放的位置)c008000(SDRAM位置)100000(大小)

run c008000

/etc/inittab //x86配置文件

cd /etc

ls

cat rc //44B0配置文件

蜂鸣器的驱动

第一步:编写蜂鸣器驱动程序testdriver.c,并将该驱动文件放在/home/s123/ uclinux/ uClinux-dist/ linux-2.4.x/drivers/char/ testdriver.c

第二步:修改makefile文件,该文件在/home/s123/ uclinux/ uClinux-dist/linux-2.4.x/drivers/char/ 添加驱动到makefile中:obj-$(CONFIG_TESTDRIVER)+=testdriver.o

第三步:加载配置文件到config.in中

bool ‘mytestdriver’CONFIG_TESTDRIVER

第四步:将test_init(void)函数加载到char/mem.c文件中

声明:#ifdef CONFIG_TESTDRIVER

extern int test_init(void)

#endif

调用:#ifdef CONFIG_TESTDRIVER

test_init();

#endif

第五步:在/home/s123/ uclinux/ uClinux-dist/vendors/Samsung/44B0/makefile创建设备节点\

test(设备名),c(设备类型),254(主设备号),0(从设备号)

第六步:启动Linux进入命令行状态:

cd /home/s123/

cd uclinux/

cd uClinux-dist/ //到uclinux顶层目录

make menuconfig

选中“Customize kernel Settings”//退出并保存,只配置内核

进入Character devices :选中“mytestdriver(NEW)”//直接编译进内核,退出保存make dep //创建依赖文件

make clean

make lib_only //编译生成的库

make user_only

make romfs

make image //生成镜像文件

make //生成最后的镜像文件

启动SecureCRT工具软件:Connect选择com1端口,进入超级终端//下载镜像文件(image-all.ram)到开发板(内存中)

load

Linux设备驱动程序举例

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 //同kernel.h,最基本的内核模块头文件 #include //同module.h,最基本的内核模块头文件 #include //这里包含了进行正确性检查的宏 #include //文件系统所必需的头文件 #include //这里包含了内核空间与用户空间进行数据交换时的函数宏 #include //I/O访问 int my_major=0; //主设备号 static int Device_Open=0; static char Message[]="This is from device driver"; char *Message_Ptr; int my_open(struct inode *inode, struct file *file) {//每当应用程序用open打开设备时,此函数被调用 printk ("\ndevice_open(%p,%p)\n", inode, file); if (Device_Open) return -EBUSY;//同时只能由一个应用程序打开 Device_Open++; MOD_INC_USE_COUNT;//设备打开期间禁止卸载 return 0; } static void my_release(struct inode *inode, struct file *file)

一般规格书电导率传感器

General 电导率 EXA SC Specifications 探头/传感器 ■ 概要 YOKOGAWA 提供监测或控制液体和溶液中电导率值的优良的在线分析仪器。 现在,YOKOGAWA 提供四线制电导率控制器(SC402G ),两线制电导率变送器(SC200、SC202)和盘装式的电导率控制器(SC150)。 当使用控制器或变送器时,为了能精确地测量液体电导率值,YOKOGAWA 也将提供各种传感器。 YOKOGAWA 的控制器/变送器与其传感器相结合可满足苛刻的要求。YOKOGAWA 的仪器除可以进行标准电厂和化工应用方面传统的水质测量以外,还满足不断增长的半导体和医药等行业的超纯水需求。

■ 电导率传感器类型 ■ 传感器测量范围 注意: 在高电导率的液体或被污染的液体中使用,传感器有可能被极化。 极化可能降低被测量液的电导率值。 ﹡四电极系统 ■ 技术参数 1、 SC4A :带针形接头的电缆(可用于SC150、 SC402G 和SC202) 测量对象:测量溶液的电导率值 测量原理:两电极系统 电极常数:0.02cm -1 、0. 1 cm -1 测量范围: 0.03-200μS/cm (电极常数:0.02cm -1 ) 0.2μS/cm -10mS/cm (电极常数:0. 1 cm -1 ) 温度范围:

电极,0-100℃ 流通池,见图1 电极消毒: 在135℃(275℉)下蒸汽持续消毒30分钟。 压力范围: 电极,0-1MPa 流通池,见图1 图1 流通池(选项:/PS、/PF、/SA1、/SA2、/SB1、/SB2、/SC1)温度和压力的容许值范围 样液条件: 虽然在测量时没有限制流速,要获得正确的 测量值,气泡不能混合在样液中。 温度电极:Pt1000 材料: 壳体和电极: SUS316L(用于所有支架型)或钛(仅适用于接头安装类型-AD),氟橡胶 O形圈。 绝缘材料:PEEK 安装接头: 聚偏二氟乙烯(仅用于/PF)或SUS316L (用于其它) 重量: 电极: ·接头安装型{SC4A-S-AD- 09-002-05}约 450g; ·接头安装型(SC4A-S-AD-15-002-05)约 520g; ·焊接头型(SC4A-S-SA-NN- 002-0.5)约 670g; ·1-1.5"焊接夹型(SC4A-S-SB- NN-002-05) 约550g; · 2"焊接夹型(SC4A-S-SC-NN-002-05) 约670g。 (注)SC4A的电极中有不同的重量,要了解每种类型电极更准确的重量请按下列参数计 算。电缆重75g/m ,0.02cm-1电极常数的 SC4A比0. 1 cm-1电极常数的SC4A重15g。 SUS314L电极比钛电极重40g。 接头: 3/4"不锈钢接头(/PS)约110g; 3/4"PVDF接头(/PF)约35g; 直焊接头(/SA1)约300g; 150弯焊接头(/SA2)约320g; 1"焊接夹(/SB1)约330g; 1.5"焊接夹(/SB2)约305g; 2"焊接夹(/SC1)约350g。 2、SC8SG:带针形接头的电缆(可用于SC150、 SC402G和SC202) 测量对象:测量液体中电导率值 测量原理:电极方法 电极常数:0.01cm-1或10 cm-1(两电极系统) 10 cm-1(四电极系统) 测量范围: 0.05-100μS/cm(电极常数:0.01cm-1) 0.1 -1000mS/cm(电极常数:10 cm-1) 温度范围: 电极,0-100℃(对电极常数为0.01cm-1电导 池,最高温度可达130℃,带聚偏二乙烯流 通池除外)。 压力范围: 最大1000KPa.( 带聚偏二乙烯流通池的电 导池,最大压力为500 KPa) 样液流速: 虽然在应用中没有特别限制流速,建议流过 电导池的流速低于20l/min。 (注)测量时液体流速的大小没有限制。但是当用流通式电导池时,若被测液内含有粘稠 物,当流速很高时电极或液体室的内壁就可 能被剧烈磨损。为了获得准确的测量值,样 液中不能有气泡。 温度补偿用电阻(RTD): Pt1000(在电极内部) 结构: 防水包装(符合日本国内的产品标准JIS C0920 重量: 旋入式—约1.3kg(电缆除外); 流通式—(SCS14池体),约3.1kg(电缆除 外); 流通式—(SCS14池体,镶边)约4.5kg (电

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用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驱动程序的编写

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结构体。

Linux驱动程序工作原理简介

Linux驱动程序工作原理简介 一、linux驱动程序的数据结构 (1) 二、设备节点如何产生? (2) 三、应用程序是如何访问设备驱动程序的? (2) 四、为什么要有设备文件系统? (3) 五、设备文件系统如何实现? (4) 六、如何使用设备文件系统? (4) 七、具体设备驱动程序分析 (5) 1、驱动程序初始化时,要注册设备节点,创建子设备文件 (5) 2、驱动程序卸载时要注销设备节点,删除设备文件 (7) 参考书目 (8) 一、linux驱动程序的数据结构 设备驱动程序实质上是提供一组供应用程序操作设备的接口函数。 各种设备由于功能不同,驱动程序提供的函数接口也不相同,但linux为了能够统一管理,规定了linux下设备驱动程序必须使用统一的接口函数file_operations 。 所以,一种设备的驱动程序主要内容就是提供这样的一组file_operations 接口函数。 那么,linux是如何管理种类繁多的设备驱动程序呢? linux下设备大体分为块设备和字符设备两类。 内核中用2个全局数组存放这2类驱动程序。 #define MAX_CHRDEV 255 #define MAX_BLKDEV 255 struct device_struct { const char * name; struct file_operations * fops; }; static struct device_struct chrdevs[MAX_CHRDEV]; static struct { const char *name; struct block_device_operations *bdops; } blkdevs[MAX_BLKDEV]; //此处说明一下,struct block_device_operations是块设备驱动程序内部的接口函数,上层文件系统还是通过struct file_operations访问的。

电导率传感器发展概况

万方数据

万方数据

万方数据

电导率传感器发展概况 作者:周明军, 尤佳, 秦浩, 傅巍, 刘其中, 徐振忠, ZHOU Ming-jun, YOU Jia, QIN Hao, FU Wei, LIU Qi-zhong, XU Zhen-zhong 作者单位:周明军,尤佳,秦浩,傅巍,徐振忠,ZHOU Ming-jun,YOU Jia,QIN Hao,FU Wei,XU Zhen- zhong(中国电子科技集团公司第四十九研究所,黑龙江,哈尔滨,150001), 刘其中,LIU Qi- zhong(第二炮兵驻哈军代室,黑龙江,哈尔滨,150036) 刊名: 传感器与微系统 英文刊名:TRANSDUCER AND MICROSYSTEM TECHNOLOGIES 年,卷(期):2010,29(4) 参考文献(28条) 1.邱善乐一种新型感应式电导率传感器的设计 2005(48) 2.张兆英海水电导率、温度和深度测量技术探讨 2003(4) 3.万明球半导体行业中超纯水的质控 2004(112) 4.傅卫卫.应伯根工业水处理过程中电导率测量方法的研究 1999(2) 5.徐海滨双向电流法测量溶液电导 1995(5) 6.陈怡.李平电磁式电导浓度仪 1997(1) 7.甄宝贵一种特殊类型的工业电导仪 1998(4) 8.兰敬辉溶液电导率测量方法的研究 2002 9.郑鹏电导率仪检定中存在的若干问题 2006(6) 10.马艺馨电阻层析成象技术及其在气/液两相泡状流检测中的应用 1999 11.Dickin F J.Wang M Eletrical resistance tomography for process applications 1996(3) 12.Hua P.Woo E J.Webster J G Using compound eletrodes in electrical impedance tomography 1993(1) 13.Kotre C J A sensitivity coefficient method for the reconstruction of electrical impedance tomograms 1989(3) 14.李建国开放式四电极电导率传感器的研制与实验 2005(3) 15.方初良电导式分析仪表 1984 16.李建国高性能七电极电导率传感器技术研究 2009(2) 17.Kotre C J A sensitivity coefficient method for the reconstruction of electrical impedance tomograms 1989(3) 18.刘铁军工程电导测试技术及应用研究 2006 19.Shadpour H.Hupert M L.Patterson D Muhichannel microchip electrophoresis device fabricated in polycarbonate with an integrated contact conductivity sensor array 2007(3) 20.Jordana J.Gasulla M.Pallàs-Areny R Electrical reisitance tomography to detect leaks from buried pipes 2001(8) 21.Sansen W.Geeraerts B.Petegen WV Electrical impedance tomography systems based on voltage drive 1992(Supp13) 22.Kim Y.Woo H W A prototype system and reconstruction algorithms for electrical impedance technique in medical body image 1987(Supp8) 23.Brown B H.Seagar A D The Sheffield data collection system 1987(Supp8) 24.魏颖电阻层析成象技术(ERT)及其在两相流测量中的应用研究 2001

Linux设备驱动程序学习(18)-USB 驱动程序(三)

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 主机控制器去划分为更小的块, 比以连续地顺序发送小缓冲的速度快得多*/

LINUX字符设备驱动编写基本流程

---简介 Linux下的MISC简单字符设备驱动虽然使用简单,但却不灵活。 只能建立主设备号为10的设备文件。字符设备比较容易理解,同时也能够满足大多数简 单的硬件设备,字符设备通过文件系统中的名字来读取。这些名字就是文件系统中的特 殊文件或者称为设备文件、文件系统的简单结点,一般位于/dev/目录下使用ls进行查 看会显示以C开头证明这是字符设备文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。 第一个数字是主设备号,第二个数字是次设备号。 ---分配和释放设备编号 1)在建立字符设备驱动时首先要获取设备号,为此目的的必要的函数是 register_chrdev_region,在linux/fs.h中声明:int register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想 要分配的起始设备编号,first的次编号通常是0,count是你请求的连续设备编号的 总数。count如果太大会溢出到下一个主设备号中。name是设备的名字,他会出现在 /proc/devices 和sysfs中。操作成功返回0,如果失败会返回一个负的错误码。 2)如果明确知道设备号可用那么上一个方法可行,否则我们可以使用内核动态分配的设 备号int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);dev是个只输出的参数,firstminor请求的第一个要用的次编号, count和name的作用如上1)对于新驱动,最好的方法是进行动态分配 3)释放设备号,void unregister_chrdev_region(dev_t first unsigned int count); ---文件操作file_operations结构体,内部连接了多个设备具体操作函数。该变量内部 的函数指针指向驱动程序中的具体操作,没有对应动作的指针设置为NULL。 1)fops的第一个成员是struct module *owner 通常都是设置成THIS_MODULE。 linux/module.h中定义的宏。用来在他的操作还在被使用时阻止模块被卸载。 2)loff_t (*llseek) (struct file *, loff_t, int);该方法用以改变文件中的当前读/ 写位置 返回新位置。 3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);该函数用 以从设备文件 中读取数据,读取成功返回读取的字节数。

一个简单字符设备驱动实例

如何编写Linux设备驱动程序 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。本文是在编写一块多媒体卡编制的驱动程序后的总结,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1)对设备初始化和释放; 2)把数据从内核传送到硬件和从硬件读取数据; 3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据; 4)检测和处理设备出现的错误。 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备

Linux设备驱动程序学习(20)-内存映射和DMA-基本概念

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 位, 依赖底层的硬件结构, 并且每个进程有它自己 的虚拟地址空间.

linux 驱动程序开发

1 什么是驱动 a)裸板驱动 b)有系统驱动linux 将驱动封装了一套框架(每个驱动) c)大量和硬件无关的代码已写好只需要编程实现和硬件相关的代码 d)难点:框架的理解代码的理解 e)需要三方面的知识: i.硬件相关的知识 1.电路原理图 2.芯片的数据手册 3.总线协议rs232 i2c等 ii.内核的知识 1.内核驱动属于内核的一部分,它运行在内核态需要对内核知识有了解 2.内存管理 3.解决竞争状态(如上锁) 4.。。。 iii.驱动框架的知识 1.内核中已经实现了大量硬件驱动完成了驱动的框架编程只需要根据硬 件进行添加 2 搭建linux驱动开发工具 a)安装交叉编译环境 i.arm-linux-gcc uboot PATH b)移植uboot c)移植内核 d)制作根文件系统然后通过nfs方式让开发板可以加载 3 内核驱动开发的基本知识 a)如何学驱动编程? i.最好的老师就是内核源码(没有man 功能) 1.要是用某个函数就去查看某个函数的定义注释 2.查看内核中其他模块儿时如何使用该函数的 3.专业书籍: a)内核开发:linux内核的设计与实现机械工程出版社 b)驱动开发:圣经级别的-LDD3:LINUX DEVICE c)操作性别叫强的:精通linux设备驱动程序开发

关于linux内核: 1)linux内核中所使用的函数都是自身实现的它肯定不会调用c库中的函数 2)linux中代码绝大多数代码时gun c语言完成的不是标准c语言可以理解为标c的扩展版和少部分汇编 需要注意的问题: 1)内核态不能做浮点数运算 2)用户空间的每个进程都有独立的0-3G的虚拟空间 多个进程共享同一个内核 内核使用的地址空间为3G-4G 3)每个线程有独立的栈空间 4 写一个最简单的内核模块儿(因为驱动时内核的一个模块套路都一样) a)几个宏 i.__FUNCTION__:展开为所在函数的名称 ii.__LINE__:展开为printk所在的行号 iii.__DATE__:展开为编译程序的日期 b)通用头文件 i.#include ii.#include c)没有main函数 然后写一个makefile 其中:obj -m +=helloworld.o -m表示生成模块儿 make -C 内核路径编译对象路径modules(固定表示模块儿) 例子:make -C /home/changjian/dirver/kernel M=$(PWD) modules 报错:如taints kernel(污染内核)因为写的驱动没有声明license 因为linux为开源所以写的驱动也必须声明为开源可以在程序里加入:MODULE_LICENSE(“GPL”);声明为开源 模块儿驱动开发 1、模块儿参数 a)内核中安装模块时也可以传递参数 i.insmod xx.ko var=123 b)模块参数的使用方法 i.首先在模块中定义全局变量 ii.然后使用module_param 或者module_param_array来修饰该变量 这样一个普通的全局变量就变成可以安装模块时传递参数的模块参数 module_param(name,type,perm) name:变量名称 type: name的类型(不包括数组) perm:权限类型rwxr-x 等类型内核做了相关的宏定义形如efine S_IRWXG 表示r w x g(同组) module_param_array(name,type,nump,perm)将某个数组声明为模块 参数

罗斯蒙特 系列电导率传感器样本

ENDURANCE?电导率传感器 ?传感器免标定,其出厂时设定好的电导池常数,可以最大限度地 保证仪器的测量精度1。 ?坚固的钛电极传感器,电导池常数分别为:0.01/cm、0.1/cm、 1.0/cm、10.0/cm 和 0.85/cm(4-电极)。这些传感器具有极好的 抗腐蚀性,并且,确保可靠的导电率检测精度。 ?多种可供选择的安装方式:拧入式/插入式、可抽取式、卫生法兰 式和流通式。 ?可以与罗斯蒙特分析仪器公司的绝大多数导电率分析仪/变送器配 合使用。 突出的优点 投运快速、方便?罗斯蒙特分析仪器公司 ENDURANCE 电导率传感器的设计,确保了仪器可以投运快速、方便,并且故障率低。传感器在出厂时,已经设置好精确的电导池常数,从而简化了日常标定工作,可以不必使用标定液,零点标定在空气中进行即可,量程标定输入其相应的电导池常数即可。 运行成本低?传感器选用的材料经久耐用,并且,抗腐蚀性能好,从而保证了 ENDURANCE 电导率传感器的使用寿命长,维护工作量小,运行成本低。400、402和404传感器由抗化学腐蚀的钛电极和PEEK(聚醚醚酮)绝缘材料构成,403-11/12/13卫生法兰传感器由钛电极和Kel-F2绝缘材料构成,401和403-14 传感器由石墨电极和环氧树脂绝缘材料构成,后者主要用于高电导率溶液的检测需要。 测量精度高?传感器出厂时,已经过标定,从而可以保证仪器的测量精度。由于其坚固耐用的外形设计和良好的抗腐蚀材料,确保了电极的间隔和表面积恒定,因此,电导池常数可以在较长的时间内,保持稳定。对于温度补偿,Pt1000测温热电阻是标准的,也可以选择 Pt100 或热敏电阻。ENDURANCE 的设计理念是使电导率传感器的检测值可以密切跟踪温度的变化,从而使电导率的测量结果可以随时得到精确的温度补偿。 多种安装方式 拧入式/插入式:400传感器电导池常数为:0.01/cm、0.1/cm 和 1.0/cm,其通过 3/4 英寸 MNPT 的不锈钢接头,与过程连接。传感器可以直接安装在3/4 英寸的T形三通上,也可以选择PN 24091-02聚碳酸酯的流通池。 可抽取式/带球阀:402 可抽取式传感器通过接口为1-1/4英寸NPT的球阀,与过程连接,其它配套附件与球阀的接口尺寸一致。 卫生法兰式:403传感器通过接口为1-1/2英寸或2英寸的卫生法兰,与过程连接,其适合于Tri-Clover3卫生过程的连接。

linux简单的gpio驱动实例

今天完成了嵌入式linux的第一个驱动的编写和测试,虽然是个简单的程序,但是麻雀虽小,五脏俱全,希望可以给刚开始接触驱动编写的人一些提示,共同进步。 源代码: 分析如下: 下面是我的驱动程序: #include //配置头文件 #include /*内核头文件,作为系统核心的一部分,设备驱动程序在申请和释放内存时,不是调用malloc和free,而是调用kmalloc和 kfree*/ #include //调度,进程睡眠,唤醒,中断申请,中断释放 #include //时钟头文件 #include //用户定义模块初始函数名需引用的头文件 #include //模块加载的头文件 #include #include //这个是2440的寄存器头文件,asm/srch只是个链接 //实际根据自己的情况查找,一般 是../../linux2.*.*/include/asm/arch-s3c2440里编译器 //自己会查询链接,以前不知道,找了半天 // GPIO_LED DEVICE MAJOR #define GPIO_LED_MAJOR 97 //定义主设备号 //define LED STATUS 我的板子 LED在GPB0 与GPB1 处大家根据自己情况改 #define LED_ON 0 //定义LED灯的状态开 #define LED_OFF 1 // // ------------------- READ ------------------------ 这个前面要加static 否则警告 static ssize_t GPIO_LED_read (struct file * file ,char * buf, size_t count, loff_t * f_ops) {

Linux设备驱动程序说明介绍

Linux设备驱动程序简介 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel 中的函数,有些常用的操作要自己来编写,而且调试也不方便。本人这几周来为实验室自行研制的一块多媒体卡编制了驱动程序,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能: 1.对设备初始化和释放. 2.把数据从内核传送到硬件和从硬件读取数据. 3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据. 4.检测和处理设备出现的错误. 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还蔤强樯璞?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck. 读/写时,它首先察看缓冲区的内容,如果缓冲区的数据 如何编写Linux操作系统下的设备驱动程序 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过. [code]#define __NO_VERSION__

电导率传感器使用说明

电导率传感器 用户手册 目录 一、设备应用环境说明 (3) 二、技术参数、功能和规格要求 (3) 1. 技术参数 (3) 2. 产品尺寸 (4) 3. 数据通讯 (4) 4. 安装方式 (6) 5. 接线 (7) 6. 保养 (8) 7. 配件和备件 (8) 8. 质量保证 (8) 9. 电极选型常识 (9) 一、设备应用环境说明 ●纯净水/饮用水/地表水/各种供水/工业污水处理 ●多种信号输出:4-20mA(2 线制模拟信号),RS485( Modbus / RTU 数字 信号)。方便连接到 PLC、DCS、工业控制计算机、通用控制器、无纸记录仪器或触摸屏等第三方设备。

●电极可使用多种连接件,包括镀金 VP 接头、BNC 接头等。用螺纹固定,电 极更换方便。 ●易于安装:3/4 英寸 NPT 螺纹(管螺纹),便于安装在管道和和罐体。探头 和显示部分可分开,通过电缆连接。 ●IP65/IP68 防护等级。 二、技术参数、功能和规格要求 1. 技术参数

2.产品尺寸 AMT-PD300: 3. 数据通讯 3.1 数据格式 Modbus 通信默认的数据格式为:9600、n、8、1(波特率 9600bps、1 个起始位、8 个数据位、无校验、1 个停止位)。波特率等参数可以定制。 3.2 信息帧格式 a) 读数据指令帧: b) 读数据应答帧: c) 写数据指令帧:

d) 写数据应答帧: 3.3 寄存器地址 注意: a) 寄存器地址为根据 Modbus 协议定义的带寄存器类型的寄存器起始地址(括号中的 16 进制表示的实际的寄存器起始地址)。 b) 更改传感器地址时,返回指令中的传感器地址为更改后的新地址。 c) 读取数据时返回测量值的数据定义:

相关主题
文本预览
相关文档 最新文档