微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > IO端口和IO内存的区别及分别使用的函数接口

IO端口和IO内存的区别及分别使用的函数接口

时间:11-23 来源:互联网 点击:
IO端口和IO内存的区别及分别使用的函数接口

每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口,通常包括:控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式,可以把CPU分成两大类。一类CPU(如M68K,Power PC等)把这些寄存器看作内存的一部分,寄存器参与内存统一编址,访问寄存器就通过访问一般的内存指令进行,所以,这种CPU没有专门用于设备I/O的指令。这就是所谓的“I/O内存”方式。另一类CPU(典型的如X86),将外设的寄存器看成一个独立的地址空间,所以访问内存的指令不能用来访问这些寄存器,而要为对外设寄存器的读/写设置专用指令,如IN和OUT指令。这就是所谓的“I/O端口”方式。但是,用于I/O指令的“地址空间”相对来说是很小的,如x86 CPU的I/O空间就只有64KB(0-0xffff)。

结合下图,我们彻底讲述IO端口和IO内存以及内存之间的关系。主存16M字节的SDRAM,外设是个视频采集卡,上面有16M字节的SDRAM作为缓冲区。

1.CPU是i386架构的情况

在i386系列的处理中,内存和外部IO是独立编址,也是独立寻址的。MEM的内存空间是32位可以寻址到4G,IO空间是16位可以寻址到64K。

在Linux内核中,访问外设上的IO Port必须通过IO Port的寻址方式。而访问IO Mem就比较罗嗦,外部MEM不能和主存一样访问,虽然大小上不相上下,可是外部MEM是没有在系统中注册的。访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。为了达到接口的同一性,内核提供了IO Port到IO Mem的映射函数。映射后IO Port就可以看作是IO Mem,按照IO Mem的访问方式即可。

3. CPU是ARM或PPC架构的情况

在这一类的嵌入式处理器中,IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus。系统的寻址能力如果是32位,IO Port+Mem(包括IO Mem)可以达到4G。

1.使用I/O端口

I/O端口是驱动用来和很多设备通讯的方法。

1.1、分配I/O端口

在驱动还没独占设备之前,不应对端口进行操作。内核提供了一个注册接口,以允许驱动声明其需要的端口:

#include
/* request_region告诉内核:要使用first开始的n个端口。参数name为设备名。如果分配成功返回值是非NULL;否则无法使用需要的端口(/proc/ioports包含了系统当前所有端口的分配信息,若request_region分配失败时,可以查看该文件,看谁先用了你要的端口) */
structresource*request_region(unsignedlongfirst,unsignedlongn,constchar*name);
/* 用完I/O端口后(可能在模块卸载时),应当调用release_region将I/O端口返还给系统。参数start和n应与之前传递给request_region一致 */
voidrelease_region(unsignedlongstart,unsignedlongn);
/*check_region用于检查一个给定的I/O端口集是否可用。如果给定的端口不可用,check_region返回一个错误码。不推荐使用该函数,因为即便它返回0(端口可用),它也不能保证后面的端口分配操作会成功,因为检查和后面的端口分配并不是一个原子操作。而request_region通过加锁来保证操作的原子性,因此是安全的 */
intcheck_region(unsignedlongfirst,unsignedlongn);

1.2、操作I/O端口

在驱动成功请求到I/O端口后,就可以读写这些端口了。大部分硬件会将8位、16位和32位端口区分开,无法像访问内存那样混淆使用。驱动程序必须调用不同的函数来访问不同大小的端口。

如同前面所讲的,仅支持单地址空间的计算机体系通过将I/O端口地址重新映射到内存地址来伪装端口I/O。为了提高移植性,内核对驱动隐藏了这些细节。Linux内核头文件(体系依赖的头文件)定义了下列内联函数来存取I/O端口:

/*inb/outb:读/写字节端口(8位宽)。有些体系将port参数定义为unsigned long;而有些平台则将它定义为unsigned short。inb的返回类型也是依赖体系的 */
unsignedinb(unsignedport);
voidoutb(unsignedcharbyte,unsignedport);
/*inw/outw:读/写字端口(16位宽)*/
unsignedinw(unsignedport);
voidoutw(unsignedshortword,unsignedport);
/*inl/outl:读/写32位端口。longword也是依赖体系的,有的体系为unsigned long;而有的为unsigned int */
unsignedinl(unsignedport);
voidoutl(unsignedlongword,unsignedport);

从现在开始,当我们使用unsigned没有进一步指定类型时,表示是一个依赖体系的定义。

注意,没有64位的I/O端口操作函数。即便在64位体系中,端口地址空间使用一个32位(最大)的数据通路。

1.3、从用户空间访问I/O端口

1.2节介绍的函数主要是提供给驱动使用,但它们也

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top