⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch08-硬件管理(转载).htm

📁 LINUX驱动编程
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<TABLE borderColor=#666666 cellPadding=2 width="90%" align=center border=2>
  <TBODY>
  <TR>
    <TD bgColor=#000000>
      <P align=center><A href="http://joyfire.net/lsdp/index.htm"><FONT 
      color=#ffffff size=2>目录页</FONT></A> | <A 
      href="http://joyfire.net/lsdp/9.htm"><FONT color=#ffffff 
      size=2>上一页</FONT></A> | <A href="http://joyfire.net/lsdp/11.htm"><FONT 
      color=#ffffff size=2>下一页</FONT></A></P>
      <P align=center><FONT face=黑体 color=#ffffff size=6>(LDD) Ch08-硬件管理(转载) 
      </FONT></P><SPAN style="LINE-HEIGHT: 1; LETTER-SPACING: 0pt"><FONT 
      color=#ffffff size=3>
      <P>发信人:&nbsp;Altmayer&nbsp;(alt),&nbsp;信区:&nbsp;GNULinux<BR>标&nbsp;&nbsp;题:&nbsp;(LDD)&nbsp;Ch08-硬件管理(转载)<BR>发信站:&nbsp;饮水思源&nbsp;(2001年12月13日08:57:23&nbsp;星期四),&nbsp;站内信件<BR>&nbsp;<BR>【&nbsp;以下文字转载自&nbsp;<FONT 
      color=#00ff00>UNIXpost&nbsp;</FONT>讨论区&nbsp;】<BR>【&nbsp;原文由<FONT 
      color=#00ff00>&nbsp;altmayer.bbs@bbs.nju.edu.cn,</FONT>&nbsp;所发表&nbsp;】<BR>&nbsp;<BR>【&nbsp;以下文字转载自&nbsp;<FONT 
      color=#00ff00>altmayer&nbsp;</FONT>的信箱&nbsp;】<BR>&nbsp;<BR>&nbsp;<BR>第8章&nbsp;硬件管理<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;尽管通过玩玩scull和类似的一些玩具程序来熟悉Linux设备驱动程序的软件接口<BR>显得很轻松,但是测试真正的设备还是要涉及硬件。驱动程序是软件概念和硬件设备间<BR>的一个抽象层;因此,我们两者都要谈谈。到目前为止,我们已经详细讨论了软件上的<BR>一些概念;本章将接着介绍驱动程序是如何在保证可移植的前提下访问I/O端口和I/O地<BR>址空间的。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;和前面一样,我的示例代码也不针对特定的设备。但是我们不能再用象scull这<BR>样的基于内存的设备。本章的例子使用并口设备来讲解I/O指令,使用字符模式VGA卡的<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>样的基于内存的设备。本章的例子使用并口设备来讲解I/O指令,使用字符模式VGA卡的<BR>显示缓冲区来讲解基于内存映射的I/O。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这里我选择并口的原因是它能提供位信息的输入输出。写向设备的数据位出现在<BR>输出引脚上,而输入引脚的电压值可以由处理器控制。实际上,你可以将LED连到并口上<BR>来观察I/O操作的结果。并口比串口更容易编程,而且几乎每台计算机(甚至Alpha)都和P<BR>C机一样提供了并口。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;至于基于内存映射的I/O,字符模式的VGA是标准的内存映射设备,而每台计算机<BR>都有VGA兼容的字符模式。遗憾的是,不是每台Alpha都有VGA显示卡,Sparc就肯定没有<BR>,所以我们的与VGA有关的代码就不象并口的例子那样可以移植了。并且,为了运行示例<BR>程序,你要切换到字符模式下,但这并不是个太大的限制。用VGA显示卡上上内存做实验<BR>可能造成的最大问题是示例驱动程序不可避免地会破坏前台的虚拟控制台。<BR>&nbsp;<BR>使用I/O端口<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;I/O端口有点类似内存位置:可以用和访问内存芯片相同的电信号对它进行读写<BR>。但这两者实际上并不一样;断口操作是直接对外设进行的,和内存相比更不灵活。而<BR>且,有8位的端口,也有16位的端口和32位的端口,不能相互混淆*。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因此,C语言程序必须调用不同的函数来访问大小不同的端口。Linux内核头文件<BR>中(就在与体系结构相关的头文件&lt;asm/io.h&gt;中)定义了如下一些内联函数。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>——————————————————————————————————————<BR>—<BR>&nbsp;<BR>注意&nbsp;&nbsp;&nbsp;&nbsp;从现在开始,如果我只使用unsigned而不进一步指定类型信息的话,那我是在<BR>谈及一个与体系结构相关的定义,此时就不必关心它的准确特性。这些函数基本是可移<BR>植的,因为编译器在赋值时会自动进行强制类型转换(cast)-类型被强制转换成unsigne<BR>d类型防止了编译时出现的警告信息。只要程序员赋值时注意避免溢出,这种强制类型转<BR>换就不会丢失信息。在本章剩余部分我会一直保持这种“不完整的类型定义”的方式。<BR>&nbsp;<BR>——————————————————————————————————————<BR>—<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>unsigned&nbsp;inb(unsigned&nbsp;port);<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>unsigned&nbsp;inb(unsigned&nbsp;port);<BR>&nbsp;<BR>void&nbsp;outb(unsigned&nbsp;char&nbsp;byte,&nbsp;unsigned&nbsp;port);<BR>&nbsp;<BR>按字节(8位宽度)读写端口。port参数在一些平台上定义为unsigned&nbsp;long,而在另一些<BR>平台上定义为unsigned&nbsp;short。不同平台上inb返回值的类型也不相同。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>unsigned&nbsp;inw(unsigned&nbsp;port);<BR>&nbsp;<BR>void&nbsp;outw(unsigned&nbsp;short&nbsp;word,&nbsp;unsigned&nbsp;port);<BR>&nbsp;<BR>这些函数用于访问16位端口(“字宽度”);Linux的M68k版本不提供,因为该处理器只支<BR>持字节宽度的I/O操作,不支持字宽度或更大宽度的操作。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>unsigned&nbsp;inl(unsigned&nbsp;port);<BR>&nbsp;<BR>void&nbsp;outl(unsigned&nbsp;doubleword,&nbsp;unsigned&nbsp;port);<BR>&nbsp;<BR>这些函数用于访问32位端口。doubleword参数根据不同平台定义成unsigned&nbsp;long类型或<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>这些函数用于访问32位端口。doubleword参数根据不同平台定义成unsigned&nbsp;long类型或<BR>unsigned&nbsp;int类型。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;除了每次只能传输一个数据单位的in和out操作,绝大多数处理器还提供了能传<BR>输多个字节,字或long类型数据的特殊指令。这些指令就是所谓的“串指令”,将在本<BR>章稍后处的“串操作”一节中介绍。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;注意这里没有定义64位的I/O操作。即使在64位的体系结构上,I/O端口也只使用<BR>32位的数据通路。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面这些函数主要是提供给设备驱动程序使用的,但它们也可以在用户空间使用<BR>(预处理定义和内联声明没有用#ifdef&nbsp;__KERNEL__保护)。但是如果要在用户空间代码中<BR>使用inb及其相关函数,必须满足下面这些条件:<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译该程序时必须带-O选项来强制内联函数的展开。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译该程序时必须带-O选项来强制内联函数的展开。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;必须用ioperm或iopl来获取对端口进行I/O操作的权限。ioperm用来获取对指<BR>定端口的操作权限,而iopl用来获取对整个I/O空间的操作权限。这两个函数都是Intel<BR>平台提供的。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;必须以root身份运行该程序才能调用ioperm。或者,该程序的某个祖先已经以<BR>root身份获取了对端口操作的权限。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;示例程序misc-progs/inp.c和misc-progs/outp.c是在用户空间通过命令行读写8<BR>位端口的一个小工具。我已经在我的PC上成功运行过。但由于缺少ioperm函数的原因,<BR>它们不能在其它平台上运行。如果你想冒险,可以将它们设置上SUID位,那么不用显式<BR>地获取特权就可以使用硬件了。<BR>&nbsp;<BR>平台相关性<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果你考虑移植问题,你会发现I/O指令是所有计算机指令中与处理器最密切相<BR>关的部分。因此,大部分与I/O端口有关的源代码都与平台相关。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>关的部分。因此,大部分与I/O端口有关的源代码都与平台相关。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Linux系统,尽管是可移植的,但处理器的特性不是完全透明的。大部分硬件驱<BR>动程序在平台间是不可移植的,而且在同一模块中驱动程序所涵盖的平台一般不超过两<BR>到三种。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;回头看看前面的函数列表,你可以看到一处不兼容的地方,数据类型,参数类型<BR>根据各平台体系结构上的不同要相应地使用不同的数据类型。例如,port参数在x86平台<BR>(处理器只支持64KB字节的I/O空间)上定义为unsigned&nbsp;short,但在Alpha平台上定义为u<BR>nsigned&nbsp;long。Alpha平台上端口是和内存在同一地址空间内的一些特定区域,它在设计<BR>上就不存在I/O地址空间,它的端口是被当作为不能被高速缓冲的内存区域。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;I/O数据类型是核心中一个仍需要整理的部分,尽管现在能正常工作。对这些不<BR>够明确的类型最好的解决方法是定义一个与体系结构有关的port_t数据类型而对数据项<BR>则使用u8,u16和u32这些数据类型(参见第10章“合理使用数据类型”的“分配确定的空<BR>间大小给数据项”一节)。但还没人真正注意过这个问题,因为这个问题主要是个书写技<BR>巧上的问题。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>巧上的问题。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其他一些与平台相关的问题来源于处理器结构根本上的差异,因此也无法避免。<BR>因为本书我假定你不会在不了解底层硬件的情况下为特定的系统写驱动程序,所以我不<BR>会详细讨论这些差异。下面是可以支持的体系结构的总结:<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>X86<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;该体系结构支持本章提到的所有函数。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>Alpha<BR>&nbsp;<BR>支持前面所有函数,但不同的Alpha平台上端口I/O操作的实现也有不同。串操作是用C语<BR>言实现的,在文件arch/alpha/lib/io.c中定义。但遗憾的是,2.0系列的核心在2.0.29<BR>版之前还只开放word和long类型数据的串操作;因此,模块中无法使用insb和outsb函数<BR>。在2.0.30和2.1.3版中这个问题已经改正过来了。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>&nbsp;<BR>&nbsp;<BR>Sparc<BR>&nbsp;<BR>Sparc不提供特殊的I/O指令。I/O空间是通过内存映射获得,在页表中设置了标志。在头<BR>文件中inb和其它函数都被定义为空函数来避免第一次把驱动程序移植到Sparc体系结构<BR>上时编译器为此报告错误。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>M68k<BR>&nbsp;<BR>只支持inb,outb和它们相应的暂停式版本(见下节)。68000上没有定义串操作,也没定<BR>义readb,writeb和相关函数。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>Mips<BR>&nbsp;<BR>支持前面所有函数。但串操作是用汇编语言写的紧凑循环(tight&nbsp;loop)实现的,因为Mip<BR>s处理器不提供机器一级的串I/O操作。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>&nbsp;<BR>&nbsp;<BR>PowerPC<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;除了串I/O操作,其它函数都能支持。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;感兴趣的读者可以从io.h文件获得更多信息,除了我在本章介绍的函数,一些与<BR>体系结构相关的函数有时也由该文件定义。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;值得提及的是,Alpha处理器并不为端口提供不同的地址空间,虽然AXP机器一般<BR>带有ISA和PCI插槽,而且这两种总线都为内存和I/O操作提供了不同的信号线。利用特别<BR>的接口芯片将指定的内存地址引用转换成对I/O端口的访问,基于Alpha的PC可以实现一<BR>个与Intel系列兼容的I/O抽象层。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Alpha平台上的I/O操作在“Alpha参考手册”中详细介绍了,该手册可从DEC公司<BR>免费获得,手册详尽地阐述了I/O问题,介绍了AXP处理器是如何将虚拟地址空间划分为<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>免费获得,手册详尽地阐述了I/O问题,介绍了AXP处理器是如何将虚拟地址空间划分为<BR>“类内存”和“不类内存”区域;后者用于内存映射的I/O。<BR>&nbsp;<BR>暂停式(pausing)I/O<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一些平台-特别是在i386上-当处理器和总线间数据得传输太快是会带来问题。<BR>问题是源于相对ISA总线处理器的时钟频率太快了,当设备卡太慢时,这个问题就容易暴<BR>露出来;解决该问题的方法是,如果后面又跟着一条I/O指令,就在该条I/O指令后添加<BR>一小段延迟。如果你的设备会丢失数据,或者你担心它会丢失数据,你可以用暂停式的I<BR>/O操作取代通常的I/O操作。暂停式I/O函数很象前面列出的那些I/O函数,但它们的名字<BR>都以_p结尾;例如inb_p,outb_p等等。对所有支持的体系结构,如果定义了不暂停的I/<BR>O函数,那么也会定义相应的暂停式的I/O函数,虽然有些平台上它们会被扩展成相同的<BR>代码。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果你想在驱动程序中显式地插入一小段延迟(小于用udelay可获得的延迟),你<BR>可以显式地使用SLOW_DOWN_IO语句。由这个宏扩展成的一段指令只是用于延迟,不做任<BR>何其他的工作。你可以将这条语句插入到源代码中的一些关键位置上。实际上,SLOW_DO<BR>WN_IO所执行的代码就是outb_p被扩展后相对于outb所增加的代码。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SLOW_DOWN_IO(和_p延迟)是否被定义取决于在包含&lt;asm/io.h&gt;文件前是否定义过<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SLOW_DOWN_IO(和_p延迟)是否被定义取决于在包含&lt;asm/io.h&gt;文件前是否定义过<BR>SLOW_IO_BY_JUMPING和/或REALLLY_SLOW_IO。好在新的硬件已经不要求程序处理这些问<BR>题了,所以我不会再讨论暂停问题了。感兴趣的读者可以读读头文件&lt;asm/io.h&gt;。但是<BR>在写驱动程序的时候,你必须记着SLOW_DOWN_IO在Sparc和M68k体系结构上是没有定义的<BR>(虽然也定义了象outb_p这样暂停式的函数调用,在本章前面的“平台相关性”一节中列<BR>出了限制条件)。<BR>&nbsp;<BR>串操作<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Linux头文件中定义了进行串操作的函数,驱动程序可以使用它们来获得比C语言<BR>写的循环更好的性能。在Linux中,取决与相应的处理器或平台,串操作被实现为一条机<BR>器指令,或者是一个紧凑循环,但也可能不提供。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>串操作函数的原型如下:<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>void&nbsp;insb(unsigned&nbsp;port,&nbsp;void&nbsp;*addr,&nbsp;unsigned&nbsp;long&nbsp;count);<BR>&nbsp;<BR>void&nbsp;outsb(unsigned&nbsp;port,&nbsp;void&nbsp;*addr,&nbsp;unsigned&nbsp;long&nbsp;count);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从内存地址addr开始连续读写count个字节。但只对一个端口port读写这些数据<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从内存地址addr开始连续读写count个字节。但只对一个端口port读写这些数据<BR>。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>void&nbsp;insw(unsigned&nbsp;port,&nbsp;void&nbsp;*addr,&nbsp;unsigned&nbsp;long&nbsp;count);<BR>&nbsp;<BR>void&nbsp;outsw(unsigned&nbsp;port,&nbsp;void&nbsp;*addr,&nbsp;unsigned&nbsp;long&nbsp;count);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对一个16位端口连续读写16位数据。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>void&nbsp;insl(unsigned&nbsp;port,&nbsp;void&nbsp;*addr,&nbsp;unsigned&nbsp;long&nbsp;count);<BR>&nbsp;<BR>void&nbsp;outsl(unsigned&nbsp;port,&nbsp;void&nbsp;*addr,&nbsp;unsigned&nbsp;long&nbsp;count);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对一个32位端口连续读写32位数据。<BR>&nbsp;<BR>使用并口<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我们用并口来测试我们的I/O代码,并口很简单,事实上,我很难想出一个比它<BR>更简单的接口适配卡。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;尽管大部分读者都能取得并口规范,但为了方便读者,在你读下面要介绍的模块<BR>之前我先将它总结一下。<BR>&nbsp;<BR>并口的基本知识<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;并口的最小配置(不涉及ECP和EPP模式)由一些8位端口组成。写到输出端口的数<BR>据表现为25脚插座的输出引脚上的电平信号,而从输入端口读到的数据则是输入引脚当<BR>前的逻辑电平值。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在并行通信中使用的电平信号是标准的TTL电平:0伏和5伏,逻辑阈值大约为1.2<BR>伏;端口要求至少满足标准的TTL&nbsp;LS电流规格,而现代的大部分并口电流和电压都超过<BR>这个规格。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>——————————————————————————————————————<BR>—<BR>&nbsp;<BR>警告&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;并口插座没有和计算机的内部电路分开,这一点在你想把逻辑门直接连到<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>警告&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;并口插座没有和计算机的内部电路分开,这一点在你想把逻辑门直接连到<BR>端口时很有用。但要注意正确连线;否则在测试自己定制的电路时,并口很容易被烧毁<BR>。如果你担心会破坏你的主板的话,可以选用可插拔的并行接口。<BR>&nbsp;<BR>——————————————————————————————————————<BR>—<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;位规范显示在图8-1中。你可以读写12个输出位和5个输入位,其中一些位在它们<BR>的信号通路上输入输出会有逻辑上的反转。唯一一个不与任何信号引脚有联系的位是2号<BR>端口的第4位(0x10)。我们将在第9章“中断处理”中使用到它。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>控制端口:基地址+2<BR>&nbsp;<BR>状态端口:基地址+1<BR>&nbsp;<BR>数据端口:基地址+0<BR>&nbsp;<BR>关键字<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>输入信号线<BR>&nbsp;<BR>输出信号线<BR>&nbsp;<BR>位<BR>&nbsp;<BR>引脚<BR>&nbsp;<BR>反转&nbsp;非反转<BR>&nbsp;<BR>图&nbsp;8-1:并口的插脚引线<BR>&nbsp;<BR>驱动程序样例<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我下面要介绍的驱动程序叫做short(Simple&nbsp;Hardware&nbsp;Operations&nbsp;and&nbsp;Raw<BR>Tests,简单硬件的操作和原始测试)。它所做的就是读写并口(或其他I/O设备)的各种8<BR>位端口。每个设备节点(拥有唯一的次设备号)访问一个不同的端口。short设备没有任何<BR>实际用途;将它独立出来只是为了能用一条指令来从外部对端口进行操作。如果你不太<BR>了解I/O端口,那么可以通过使用short来熟悉它;你可以测量它传输数据要消耗的时间<BR>或者进行其它的测试。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;我建议你把一个LED焊到输出引脚上以便观察并口插座。每个LED都要串联一个1K<BR>Ω的电阻到一个接地的引脚上。如果将输出端口接到输入端口上,你就可以产生自己的<BR>输入供输入端口读取。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果你想将LED焊到D型插座上来观察并行数据,我建议你不要使用9号和10号引<BR>脚,因为在运行第9章的示例代码时我们要将它们相连。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;至于short,它是通过紧凑循环将用户数据拷贝到输出端口来实现写设备/dev/sh<BR>ort0的,每次一个字节:<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(count--)<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;outb(*(ptr++),&nbsp;port);<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;你可以运行下面的命令来使你的LED发光:<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;-n&nbsp;any&nbsp;string&nbsp;&gt;&nbsp;/dev/short0<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每个LED监控输出端口的一个位。注意只有最后写的字符数据才会在输出引脚上<BR>稳定地保持下来,被你观察到。因此,我建议你将-n选项传给echo程序来制止输出字符<BR>后的自动换行。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;读端口也是使用类似的函数,只是用inb代替了outb。为了从并口读取“有意义<BR>的”值,你需要将某个硬件连到并口插座的输入引脚上来产生信号。如果没有输入信号<BR>,你只会读到始终是相同字节的无穷的输出流。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;覆盖所有可能的I/O函数,每个short设备都提供了三个变种:/dev/short0执行<BR>的是上面给出的循环,/dev/short0p使用outb_p和inb_p来替代前者使用的“较快的”函<BR></P></FONT><FONT 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -