




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、3:181第 9 章 LED实例3:182内容提要: 9.1 字符设备驱动程序的结构 9.1.1 常用的头文件 9.1.2 主次设备号 9.1.3 cdev结构体 9.1.4 分配和释放设备号 9.1.5 File_operation结构体 9.1.6 字符设备驱动程序一般结构3:183 9.2 LED设备驱动实例 9.2.1 实验目的 9.2.2 实验原理 9.2.3 实验任务 9.2.4 实验步骤3:1849.1 字符设备驱动程序的结构 字符设备驱动是嵌入式Linux最基本,也是最常用的驱动程序。表9-1-1显示了应用程序,驱动程序和硬件设备之间的层次结构。 应用程序 标准的系统调用:re
2、ad(),write(),ioctl(),open(),close()等 file_operations 文件操作结构体 驱动程序中对应硬件设备的接口函数: xxx_read(),xxx_write(),xxx_ioctl(),xxx_open(),xxx_close()等 硬件设备3:1859.1.1 常用的头文件 #include /内核相关的头文件 #include /所有模块都需要的头文件 #include #include /设备类型定义 #include /版本相关 #include /IO操作 #include /错误处理 #include /延时处理相关的头文件3:186 #i
3、nclude /设备驱动初始化相关的头文件 #include /字符设备相关的头文件 #include /文件系统相关的头文件 #include /内存管理相关的头文件 /调用get_user,put_user,copy_to_user,copy_from_user等函数时引用的头文件 #include / 处理器GPIO相关的头文件 #include #include / 中断相关的头文件 #include #include #include 3:1879.1.2 主次设备号 字符设备通过文件系统中的名字来存取。那些名字称为文件系统的设备文件, 或者文件系统的结点,它们一般位于 /dev 目
4、录。 进入宿主机的/dev目录,然后输入ls -l命令可以查看宿主机的所有设备文件,下图是笔者截取的输入ls -l命令后的部分显示结果: crw-rw- 1 root root 7, 135 2009-12-11 21:39 vcsa7 crw-rw- 1 root root 7, 136 2009-12-11 21:38 vcsa8 crw- 1 root root 253, 0 2009-12-11 21:39 vmci crw-rw-rw- 1 root root 10, 63 2009-12-11 21:39 vsock prw-r- 1 syslog adm 0 2009-12-12
5、 08:01 xconsole crw-rw-rw- 1 root root 1, 5 2009-12-11 21:38 zero3:188 主设备号标识设备相连的驱动。例如, /dev/null 和 /dev/zero 都由驱动 1 来管理, 而虚拟控制台和串口终端都由驱动 4 管理;相同类型的的设备可以共用同一个设备驱动,次设备号就是被内核用来决定引用这些设备中的哪个设备。3:1899.1.3 cdev结构体 在Linux 2.6 内核中使用cdev结构体来描述字符设备,cdev结构体的定义如下: struct cdev struct kobject kobj; /内嵌的kojbect对象
6、struct module *owner; /所属模块const struct file_operations *ops; /文件操作结构struct list_head list;dev_t dev; /设备号unsigned int count; ;3:1810 cdev结构体的dev_t 类型成员定义了设备号,主次设备号都包括。对于2.6内核, dev_t 是 32 位的量,12 位用作主设备号,20 位用作次设备号。利用在 中的一套宏定义,可以获得一个 dev_t 的主次设备号: MAJOR(dev_t dev) MINOR(dev_t dev) 相反, 如果你已经有了主次设备号, 可
7、以使用下面的宏将其转换为一个dev_t: MKDEV(int major, int minor)3:1811 一组函数用来对cdev结构体进行操作: void cdev_init(struct cdev *, const struct file_operations *); /初始化cdev struct cdev *cdev_alloc(void); /动态申请一个cdev内存 void cdev_put(struct cdev *p); int cdev_add(struct cdev *, dev_t, unsigned);/注册cdev设备 void cdev_del(struct c
8、dev *);/注销cdev设备 3:1812(1)初始化cdev成员 cdev_init()函数用来初始化cdev的成员,并建立cdev和file_operations之间的连接,其源代码如下: void cdev_init(struct cdev *cdev,struct file_operations *fops) memset(cdev,0,sizeof *cdev); /对字符设备进行清零操作 INIT_LIST_HEAD(&cdev-list); /该宏定义使得list_head的next和prev都指向自身 dev-kobj.ktype=&ktype_cdev_d
9、efault; kobject_init(&cdev-kobj); /进行kobject的初始化 cdev-ops=fops; /将传入的文件操作结构体指针赋值给cdev的ops 3:1813(2)动态申请一个cdev内存 cdev_alloc()函数用于动态申请一个cdev内存,其源代码如下: struct cdev *cdev_alloc(void) / kmalloc 的第1个参数是要分配的块的大小,第2个参数分配标志。 struct cdev *p=kmalloc(sizeof(struct cdev),GFP_KERNEL); if(p) memset(p,0,sizeof(
10、struct cdev); p-kobj.ktype=&ktype_cdev_dynamic; INIT_LIST_HEAD(&p-list); kobject_init(&p-kobj); return p; 3:1814(3)注册cdev设备 cdev_add()函数向系统添加一个cdev,完成字符设备的注册,对cdev_add()函数的调用通常发生在字符设备驱动模块加载函数中。下面是cdev_add()函数的定义: int cdev_add(struct cdev *p, dev_t dev, unsigned count) p-dev = dev;p-count
11、 = count; /添加一个对应的cdev对象. return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); 3:1815(4)注销cdev设备 cdev_del()函数向系统删除一个cdev,完成字符设备的注销,对cdev_del()函数的调用通常发生在字符设备驱动模块卸载函数中。下面是cdev_del()函数的定义: void cdev_del(struct cdev *p) cdev_unmap(p-dev, p-count); /删除一个cdev kobject_put(&p-kobj);
12、/减少计数量 3:18169.1.4 分配和释放设备号 register_chrdev_region()用于已知设备号的情况;而int alloc_chrdev_region()用于动态申请系统中未被占用的设备号的情况,其优点在于不会造成设备号重复的冲突。 在注销设备之后,应该调用void unregister_chrdev_region(dev_t from,unsigned count)函数释放原先申请的设备号。 他们之间的顺序关系如下: register_chrdev_region()cdev_add() /此过程在加载模块中 cdev_del()-unregister_chrdev_r
13、egion() /此过程在卸载模块中3:18179.1.5 File_operation结构体 由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open,read,write,close等,但是如何把这些系统调用和设备驱动程序关联起来呢?这需要了解一个非常关键的数据结构file_operations,它定义在include/linux/fs.h中。 3:1818 file_operations结构的每一个成员的名字都对应着一个系统调用。在设备驱动程序中,一般file_operations结构体初始化如下: static struct file_opera
14、tions mydevice_fops = .owner = THIS_MODULE, .open = mydevice_open, .release = mydevice_release, .ioctl = mydevice_ioctl,.read = mydevice_read,.write = mydevice_write, /more ;3:1819 应用程序通过系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取设备驱动程序中初始化的file_operations结构体(如mydevice_fops ),获取相应操作(
15、read/write等)对应的函数指针(mydevice_read/mydevice_write),接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。因此,编写设备驱动程序的主要工作就是编写这些文件操作的接口函数,并填充file_operations的各个域。3:18209.1.6 字符设备驱动程序一般结构 /*头文件* #include #include #include /more3:1821/* 文件操作接口函数定义 * static int mydevice_open(struct inode *inode,struct file *filp) /打开设备操作 sta
16、tic int mydevice_release(struct inode *inode,struct file *filp) /关闭设备操作 static int mydevice_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg) /设备I/O控制 static ssize_t mydevice_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)3:1822 /设备读操作 static ssize_t myde
17、vice_write(struct file *filp,char *buffer,size_t count,loff_t *ppos) /设备写操作 /more3:1823/* 文件操作结构体 static struct file_operations mydevice_fops = .owner = THIS_MODULE, .open = mydevice_open, .release = mydevice_release, .ioctl = mydevice_ioctl, .read = mydevice_read, .write = mydevice_write, /more ;3:
18、1824/*设备驱动模块加载函数* static int mydevice_init(void) /动态或静态分配设备号 /注册设备 3:1825/*设备驱动模块卸载函数 static void mydevice_exit(void) /注销设备 /释放设备号 module_init(mydevice_init); module_exit(mydevice_exit);3:1826/* 设备驱动许可证 MODULE_LICENSE(GPL); 3:18279.2 LED设备驱动实例 9.2.1 实验目的 (1)了解LED原理及其与S3C2410的接口电路设计 (2)了解S3C2410芯片的I/
19、O端口配置方法 (3)通过S3C2410芯片的GPF4端口控制Super-ARM主板上D1的亮灭 (4)掌握LED驱动的编写及测试过程,进而熟悉开发简单字符设备驱动的方法3:18289.2.2 实验原理 (1)LED接口电路 由于单只LED管的工作电压低(大约在1.52V),个别需达到4V,同时工作电流仅为15mA,因此可以用CPU的通用输入输出管脚(GPIO)直接控制LED的亮灭。 LED的接口电路如下图所示:3:1829图中GPF4和GPF7是S3c2410的GPIO引脚,R1和R2是1K欧姆的限流电阻,D1和D2是LED发光管,VDD33代表3.3V供电电源。当GPIO引脚输出低电平的时
20、候,对应的LED灯将被点亮。3:1830(2)S3c2410的GPIO概述 打开S3C2410的数据手册(本章附件中的S3C2410.pdf),看看它的I/O端口,共有117只多功能输入/输出端口,它们分别为: GPA口:23只输出口 GPB口:11只输入/输出口 GPC口:16只输入/输出口 GPD口:16只输入/输出口 GPE口:16只输入/输出口 GPF口: 8只输入/输出口 GPG口:16只输入/输出口 GPH口:11只输入/输出口3:1831 每组端口都有复用的功能,例如作为输入口或输出口,还可以定义为中断触发功能,我们可以通过配置这些端口的控制寄存器来满足不同系统和设计的需要,下面
21、马上就会说到如何配置端口控制寄存器,数据寄存器等。在运行主程序之前,必须先对每一个用到的引脚的功能进行设置。如果某些引脚的复用功能没有使用,那么可以先将该引脚设置为I/O口。3:18323)GPIO对应的特殊功能寄存器端口控制寄存器(GPFCON)在S3C2410中,大多数的引脚都是复用的。因此,需要通过控制寄存器来配置每个I/O引脚的功能。如果GPF0GPF7和GPG0GPG7、用做断电模式下的唤醒信号,这些端口必须配置成中断模式。 下表是GPFCON寄存器的配置说明:GPFCON中每两位对应一个端口,每个端口可以配置三种功能。如果某个端口所对应的两位配成00,则该端口作为输入,01为输出,
22、10为外部中断功能,11保留。3:1833 GPFCONBIT描述描述GPF715:1400:Input, 01:Output, 10:EINT7, 11:ReservedGPF613:1200:Input, 01:Output, 10:EINT6, 11:ReservedGPF511:1000:Input, 01:Output, 10:EINT5, 11:ReservedGPF49:800:Input, 01:Output, 10:EINT4, 11:ReservedGPF37:600:Input, 01:Output, 10:EINT3, 11:ReservedGPF25:400:Inpu
23、t, 01:Output, 10:EINT2, 11:ReservedGPF13:200:Input, 01:Output, 10:EINT1, 11:ReservedGPF01:000:Input, 01:Output, 10:EINT0, 11:Reserved3:1834端口数据寄存器(GPFDAT) 如果端口被配置成输出口,那么输出数据可以写入GPnDAT相应的位;如果端口定义为输入口,那么输入数据可以从GPnDAT相应的位读入。如果端口定义为功能引脚(如外部中断),从该寄存器中读出来的值是未定义的。3:1835GPFDATBIT描述描述GPF7:07:0 When the port
24、is configured as input port,data from externel sources can be read to the corresponding pin.When the port is configured as output port,data written in this register can be sent to the corresponding pin.When the port is configured as functional pin,undefined value will be read.3:1836端口上拉寄存器(GPFUP) 当G
25、PF口作为输入口时,还可以设置内部上拉电阻,其定义如表9-2-3所示。端口上拉寄存器控制每个端口的上拉电阻的使能和关闭。当相应位为“0”时,上拉电阻使能,当相应位为“1”时,上拉电阻关闭。3:1837GPFUPBIT描述描述GPF7:07:00:the pull-up function attached to the corresponding port pin is enabled.1:the pull-up function is disabled.3:18389.2.3 实验任务 (1)编写LED设备驱动程序,驱动程序中手动定义设备名称及主设备号,这个设备号必须是系统尚未使用的设备号,笔
26、者这里暂用212,你也可以尝试用别的数字。当然最好的方法是动态分配设备号,如果使用这种方法,在/dev目录下为LED设备建立设备节点的时候,需要先从/proc/devices文件中获取该设备的主设备号。在驱动程序中还要实现与LED相应的I/O配置,以及读写设备的接口函数等。 (2)将驱动编译成模块,并实现模块的加载及卸载。 (3)编写驱动的测试程序,在程序中实现打开LED设备,控制LED设备一亮一灭,关闭LED设备等。3:18399.2.4 实验步骤 以下操作都在nfs文件系统目录(/armnfs)下进行,因此先执行cd /armnfs命令。 (1)编写led.c文件 建立led目录: mkd
27、ir usr/led 进入led目录,在该目录下建立两个子目录driver 和test ,前者用来存放驱动程序,后者用来存放驱动测试程序:3:1840cd usr/led mkdir driver test进入驱动程序目录,建立设备驱动文件led.c:cd driver gedit led.c LED驱动程序如代码清单9-2-1所示:3:1841(2)编写Makefile文件(主机的/armnfs/usr/led/driver目录下) gedit Makefile 在该文件中加入以下内容:(其中KDIR是内核目录,读者要根据自己的内核所在目录来设置) obj-m := led.o KDIR :
28、= /home/hm/book2410/kernel/linux-2.6.24 PWD := $(shell pwd) default:$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 3:1842(3)编译 make 编译完成后,该目录下会生成led.ko文件,该文件就是编译成功的模块文件。 创建设备 进入/armnfs/dev目录,创建设备,设备名为led,属于字符型设备,主设备号是212,次设备号是0。(要与led.c文件中的定义相符): cd /armnfs/dev sudo mknod led c 212 03:1843(4)加载模块 首先设置内核以NFS方式加载根文件系统,在宿主机上运行minicom,打开实验箱电源,加载完U-Boot,内核和nfs文件系统之
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 车辆风险押金合同协议
- 还贷免责协议书模板
- 建筑设计与施工合同及协议
- 历史文化保护与传承的试题研究
- 《当代生产管理策略》课件
- 猪肉购销合同
- 民政合作协议书
- 语培课程合同协议书模板
- 返建房房屋合同补充协议
- 车场使用协议书范本
- 2025届陕西省高考适应性检测(三)数学试题+答案
- 离婚协议书正规打印电子版(2025年版)
- 【数学】三角形 问题解决策略:特殊化课件2024-2025学年北师大版数学七年级下册
- 2023-2024年外卖骑手行业现状及发展趋势研究报告
- 【MOOC】跨文化交际入门-华中师范大学 中国大学慕课MOOC答案
- 人工智能导论(天津大学)知到智慧树章节答案
- 中考数学计算题练习100道(2024年中考真题)
- MOOC 光学发展与人类文明-华南师范大学 中国大学慕课答案
- 五年级下册美术课件-第14课 桥|苏少版 (共28张PPT)
- 激光跟踪仪使用手册
- 热控检修人员岗位达标安全知识题库(终版)
评论
0/150
提交评论