毕设中涉及到一个关于GPIO驱动的编程,在这里想总结一下,也想和大家分享一下。
首先,开发平台是基于AT91RM9200的,主要是用该CPU去读取DS1820温度传感器的温度。至于DS1280这块芯片,我将在下面的文章中将有所介绍。GPIO(Gereral Programable Input Output)就是通用可编程输入输出口。
下面就总结一下,编写一个基于AT91RM9200的GPIO驱动的一些规则:
(1)需要定义函数结构体,比如:static int at91_NETMAN_ioctl(struct inode *,struct file *,int,unsigned long);这种函数主要是定义在一个叫file_operations的大的结构体中的,file_operations该结构体整合了所有需要对模块进行操作的一些函数。
在驱动程序中,
file_operations
是一个重要的结构体,通过它把针对设备的具体操作注册给内核的统一接口。结构体中全是函数指针,
open
、
release
、
ioctl
,用于设备的打开与释放、设备的读写以及设备的控制。
编写驱动程序就是构造一系列可供应用程序调动的函数(包括
open
、
release
、
read
、
write
、
llseek
、
ioctl
等)。在用户自己的驱动程序中,首先要根据驱动程序的功能,实现
file_operations
结构中的函数,不需要的函数接口可以直接
file_operations
结构中初始化为
NULL
;
file_operations
变量会在驱动程序初始化时注册到系统内部。当操作系统对设备操作时,会调用驱动程序注册的
file_operations
结构中的函数指针。方法一: S truct 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 *);
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 9200_ioctl(struct inode *,struct file *,int,unsigned long);
然后调用上面的结构体struct file_operations 9200_file{
ioctl:(void(*))9200_ioctl
};
在这种结构体中的每一个名称都对应着一个系统调用,用户进程利用系统调用,对主设备进行read和write等操作,系统调用通过设备文件的主设备号,找到相应的设备驱动程序,然后读取这个数据结构相应的函数,接着把控制权交给函数。
(2)设置基地址:AT91PS_SYS 9200_sys= (AT91PS_SYS)AT91C_VA_BASE_SYS
其中9200_sys是我自己定义的结构体名称,而AT91PS_SYS 是库函数定义的一个结构体,
其中9200_
SYS
是一个结构体,包含了
ATM9200
的所有寄存器,以上语句的功能就是把所有寄存器的最低地址赋值给9200_
SYS
,
这样结构体中的寄存器名称就和实际的地址对应起来了。注意:这里用到的地址都是经过映射过的虚拟地址,在实际运行中会通过处理器内部的
MMU
单元转换为实际的物理地址发往地址总线。
(3)打开设备:int 9200_open(struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return 0;
}
其中9200_open应该已经在file_operations
中定义。 而MOD_INC_USE_COUNT语句实现的功能我将详细介绍一下:因为我们在写好一个驱动程序,则需要将我们的主设备作为一个模块加载到内核当中去的,所以其他的模块也可能会使用这个模块,MOD_INC_USE_COUNT就是用来增加当前使用该模块的进程。另外还有MOD_DEC_USE_COUNT这个主要是减少减少当前用户或进程的数目,这个主要是用在释放设备的时候用到。
(4)释放设备:int 9200_close(struct inode *inode,struct file *filp)
{
MOD_DEC_USE_COUNT;
return 0;
同样,9200_close也是在file_operations 中定义的,
(5)注册和注销驱动设备:
一个新的文件系统要加入系统,必须进行注册。那么,一个新的驱动程序要加入系统,也必须进行注册。在下一章我们会看到,我们把设备大体分为字符设备和块设备。字符设备的注册和注销调用register_chrdev()和unregister_chrdev()函数。注册了设备驱动程序以后,驱动程序应该调用devfs_register()登记设备的入口点,所谓设备的入口点就是设备所在的路径名;在注销设备驱动程序之前,应该调用devfs_unregister()取消注册。
devfs_register()和devfs_unregister() 函数原型为: devfs_handle_t devfs_register(devfs_handle_t dir, const char *name,unsigned int flags,unsigned int major, unsigned int minor,umode_t mode, void *ops, void *info)其中devfs_handle_t表示Devfs的句柄(一个结构类型),每个参数的含义如下:
dir :
我们要创建的文件所在的Devfs的句柄。NULL意味着这是Devfs的根,即 /dev。
flags :
设备文件系统的标志,缺省值为DEVFS_FL_DEFAULT。
major :
主设备号,普通文件不需要这一参数。
minor :
次设备号, 普通文件也不需要这一参数
mode :
缺省的文件模式(包括属性和许可权)。
ops :
指向 file_operations 或 block_device_operations结构的指针
info :
任意一个指针,这个指针将被写到file结构的private_data域。
使用注册和注销的过程中需要使用到_init和_exit
(6)主函数ioctl():
在这个函数中将对AT91RM9200管脚的设置,实现对外部设备的控制,这个也是最核心的东西。
以上纯属个人的心得,希望对在学习CPIO驱动编程的朋友有所帮助。