IO接口驱动

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
}
void cleanup_module(){ unregister_chrdev(223,"LED device");
}
//Makefile ifneq ($(KERNELRELEASE),) obj-m :=LedDevice.o
else KDIR :=/home/student/steven/linux-2.6.35 PWD :=$(shell pwd) default:
volatile int *pConfReg = ioremap(GPJ0CON,4); volatile int *pDataReg = ioremap(GPJ0DAT,4);
void LED3_on(void){ *pConfReg &=0xFFFF0FFF; *pConfReg |=0x00001000; *pDataReg &=~(1<<3); printk("led3 on\n");
} }
struct file_operations fop={
.open
=led_open,
.release =led_close,
.ioctl =led_ioctl,
};
int init_module(){ int ret; ret=register_chrdev(223,"LED device",&fop); if(ret<0){ printk("init module failed!\n"); return ret; } printk("init module successfully!\n"); return 0;
case _LED5_on: LED5_on(); break;
case _LED3_off: LED3_off(); break;
case _LED4_off: LED4_off(); break;
case _LED5_off: LED5_off(); break;
default: printk("operate wrong!\n"); break;
嵌入式系统中的 I/O 接口驱动
吴宏伟
131180132
南京大学电子科学与工程学院
2016/12/11
一、实验目的
1、学习 Linux 操作系统下内核程序的编写和应用; 2、学习可编程接口芯片的编程控制方法; 3、学习嵌入式 Linux 操作系统设备驱动的方法。
二、实验相关介绍
1、接口电路介绍 Linux 以模块的形式加载设备类型,通常一个模块对应一个设备驱动。一般情况下,一 个设备驱动对应一类设备的模块方式,这样便于多个设备的协调工作,也利于应用程序 的开发和扩展。设备驱动程序负责将应用程序如读、写等操作正确无误的传递给相关的 硬件,并使硬件能够做出正确反应。因此在编写设备驱动程序时,必须要了解相应的硬 件设备的寄存器、IO 口及内存的配置参数。 设备驱动在准备好以后可以编译到内核中,在系统启动时和内核一起启动,这种方法在 嵌入式 Linux 系统中经常被采用。在开发阶段,设备驱动的动态加载更为普遍。开发 人员不必在调试过程中频繁启动机器就能完成设备驱动的调试工作。 嵌入式处理器片内集成了大量的可编程设备接口,为构成处理器系统带来了极大的便利。 S5PV210 有 237 只多功能引脚,大多数是功能复用的,可以通过初始化编程将它们设 置为 GPIO(General Purpose I/O,通用 IO)或 者某个具体功能。 2、内核模块介绍 一个内核模块至少包括两个函数: *由 module_init(function)声明,省缺函数名为 int init_module(),在装载时被 调用 *由 module_exit(function)声明,省缺函数名为 void cleanup_module(),卸载模 块式时被调用 Linux 内核版本编译内核模块通常使用下面的 Makefile: ifneq ($(KERNELRELEASE),) obj-m :=mod_name.o else KDIR :=/home/student/steven/linux-2.6.35 PWD :=$(shell pwd)
}
void LED3_off(void){ *pConfReg &=0xFFFF0FFF; *pConfReg |=0x00001000; *pDataReg |=(1<<3); printk("led3 off\n");
}
void LED4_off(void){ *pConfReg &=0xFFF0FFFF; *pConfReg |=0x00010000; *pDataReg |=(1<<4); printk("led4 off\n");
static int device_value=0;
static int led_open(struct inode*inode,struct file*file){ printk("led_open(%p)\n",file); if(device_value) return -1; device_value++; return 0;
default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
endif 执行命令 make 后生成以.ko 为后缀的文件名,它的作用和用法和.o 为后缀的文件时一 样的。 内核模块不是可独立运行的程序,它要依赖操作系统或者应用程序对其调用才能够实现 它的功能: $insmod mod_name.ko $rmmod mod_name 这两条命令需要获取 root 权限才可以执行,实验室的机器已经赋予了我们的权限。 3、实验系统的 LED 硬件资源介绍 本系统有 5 个 LED,其中 1 个是电源指示灯,当系统上电的时候就会亮,不属于可控制 的 LED;两外 3 个通过 GPJ0 的三个引脚控制,1 个通过 PWMTOUT1 控制。GPJ 表示的是 通用接口,GPJ0 是其中的一个口(S5PV210 中有 GPJ0、GPJ1、GPJ2、GPJ3、GPJ4 等), PWMTOUT1 表示脉冲宽度调制信号输出引脚,也就是说,这个端口可以输出占空比可调 节的脉冲。下为具体的电路图:
}
static int led_close(struct inode*inode,struct file*file){ printk("led_close(%p)\n",file); device_value--; return 0;
}
int led_ioctl(struct inode*inode,struct file*file, unsigned int cmd,unsigned long arg){
相关的引脚的含义可以查阅书中提供的表格。
三、实验内容
1、简单的 hello 程序 //Hello.c #include"linux/kernel.h"
int init_module(void){ printk("hello world!\n"); return 0;
}
void cleanup_module(void){ printk("goodbye world!\n");
#include"linux/ioctl.h" #include"linux/ioport.h" #include"asm/uaccess.h" #include"asm/io.h"
#define GPJ0CON 0xE0200240 #define GPJ0DAT 0xE0200244
#define _LED3_on _IO(100,0) #define _LED4_on _IO(100,10) #define _LED5_on _IO(100,20) #define _LED3_off _IO(100,30) #define _LED4_off _IO(100,40) #define _LED5_ofid){ *pConfReg &=0xFF0FFFFF; *pConfReg |=0x00100000; *pDataReg |=(1<<5); printk("led5 off\n");
}
switch(cmd){ case _LED3_on: LED3_on(); break; case _LED4_on: LED4_on(); break;
}
//Makefile ifneq ($(KERNELRELEASE),) obj-m :=Hello.o else KDIR :=/lib/modules/$(shell uname -r)/build PWD :=$(shell pwd) default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean endif 在终端下执行“$ make”之后,将生成 Hello.ko(在 make 命令前都需要执行$export ARCH=arm,下文涉及的 make 同样如此),这个文件用于模块的加载。接着启动目标机, 配置目标机网络: #ifconfig eth0 192.168.208.134 利用 mount 命令将主机的/srv/nfs4 目录挂在到/mnt 目录下: #mkdir /mnt #mount 192.168.208.34:/srv/nfs4 /mnt -o nolock -o proto=tcp 这样就实现了 NFS 文件共享。由于现在的内核模块在插入和卸载的时候都会转到 /lib/modules/内核版本号/这个目录里(目标机的内核版本号为 2.6.35),在目标机 上建立该目录,将模块的加载和卸载都在该目录下执行。 在客户机终端执行 # insmod ./Hello.ko 即可加载模块,在本次试验中分配的设备号是 223,可以通过 # cat /proc/devices 指令得到这个主设备号,可以看到字符串“hello world!”,表明模块成功的加载。根 据这个主设备号,就可以创建具体的设备文件了,这个设备文件即可实现用户空间和内 核空间的联系,通过以下命令的执行 # mknod /dev/Hello c 223 0 –m 666 在/dev 文件夹里面就有了一个新的字符驱动设备 Hello 了,在程序里可以通过 open 函数打开这个设备文件实现 IO 口的控制。 卸载模块调用命令 #rmmod Hello 通过命令 #cat /proc/devices 可以看到字符串“goodbye world!”表明模块已经成功卸载. 2、简单的 LED 程序 //LedDevice.c #include"linux/kernel.h" #include"linux/module.h" #include"linux/init.h" #include"linux/fs.h"
}
void LED4_on(void){ *pConfReg &=0xFFF0FFFF; *pConfReg |=0x00010000; *pDataReg &=~(1<<4); printk("led4 on\n");
}
void LED5_on(void){ *pConfReg &=0xFF0FFFFF; *pConfReg |=0x00100000; *pDataReg &=~(1<<5); printk("led5 on\n");
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean endif “linux/kernel.h”中包含了内核中必要的函数,“linux/module.h”中包含了模块 加载卸载等的函数,“linux/init.h”中有对函数 init_module(),clearup_module() 的 支 持 ,“ linux/ioctl.h ” 头 文 件 中 有 对 ioctl() 函 数 的 支 持 , 另 外 ,” linux/ioport.h”、”asm/io.h”对于下面的操作也是必要的。定义 MAJOR_NUM 为 0, 主要目的是为了动态分配得一个主设备号,避免和系统中已有的设备号冲突。 程序中有对驱动设备被打开时的信息提示,在函数 led_open 中;释放驱动设备时,也 有相关的信息提示,在函数 led_release 中,另外有驱动模块注册和卸载时的相关信 息,分别在函数 init_module 和函数 cleanup_module 中有相关信息,其中,设备的 主设备号在函数 init_module 中有打印到终端上,程序员可根据这个主设备号建立设 备文件。另外,函数 led_ioctl 中是对 IO 端口的操作,里面有 7 种情况,分别对应着 三个 LED 灯的亮和灭以及对应三个 LED 灯全灭,Linux 的内核函数 ioremap() 用来 将 I/O 设备的物理地址映射到内核虚地址空间。理论上来说,有了以上驱动程序对三个 LED 灯的亮灭的控制,就可以实现对 LED 的各种操作了,具体包括 LED 的亮灭,LED 亮 度的控制等等。在加载完模块之后,执行 # mknod /dev/led0 c 223 0 –m 666 测试程序: //LedTest.c #include"stdio.h" #include"fcntl.h" #include"unistd.h" #include"linux/ioctl.h" #include"linux/kernel.h" #include"stdlib.h"
相关文档
最新文档