Hardware Access/zh CN: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Hardware Access/zh CN
| Old | New | Line | |
|---|---|---|---|
| 1 | + | ## 概览 | |
| 2 | + | ||
| 3 | + | 本页是关于在Lazarus上访问硬件设备的教程的开始。这些设备包括,但不限于:ISA,PCI,USB,并行端口,串行端口。 | |
| 4 | + | ||
| 5 | + | RTL或LCL没有实现统一的多平台硬件设备的访问。因此本教程将基本覆盖不同平台上的硬件访问方法。在不同环境下可以使用条件编译来编译代码,像这样: | |
| 6 | + | ||
| 7 | + | ``` pascal | |
| 8 | + | uses | |
| 9 | + | Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls, | |
| 10 | + | {$IFDEF WIN32} | |
| 11 | + | Windows; | |
| 12 | + | {$ENDIF} | |
| 13 | + | {$IFDEF Unix} | |
| 14 | + | ports; | |
| 15 | + | {$ENDIF} | |
| 16 | + | ``` | |
| 17 | + | ||
| 18 | + | 此时还不知道Mac OS X/x86是否会允许HW访问。它可能不允许,虽然那种情形下我假设像io.dll的驱动器将会及时出现。 | |
| 19 | + | ||
| 20 | + | ## 并行和串行的比较 | |
| 21 | + | ||
| 22 | + | ISA卡,PCI卡和并行端口使用“并行”协议与计算机进行通信。串行端口和USB设备使用“串行”协议。因为处理器和编程语言都通过并行方式处理数据,在软件端比较容易实现对这类协议的访问。例如,访问一个整型变量时,可以仅用一个命令就能访问它的值。然而,使用串行协议,一次仅能知道一位(bit),需要将所有片断(piece)放在一起才能理解数据。 | |
| 23 | + | ||
| 24 | + | 串行通信比较难于直接实现,如果使用预制(pre-made)组件,可能会稍微容易些。在硬件端也比较困难,因此许多设备使用专门的集成电路或甚至微控制器来实现。 | |
| 25 | + | ||
| 26 | + | 现在来做一个硬件访问协议的简要比较: | |
| 27 | + | ||
| 28 | + | | | 速度 | 硬件实现难度 | | |
| 29 | + | |----------|----------------------|--------------| | |
| 30 | + | | 串行端口 | 非常慢 (\< E5 bit/s) | 中等 | | |
| 31 | + | | 并行端口 | 慢 (~ E6 bit/s) | 容易 | | |
| 32 | + | | ISA卡 | 中等 (~ E7 bit/s) | 中等 | | |
| 33 | + | | USB | 中等 (~ E7 bit/s) | 困难 | | |
| 34 | + | | PCI卡 | 非常快 (\> E9 bit/s) | 非常难 | | |
| 35 | + | ||
| 36 | + | ## 并行通信 | |
| 37 | + | ||
| 38 | + | ### 在Windows操作系统中使用inpout32.dll | |
| 39 | + | ||
| 40 | + | 在9x系列和NT系列,Windows有不同的方法来访问硬件设备。在9x系列(95,98,Me),程序可以直接访问硬件,正如在DOS上一样。然而,NT系列(Windows NT和XP)不允许这种方式。在该架构上,所有与硬件端口的通信必须通过一个设备驱动器。这是一种安全机制,但是为一个小项目开发一个驱动器可能会花费太多的时间和金钱。 | |
| 41 | + | ||
| 42 | + | 幸运地是有一个库解决了这个问题。如果检测到Windows NT,它解压HWInterface.sys内核设备驱动器并安装。如果检测到Windows 9x,它简单地使用汇编操作码访问硬件。 | |
| 43 | + | ||
| 44 | + | 但是如何使用这个库呢?非常简单!它仅有2个函数:Inp32和Out32,它们的使用是比较直观的。 | |
| 45 | + | ||
| 46 | + | 我们将动态加载该库,因此让我们首先定义这2个函数: | |
| 47 | + | ||
| 48 | + | ``` pascal | |
| 49 | + | type | |
| 50 | + | TInp32 = function(Address: SmallInt): SmallInt; stdcall; | |
| 51 | + | TOut32 = procedure(Address: SmallInt; Data: SmallInt); stdcall; | |
| 52 | + | ``` | |
| 53 | + | ||
| 54 | + | - Address代表期望访问的端口地址 | |
| 55 | + | - Out32发送数据到指定地址的端口 | |
| 56 | + | - Inp32从指定地址的端口返回一字节 | |
| 57 | + | ||
| 58 | + | 现在可以加载该库了。这可能在一个类似程序主form的OnCreate方法的地方来实现: | |
| 59 | + | ||
| 60 | + | ``` pascal | |
| 61 | + | type | |
| 62 | + | TMyForm = class(TForm) | |
| 63 | + | ......... | |
| 64 | + | private | |
| 65 | + | { private declarations } | |
| 66 | + | Inpout32: THandle; | |
| 67 | + | Inp32: TInp32; | |
| 68 | + | Out32: TOut32; | |
| 69 | + | ......... | |
| 70 | + | implementation | |
| 71 | + | ......... | |
| 72 | + | procedure TMyForm.FormCreate(Sender: TObject); | |
| 73 | + | begin | |
| 74 | + | {$IFDEF WIN32} | |
| 75 | + | Inpout32 := LoadLibrary('inpout32.dll'); | |
| 76 | + | if (Inpout32 <> 0) then | |
| 77 | + | begin | |
| 78 | + | // needs overtyping, plain Delphi's @Inp32 = GetProc... leads to compile errors | |
| 79 | + | Inp32 := TInp32(GetProcAddress(Inpout32, 'Inp32')); | |
| 80 | + | if (@Inp32 = nil) then Caption := 'Error'; | |
| 81 | + | Out32 := TOut32(GetProcAddress(Inpout32, 'Out32')); | |
| 82 | + | if (@Out32 = nil) then Caption := 'Error'; | |
| 83 | + | end | |
| 84 | + | else Caption := 'Error'; | |
| 85 | + | {$ENDIF} | |
| 86 | + | end; | |
| 87 | + | ``` | |
| 88 | + | ||
| 89 | + | 如果在OnCreate加载了该库,那么不要忘记在OnDestroy卸载它: | |
| 90 | + | ||
| 91 | + | ``` pascal | |
| 92 | + | procedure TMyForm.FormDestroy(Sender: TObject); | |
| 93 | + | begin | |
| 94 | + | {$IFDEF WIN32} | |
| 95 | + | FreeLibrary(Inpout32); | |
| 96 | + | {$ENDIF} | |
| 97 | + | end; | |
| 98 | + | ``` | |
| 99 | + | ||
| 100 | + | 这里是一个如何使用Inp32函数的简单例子: | |
| 101 | + | ||
| 102 | + | ``` pascal | |
| 103 | + | {$IFDEF WIN32} | |
| 104 | + | myLabel.Caption := IntToStr(Inp32($0220)); | |
| 105 | + | {$ENDIF} | |
| 106 | + | ``` | |
| 107 | + | ||
| 108 | + | 该代码在Windows XP上使用Lazarus 0.9.10,在端口\$0220的一个自定义ISA卡上测试过。当然为了让该代码运行,你需要有一个有使用条款的Windows。为了部署,你仅需要把“inpout32.dll”包含在应用的相同目录下。 | |
| 109 | + | ||
| 110 | + | 这是该库的主页:[www.logix4u.net/inpout32.htm](http://www.logix4u.net/inpout32.htm) *查看讨论* | |
| 111 | + | ||
| 112 | + | ### 在Windows 9x上使用汇编 | |
| 113 | + | ||
| 114 | + | 在Windows 9x上也可以使用汇编代码。假设希望把\$CC发送到\$320端口。下面代码可以实现: | |
| 115 | + | ||
| 116 | + | ``` pascal | |
| 117 | + | {$ASMMODE ATT} | |
| 118 | + | ... | |
| 119 | + | asm | |
| 120 | + | movl $0x320, %edx | |
| 121 | + | movb $0xCC, %al | |
| 122 | + | outb %al, %dx | |
| 123 | + | end ['EAX','EDX']; | |
| 124 | + | ``` | |
| 125 | + | ||
| 126 | + | ### 在Windows上的疑难解答 | |
| 127 | + | ||
| 128 | + | 在Windows上使用不支持即插即用并行硬件的一个可能问题来源是,Windows可能将该硬件使用的端口分配给了另外的设备。你可以在下面的URL里找到关于如何告诉Windows不要将你设备的地址分配给即插即用设备的指示: | |
| 129 | + | ||
| 130 | + | <http://support.microsoft.com/kb/135168> | |
| 131 | + | ||
| 132 | + | ### 在Linux上使用ioperm访问端口 | |
| 133 | + | ||
| 134 | + | 在Linux上访问硬件的最好方法通过设备驱动器,但是考虑到创建一个驱动器任务的复杂性,有时候一个快速的方法是非常有用的。 | |
| 135 | + | ||
| 136 | + | 为了使用Linux下的“[[doc:rtl/ports|ports]]”单元,程序必须以root身份运行,并且必须调用IOPerm设置端口访问的合适权限。你可以在[这里](http://www.freepascal.org/docs-html/rtl/ports/index.html)找到关于“[[doc:rtl/ports|ports]]”单元的文档。 | |
| 137 | + | ||
| 138 | + | 要做的第一件事是链接(g)libc和调用IOPerm。一个链接整个(g)libc的单元存在于free pascal,但是当应用直接使用时该单元出现了问题,并且静态链接整个(g)libc库不是一个非常好的主意,因为在不同版本间它以不兼容的方式改变。然而,像ioperm类的函数不大可能改变。 | |
| 139 | + | ||
| 140 | + | ``` pascal | |
| 141 | + | {$IFDEF Linux} | |
| 142 | + | function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external 'libc'; | |
| 143 | + | {$ENDIF} | |
| 144 | + | ``` | |
| 145 | + | ||
| 146 | + | - from 代表访问的第一个端口. | |
| 147 | + | - num 是访问的端口数,因此ioperm(\$220, 8, 1)将为程序访问\$220——\$227之间(含)的所有端口。 | |
| 148 | + | ||
| 149 | + | 在链接IOPerm后,可以使用port\[\<Address\>\]访问端口。 | |
| 150 | + | ||
| 151 | + | ``` pascal | |
| 152 | + | {$IFDEF Linux} | |
| 153 | + | i := ioperm($220, 8, 1); | |
| 154 | + | port[$220] := $00; | |
| 155 | + | myLabel.Caption := 'ioperm: ' + IntToStr(i); | |
| 156 | + | i := Integer(port[$220]); | |
| 157 | + | myOtherLabel.Caption := 'response: ' + IntToStr(i); | |
| 158 | + | {$ENDIF} | |
| 159 | + | ``` | |
| 160 | + | ||
| 161 | + | 该代码在Mandriva Linux 2005和Damn Small Linux 1.5上使用Lazarus 0.9.10,在端口\$0220的一个自定义ISA卡上测试过。 | |
| 162 | + | ||
| 163 | + | ### 通用UNIX硬件访问 | |
| 164 | + | ||
| 165 | + | ``` pascal | |
| 166 | + | {$IFDEF Unix} | |
| 167 | + | Uses Clib; // retrieve libc library name. | |
| 168 | + | {$ENDIF} | |
| 169 | + | ||
| 170 | + | {$IFDEF Unix} | |
| 171 | + | function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external clib; | |
| 172 | + | {$ENDIF} | |
| 173 | + | ``` | |
| 174 | + | ||
| 175 | + | **注意** FPC在[[doc:rtl/x86/index.html|unit x86]]里为ioperm提供了一个叫做“fpioperm”的抽象,也定义了fpIOPL和输出/输入函数。这些函数当前是为Linux/x86和FreeBSD/x86实现的。 | |
| 176 | + | ||
| 177 | + | 不建议链接到的libc,除非绝对必要的,因为可能的部署和移植功能。 像上面那样(通过为别处有效的函数声明特设导入)手工链接libc也是不建议的(例如,如果标准C库不叫作libc,那么上面的libc导入行将不必要(unnecessarily)地失败,比如BeOS或非标准C符号扩展(symbol mangling)平台上的libroot)。 | |
| 178 | + | ||
| 179 | + | **注意 2** 不建议在除了Kylix兼容性的任何环境下使用**unit** libc。参见[[libc unit]] | |
| 180 | + | ||
| 181 | + | ## 串行通信 | |
| 182 | + | ||
| 183 | + | 使用[Synaser库](http://synapse.ararat.cz/doku.php)开发一个串行通信软件是非常容易的。当与[Synaser文档](http://synapse.ararat.cz/doc/help/synaser.html)一起使用时,例子应该很好理解。最主要的部分是TBlockSerial.Config设置速度(位/秒),数据位,奇偶校验位,停止位和握手协议,如果有的话。下面代码在一个连接到COM 1的串行鼠标上测试过。 | |
| 184 | + | ||
| 185 | + | ``` pascal | |
| 186 | + | program comm; | |
| 187 | + | ||
| 188 | + | {$apptype console} | |
| 189 | + | ||
| 190 | + | uses | |
| 191 | + | Classes, SysUtils, Synaser; | |
| 192 | + | ||
| 193 | + | var | |
| 194 | + | ser: TBlockSerial; | |
| 195 | + | begin | |
| 196 | + | ser:=TBlockSerial.Create; | |
| 197 | + | try | |
| 198 | + | ser.Connect('COM1'); | |
| 199 | + | ser.config(1200, 7, 'N', SB1, False, False); | |
| 200 | + | while True do | |
| 201 | + | Write(IntToHex(ser.RecvByte(10000), 2), ' '); | |
| 202 | + | finally | |
| 203 | + | ser.free; | |
| 204 | + | end; | |
| 205 | + | end. | |
| 206 | + | ``` | |
| 207 | + | ||
| 208 | + | 下面的代码例子是上面例子的一个替代版本。上面的例子看起来在主要概念上有严重错误,准确地是“while true do...”部分。在测试系统(Asus A6T Laptop with Digitus USB to RS232 Adapter, Ubuntu 8.04.1)上,该部分导致了下面错误:每个会话只能成功运行一次应用,当再次起动应用时,应用不能连接到串行端口。因此,每次用户试图重启应用时,需要重启(计算机),这是一个非常恼人的bug。 | |
| 209 | + | ||
| 210 | + | 原因不难理解:应用处于while true do循环,更准确地说是无限循环。没有退出条件,因此关闭应用的唯一办法是关闭终端或按CTRL-C。但是如果通过种办法退出应用,释放串行端口的重要部分“ser.free”从来不被调用。德语Lazarus论坛的下面贴子里描述了这个问题[<http://www.lazarusforum.de/viewtopic.php?f=10&t=2082>](http://www.lazarusforum.de/viewtopic.php?f=10&t=2082) | |
| 211 | + | ||
| 212 | + | 为了让每个用户明白,而不是按CTRL-C,在主要应用周围有一些代码。也许有人担心,为什么/dev/ttyUSB0被用作com端口:这是由于在测试系统上使用的USB串行适配器。如果有内置串行端口,请使用“Com0”——像上面例子代码中声明。 | |
| 213 | + | ||
| 214 | + | ``` pascal | |
| 215 | + | ||
| 216 | + | program serialtest; | |
| 217 | + | ||
| 218 | + | {$mode objfpc}{$H+} | |
| 219 | + | ||
| 220 | + | uses | |
| 221 | + | {$IFDEF UNIX}{$IFDEF UseCThreads} | |
| 222 | + | cthreads, | |
| 223 | + | {$ENDIF}{$ENDIF} | |
| 224 | + | Classes,SysUtils,Synaser,Crt | |
| 225 | + | { you can add units after this }; | |
| 226 | + | ||
| 227 | + | var l:boolean; | |
| 228 | + | ||
| 229 | + | function check_affirmation():boolean; | |
| 230 | + | var k:string; | |
| 231 | + | begin | |
| 232 | + | Writeln('To quit the application please do NOT use CTRL-C! Instead, please press any key to quit the application! '+ | |
| 233 | + | 'Please confirm this notification before the application continues! '+ | |
| 234 | + | '[0]=Quit, [1]=Confirm, please continue! '); | |
| 235 | + | Writeln('Your decision: '); | |
| 236 | + | Read(k); | |
| 237 | + | if StrtoInt(k) = 1 then | |
| 238 | + | begin | |
| 239 | + | check_affirmation:=true; | |
| 240 | + | Writeln('OK, application continues ...'); | |
| 241 | + | end | |
| 242 | + | else | |
| 243 | + | begin | |
| 244 | + | check_affirmation:=false; | |
| 245 | + | Writeln('Abort'); | |
| 246 | + | end | |
| 247 | + | end; | |
| 248 | + | ||
| 249 | + | procedure RS232_connect; | |
| 250 | + | var | |
| 251 | + | ser: TBlockSerial; | |
| 252 | + | begin | |
| 253 | + | ser:=TBlockSerial.Create; | |
| 254 | + | try | |
| 255 | + | ser.Connect('/dev/ttyUSB0'); //ComPort | |
| 256 | + | Sleep(1000); | |
| 257 | + | ser.config(1200, 7, 'N', SB1, False, False); | |
| 258 | + | Write('Device: ' + ser.Device + ' Status: ' + ser.LastErrorDesc +' '+ | |
| 259 | + | Inttostr(ser.LastError)); | |
| 260 | + | Sleep(1000); | |
| 261 | + | repeat | |
| 262 | + | Write(IntToHex(ser.RecvByte(10000), 2), ' '); | |
| 263 | + | until keypressed; //Important!!! | |
| 264 | + | finally | |
| 265 | + | Writeln('Serial Port will be freed...'); | |
| 266 | + | ser.free; | |
| 267 | + | Writeln('Serial Port was freed successfully!'); | |
| 268 | + | end; | |
| 269 | + | end; | |
| 270 | + | ||
| 271 | + | begin | |
| 272 | + | l:=check_affirmation(); | |
| 273 | + | if l=true then | |
| 274 | + | RS232_connect() | |
| 275 | + | else | |
| 276 | + | Writeln('Program quit! '); | |
| 277 | + | end. | |
| 278 | + | ``` | |
| 279 | + | ||
| 280 | + | 另外,[[Hardware Access|外部链接]]节有UNIX和Windows串行端口教程。 | |
| 281 | + | ||
| 282 | + | 值得一提的是linux下TBlockSerial.LinuxLock参数的功能。当设置默认为True时,连接将试图在/var/lock下创建一个锁文件(例如,“LCK..ttyUSB0”),如果所请求的端口存在一个锁就会失败。如果没有调用释放,锁文件将一直存在。设置LinuxLock为False将使Synaser忽略端口锁定。 | |
| 283 | + | ||
| 284 | + | 替代Synaser: | |
| 285 | + | ||
| 286 | + | 基于Synaser的可视组件[5dpo](http://wiki.lazarus.freepascal.org/5dpo)。 | |
| 287 | + | ||
| 288 | + | 另外一个非常简单的fpc单元现在是freepascal(至少版本2.2.2)的部分了,只要将Serial放到你的Uses清单里,然而除了Serial.pp源文件外还没有其它文档。 | |
| 289 | + | ||
| 290 | + | ## USB | |
| 291 | + | ||
| 292 | + | ### libusb | |
| 293 | + | ||
| 294 | + | 驱动开发向来是内核开发中工作量最多的一块,随着USB设备的普及,大量的USB设备的驱动开发也成为驱动开发者手头上做的最多的事情。本文主要介绍Linux平台下基于libusb的驱动开发,希望能够给从事Linux驱动开发的朋友带来些帮助,更希望能够给其他平台上的无驱设计带来些帮助。文章是我在工作中使用libusb的一些总结,难免有错误,如有不当的地方,还请指正。 | |
| 295 | + | ||
| 296 | + | Linux 平台上的usb驱动开发,主要有内核驱动的开发和基于libusb的无驱设计。 | |
| 297 | + | ||
| 298 | + | 对于内核驱动的大部分设备,诸如带usb接口的hid设备,linux本身已经自带了相关的驱动,我们只要操作设备文件便可以完成对设备大部分的操作,而另外一些设备,诸如自己设计的硬件产品,这些驱动就需要我们驱动工程师开发出相关的驱动了。内核驱动有它的优点,然而内核驱动在某些情况下会遇到如下的一些问题: | |
| 299 | + | ||
| 300 | + | 1 当使用我们产品的客户有2.4内核的平台,同时也有2.6内核的平台,我们要设计的驱动是要兼容两个平台的,就连makefile 我们都要写两个。 | |
| 301 | + | ||
| 302 | + | 2 当我们要把linux移植到嵌入平台上,你会发现原先linux自 带的驱动移过去还挺大的,我的内核当然是越小越好拉,这样有必要么。这还不是最郁闷的地方,如果嵌入平台是客户的,客户要购买你的产品,你突然发现客户设 备里的系统和你的环境不一样,它没有你要的驱动了,你的程序运行不了,你会先想:“没关系,我写个内核驱动加载一下不就行了“。却发现客户连insmod加载模块的工具都没移植,那时你就看看老天,说声我怎么那么倒霉啊,客户可不想你动他花了n时间移植的内核哦 | |
| 303 | + | ||
| 304 | + | 3 花了些功夫写了个新产品的驱动,挺有成就感啊,代码质量也是相当的有水准啊。正当你沉醉在你的代码中时,客服不断的邮件来了,“客户需要2.6.5内核的驱动,config文件我已经发你了” “客户需要双核的 2.6.18-smp 的驱动” “客户的平台是自己定制的是2.6.12-xxx “ 你恨不得把驱动的源代码给客户,这样省得编译了。你的一部分工作时间编译内核,定制驱动 | |
| 305 | + | ||
| 306 | + | 有问题产生必然会有想办法解决问题的人, libusb的出现给我们带来了某些方便,即节约了我们的时间,也降低了公司的成本。 所以在一些情况下,就可以考虑使用libusb的无驱设计了。 | |
| 307 | + | ||
| 308 | + | 下面我们就来详细讨论一下libusb, 并以写一个hid设备的驱动来讲解如何运用libusb,至于文章中涉及的usb协议的知识,限于篇幅,就不详细讲解了,相关的可自行查看usb相关协议。 | |
| 309 | + | ||
| 310 | + | 一 libusb 介绍 | |
| 311 | + | ||
| 312 | + | libusb 设计了一系列的外部API 为应用程序所调用,通过这些API应用程序可以操作硬件,从libusb的源代码可以看出,这些API 调用了内核的底层接口,和kernel driver中所用到的函数所实现的功能差不多,只是libusb更加接近USB 规范。使得libusb的使用也比开发内核驱动相对容易的多。 Libusb 的编译安装请查看Readme,这里不做详解 | |
| 313 | + | ||
| 314 | + | 二 libusb 的外部接口 | |
| 315 | + | ||
| 316 | + | 2.1 初始化设备接口 | |
| 317 | + | ||
| 318 | + | 这些接口也可以称为核心函数,它们主要用来初始化并寻找相关设备。 | |
| 319 | + | ||
| 320 | + | usb_init | |
| 321 | + | ||
| 322 | + | 函数定义: void usb_init(void); | |
| 323 | + | ||
| 324 | + | 从函数名称可以看出这个函数是用来初始化相关数据的,这个函数大家只要记住必须调用就行了,而且是一开始就要调用的. | |
| 325 | + | ||
| 326 | + | usb_find_busses | |
| 327 | + | ||
| 328 | + | 函数定义: int usb_find_busses(void); | |
| 329 | + | ||
| 330 | + | 寻找系统上的usb总线,任何usb设备都通过usb总线和计算机总线通信。进而和其他设备通信。此函数返回总线数。 | |
| 331 | + | ||
| 332 | + | usb_find_devices | |
| 333 | + | ||
| 334 | + | 函数定义: int usb_find_devices(void); | |
| 335 | + | ||
| 336 | + | 寻找总线上的usb设备,这个函数必要在调用usb_find_busses()后使用。以上的三个函数都是一开始就要用到的,此函数返回设备数量。 | |
| 337 | + | ||
| 338 | + | usb_get_busses | |
| 339 | + | ||
| 340 | + | 函数定义: struct usb_bus *usb_get_busses(void); | |
| 341 | + | ||
| 342 | + | 这个函数返回总线的列表,在高一些的版本中已经用不到了,这在下面的实例中会有讲解 | |
| 343 | + | ||
| 344 | + | 2.2 操作设备接口 | |
| 345 | + | ||
| 346 | + | usb_open | |
| 347 | + | ||
| 348 | + | 函数定义: usb_dev_handle *usb_open(struct *usb_device dev); | |
| 349 | + | ||
| 350 | + | 打开要使用的设备,在对硬件进行操作前必须要调用usb_open 来打开设备,这里大家看到有两个结构体 usb_dev_handle 和 usb_device 是我们在开发中经常碰到的,有必要把它们的 结构看一看。在libusb 中的usb.h和usbi.h中有定义。 | |
| 351 | + | ||
| 352 | + | 这里我们不妨理解为返回的 usb_dev_handle 指针是指向设备的句柄,而行参里输入就是需要打开的设备。 | |
| 353 | + | ||
| 354 | + | usb_close | |
| 355 | + | ||
| 356 | + | 函数定义: int usb_close(usb_dev_handle *dev); | |
| 357 | + | ||
| 358 | + | 与usb_open相对应,关闭设备,是必须调用的, 返回0成功,\<0 失败。 | |
| 359 | + | ||
| 360 | + | usb_set_configuration | |
| 361 | + | ||
| 362 | + | 函数定义: int usb_set_configuration(usb_dev_handle *dev, int configuration); | |
| 363 | + | ||
| 364 | + | 设置当前设备使用的configuration,参数configuration 是你要使用的configurtation descriptoes中的bConfigurationValue, 返回0成功,\<0失败( 一个设备可能包含多个 configuration,比如同时支持高速和低速的设备就有对应的两个configuration,详细可查看usb标准) | |
| 365 | + | ||
| 366 | + | usb_set_altinterface | |
| 367 | + | ||
| 368 | + | 函数定义: int usb_set_altinterface(usb_dev_handle *dev, int alternate); | |
| 369 | + | ||
| 370 | + | 和名字的意思一样,此函数设置当前设备配置的interface descriptor,参数alternate是指interface descriptor中的bAlternateSetting。返回0成功,\<0失败 | |
| 371 | + | ||
| 372 | + | usb_resetep | |
| 373 | + | ||
| 374 | + | 函数定义: int usb_resetep(usb_dev_handle *dev, unsigned int ep); | |
| 375 | + | ||
| 376 | + | 复位指定的endpoint,参数ep 是指bEndpointAddress,。这个函数不经常用,被下面介绍的usb_clear_halt函数所替代。 | |
| 377 | + | ||
| 378 | + | usb_clear_halt | |
| 379 | + | ||
| 380 | + | 函数定义: int usb_clear_halt (usb_dev_handle *dev, unsigned int ep); | |
| 381 | + | ||
| 382 | + | 复位指定的endpoint,参数ep 是指bEndpointAddress。这个函数用来替代usb_resetep | |
| 383 | + | ||
| 384 | + | usb_reset | |
| 385 | + | ||
| 386 | + | 函数定义: int usb_reset(usb_dev_handle *dev); | |
| 387 | + | ||
| 388 | + | 这个函数现在基本不怎么用,不过这里我也讲一下,和名字所起的意思一样,这个函数reset设备,因为重启设备后还是要重新打开设备,所以用usb_close就已经可以满足要求了。 | |
| 389 | + | ||
| 390 | + | usb_claim_interface | |
| 391 | + | ||
| 392 | + | 函数定义: int usb_claim_interface(usb_dev_handle *dev, int interface); | |
| 393 | + | ||
| 394 | + | 注册与操作系统通信的接口,这个函数必须被调用,因为只有注册接口,才能做相应的操作。 | |
| 395 | + | ||
| 396 | + | Interface 指 bInterfaceNumber. (下面介绍的usb_release_interface 与之相对应,也是必须调用的函数) | |
| 397 | + | ||
| 398 | + | usb_release_interface | |
| 399 | + | ||
| 400 | + | 函数定义: int usb_release_interface(usb_dev_handle *dev, int interface); | |
| 401 | + | ||
| 402 | + | 注销被usb_claim_interface函数调用后的接口,释放资源,和usb_claim_interface对应使用。 | |
| 403 | + | ||
| 404 | + | 2.3 控制传输接口 | |
| 405 | + | ||
| 406 | + | usb_control_msg | |
| 407 | + | ||
| 408 | + | 函数定义:int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout); | |
| 409 | + | ||
| 410 | + | 从默认的管道发送和接受控制数据 | |
| 411 | + | ||
| 412 | + | usb_get_string | |
| 413 | + | ||
| 414 | + | 函数定义: int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, size_t buflen); | |
| 415 | + | ||
| 416 | + | usb_get_string_simple | |
| 417 | + | ||
| 418 | + | 函数定义: int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen); | |
| 419 | + | ||
| 420 | + | usb_get_descriptor | |
| 421 | + | ||
| 422 | + | 函数定义: int usb_get_descriptor(usb_dev_handle *dev, unsigned char type, unsigned char index, void *buf, int size); | |
| 423 | + | ||
| 424 | + | usb_get_descriptor_by_endpoint | |
| 425 | + | ||
| 426 | + | 函数定义: int usb_get_descriptor_by_endpoint(usb_dev_handle *dev, int ep, unsigned char type, unsigned char index, void *buf, int size); | |
| 427 | + | ||
| 428 | + | 2.4 批传输接口 | |
| 429 | + | ||
| 430 | + | usb_bulk_write | |
| 431 | + | ||
| 432 | + | 函数定义: int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout); | |
| 433 | + | ||
| 434 | + | usb_interrupt_read | |
| 435 | + | ||
| 436 | + | 函数定义: int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout); | |
| 437 | + | ||
| 438 | + | 2.5 中断传输接口 | |
| 439 | + | ||
| 440 | + | usb_bulk_write | |
| 441 | + | ||
| 442 | + | 函数定义: int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout); | |
| 443 | + | ||
| 444 | + | usb_interrupt_read | |
| 445 | + | ||
| 446 | + | 函数定义: int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout); | |
| 447 | + | ||
| 448 | + | A cross platform possibility for Linux, BSDs and Mac OS X is [libusb](http://libusb.sourceforge.net/). | |
| 449 | + | ||
| 450 | + | Headers are listed in <http://www.freepascal.org/contrib/db.php3?category=Miscellaneous>: | |
| 451 | + | ||
| 452 | + | | 文件名 | 作者 | 版本 | 日期 | 链接 | 备注 | | |
| 453 | + | |----|----|----|----|----|----| | |
| 454 | + | | libusb.pp | nowrap \| Uwe Zimmermann | 0.1.12 | nowrap \| 2006-06-29 | <http://www.sciencetronics.com/download/fpc_libusb.tgz> | | | |
| 455 | + | | libusb.pas | Johann Glaser | | nowrap \| 2012-09-23 | <https://github.com/hansiglaser/pas-libusb> | includes OOP wrapper, see branch "libusb-1.0" for libusb 1.0 | | |
| 456 | + | | fpcusb | Joe Jared | 0.11-14 | nowrap \| 2006-02-02 | <http://relays.osirusoft.com/fpcusb.tgz> | nowrap \| download link broken | | |
| 457 | + | | libusb.pp | Marko Medic | 1.0 | nowrap \| 2010-12-14 | <http://www.lazarus.freepascal.org/index.php/topic,11435.0.html> | | | |
| 458 | + | ||
| 459 | + | ### FTDI | |
| 460 | + | ||
| 461 | + | If you use one of the chips from [FTDI](http://www.ftdichip.com/), you can use their pascal headers for their dll interface to the chips. | |
| 462 | + | ||
| 463 | + | ## 外部链接 | |
| 464 | + | ||
| 465 | + | 通信协议速度比较: | |
| 466 | + | ||
| 467 | + | 1. <http://en.wikipedia.org/wiki/Serial_port#Speed> | |
| 468 | + | 2. <http://www.lvr.com/jansfaq.htm> - Jan Axelson's Parallel Port FAQ | |
| 469 | + | 3. <http://en.wikipedia.org/wiki/USB#Transfer_Speed> | |
| 470 | + | 4. <http://en.wikipedia.org/wiki/PCI#Conventional_PCI_bus_specifications> | |
| 471 | + | ||
| 472 | + | 串行通信链接: | |
| 473 | + | ||
| 474 | + | 1. On UNIX: [1](http://www.easysw.com/~mike/serial/serial.html) | |
| 475 | + | 2. On Windows: <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp> | |
| 476 | + | 3. Synaser component: <http://synapse.ararat.cz/> | |
| 477 | + | 4. Comport Delphi package: <http://sourceforge.net/projects/comport/> | |
| 478 | + | ||
| 479 | + | ISA数字示波器——一个硬件访问例子的全部源代码包含在: | |
| 480 | + | ||
| 481 | + | [2](http://eletronicalivre.incubadora.fapesp.br/portal/english/oscilloscope/) | |
| 482 | + | ||
| 483 | + | --- | |
| 484 | + | *From the Free Pascal wiki, page "Hardware Access/zh CN", as it stood on 2020-02-16 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
