📄 io-port-programming.txt
字号:
6.2 游戏 (操纵□) 埠 (game port) 游戏埠的埠位址□围为 0x200-0x207. 想要控制一般的操纵□, 有一个核心层次 的操纵□驱动程式, 可参考网址 [10]ftp://sunsite.unc.edu/pub/Linux/kernel/patches/, 档名 joystick-*. 埠的脚位排列 (Pinout) 方式 (该埠是一个 15 只脚 D 字形外壳 (D-shell) 的 母头连接器): * 1,8,9,15: +5 V (电源) * 4,5,12: 接地 * 2,7,10,14: 分别是 BA1, BA2, BB1, 和 BB2 等数位输入 * 3,6,11,13: 分别是 AX, AY, BX, 和 BY 等``类比''输入 +5 V 的脚位似乎通常会被直接连接到主机板的电源线上, 所以他应该能够提供相 当的电力, 这还要看所使用主机板, 电源供给器, 以及游戏埠的类型. 数位输入用於操纵□的按钮可以让你连接二个操纵□的四个按钮 (操纵□ A 和 操纵□ B, 各有二个按钮) 到游戏埠也就是数位输入的四个脚位. 他们应该是一 般 TTL 电压位准的输入, 你可以直接从状态埠 (参考下面说明) 读出他们的位准 状态. 一个实际的操纵□在按钮被压下时会传回低位准 (0 V) 状态否则就是高位 准 (5V 经由 1 Kohm 的电阻连接到电源脚位) 状态. 所谓的类比输入实际是量测到的阻抗值. 游戏埠有四个单击多谐振□器 (one-shot multivibrator) (一个 558 晶片) 连接到四个类比输入脚位. 每个类 比输入脚位与多谐振□器的输出之间连接著一个 2.2 Kohm 的电阻, 而且多谐振 □器的输出与地之间连接著一个 0.01 uF 的时序电容 (timing capacitor). 一 个实际的操纵□其每个座标 (X 和 Y) 上会有一个可变电阻, 连接在 +5 V 与每 个相对的类比输入脚位之间 (脚位 AX 或 AY 是给操纵□ A 用的, 而脚位 BX 或 BY 是给操纵□ B用的). 操作的时候, 多谐振□器将其输出设定为高位准 (5 V) 并且等到时序电容上的电 压达到 3.3 V 之後将相对的输出设定为低位准. 因此操纵□中多谐振□器输出的 高位准时间周期 与可变电阻的电阻值成正比 (也就是, 操纵□在相对座标的位 置), 如下所示: R = (t - 24.2) / 0.011, 其中 R 是可变电阻的阻抗值 (ohms) 而 t 是高位准时间周期的长度 (秒). 因此要读出类比输入脚位的数值, 首先你得启动多谐振□器 (以埠写入的方式; 请看下面), 然後查询四个座标的信号状态(以持续的埠读出方式)一直到信号状态 由高位准变成低位准, 计算其高位准时间周期的长度. 这个持续查询的动作花费 相当多的 CPU 时间, 而且在一个非即时的多工环境像是 (一般的使用者模式 (user-mode) ) Linux, 所得的结果不是非常准确因为你无法以固定的时间来查询 信号的状态 (除非你使用核心层次的驱动程式而且你得在你查询的时候抑制掉中 断的产生, 但是这样做会浪费更多的 CPU 时间). 如果你知道信号的状态将会花 费一段不短的时间 (数十毫秒) □会成为低位准, 你可以在查询之前呼叫函式 usleep() 将 CPU 的时间让给其他想要执行的行程 (processes). 游戏埠中唯一需要你来存取的埠位址是 0x201 (其他的埠位址不是动作一样就是 没用). 任何对这个埠位址所做的写入动作 (不论你写入什麽) 都会启动多谐振□ 器. 对这个埠位址做读出动作会取回输入信号的状态: * Bit 0: AX ( (1=高位准) 多谐振□器的输出状态) * Bit 1: AY ( (1=高位准) 多谐振□器的输出状态) * Bit 2: BX ( (1=高位准) 多谐振□器的输出状态) * Bit 3: BY ( (1=高位准) 多谐振□器的输出状态) * Bit 4: BA1 (数位输入, 1=高位准) * Bit 5: BA2 (数位输入, 1=高位准) * Bit 6: BB1 (数位输入, 1=高位准) * Bit 7: BB2 (数位输入, 1=高位准) 6.3 串列埠 (serial port) 如果你所说的装置是支援一些像是 RS-232 那类的东西, 你应该可以如你所愿地 使用串列埠. Linux 所提供的串列埠驱动程式应该能够应用在任何地方 (你应该 不需要直接撰写串列埠程式, 或是核心的驱动程式); 他相当具有通用性, 所以像 是使用非标准的 bps 速率以及其他等等应该不是问题. 请参考 termios(3) 说明 文件, 串列埠驱动程式原始程式码 (linux/drivers/char/serial.c), 以及网页 [11]http://www.easysw.com/~mike/serial/index.html 上有更多在 Unix 作业 系统撰写串列埠程式的相关资料. 7. 提示 如果你想要有好的 I/O 品值, 你可以在并列埠上自行组装 ADC 且/或 DAC 晶片 (提示: 电源部分, 可使用游戏埠上的或将未用到的磁碟电源连接头接至 机壳之 外, 如果你的装置功率消耗低则可以拿并列埠来充当电源, 不然就是使用外部的 电源供给), 或是买 AD/DA 卡片 (大部分较旧型/较低速的产品可由 I/O 埠控 制). 或者是 Linux 音效卡驱动程式所支援的便宜音效卡 (速度还相当的快) 上 1 或 2 个不精确, (可能会) 无法归零的信号通道对你而言就够了. 使用精确的类比装置, 不当的接地可能造成类比输出入信号的误差. 如果你有这 方面的经验, 你可能会尝试以光耦合器来隔绝 (电脑与你的装置之间 所有的 信 号) 电子干扰. 试著从电脑上取得光耦合器的电源 (在埠上未用到的信号脚位可 以提供足够的电源) 以求达到最佳的隔绝效果. 如果你现在正在寻找能在 Linux 上使用的印刷电路板设计软体, 有一个称为 Pcb 免费的 X11 应用程式应该能够胜任, 只要你不要做一些太复杂的事. 许多的 Linux 发行版本 (distributions) 都内含这个程式, 同时他也被放在网址 [12]ftp://sunsite.unc.edu/pub/Linux/apps/circuits/ 上(档名为 pcb-*). 8. 问题排除 Q1. 当我存取 I/O 埠时结果碰到 segmentation faults 这个问题 A1. 不是你的程式没有 root 权限, 就是因为某些理由导致函式 ioperm() 呼 叫失败. 检查函式 ioperm() 的传回值. 同时, 检查你所存取的埠也就是 你以 函式 ioperm() 所启用的埠位址 (参考 Q3). 如果你使用的是延迟 时间的巨集指令 (inb_p(), outb_p(), 等等), 记得也要呼叫函式 ioperm() 以便存取埠位址 0x80. Q2. 我无法找到 in*(), out*() 等函式被定义在何处, 同时 gcc 也抱怨参考 到未定义的符号 (undefined references). A2. 你在编译程式时没有打开最佳化选项 (-O), 因此 gcc 不能解析 asm/io.h 中的巨集指令. 或是你根本就没有使用 #include <asm/io.h>. Q3. out*() 没有动作, 或是动作怪怪的. A3. 检查参数所放置的次序; 他应该是这样 outb(value, port) , 而不是 MS-DOS 上常用的那样 outportb(port, value) Q4. 我想要控制一个标准的 RS-232 装置/连接并列埠的印表机/操纵□... A4. 你最好能停止此事而使用现有的驱动程式 (他们存在於 Linux 的核心中 或 X 伺服器中或其他的地方) 来达成你的目标. 这些驱动程式通常相当 具通用性, 所以就算是有点不标准的装置, 他们通常都能正常运作. 这些 标准 I/O 埠的相关资讯请参考前面说过的文件指引. 9. 程式码□例 这边是一段用来存取 I/O 埠的简单的程式码□例: ______________________________________________________________ /* * example.c: 一个用来存取 I/O 埠的非常简单的□例 * * 这个程式码并没有什麽用处, 他只是做了埠的写入, 暂停, * 以及埠的读出几个动作. 编译时请使用 `gcc -O2 -o example example.c', * 并以 root 的身份执行 `./example'. */#include <stdio.h>#include <unistd.h>#include <asm/io.h>#define BASEPORT 0x378 /* lp1 */int main(){ /* 取得埠位址的存取权限 */ if (ioperm(BASEPORT, 3, 1)) {perror("ioperm"); exit(1);} /* 设定埠的输出资料信号 (D0-7) 全为零 (0) */ outb(0, BASEPORT); /* 休息一下 (100 ms) */ usleep(100000); /* 从状态埠 (BASE+1) 读出资料并显示结果 */ printf("status: %d\n", inb(BASEPORT + 1)); /* 我们不再需要这些埠位址 */ if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);} exit(0);}/* 结束 example.c */ ______________________________________________________________ 10. 致谢 协助过我的人实在太多无法一一列出, 但还是要跟各位说声多谢了. 对所有来信 协助我的人并没有一一回覆致上抱歉之意, 并再次谢谢你们的协助.References 1. http://sunsite.unc.edu/pub/Linux/docs/HOWTO/COPYRIGHT 2. http://www.redhat.com:8080/HyperNews/get/khg.html 3. http://luz.cs.nmt.edu/~rtlinux/ 4. http://sunsite.unc.edu/pub/Linux/docs/HOWTO/Hardware-HOWTO 5. http://www.hut.fi/Misc/Electronics/ 6. http://sunsite.unc.edu/pub/Linux/docs/HOWTO/Printing-HOWTO 7. http://www.fapo.com/ 8. http://www.senet.com.au/~cpeacock/parallel.htm 9. http://www.hut.fi/Misc/Electronics/circuits/lptpower.html 10. ftp://sunsite.unc.edu/pub/Linux/kernel/patches/ 11. http://www.easysw.com/~mike/serial/index.html 12. ftp://sunsite.unc.edu/pub/Linux/apps/circuits/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -