登录站点

用户名

密码

通用I/O模块设计

已有 836 次阅读  2009-09-27 16:32   标签模块  设计 

通用I/O接口(General Purpose IO Interface,GPIO)是ARM系统及片上SoC(System-on-Chip)系统中非常重要的一种I/O接口,具有使用灵活、可配置性好、硬件代价小等优点,在ARM系统中广泛应用。

1 实例说明

ARM Linux系统的设备可分为字符设备、块设备和网络设备三种。GPIO属于字符设备,字符设备是指存取时具有较少缓存的设备。而块设备的读写都由缓存来支持,并且块设备必须能够随机存取,字符设备则没有这个要求。

GPIO不仅支持常用的中断和查询低速数据传输方式,还支持DMA高速数据传输方式,支持多种I/O端口类型,可满足多种应用需求。

本章介绍ARM的通用I/O资源,并结合外围电路给出了这些GPIO的一般使用方法。

2  GPIO原理

2.1 GPIO设备驱动原理

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

GPIO属于字符设备,其驱动程序的使用可以按照两种方式编译,一种是静态编译进内核,另种是编译成模块以供动态加载。由于嵌入式Linux支持静态编译和动态加载两种模式,如果考虑到精简内核的需要,这里可以使用动态加载的方法来实现驱动的装载。

设备驱动程序必须向Linux核心或者它所在的子系统提供个标准的接口。例如,USB驱动程序向Linux核心提供了一个设备文件I/O接口,GPIO设备驱动程序向GPIO子系统提供了GPIO设备接口,接着向核心提供了文件I/O和缓冲区的接口。

1. 用户程序访问GPIO

     用户程序访问设备的整体工作情况如图8-1所示。


如图8-l所示的数据结构是在内核态工作,而内核通过对相应数据结构的赋值,以此记录了用户程序对外部设备的使用情况。节点数据结构工作在JFFS(一种基于Flash的文件系统)阶段,其定位了用户程序访问的设备文件,并根据相应文件属性满足用户程序对该设备文件的访问。在对设备数据的交换过程中,File数据结构维护着缓冲区的数据。

设备驱动程序使用标准的核心服务如内存分配、中断转发和等待队列来完成工作。大多数设备驱动程序可以在需要的时候作为核心模块加载,在不需要的时候卸载。这使得核心服务对于系统资源非常具有适应性和效率性。

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

·对设备初始化和释放。

·把数据从内核传送到硬件,以及从硬件读取数据。

     ·读取应用程序传送给设备文件的数据,以及回送应用程序请求的数据。

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

  2.用户进程调用

    用户进程通过设备文件与实际的硬件打交道。每个设备文件都有其文件属性(c/b),表示是字符设备还是块设备。另外,每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是次设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用次设备号来区分它们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。

    在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在驱动程序的子函数返回后才能进行其他的工作。如果驱动程序陷入死循环,那么整个内核就会崩溃,只有重新启动系统。

2.2如何驱动GPIO及对应设备

一般的ARM都提供超过100路的GPIO复用管脚,要使用这些管脚,就必须首先将其驱动起来。如果使用的芯片带有MMU内存管理,那么在写驱动模块的时候不能直接去操作物理地址,必须利用io rein ap命令重新映射。

1. I/0端口

和硬件打交道离不开I/O端口,老的ISA设备经常是占用实际的I/0端口,在Linux下,操作系统没有对I/0端口屏蔽。也就是说,任何驱动程序都可以对任意的I/O;端口操作,这样就很容易引起混乱。每个驱动程序都应该自己避免误用端口,有两个重要的kernel函数可以保证驱动程序做到这一点。

check-region(int  i0-port,  int 0ff_set)

这个函数用来查看系统的I/0表,看是否有别的驱动程序占用某一段I/0口。参数1:I/0端口的基地址;参数2:I/O端口占用的范围。返回值:0,没有占用;非0,已经被占用。

request_region(int i0_port,  inL 0ff_set,char★deVname)

如果这段:I/0端口没有被占用,那么在驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用;登记后,在/proc/ioports文什中可以看到登记的I/O口。参数l:I/O端口的基地址;参数2:I/O端口占用的范围;参数3:使用这段I/0地址的设备名。在对I/O口登记后,就可以放心地用inb(),outb()之类的函数来访问了。

2. 和设备文件对话

驱动程序提供了对设备操作的接口,同时在程序中实现了基本操作所需要的基本函数。用户程序通过访问设备文件的方式对设备间接操作,Linux系统提供了ioctl(input outputcontrol的缩写)函数可以很方便地实现这一操作。

其中,fd就是用户程序打开设备时使用open函数返回的文件标识符;cmd就是用户程序对设备的控制命令;后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。

选择设备的控制命令时,需要根据Linux文档所提供的标准控制字(Documentation/ioctl-number.txt)中选择的合适控制字,如果选择不合适会和系统中的其他设备发生冲突。

3 GPlO应用举例

GPIO的硬件应用如图8-2所示,它可以多路复用,以在需要时提供额外的功能。但GPIO的许多引脚是和地址线、数据线、串口线等引脚复用的。


4 GPlO设备程序开发

4.1开发步骤

     GPIO驱动可以归类为Linux设备驱动的字符设备驱动,以下是开发它的一些具体步骤。

     (1)模块化驱动程序

    不失Linux驱动开发的一般性,在写字符设备的驱动程序时,也要遵守模块化编程的一般规范。设备模块在用户空间的初始化和终止:

·Init_mnodule()向内核注册模块提供数据结构、局部和全局变量。

·Cleanup_module()取消所有init_module在内核中的注册。

(2)设备模块在内核空间的内存申请和释放

kmalloc()函数分配一段内存,这样就实现了Chrdevs向量表中指向设备驱动程序名称的指针。使用kfree释放内存。

(3)字符设备主设备号和次设备号的分配

主设备号标志设备对应的驱动程序,内核利用主设备号将设备与相应的驱动程序对应起来。主设备号的取值范围是0~255,如果不善加规划,则容易造成主设备号的冲突。主设备号的分配有静态和动态之分,从开发设备的角度来看,推荐静态设备号的分配。

次设备号由驱动程序使用,内核的其他部分并不使用它,仅将它传递给驱动程序。一个驱动程序控制若干个设备,次设备号提供了一种区分它们的方法。

(4)设备模块在内核空间的注册与注销

字符设备的注册有2种方法,一种是常用注册方法,通过系统函数registel_chrdev()将设备加入到系统设备列表中;另一种方法是devfs技术,通过系统函数devfs-regisrer()实现设备的注册。注销与注册相反,分别调用unregister_chrdev()函数和devfs_unregister()函数。

(5)设备模块在内核空间提供系统调用的函数

设备驱动程序在注册成功以后,用户可以通过访问设备特殊文件(一般情况在/dev目录下)实现系统调用。

GPIO驱动程序只需要一个file_operations数据结构体就可以了。这是因为GPIO本身在设计阶段就定制好了接入的物理设备,接入到GPIO端口的物理设备并不需要即插即用。因此可以在开发阶段定制该接口的物理设备,也可以把这个接口的设备作为该产品的标配外设发布。

这样,在编写驱动程序的过程中,只要写好应用程序使用标准系统调用打开、读取、写和关闭等处理函数,就可以完成驱动程序的开发。其中,标准系统调用处理函数在所有的字符设备里面具有同样的功能。

4.2 GPlO端口编程

以下程序的运行效果为使接在GPIO口的LED显示器轮流被点亮。


5实例总结

    本章介绍了ARM的GPIO资源,并结合ARM的外围电路给出了这些GPIO的一般使用方法。GPIO为外围设备提供了信号输出和从外围设备输入信号到ARM的引脚。这些引脚能通过软件提供多用途的输入和输出信号。另外,ARM提供多路GPIO复用管脚,要使用这些管脚,就必须首先将其驱动起来。

上一篇: SDRAM模块设计 下一篇: 中断处理模块设计

分享 举报