协议:CC BY-NC-SA 4.0
七、通用串行总线
USB 端口在数字世界中已经变得无处不在,允许使用大量外围设备。Raspberry Pi 型号支持一到四个 USB 端口,具体取决于型号。
本章简要分析了与 USB 支持和电源集线器相关的一些电源注意事项。本章的其余部分将通过对 EZ-USB FX2LP 开发板的编程访问,研究 Raspbian Linux 开发人员可用的设备驱动程序接口。
电源
非常早期的 Raspberry Pi 型号将每个 USB 端口限制在 100 mA,因为板上有多保险丝。修订版 2.0 模型及以后的版本取消了这些,使您从可能发生的各种故障中解脱出来。USB 2 功率限制为单个端口 500 mA。在设计你的 IoT(物联网)的时候要记住这一点。
注意
无线 USB 适配器的功耗介于 350 毫安和 500 毫安之间。
电动集线器
有些应用需要一个带电源的 USB 集线器来连接大电流外设。对于无线网络适配器来说尤其如此,因为它们需要高达 500 mA 的电流。但是 USB 集线器需要与 Linux 内核协调,因此需要软件支持。据报道,许多集线器不起作用。下面的网页是一个很好的资源,可以列出已知可以与 Raspbian Linux 一起工作的集线器:
http://elinux.org/RPi_Powered_USB_Hubs
插入通电的 USB 集线器后,您可以使用lsusb
命令列出已经注册到内核的 USB 设备:
$ lsusb
Bus 001 Device 008: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 007: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 004: ID 045e:00d1 Microsoft Corp. Optical Mouse with Tilt Wheel
Bus 001 Device 005: ID 04f2:0841 Chicony Electronics Co., Ltd HP Multimedia Keyboard
Bus 001 Device 006: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
示例会话中的前两个显示了 my Terminus Technology Inc .提供的 hub,它是在插入 Pi 后注册的。鼠标(微软)和键盘(惠普)是插入 Pi 的两个外围设备。剩下的是支持 USB 端口的 Pi 集线器的驱动程序。本次会议使用的轮毂如图 7-1 所示。
图 7-1
通电的 USB 集线器
EZ-USB FX2LP
在这一章中,我们不仅仅要讨论 USB。相反,我们将把你的 Pi 接口到一个叫做 EZ-USB FX2LP 的经济型主板上,这款主板在易贝可以买到,价格大约为 4 美元。板上芯片为 CY7C68013A,由赛普拉斯公司制造。如果你在易贝搜索“EZ-USB FX2LP 板”,你应该能找到几个来源。
有一个 FX3LP 芯片可用,但它不是爱好定价。此外,安装驱动程序支持需要特殊说明。如果您继续使用 FX2LP,Raspbian Linux 内核驱动程序应该会自动支持它。
图 7-2 显示了作者正在使用的设备,其中插入了 USB Mini-b (5 针)电缆。如果你还没有,你需要订购电缆。通过使用 USB 开发板,您可以控制 USB 连接的两端。然而 EZ-USB 使用起来非常简单,让我们可以避开火箭科学。
图 7-2
FX2LP EZ-USB 开发板
当你第一次得到这个设备时,你应该能够简单地通过把它插入 Pi USB 端口来测试它。然后使用lsusb
命令查看 Linux 内核是否看到它(下面显示的行被换行以适应页面宽度)。
$ lsusb
Bus 001 Device 011: ID 04b4:8613 Cypress Semiconductor Corp. CY7C68013 EZ-USB FX2 \
USB 2.0 Development Kit
...
设备介绍
Anchor Chips Inc .于 1999 年被 Cypress Semiconductor corp .8Anchor 设计了一种 8051 芯片,允许软件通过 USB 上传到 SRAM 中,以支持各种外设功能。这种方法允许通过软件来配置一个硬件设备,以获得最大的灵活性。此后,Cypress 在 FX2LP (USB 2.0)等新设计中改进并扩展了其功能。这款设备的最大特点之一是硬件内置了大量 USB 支持。
完整的 PDF 手册可从以下网址下载:
http://www.cypress.com/file/126446/download
在本文档中,您将找到大量关于设备和 USB 的信息。关于这款设备,可以写一整本书,但让我们简单地列出一些突出的特点:
-
带有 Cypress 扩展的 8051 微控制器架构
-
16 KB SRAM,用于微控制器代码和数据
-
硬件 FIFO 支持快速无软件传输(高达 96 MB/s)
-
用于快速状态机传输的 GPIF(通用编程接口)
-
2 个 UART 串行通信
-
带闪存的 I/O I2C 主外设
-
硬件 USB 2.0 串行引擎
我选择这个产品的原因之一是,你可以在圆周率上编程所有的软件,并尝试你的改变,而不必刷新任何东西。并且不需要特殊的微控制器程序员。
USB API 支持
在 Linux 方面,我们显然也需要软件支持。USB 设备通常由设备驱动程序支持,并作为通用外设出现,如键盘、鼠标或存储器。关于 EZ-USB 设备有趣的是,我们在 Linux 内核中有足够的支持将 FX2LP 固件上传到设备。一旦上传到 FX2LP 的 SRAM,设备将重置。
USB 枚举
当一个 USB 设备第一次插入 USB 网络时(或者在引导时第一次看到),它必须通过枚举的工作来发现总线上存在什么设备,并知道它们的要求。
总线的主人是主机(PC/laptop/Pi)。所有插入总线的设备都是从设备,必须等待主机请求应答。除了极少数例外,奴隶只有在主人叫他们说话的时候才说话。
发现过程要求主机使用地址零查询设备(所有设备都必须对此做出响应)。该请求是一个 Get-Descriptor-Device 请求,允许设备描述它的一些属性。接下来,主机将通过设置地址请求分配特定的设备地址。主机发出额外的 Get-Descriptor 请求以获得更多信息。从这些信息传输中,主机了解到端点的数量、电源要求、所需的总线带宽以及要加载什么驱动程序等。
ReNumeration
Cypress 使用这个术语来描述一个活动的 EZ-USB 设备如何从 USB 总线断开,并再次枚举,可能是作为一个不同的 USB 设备。当在 EZ-USB SRAM 中执行下载的固件时,这是可能的。或者,EZ-USB 可以配置为使用其 I2C 总线将其固件从板载闪存下载到 SRAM 中。
Raspbian Linux 安装
要在 Pi 上演示 USB,我们必须首先能够在 FX2LP 板上编译、上传和运行软件。为此,我们需要安装一些软件工具。所有这些安装都必须从 root 帐户执行。请使用 sudo:
$ sudo -i
#
安装 sdcc
sdcc
包包括 8051 交叉编译器和库。谢天谢地,只需要一个命令:
# apt-get install sdcc
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
gputils gputils-common gputils-doc sdcc-doc sdcc-libraries
Suggested packages:
sdcc-ucsim
The following NEW packages will be installed:
gputils gputils-common gputils-doc sdcc sdcc-doc sdcc-libraries
0 upgraded, 6 newly installed, 0 to remove and 2 not upgraded.
Need to get 0 B/4,343 kB of archives.
After this operation, 53.6 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Selecting previously unselected package sdcc-libraries.
(Reading database ... 128619 files and directories currently installed.)
Preparing to unpack .../0-sdcc-libraries_3.5.0+dfsg-2_all.deb ...
Unpacking sdcc-libraries (3.5.0+dfsg-2) ...
Selecting previously unselected package sdcc.
Preparing to unpack .../1-sdcc_3.5.0+dfsg-2_armhf.deb ...
Unpacking sdcc (3.5.0+dfsg-2) ...
Selecting previously unselected package sdcc-doc.
Preparing to unpack .../2-sdcc-doc_3.5.0+dfsg-2_all.deb ...
Unpacking sdcc-doc (3.5.0+dfsg-2) ...
Selecting previously unselected package gputils-common.
Preparing to unpack .../3-gputils-common_1.4.0-0.1_all.deb ...
Unpacking gputils-common (1.4.0-0.1) ...
Selecting previously unselected package gputils.
Preparing to unpack .../4-gputils_1.4.0-0.1_armhf.deb ...
Unpacking gputils (1.4.0-0.1) ...
Selecting previously unselected package gputils-doc.
Preparing to unpack .../5-gputils-doc_1.4.0-0.1_all.deb ...
Unpacking gputils-doc (1.4.0-0.1) ...
Setting up sdcc-libraries (3.5.0+dfsg-2) ...
Setting up gputils-common (1.4.0-0.1) ...
Setting up gputils-doc (1.4.0-0.1) ...
Setting up sdcc-doc (3.5.0+dfsg-2) ...
Setting up sdcc (3.5.0+dfsg-2) ...
Processing triggers for man-db (2.7.6.1-2) ...
Setting up gputils (1.4.0-0.1) ...
#
下一个包是可选的,但你可能有一天会用到它。它允许您在 Pi 上模拟 8051 代码:
# apt-get install sdcc-ucsim
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
sdcc-ucsim
0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded.
Need to get 705 kB of archives.
After this operation, 1,952 kB of additional disk space will be used.
Get:1 http://raspbian.mirror.colo-serv.net/raspbian stretch/main armhf sdcc-ucsim armhf 3.5.0+dfsg-2 [705 kB]
Fetched 705 kB in 2s (268 kB/s)
Selecting previously unselected package sdcc-ucsim.
(Reading database ... 131104 files and directories currently installed.)
Preparing to unpack .../sdcc-ucsim_3.5.0+dfsg-2_armhf.deb ...
Unpacking sdcc-ucsim (3.5.0+dfsg-2) ...
Processing triggers for man-db (2.7.6.1-2) ...
Setting up sdcc-ucsim (3.5.0+dfsg-2) ...
# sync
在做出重大更改后,Pi 上的sync
命令(在最后)是一个好主意。它会导致内核将磁盘缓存刷新到闪存文件系统。这样,如果您的 Pi 由于任何原因崩溃,您至少可以确保这些更改现在保存在闪存中。如果你有猫在你的 Pi 周围嗅来嗅去,这是一个救命稻草。
安装 cycfx2prog
接下来安装 cycfx2prog 包。我们将使用 cycfx2prog 命令将我们的固件上传到 FX2LP。
# apt-get install cycfx2prog
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
cycfx2prog
0 upgraded, 1 newly installed, 0 to remove and 2 not upgraded.
Need to get 12.6 kB of archives.
After this operation, 52.2 kB of additional disk space will be used.
Get:1 http://muug.ca/mirror/raspbian/raspbian stretch/main armhf cycfx2prog armhf 0.47-1 [12.6 kB]
Fetched 12.6 kB in 1s (8,007 B/s)
Selecting previously unselected package cycfx2prog.
(Reading database ... 131163 files and directories currently installed.)
Preparing to unpack .../cycfx2prog_0.47-1_armhf.deb ...
Unpacking cycfx2prog (0.47-1) ...
Setting up cycfx2prog (0.47-1) ...
# sync
安装 libusb-1.0-0-dev
此时你应该做的第一件事是更新你的系统,如果你最近还没有这样做的话。最初安装 dev 包时出现了一个问题,因此以 root 用户身份执行以下操作来纠正这个问题:
# apt-get update
# apt-get upgrade
完成后,安装 libusb:
# apt-get install libusb-1.0-0-dev
如果还没有安装 libusb-1.0-0(没有“dev”),安装这个包也会安装它。检查头文件是否存在,这将非常重要:
# ls -l /usr/include/libusb-1.0/libusb.h
-rw-r--r-- 1 root root 71395 Oct 26 2016 /usr/include/libusb-1.0/libusb.h
黑名单 usbtest
这一步很可能是必要的,除非以前做过。它禁用 Linux 内核模块usbtest,
,该模块将连接到无人认领的设备。除非禁用此功能,否则我们的代码将无法附加到 FX2LP 设备。在 root 中,执行以下操作以使更改永久生效:
# sudo -i
# echo 'blacklist usbtest' >> /etc/modprobe.d/blacklist.conf
如果您不想进行这种更改,可以在需要时手动删除已加载的模块(以 root 用户身份):
# rmmod usbtest
从 github.com 获得软件
现在让我们从 github.com 下载这本书的源代码。从顶级(主)目录执行:
$ git clone https://github.com/ve3wwg/Advanced_Raspberry_Pi.git
Cloning into './Advanced_Raspberry_Pi'...
如果您不喜欢使用的子目录名称,您可以简单地将其重命名:
$ mv ./Advanced_Raspberry_Pi ./RPi
或者,您可以将其直接克隆到您选择的子目录名称中(注意添加的参数):
$ git clone https://github.com/ve3wwg/Advanced_Raspberry_Pi.git ./RPi
Cloning into './RPi'...
测试 EZ-USB FX2LP 设备
在我们进入实际的 USB 项目之前,让我们确保我们的工具和 EZ-USB 设备工作正常。转到以下子目录:
$ cd ~/RPi/libusb/blink
列出那里的文件,您应该会看到:
$ ls
blink.c Makefile
$
那里的 Makefile 还引用了以下文件:
../ezusb/Makefile.incl
如果您是高级用户,需要进行更改,请务必检查该文件。这用于定义如何上传到 FX2LP 设备等。那里也有一些定制的 FX2LP 包含文件。
编译闪烁
使用 sdcc 交叉编译器,我们可以如下编译blink.c
模块(长行用反斜杠断开):
$ make
sdcc --std-sdcc99 -mmcs51 --stack-size 64 --model-small --xram-loc 0x0000 \
--xram-size 0x5000 --iram-size 0x0100 --code-loc 0x0000 -I../ezusb blink.c
生成的感兴趣的文件命名为 blink.ihx(英特尔十六进制):
$ cat blink.ihx
:03000000020006F5
:03005F0002000399
:0300030002009068
:20006200AE82AF837C007D00C3EC9EED9F501E7AC87B00000000EA24FFF8EB34FFF9880279
:200082008903E84970ED0CBC00DE0D80DB2275B203D280C2819003E8120062C280D2819041
:0700A20003E812006280EA8E
:06003500E478FFF6D8FD9F
:200013007900E94400601B7A009000AD780075A000E493F2A308B8000205A0D9F4DAF275E7
:02003300A0FF2C
:20003B007800E84400600A790075A000E4F309D8FC7800E84400600C7900900000E4F0A3C5
:04005B00D8FCD9FAFA
:0D0006007581071200A9E582600302000366
:0400A900758200223A
:00000001FF
这是已编译的 blink 固件的英特尔十六进制格式文件,将上传到 FX2LP 设备上执行。
EZ-USB 程序执行
这部分需要特别注意,因为 Makefile 不知道 FX2LP 如何枚举总线和设备号。首先列出 USB 总线上的设备:
$ lsusb
Bus 001 Device 010: ID 045e:00d1 Microsoft Corp. Optical Mouse with Tilt Wheel
Bus 001 Device 009: ID 04f2:0841 Chicony Electronics Co., Ltd HP Multimedia Keyboard
Bus 001 Device 011: ID 04b4:8613 Cypress Semiconductor Corp. CY7C68013 EZ-USB \
FX2 USB 2.0 Development Kit
Bus 001 Device 006: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
在此会话中,找到 EZ-USB 设备。这里总线号是001
,设备号是011
。使用此信息,键入以下内容(修改以匹配您自己的总线和设备号):
$ make BUS=001 DEV=011 prog
sudo cycfx2prog -d=001.011 reset prg:blink.ihx run
Using ID 04b4:8613 on 001.011.
Putting 8051 into reset.
Putting 8051 into reset.
Programming 8051 using "blink.ihx".
Putting 8051 out of reset.
$
如果一切顺利,您现在应该会看到 FX2LP 板上的两个内置 led 在交替闪烁。源代码如清单 7-1 所示。
0006: #include <fx2regs.h>
0007: #include <fx2sdly.h>
0008:
0009: static void
0010: delay(unsigned times) {
0011: unsigned int x, y;
0012:
0013: for ( x=0; x<times; x++ ) {
0014: for ( y=0; y<200; y++ ) {
0015: SYNCDELAY;
0016: }
0017: }
0018: }
0019:
0020: void
0021: main(void) {
0022:
0023: OEA = 0x03; // PA0 & PA1 is output
0024:
0025: for (;;) {
0026: PA0 = 1;
0027: PA1 = 0;
0028: delay(1000);
0029: PA0 = 0;
0030: PA1 = 1;
0031: delay(1000);
0032: }
0033: }
Listing 7-1The EZ-USB FX2LP blink.c source code
如果您使用不同的制造板,您可能需要跟踪 LED 引脚并对代码进行小的更改。据我所知,所有可用的板都使用这些相同的 led。我正在使用的板有连接到 GPIO 端口 A 引脚 0 ( PA0
)和PA1
的 led。如果你的不同,替换代码中的PA0
和PA1
。
此外,您需要更改以下行:
0023: OEA = 0x03; // PA0 & PA1 is output
OEA
是端口 A 输出使能的寄存器名称。该寄存器中的位设置为 1,并将相应的端口 A 引脚配置为输出引脚。例如,如果您的电路板使用PA2
和PA3
来代替,您需要将该行改为:
0023: OEA = 0x0C; // PA2 & PA3 is output (bits 2 & 3)
如果 led 完全位于不同的端口,则更改OEA
中的“A”以匹配正在使用的端口。
USB 演示
现在我们终于可以使用 libusb 与 usb 设备(FX2LP)进行通信来演示 Raspberry Pi 了。为了简单起见,我们的任务相当简单,除了在两端之间使用 USB。目标是能够从 Raspberry Pi 端打开/关闭 FX2LP 设备上的 led。同时,Pi 将从 USB 设备读取有关 led 当前状态的确认信息。实际上,该演示练习了向 FX2LP 发送命令信息,同时还从 FX2LP 接收有关 LED 状态的更新。
FX2LP 源代码
我们的主要焦点是 Raspberry Pi 方面,但是让我们检查 FX2LP 源代码的重要方面,以便您可以看到远程设备中正在发生什么。首先转到以下子目录:
$ cd ~/RPi/libusb/controlusb
感兴趣的 FX2LP 源文件被命名为ezusb.c
,主程序如清单 7-2 所示。
0091: void
0092: main(void) {
0093:
0094: OEA = 0x03; // Enable PA0 and PA1 outputs
0095: initialize(); // Initialize USB
0096:
0097: PA0 = 1; // Turn off LEDs..
0098: PA1 = 1;
0099:
0100: for (;;) {
0101: if ( !(EP2CS & bmEPEMPTY) )
0102: accept_cmd(); // Have data in EP2
0103:
0104: if ( !(EP6CS & bmEPFULL) )
0105: send_state(); // EP6 is not full
0106: }
0107: }
Listing 7-2FX2LP main program in ezusb.c
第 94 至 98 行将 FX2LP 的 GPIO 引脚配置为输出。然后它永远运行第 100 行到第 106 行的循环。第 101 行的 if 语句测试 USB 端点 2 中是否有接收到的数据,当不为空时,调用函数 accept_cmd()。
行 104 检查端点 6 是否未满。如果未满,则调用函数 send_state()发送状态信息。现在让我们更详细地检查这两个函数。
函数接受 _ 命令
该功能显示在清单 7-3 中。
0047: static void
0048: accept_cmd(void) {
0049: __xdata const unsigned char *src = EP2FIFOBUF;
0050: unsigned len = ((unsigned)EP2BCH)<<8 | EP2BCL;
0051:
0052: if ( len < 1 )
0053: return; // Nothing to process
0054: PA0 = *src & 1; // Set PA0 LED
0055: PA1 = *src & 2; // Set PA1 LED
0056: OUTPKTEND = 0x82; // Release buffer
0057: }
Listing 7-3The FX2LP function accept_cmd() in ezusb.c
FX2LP 的神奇之处在于,大部分 USB 设备都是用硅片处理的。线 50 访问硅中的寄存器,该寄存器指示有多少数据被传送到端点 2。如果没有数据,函数只在第 53 行返回。
否则,通过第 49 行获得的特殊指针访问数据。第 54 行根据接收到的第一个字节的位 0 设置 LED 输出引脚PA0
(Raspberry Pi 程序将只发送一个字节)。PA1
同样由同一命令字节的位 1 设置。
最后,第 56 行告诉芯片端点 2 中的数据可以被释放。如果不这样做,就不会收到更多的数据。
函数发送状态
send_state()函数读取 GPIO 端口PA0
和PA1
的当前状态,并形成 ASCII 报文发送回 Raspberry Pi(清单 7-4 )。选择详细消息格式作为发送/接收几个字节信息的示例。
0063: static void
0064: send_state(void) {
0065: __xdata unsigned char *dest = EP6FIFOBUF;
0066: const char *msg1 = PA0 ? "PA0=1" : "PA0=0";
0067: const char *msg2 = PA1 ? "PA1=1" : "PA1=0";
0068: unsigned char len=0;
0069:
0070: while ( *msg1 ) {
0071: *dest++ = *msg1++;
0072: ++len;
0073: }
0074: *dest++ = ',';
0075: ++len;
0076: while ( *msg2 ) {
0077: *dest++ = *msg2++;
0078: ++len;
0079: }
0080:
0081: SYNCDELAY;
0082: EP6BCH=0;
0083: SYNCDELAY;
0084: EP6BCL=len; // Arms the endpoint for transmission
0085: }
Listing 7-4The FX2LP function send_state() in ezusb.c
第 65 行访问端点 6 FIFO 缓冲区,用于将消息放入。第 66 和 67 行只是根据 GPIO 端口是 1 位还是 0 位来选择消息。然后,第 70 到 73 行将这条消息从第 65 行复制到端点缓冲区。第 74 行和第 75 行添加了一个逗号,然后第 76 行到第 79 行的循环将第二条消息复制到端点缓冲区。
当以最高时钟速度运行时,SYNCDELAY 宏是 FX2LP 特有的时序问题。第 82 行将 FIFO 长度的高位字节设置为零(我们的消息小于 256 字节)。第 84 行将 FIFO 长度的低位字节设置为我们在变量len
中累积的长度。一旦设置了 FIFO 长度的低位字节,芯片就会运行缓冲器,并将其发送到 USB 总线上的 Pi。
除了 FX2LP 端点的初始化和设置之外,这就是 EZ-USB 实现的全部内容。初始化源代码也在 ezusb.c 中,供那些想更仔细研究它的人使用。
EZ-USB 初始化
为了初始化 FX2LP 设备,需要将一些值填充到一些寄存器中,以便对其进行配置。忽略 SYNCDELAY 宏调用——这些只是为了让 FX2LP 有足够的时间在器件以最高时钟速率工作时接受配置更改。清单 7-5 说明了相关的配置步骤。
0010: static void
0011: initialize(void) {
0012:
0013: CPUCS = 0x10; // 48 MHz, CLKOUT disabled.
0014: SYNCDELAY;
0015: IFCONFIG = 0xc0; // Internal IFCLK @ 48MHz
0016: SYNCDELAY;
0017: REVCTL = 0x03; // Disable auto-arm + Enhanced packet handling
0018: SYNCDELAY;
0019: EP6CFG = 0xE2; // bulk IN, 512 bytes, double-buffered
0020: SYNCDELAY;
0021: EP2CFG = 0xA2; // bulk OUT, 512 bytes, double-buffered
0022: SYNCDELAY;
0023: FIFORESET = 0x80; // NAK all requests from host.
0024: SYNCDELAY;
0025: FIFORESET = 0x82; // Reset EP 2
0026: SYNCDELAY;
0027: FIFORESET = 0x84; // Reset EP 4..
0028: SYNCDELAY;
0029: FIFORESET = 0x86;
0030: SYNCDELAY;
0031: FIFORESET = 0x88;
0032: SYNCDELAY;
0033: FIFORESET = 0x00; // Back to normal..
0034: SYNCDELAY;
0035: EP2FIFOCFG = 0x00; // Disable AUTOOUT
0036: SYNCDELAY;
0037: OUTPKTEND = 0x82; // Clear the 1st buffer
0038: SYNCDELAY;
0039: OUTPKTEND = 0x82; // ..both of them
0040: SYNCDELAY;
0041: }
Listing 7-5The EZ-USB initialization code from ezusb.c
第 13 行将 CPU 时钟配置为 48 MHz,而第 15 行也将接口时钟配置为 48 MHz。第 19 行配置端点 6 用于批量输入(从主机的角度来看),而第 21 行配置端点 2 用于批量输出。第 23 到 31 行执行 FIFO 复位。第 37 和 39 行清除双缓冲 FIFO,然后 FX2LP 芯片准备好处理 USB 请求。
树莓 Pi 源代码
现在让我们把注意力转向 Raspberry Pi 代码,使用 libusb。清单 7-6 展示了在 controlusb.cpp 中找到的主程序源代码。我们仍然在这个目录中:
0164: int
0165: main(int argc,char **argv) {
0166: Tty tty;
0167: int rc, ch;
0168: char buf[513];
0169: unsigned id_vendor = 0x04b4,
0170: id_product = 0x8613;
0171: libusb_device_handle *hdev;
0172: unsigned state = 0b0011;
0173:
0174: hdev = find_usb_device(id_vendor,id_product);
0175: if ( !hdev ) {
0176: fprintf(stderr,
0177: "Device not found. "
0178: "Vendor=0x%04X Product=0x%04X\n",
0179: id_vendor,id_product);
0180: return 1;
0181: }
0182:
0183: rc = libusb_claim_interface(hdev,0);
0184: if ( rc != 0 ) {
0185: fprintf(stderr,
0186: "%s: Claiming interface 0.\n",
0187: libusb_strerror(libusb_error(rc)));
0188: libusb_close(hdev);
0189: return 2;
0190: }
0191:
0192: printf("Interface claimed:\n");
0193:
0194: if ( (rc = libusb_set_interface_alt_setting(hdev,0,1)) != 0 ) {
0195: fprintf(stderr,"%s: libusb_set_interface_alt_setting(h,0,1)\n",
0196: libusb_strerror(libusb_error(rc)));
0197: return 3;
0198: }
0199:
0200: tty.raw_mode();
0201:
0202: // Main loop:
0203:
0204: for (;;) {
0205: if ( (ch = tty.getc(500)) == -1 ) {
0206: // Timed out: Try to read from EP6
0207: rc = bulk_read(hdev,0x86,buf,512,10/*ms*/);
0208: if ( rc < 0 ) {
0209: fprintf(stderr,
0210: "%s: bulk_read()\n\r",
0211: libusb_strerror(libusb_error(-rc)));
0212: break;
0213: }
0214:
0215: assert(rc < int(sizeof buf));
0216: buf[rc] = 0;
0217: printf("Read %d bytes: %s\n\r",rc,buf);
0218: if ( !isatty(0) )
0219: break;
0220: } else {
0221: if ( ch == 'q' || ch == 'Q' || ch == 0x04 /*CTRL-D*/ )
0222: break;
0223: if ( ch == '0' || ch == '1' ) {
0224: unsigned mask = 1 << (ch & 1);
0225:
0226: state ^= mask;
0227: buf[0] = state;
0228: rc = bulk_write(hdev,0x02,buf,1,10/*ms*/);
0229: if ( rc < 0 ) {
0230: fprintf(stderr,
0231: "%s: write bulk to EP 2\n",
0232: libusb_strerror(libusb_error(-rc)));
0233: break;
0234: }
0235: printf("Wrote %d bytes: 0x%02X (state 0x%02X)\n",
0236: rc,unsigned(buf[0]),state);
0237: } else {
0238: printf("Press q to quit, else 0 or 1 to "
"toggle LED.\n");
0239: }
0240: }
0241: }
0242:
0243: rc = libusb_release_interface(hdev,0);
0244: assert(!rc);
0245: libusb_close(hdev);
0246:
0247: close_usb();
0248: return 0;
0249: }
Listing 7-6The main program in controlusb.cpp for the Raspberry Pi
$ cd ~/RPi/libusb/controlusb
Raspberry Pi 代码使用 C++来简化一些事情。非 C++程序员不必担心。很多 Arduino 同学都在使用 C++,而没有意识到。Arduino 的人可能会因为我这么说而退缩,因为他们不想吓到任何人。在这个项目中,我们将关注看起来和工作起来都像 c 的东西。
第 166 行定义了名为 tty 的类实例。不要担心它的细节,因为我们只是用它来做一些终端 I/O 的事情,这些事情对我们来说并不重要。
第 169 和 170 行定义了我们将在某个 USB 总线上寻找的供应商和产品 ID。第 174 行基于这两个 ID 号调用 libusb 函数find_usb_device
。如果没有找到设备,它返回一个空指针,在第 175 行测试。
当设备被找到时,控制传递到第 183 行,这里我们声明接口 0。如果这个失败,很可能是因为它被另一个驱动程序声明了(如usbtest
)。
在线 194 中选择备用接口 1。这是导致成功的 USB 设备访问的序列中的最后一步,用于随后的循环,从第 204 行开始。一旦循环退出(我们马上会看到如何退出),接口在第 243 行被释放,然后在第 245 行被关闭。第 247 行关闭 libusb 库。
USB I/O 环路
第 200 行使用 tty 对象为终端启用“raw 模式”。这允许这个程序一次接收一个字符。通常在程序看到任何输入之前必须按下一个RETURN
键,这对于这个演示来说是不方便的。
在循环中,第 205 行试图读取一个终端字符,等待 500 毫秒。如果在此时间内没有收到任何消息,调用返回-1 以指示超时。发生这种情况时,从第 207 行开始的代码尝试从 USB 端点 6 读取(从主机的角度来看,0x86 中的高位表示这是一个 OUT 端口)。这是我们的 FX2LP 将向我们发送状态更新(作为字符串消息)的端点。
当接收到来自 Raspberry Pi 键盘的字符时,执行第 150 和 152 行。如果字符是“q”,程序在第 151 行退出循环。这允许一个干净的程序退出。
如果键入‘0’或‘1’,则执行第 224 到 236 行。第 224 行将变量mask
中的字符转换为 0 位或 1 位。换句话说,掩码分配为 0x01 或 0x02,具体取决于输入字符分别为“0”或“1”。行 226 跟踪名为state
的变量中 LED 位的状态。然后,mask
的值根据其先前的状态切换相应位的开或关。
第 227 行将状态字节放入第一个缓冲区字节。然后,这个 1 字节的缓冲区被写入端点 2(参数0x02
),如果需要,在行 228 中的 10 ms 之后超时。如果超时发生,rc
的返回值将为负。否则,写入的字节将从第 235 行显示在终端上。
如果程序不理解在终端输入的字符,就执行第 238 行。
清单 7-7 展示了函数 find_usb_device 的源代码。
0080: static libusb_device_handle *
0081: find_usb_device(unsigned id_vendor,unsigned id_product) {
0082:
0083: if ( !usb_devs ) {
0084: libusb_init(nullptr); // Initialize
0085: // Fetch list of devices
0086: n_devices = libusb_get_device_list(nullptr,&usb_devs);
0087: if ( n_devices < 0 )
0088: return nullptr; // Failed
0089: }
0090: return libusb_open_device_with_vid_pid(
0091: nullptr,id_vendor,id_product);
0092: }
Listing 7-7The function find_usb_device in file controlusb.cpp
第一次调用 libusb 时,必须调用函数 lib usb_init
。如果变量usb_devs
是一个空指针(注意,变量usb_devs
是一个static
变量,并被初始化为空(C++中的nullptr
),那么这在第 84 行完成)。之后,第 86 行获取一个 USB 设备列表,并将一个指针存储到usb_devs
中以备将来使用。
一旦手续办完,我们就叫libusb_open_device_with_vid_pid
找到并打开我们的设备。
函数 bulk_read
在清单 7-6 所示的主循环中,从第 207 行调用了函数 bulk_read。清单 7-8 展示了该功能的代码。
0111: static int
0112: bulk_read(
0113: libusb_device_handle *hdev,
0114: unsigned char endpoint,
0115: void *buffer,
0116: int buflen,
0117: unsigned timeout_ms
0118: ) {
0119: unsigned char *bufp = (unsigned char*)buffer;
0120: int rc, xlen = 0;
0121:
0122: assert(endpoint & 0x80);
0123: rc = libusb_bulk_transfer(hdev,endpoint,
bufp,buflen,&xlen,timeout_ms);
0124: if ( rc == 0 || rc == LIBUSB_ERROR_TIMEOUT )
0125: return xlen;
0126: return -int(rc);
0127: }
Listing 7-8The bulk_read function in controlusb.cpp
本质上,这个函数是第 123 行中库函数libusb_bulk_transfer
的一个简单插曲。在这个调用中,实际读取的字节数被返回到变量int
中xlen
。对于较大的数据包,可以将其分成数据段。这里我们使用一个简单的假设,我们将在一次传输中接收所有的数据。
注意,如果传输超时,我们仍然可以传输一些数据(第 124 行对此进行了测试)。第 125 行返回读取的字节数。否则我们返回错误代码的负整数。
函数批量写入
bulk_write 函数更加复杂,因为它必须确保传输完整的消息,即使是以小块的形式发送。清单 7-9 说明了。
0133: static int
0134: bulk_write(
0135: libusb_device_handle *hdev,
0136: unsigned char endpoint,
0137: void *buffer,
0138: int buflen,
0139: unsigned timeout_ms
0140: ) {
0141: unsigned char *bufp = (unsigned char*)buffer;
0142: int rc, xlen = 0, total = 0;
0143:
0144: assert(!(endpoint & 0x80));
0145:
0146: for (;;) {
0147: rc = libusb_bulk_transfer(hdev,endpoint,
bufp,buflen,&xlen,timeout_ms);
0148: if ( rc == 0 || rc == LIBUSB_ERROR_TIMEOUT ) {
0149: total += xlen;
0150: bufp += xlen;
0151: buflen -= xlen;
0152: if ( buflen <= 0 )
0153: return total;
0154: } else {
0155: return -int(rc); // Failed
0156: }
0157: }
0158: }
Listing 7-9The bulk_write function in controlusb.cpp
消息传输再次使用 libusb_bulk_transfer,但根据端点号知道这是发送给主机的(第 144 行的断言检查)。调用实际发送的字节数在 xlen 变量中返回(参数五)。如果传输成功或超时,总字节数将作为正数返回(第 153 行)。否则,返回负的错误代码。
注意,该例程跟踪第 149 行中传输的总字节数。在行 150 中增加缓冲器开始指针,在行 151 中减少要发送的计数。该例程仅在所有字节都已发送或请求超时时返回。理想情况下,应该为超时情况提供更好的处理。
示威游行
现在让我们来表演插图。将 FX2LP 设备插入 USB 端口后,找出其总线和设备号,以便向其上传固件:
$ lsusb
Bus 001 Device 007: ID 04b4:8613 Cypress Semiconductor Corp. CY7C68013 \ EZ-USB FX2 USB 2.0 Development Kit
Bus 001 Device 006: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$
现在知道它在总线001
和设备007
上,上传固件。您应该会看到如下所示的会话输出:
$ sudo make BUS=001 DEV=007 prog
sudo cycfx2prog -d=001.007 prg:ezusb.ihx run delay:10 dbulk:6,-512,5
Using ID 04b4:8613 on 001.007.
Putting 8051 into reset.
Programming 8051 using "ezusb.ihx".
Putting 8051 out of reset.
Delay: 10 msec
Reading <=512 bytes from EP adr 0x86 ...etc.
一旦cycfx2prog
使 FX2LP 脱离复位,我们的固件代码就开始执行,这就是“Reading <=512 bytes
消息的意义所在。现在,如果您还没有编写树莓 Pi 程序,请编写它:
$ make -f Makefile.posix
g++ -c -std=c++11 -Wall -Wno-deprecated -I. -g -O0 controlusb.cpp \ -o controlusb.o
g++ controlusb.o -o controlusb -lusb
现在启动它:
$ sudo ./controlusb
Interface claimed:
Read 11 bytes: PA0=1,PA1=1
Read 11 bytes: PA0=1,PA1=1
Read 11 bytes: PA0=1,PA1=1
Wrote 1 bytes: 0x01 (state 0x01)
Read 11 bytes: PA0=1,PA1=1
Read 11 bytes: PA0=1,PA1=1
Read 11 bytes: PA0=1,PA1=0
Read 11 bytes: PA0=1,PA1=0
Read 11 bytes: PA0=1,PA1=0
Read 11 bytes: PA0=1,PA1=0
Wrote 1 bytes: 0x00 (state 0x00)
Read 11 bytes: PA0=1,PA1=0
Read 11 bytes: PA0=1,PA1=0
Wrote 1 bytes: 0x02 (state 0x02)
Read 11 bytes: PA0=0,PA1=0
Read 11 bytes: PA0=0,PA1=0
Wrote 1 bytes: 0x00 (state 0x00)
Read 11 bytes: PA0=0,PA1=1
该程序需要 root 用户,所以用sudo
启动它。否则,它将找到设备,但不能声明接口。第一行:
Wrote 1 bytes: 0x01 (state 0x01)
是我打了一个1
的时候写的。不久之后,PA1
上的 LED 亮起(LED 为低电平有效,因此 0 位打开 LED)。两行之后,FX2LP 能够向我们发送一条消息,报告PA1=0
(LED 亮起)。这并不是 FX2LP 的一部分出现了延迟,而是因为直到 Pi 读取了之前的 USB 消息,它才能够发送关于它的消息。
用“0”和“1”进行一些其他涂鸦,直到“q”键结束演示。
摘要
这一章涵盖了很多内容。对 FX2LP EZ-USB 设备进行了旋风式的介绍。好奇的人应该看看 EZ-USB 设备的 PDF 文档,并为它寻找书籍和在线资源。这一章仅仅触及了硅能做什么的表面。
本章的主要焦点是如何在 Raspberry Pi 上直接从用户模式程序处理 USB I/O。一旦您了解了基础知识,libusb 库会使这变得相当容易。这些都包含在 controlusb.cpp 源代码中。现在你有了武器和危险,你可以通过使用 USB 设计新的应用来将你的 USB 知识提高到一个新的水平。
八、以太网
无论是有线还是无线,网络已经成为日常生活的重要组成部分。在您的 Raspberry Pi 上安装一个网络适配器可以让您连接到它,并在您的台式机或笔记本电脑上舒适地工作。它还允许 Pi 上的应用与外界通信。即使当 Raspberry Pi 作为嵌入式项目的一部分部署时,网络接口仍然会继续发光。远程日志和控制只是几个例子。
有线以太网
标准 Raspbian SD 卡映像提供有线网络连接,使用 DHCP(动态主机配置协议)自动为其分配 IP 地址。如果您使用 HDMI 输出和键盘设备在 Pi 上工作,动态分配的 IP 地址不是问题。但是,如果您想去掉附加的显示器和键盘,并进行“无头”操作,那么通过网络连接到 Pi 是很有吸引力的。唯一的问题是 DHCP 分配的 IP 地址可能会改变。
DHCP 不会总是使用不同的 IP 地址,因为它会暂时向 ?? 出租该地址。但是动态分配的地址使得当它改变时很难从另一台计算机连接到您的 Raspberry Pi。正如在第二章中所讨论的,您可以使用nmap
命令来扫描它,但是这并不方便(这个例子来自 Devuan Linux):
root@devuan:~# nmap -sP 192.168.1.1-250
Starting Nmap 6.47 ( http://nmap.org ) at 2018-06-01 19:59 EDT
Nmap scan report for 192.168.1.1
Host is up (0.00026s latency).
MAC Address: C0:FF:D4:95:80:04 (Unknown)
Nmap scan report for 192.168.1.12
Host is up (0.044s latency).
MAC Address: 00:1B:A9:BD:79:12 (Brother Industries)
Nmap scan report for 192.168.1.77
Host is up (0.15s latency).
MAC Address: B8:27:EB:ED:48:B1 (Raspberry Pi Foundation)
Nmap scan report for 192.168.1.121
Host is up (0.00027s latency).
MAC Address: 40:6C:8F:11:8B:AE (Apple)
Nmap scan report for 192.168.1.89
Host is up.
Nmap done: 250 IP addresses (4 hosts up) scanned in 7.54 seconds
root@devuan:~#
如果您在学校或离家在外使用您的 Pi,使用 DHCP 可能仍然是您的最佳选择。如果你在旅行时将它接入不同的网络,DHCP 会正确设置你的 IP 地址,会负责域名服务器配置。但是,如果您在家里使用您的设备,或者您的学校可以为您分配一个有效的 IP 地址,静态 IP 地址可以简化访问。
注意
确保获得批准并分配 IP 地址,以防止网络冲突。
静态有线地址
设置静态有线以太网地址的最简单方法是使用图形桌面并打开“无线和有线网络设置”对话框这可以通过右键单击扬声器图标左侧的 WIFI 图标在屏幕右上角找到。图 8-1 所示。
图 8-1
包含“无线和有线网络设置”对话框的弹出式菜单
选择并单击“无线和有线网络设置”应该会出现如图 8-2 所示的对话框。
图 8-2
“网络首选项”对话框
要配置有线接口,请选择“接口”,然后选择“eth0”。如果你愿意,让“自动配置空选项”保持选中状态,就像我在图中所做的那样。填写您的地址、路由器和 DNS 服务器信息,点击“应用”,然后点击“关闭”此操作更新了什么?以下行将被附加到文件/etc/dhcpcd.conf 中:
interface eth0
inform 192.168.1.177
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
建立这些设置后,有线以太网端口应自动分配静态 IP 地址192.168.1.177
,在本例中。
无线配置
无线网络接口的配置方式与有线适配器类似。调出您在图 8-2 中看到的对话框,除了您将选择“SSID”而不是“接口”图 8-3 显示无线配置对话框。
图 8-3
无线配置对话框
在“SSID”的右侧,选取您想要加入的无线网络。单击应用,然后单击关闭。完成此操作后,文件/etc/dhcpcd.conf 将更新为:
SSID BaldEaglesLair
inform 192.168.1.77
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
疯狂的
图形对话框工作很好,除了如果您重复无线网络的配置,您将在文件的底部得到添加的条目。如果同一个 SSID 的多个设置发生冲突,您的无线网络可能永远无法工作。如果是这种情况,您需要编辑文件以删除冲突的重复项:
# sudo -i
# nano /etc/dhcpcd.conf
WIFI 国家
如果您还没有这样做,请确保您的 WIFI 国家已正确配置。从桌面左上方的树莓下拉菜单中选择“首选项”,然后选择“树莓 Pi 配置”点击图 8-4 中所示的“本地化”标签。
图 8-4
带有 WIFI 国家设置的“本地化”选项卡
一旦有,这是一个简单的事情,点击“设置 WIFI 国家”选择您的国家。这是一个重要的配置项目,因为它决定了您所在国家的无线 LAN(局域网)适配器的合法工作频率。
测试静态 IP 地址
一旦你配置好了,最简单的事情就是重启你的 Raspberry Pi 让新的设置生效。通常,图形对话框会让您的更改几乎立即生效。
一个好的手动检查是使用ifconfig
命令:
$ ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.177 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::8cc8:d1d2:61ba:d377 prefixlen 64 scopeid 0x20<link>
ether b8:27:eb:b8:1d:e4 txqueuelen 1000 (Ethernet)
RX packets 10505 bytes 900810 (879.6 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 26412 bytes 14866204 (14.1 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
由此您可以看到有线网络适配器的 IP 地址配置如下:
inet 192.168.1.177 netmask 255.255.255.0 broadcast 192.168.1.255
同样,您可以检查无线适配器:
$ ifconfig wlan0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.77 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::dd0c:a1af:9a22:a0c0 prefixlen 64 scopeid 0x20<link>
ether b8:27:eb:ed:48:b1 txqueuelen 1000 (Ethernet)
RX packets 24977 bytes 1777643 (1.6 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6924 bytes 7627770 (7.2 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
现在让我们检查一下名字是解析。通常,我会推荐使用nslookup
或dig
命令,但是 Raspbian 上没有预装这两个命令。所以让我们使用ping
命令:
$ ping -c 1 google.com
PING google.com (172.217.0.238) 56(84) bytes of data.
64 bytes from yyz10s03-in-f14.1e100.net (172.217.0.238): icmp_seq=1 ttl=55 time=17.9 ms
--- google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 17.931/17.931/17.931/0.000 ms
在本例中,我们看到google.com
被查找并转换为 IP 地址172.217.0.238
(您的尝试可能有所不同)。由此,我们得出结论,名称服务正在工作。ping
命令行上的-c1
选项导致只执行一次 ping。否则,ping
将继续尝试,您可能需要^C
来中断它的执行。
如果名称google.com
无法解析,您需要对/etc/resolv.conf
进行故障诊断。它应该看起来像这样(注意名称服务器行):
$ cat /etc/resolv.conf
# Generated by resolvconf
search subversive.cats.ca
nameserver 192.168.1.1
有关此文件的更多信息,请参考:
$ man 5 resolv.conf
USB 适配器
如果您有有线 USB 以太网适配器,您也可以设置网络。图 8-5 显示了一个廉价装置的例子。当你已经有一个像 Raspberry Pi 3 B+这样的内置有线适配器时,它应该在你的对话框中显示为“interface”“et h1”。用于树莓 Pi Zero(非 Zero W)时,会显示为“eth0”。这是一个与你的零暂时交流的好方法。
如果您的 Raspbian Linux 支持的话,也可以使用无线 USB 适配器。通常需要加载特定于设备的固件来提供支持。还要记住,无线适配器可能需要来自 USB 端口的 350 到 500 mA 的电流。
Radicom 宣传他们的非无线型号 LUHM200 型号最大需要 165 mA(未经证实是否支持 Raspbian Linux)。Pi 兼容性列表可在以下网站找到:
https://elinux.org/RPi_USB_Ethernet_adapters
该网站列出了一些其他的电流消耗数据,包括一个需要 250 毫安的苹果适配器。一般来说,有线适配器应该比无线适配器消耗少得多,并且在支持时,应该不需要特殊的驱动程序。我的单位如图 8-5 所示使用了大约 45 mA。
图 8-5
插入 USB 延长线的有线 USB 以太网适配器
/etc/hosts 文件
如果你的 Raspberry Pi 有一个静态的 IP 地址,为什么不用一个主机名来更新你的 Linux 或者 OS X 文件呢?例如,您的 hosts 文件可以添加以下行:
$ cat /etc/hosts
. . .
192.168.1.177 rasp raspi rpi pi # My Raspberry Pi
现在,您可以使用主机名rasp
、raspi
、rpi
或pi
在网络上访问您的 Raspberry Pi。
Pi Direct
鉴于 Raspberry Pi SBCs(单板计算机)的低成本,您可以在您的主 Raspberry Pi 3 B+之外运行另一个 Pi 作为卫星。如果你是一名游戏开发人员,你可能想利用卫星 Pi 进行 VR(虚拟现实)显示。如果两者之间的网络连接足够快,这将为您提供 2 倍的 HDMI 输出。对于较轻的图形负载,您甚至可以使用 Raspberry Pi Zero。
为了探索这种可能性,让我们概述一下通过有线以太网连接直接链接两个 Raspberry Pi 的步骤。我将使用通过 WIFI(接口 wlan0)访问的 Raspberry Pi 3 B+,但通过两者上的有线 eithernet 端口(两者上的接口 eth0)链接卫星 Pi (Raspberry Pi 3 B, not plus )。远程(卫星)Pi 将通过 Pi 3 B+完全访问互联网。
过去有必要使用以太网交叉电缆,但现在没有必要了。以太网固件现在自动配置带有直电缆的端口。图 8-6 展示了我将用来简化讨论的示例网络。现在花一点时间沉浸其中。
图 8-6
Pi 3 B+和点对点连接 Pi 3 B 的示例网络
在这个例子中,我们将重点讨论 Pi 3 B+和 Pi 3 B。但是请注意,B+节点的互联网接入通过 WIFI 路由器(192.168.1.1)经由 WIFI(接口 wlan0)到达。这是通过位于 192.168.0.1 的 ISP 路由器传送的。B+节点的桌面访问也是通过 WIFI,使用 IP 地址 192.168.1.77。
要解决的问题如下:
-
在 B+和 B 之间建立点对点连接,在两个 Pi 上使用从 eth0 到 eth0 的电缆。
-
启用 IP 路由,以便所有连接请求都通过 B+(来自 B)。
-
启用从 B 到 internet 的转发(这包括设置名称服务器访问)。
启用除互联网转发之外的所有功能非常简单。让最后一步工作,以便您可以从互联网上升级您的远程 Pi (B)有点棘手。我们开始吧。
点对点
第一步是让 Pi 3 B(远程 Pi)与 Pi 3 B+ (B+)对话。这需要两个 Pi 主机的合作。让我们从远程 Pi 开始(使用 Pi 3 B 键盘、鼠标和显示器进行初始设置)。
远程 Pi 配置
要让远程 Pi 设置其 eth0 有线连接,请编辑以下文件(以 root 用户身份):
# nano /etc/dhcpcd.conf
添加/编辑行,使您最终得到:
interface eth0
inform 192.168.2.86
static ip_address=192.168.2.86/24
static domain_name_servers=192.168.1.1
static routers=192.168.2.87
如果您以前来过这里,请确保注释掉或者禁用对接口 eth0 的旧引用。
-
inform
选项在启动时告诉 DHCP 以太网接口 eth0 将使用 IP 地址 192.168.2.86 启动。 -
除了
/24
指示网络和主机地址之间的边界之外,static ip_address
行指定了相同的内容。 -
static domain_name_servers
行配置名称服务器请求应该去哪里。在这里,我们将名称服务器请求转发到位于 192.168.1.1 的 WIFI 路由器。 -
这条线在这里至关重要。它会将所有发往未知主机的流量“翻墙”发送给 Pi 3 B+主机。
192.168.2.87 是那个直连有线链路的 B+端(审图 8-6 )。重要的是,IP 地址是该链路的 B+端。如果您错误地提供了 192.168.2.86,它最终会尝试转发给自己(B)。
保存这些更改并重新启动。在它出现后,您应该能够验证它的 IP 地址和路由表,如下所示(通过键盘和显示器):
# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.2.86 netmask 255.255.255.0 broadcast 192.168.2.255
inet6 fe80::595f:6363:5a8:d68 prefixlen 64 scopeid 0x20<link>
ether b8:27:eb:4d:56:6f txqueuelen 1000 (Ethernet)
RX packets 163 bytes 13423 (13.1 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 143 bytes 22865 (22.3 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
如果链接没有启动(运行),先不要担心。我们在连接的 B+端还有工作要做。检查路由:
-
目的地 0.0.0.0 代表默认目的地。我们从第一行看到,它被配置为发送到 192.168.2.87(点对点的 B+端)链路。
-
第二行表示默认情况下,192.168.2.0 的任何网络请求也将被路由。
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.2.87 0.0.0.0 UG 202 0 0 eth0
192.168.2.0 0.0.0.0 255.255.255.0 U 202 0 0 eth0
这将完成远程 Pi (B)配置。
WIFI Pi (B+)
现在,我们必须配置 Pi 3 B+,以便它也能提供点对点接口 eth0。编辑其文件/etc/dhcpcd.conf,以便为 eth0 保留以下行:
-
当界面可用时,行
auto eth0
调出界面。 -
行
interface eth0
导致所有后续行应用于该接口。 -
inform
行告诉 DHCP 服务器接口 eth0 的 IP 地址是 192.168.2.87。 -
static ip_address
行指定 IP 地址,并用/24
间接指定网络任务。 -
nogateway
选项表示没有要配置的网关(在 192.168.2.0 网络上找不到其他主机)。
auto eth0
interface eth0
inform 192.168.2.87
static ip_address=192.168.2.87/24
nogateway
重新启动 Pi 3 B+(和 Pi 3 B)后,您现在应该有了两者之间的直接链接。检查 Pi 3 B+:
# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.2.87 netmask 255.255.255.0 broadcast 192.168.2.255
inet6 fe80::8cc8:d1d2:61ba:d377 prefixlen 64 scopeid 0x20<link>
ether b8:27:eb:b8:1d:e4 txqueuelen 1000 (Ethernet)
RX packets 26 bytes 3670 (3.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 31 bytes 4206 (4.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
希望您的链接既有地址 192.168.2.87,也有 up(“运行”)。还要检查常规:
# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 303 0 0 wlan0
192.168.1.0 0.0.0.0 255.255.255.0 U 303 0 0 wlan0
192.168.2.0 0.0.0.0 255.255.255.0 U 202 0 0 eth0
在此显示中,请注意以下内容:
-
默认网关是 192.168.1.1(来自早期的 WIFI 设置)。所以任何主机不知道怎么路由的东西都会翻墙扔给 WIFI 路由器处理。
-
任何与 WIFI 路由器网络(192.168.1.0)相关的内容也会被路由到 WIFI 路由器。
-
任何被定向到网络 192.168.2.0 的内容都被发送到接口 eth0,这是我们到远程 Pi 的点对点链路。
从这个 B+端,让我们使用远程端的 IP 号测试登录到远程 Pi (B ):
$ ssh pi@192.168.2.86
pi@192.168.2.86's password:
Linux raspberrypi3 4.14.34-v7+ #1110 SMP ...
目前为止一切顺利。
iptables
我希望我能告诉你我们结束了。不幸的是,还剩下两个步骤。在 B+上,我们必须:
-
启用 IP 转发
-
配置 iptables,以便它知道如何转发哪些数据包。
默认情况下禁用 IP 转发功能,因为它可能会导致安全漏洞。当你需要的时候,把这个单独的设置设为关可以让你安心。
启用 IP 转发
要打开 IP 转发,请执行以下操作:
# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
-w
选项会更新系统文件以保存您的设置。如果没有此选项,该设置将不会在下次重新启动时恢复。IP 转发尚未投入使用——这只是允许转发数据包。
配置 IP 转发
现在我们将注意力转向 Linux 中的iptables
工具。如果你像我一样,你可能会抱怨“我真的需要学习所有这些关于防火墙的东西吗?”或者也许是“唉,我只是想让它工作!”请原谅,只需要多一点点。
如果你还没有弄乱iptables
的话,那么你可以跳过清除这一步。如果您已经安装了想要保留的防火墙规则,您也会想要避免清除。否则,我们检查一下iptables
,然后清除规则。首先列出过滤器:
# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
如果一切都很清楚,这就是你应该在显示屏上看到的。当您不使用-t
选项时,您隐式地引用了一个名为filters
的表。但是,我们还必须检查名为nat
的表:
# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
显示屏上没有任何添加的规则。如果您看到想要删除的规则,请使用-F 和-X 选项将其全部清除:
# iptables -F
# iptables -F -t nat
# iptables -X
# iptables -X -t nat
运行这些命令后,您应该能够获得如前所示的空列表。-F
选项删除所有的规则链。X 删除任何特殊的用户定义的链。同样,当没有-t
选项时,就好像您指定了-t filters
。选项-t nat
适用于网络地址转换(NAT)表。
现在我们可以告诉 iptables 转发我们的数据包,并在必要时应用 NAT。
# iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
该命令附加到nat
表中,以引导任何转发到接口wlan0
的数据包进行网络地址转换(NATted)。这些数据包被“伪装”成来自 192.18.1.77(b+),而不是远程 Pi (B)。
准备就绪后,让我们在远程 Pi (B)上重新测试。
第二次远程 Pi 测试
用键盘登录远程 Pi (B ),测试链路路由:
$ ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=63 time=3.22 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=63 time=4.12 ms
^C
--- 192.168.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 3.229/3.675/4.121/0.446 ms
如果事情是正确的,这个 ping 您的 WIFI 网关应该是成功的,如这个例子所示。目前为止,一切顺利。
另一个很好的测试是 ping google:
$ ping 8.8.8.8
如果失败了,这可能表明你的网络(或谷歌)的其他部分有问题。
假设这是成功的,尝试测试名称解析器:
# ping google.com
PING google.com (172.217.0.110) 56(84) bytes of data.
...
事实上,它显示你 192.217.0.110(在这个例子中)一个 IP 号码的名称 google.com 意味着名称服务器正在工作。
作为最后的测试,您应该能够进行apt-get
更新:
# apt-get update
Hit:1 http://archive.raspberrypi.org/debian stretch InRelease
Hit:2 http://raspbian.raspberrypi.org/raspbian stretch InRelease
Reading package lists... Done
此时,您应该能够像往常一样理解这一点:
# apt-get upgrade
持久性 iptables
为了避免每次都必须设置iptables
,我们需要让规则在引导时可恢复。但是在我们保存规则之前,让我们确保我们没有破坏现有的文件。/etc/iptables
目录应该还不存在:
# ls -d /etc/iptables
ls: cannot access '/etc/iptables': No such file or directory
如果没有,现在就创建它:
# mkdir /etc/iptables
如果该目录确实存在,检查名为rules
的文件是否已经存在或者没有重要内容:
# cat /etc/iptables/rules
假设规则文件尚不存在或者没有实质性内容,我们可以将我们的iptables
规则保存到这个文件中:
# iptables-save >/etc/iptables/rules
要在引导时自动恢复 iptables 规则,请创建/编辑以下文件:
# nano /etc/dhcpcd.enter-hook
向其中添加以下行:
iptables-restore </etc/iptables/rules
保存编辑。现在,当您启动时,DHCP 服务器应该从该文件中恢复您的iptables
规则。
布图规则检测
即使事情进展顺利,也很高兴能验证这种iptables
东西正在工作。通过添加-v
选项,用计数列出规则,iptables
命令可以显示正在执行的规则:
# iptables -L -v
Chain INPUT (policy ACCEPT 1277 packets, 122K bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 258 packets, 83716 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 1181 packets, 120K bytes)
pkts bytes target prot opt in out source destination
从这个显示中,我们可以确认除了关于输入和输出规则的信息之外,还转发了 258 个数据包。通过添加-t nat
选项,相同的报告可用于nat
表。
接近
刚才概述的过程允许您以间接的方式登录到您的远程 Pi。使用本章的例子,您将首先 ssh 到 192.168.1.77,然后从那里 ssh 到 192.168.2.86。如果没有额外的配置,您将无法直接从台式计算机 ssh 到 192.168.2.86。这是可以做到的,但是这个话题超出了我们的范围。
然而,目前的安排确实允许两个 Pi 相互直接通信。游戏应用可以利用共享的 CPU 资源,并利用两个 HDMI 显示器。作为一个额外的奖励,远程 Pi 还可以访问互联网,包括用apt-get
更新的能力。
安全
本演示是一个入门示例。有更多的选项和规则可以用于更好的保护。但是,如果您已经在防火墙后运行,那么就没有必要增加管理的复杂性。
如果你的 Pi 没有在防火墙后面,并且你计划直接在互联网上暴露你的设备,那么你必须花时间去学习更多关于iptables
和防火墙原理的知识。
摘要
本章介绍了如何使用图形桌面配置无线和有线以太网适配器。这非常适合正常和常规的情况。
然而,对于特殊的配置,事情会变得更加复杂。本章的其余部分讲述了文件的编辑和iptables
设置,以建立点对点连接。这允许远程 Pi 通过第一个 Pi 访问互联网。了解如何保存iptables
配置并在引导时哄 DHCP 服务器恢复这些规则,就完成了这幅画面。以此为起点,您可以扩展您的网络配置知识,以满足特殊需求。
九、SD 卡存储
文件系统是 Linux 借鉴的 Unix 系统设计的核心。传统上,大容量存储需求是通过硬盘子系统来满足的。然而,随着主机变得像信用卡一样小,闪存技术已经取代了笨重的机械驱动器。
SD 卡介质
第一个私家侦探用的是标准尺寸的 SD 卡。然而,所有较新的型号现在都使用如图 9-1 所示的 MicroSD 卡,以及标准 SD 适配器。
图 9-1
SD MicroSD 适配器(左侧)和 8 GB MicroSD 卡(右侧)
MicroSD 底面的 8 个引脚如图 9-2 所示。
图 9-2
MicroSD 卡的底部,有 8 个引脚暴露在外
SD 卡基础知识
SD 卡包括一个内部控制器,也称为闪存处理器(FSP)。在这种配置中,主机仅提供命令并等待响应。FSP 负责完成命令所需的所有擦除、编程和读取操作。以这种方式,随着新的性能和存储密度的实现,闪存卡设计的复杂性被允许增加。
SD 卡管理扇区大小为 512 字节的数据。为了与现有操作系统兼容,特意将它设计为与 IDE 磁盘驱动器相同。主机发出的命令包括扇区地址,以允许一个或多个扇区的读/写。
注意
操作系统可能会使用多个 512 字节的扇区。
命令和数据受 FSP 生成的 CRC(循环冗余校验)码保护。FSP 还会在写入后自动执行读取,以验证数据是否正确写入。 9 如果写入有缺陷,FSP 会自动纠正,必要时用另一个物理扇区替换。
SD 卡的软错误率远低于磁盘驱动器。在极少数情况下,当发现错误时,最后一道防线是纠正 ECC(纠错码),它允许数据恢复。这些错误在介质中被纠正,以防止将来出现不可恢复的错误。所有这些活动对主机都是透明的。
拉斯比安块大小
操作系统使用的块大小可能是介质扇区大小的倍数。为了确定在 Raspbian 下使用的物理块大小,我们首先发现根文件系统是如何挂载的(下面的清单已经被修改):
$ mount
/dev/mmcblk0p2 on / type ext4 (rw,noatime,data=ordered)
...
/dev/mmcblk0p1 on /boot type vfat (rw,relatime,fmask=0022,dmask=0022, \
codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro)
由此我们推断用于根文件系统的设备是/dev/mmcblk0p2
。使用的命名约定告诉我们以下内容:
成分
|
名字
|
数字
|
类型
|
| — | — | — | — |
| 前缀 | /dev/mmcblk | | MMC 块 |
| 设备号 | Zero | Zero | |
| 分区号码 | p2 | Two | |
从前面的mount
命令输出中,注意到/boot
文件系统被挂载在/dev/mmcblk0p1
上。这表明/boot
文件系统来自同一个 SD 卡设备的分区 1。
使用设备信息,我们参考/sys
伪文件系统来找出物理和逻辑扇区大小。这里我们提供mmcblk0
作为第三级路径名限定符来查询设备:
$ cat /sys/block/mmcblk0/queue/physical_block_size
512
$ cat /sys/block/mmcblk0/queue/logical_block_size
512
$
显示的结果告诉我们,本例中使用的 Raspbian Linux 使用 512 字节的块(扇区)大小,无论是物理上还是逻辑上。这与 SD 卡的扇区大小完全匹配。
磁盘高速缓存
在我们检查已安装的 SD 卡文件系统时,我们还要检查所使用的设备节点的类型:
$ ls -l /dev/mmcblk0p?
brw-rw---- 1 root disk 179, 1 Jun 19 07:42 /dev/mmcblk0p1
brw-rw---- 1 root disk 179, 2 Jun 19 07:42 /dev/mmcblk0p2
示例输出在 brw-rw 字段的开头显示一个b
。这告诉我们磁盘设备是一个块设备,而不是一个字符设备。(相关的字符设备将显示一个c
。)块设备对于文件系统非常重要,因为它们提供了磁盘缓存功能,可以极大地提高文件系统的性能。输出显示root
(分区 2)和/boot
(分区 1)文件系统都是使用块设备挂载的。
容量和性能
SD 卡允许在介质限制范围内配置数据总线宽度。所有 SD 卡都从一条数据位线开始,直到存储卡的容量已知。在知道介质的能力之后,数据总线可以在软件控制下扩展,如支持的那样。表 9-1 总结了 SD 卡的功能。 10
表 9-1
SD 卡功能
|标准
|
描述
|
大于
|
直到
|
数据总线
|
| — | — | — | — | — |
| 级计算中心 | 标准容量 | Zero | 2 GB | 1 位 |
| SDHC(消歧义) | 大容量 | 2 GB | 32 GB | 4 位 |
| SDXC(消歧义) | 扩展容量 | 32 GB | 2 TB | 4 位 |
转移模式
SD 卡使用三种基本的数据传输模式:
-
SPI 总线模式
-
1 位标清模式
-
4 位标清模式
SPI 总线模式
SPI 总线模式主要由使用支持 SPI 总线的小型微控制器的消费电子产品使用。检查表 9-2 显示,在此模式下,数据一次传输 1 位(引脚 2 或 7)。
表 9-2
spi 总线模式微秒
|别针
|
名字
|
输入-输出
|
逻辑
|
描述
|
精力
|
| — | — | — | — | — | — |
| one | 网络计算机 | | | | |
| Two | /CS | 我 | 包裹邮递(Parcel Post) | 卡选择(低电平有效) | 特许测量员 |
| three | 国防情报部 | 我 | 包裹邮递(Parcel Post) | 数据输入 | 工业博物馆 |
| four | 电源电压 | S | S | 电源 | |
| five | 时钟信号 | 我 | 包裹邮递(Parcel Post) | 时钟 | sclk-sclk-sclk-sclk-sclk-sclk-sclk-sclk-sclk-sclk-sclk-sclk |
| six | 虚存系统 | S | S | 地面 | |
| seven | 防御命令(Defense Order) | O | 包裹邮递(Parcel Post) | 数据输出 | 军事情报部门组织(Military Intelligence Service Organization) |
| eight | | | | 内向的; 寡言少语的; 矜持的 | |
各种 SD 卡连接的使用方式各不相同,如表 9-2I/O 和逻辑列中的助记符所示。表 9-3 是这些的图例,也适用于表 9-4 。
表 9-3
I/O 和逻辑图例
|注释
|
意义
|
笔记
|
| — | — | — |
| 我 | 投入 | 相对于卡片 |
| O | 输出 |
| 输入-输出 | 输入或输出 |
| 包裹邮递(Parcel Post) | 推/拉逻辑 |
| 平均海面 | 明渠 |
| S | 电源 |
| 网络计算机 | 未连接 | 或逻辑高 |
标清模式
SD 模式允许改变数据总线宽度,以增加 SDHC 和 SDXC 卡支持的 I/O 速率。更高的数据时钟速率也能提高传输速率。表 9-4 列出了引脚分配。
表 9-4
微型标清模式引脚
|别针
|
名字
|
输入-输出
|
逻辑
|
描述
|
| — | — | — | — | — |
| one | DAT2 | 输入-输出 | 包裹邮递(Parcel Post) | dota2 |
| Two | CD/日期 3 | 输入-输出 | 包裹邮递(Parcel Post) | 卡检测/数据 3 |
| three | 煤矿管理局 | 输入-输出 | 聚丙烯/外径 | 命令/响应 |
| four | 电源电压 | S | S | 电源 |
| five | 时钟信号 | 我 | 包裹邮递(Parcel Post) | 时钟 |
| six | 虚存系统 | S | S | 地面 |
| seven | 日期 0 | 输入-输出 | 包裹邮递(Parcel Post) | 数据 0 |
| eight | DAT1 | 输入-输出 | 包裹邮递(Parcel Post) | 数据 1 |
磨损均衡
不幸的是,对于执行的每个写操作,闪存遭受磨损(因为每个写操作需要擦除和编程数据块)。闪存的设计要求擦除和重写大块存储器,即使单个扇区的值已经改变。因此,损耗均衡被用作延长介质寿命的技术。损耗均衡通过将数据移动到不同的物理块,同时保留相同的逻辑地址来延长寿命。
在没有支持文档的情况下,给定的存储卡是否支持损耗均衡是一个公开的问题。一些制造商可能根本不实施损耗均衡,或者使用较低水平的过度配置。SD 卡标准中没有规定损耗均衡,因此没有制造商被迫遵循 SanDisk 的领导。
直接文件系统挂载
有时候,在 Pi 离线的情况下,使用 Linux 对 SD 卡文件系统进行更改是很方便的。如果您有 USB 卡适配器,这也可以通过不同的 Pi 来完成。使用连接到 Linux 系统的 SD 卡插槽或 SD 卡读卡器,您可以直接挂载文件系统。
但是,您如何知道要安装什么呢?您至少可以应用两个有用的命令:
-
lsblk(消歧义)
-
布瑞克
lsblk
命令非常适合向您展示块设备和分区安排:
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 149.1G 0 disk
├─sda1 8:1 0 147.3G 0 part /
├─sda2 8:2 0 1K 0 part
└─sda5 8:5 0 1.8G 0 part [SWAP]
sdb 8:16 1 14.5G 0 disk
├─sdb1 8:17 1 41.8M 0 part
└─sdb2 8:18 1 14.5G 0 part
sr0 11:0 1 1024M 0 rom
#
从这个显示中,您可以看到 Linux 根文件系统是从/dev/sda1
开始挂载的。我们的 SD 卡出现在/dev/sdb
上,有分区sdb1
和sdb2
。blkid
命令给了我们更多的信息,包括分区标签:
# blkid
/dev/sda1: UUID="51d355c1-2fe1-4f0e-aaae-01d526bb27b5" TYPE="ext4" PARTUUID="61c63d91-01"
/dev/sda5: UUID="83a322e3-11fe-4a25-bd6c-b877ab0321f9" TYPE="swap" PARTUUID="61c63d91-05"
/dev/sdb1: LABEL="boot" UUID="A75B-DC79" TYPE="vfat" PARTUUID="2e37b5e0-01"
/dev/sdb2: LABEL="rootfs" UUID="485ec5bf-9c78-45a6-9314-32be1d0dea38" TYPE="ext4" \
PARTUUID="2e37b5e0-02"
这个显示表明我们的 Pi /boot
分区在/dev/sdb1
上,而它的根分区在/dev/sdb2
上可用。这些可以直接安装。首先确保您有目录条目来挂载它们(如果它们还不存在的话):
# mkdir /mnt/1
# mkdir /mnt/2
现在它们可以安装在:
# mount /dev/sdb1 /mnt/1
# mount /dev/sdb2 /mnt/2
这样挂载之后,您可以随意列出或更改/mnt/1 或/mnt/2 目录中的文件。
只读问题
当 Linux 抱怨你的 SD 卡是只读的,你会怎么做?
# mount /dev/sdb1 -o rw /mnt/1
mount: /dev/sdb1 is write-protected, mounting read-only
这个问题至少有三个可能的原因:
-
MicroSD 适配器上的开关已经滑到“保护”(或锁定)位置。
-
Linux 在设备上有一个软件锁。
-
或者 MicroSD 适配器有故障(连接不良)。
MicroSD 适配器开关
用于 MicroSD 适配器的滑动开关可能是一个真正的麻烦,因为它会意外地滑入“锁定”位置。解决办法是把它拉回来,并调整开关设置。
软件保护
另一种可能是 Linux 在设备上有一个软件锁。-r1
选项打开该功能:
# hdparm -r1 /dev/sdb1
/dev/sdb1:
setting readonly to 1 (on)
readonly = 1 (on)
# mount /dev/sdb1 /mnt/1
mount: /dev/sdb1 is write-protected, mounting read-only
#
使用-r1
命令的hdparm
命令可以为设备设置软件锁。尝试在启用此锁的情况下装载文件系统会导致只读装载。解决方案是使用选项-r0
禁用该保护:
# hdparm -r0 /dev/sdb1
/dev/sdb1:
setting readonly to 0 (off)
readonly = 0 (off)
# mount /dev/sdb1 /mnt/1
MicroSD 适配器质量
当这个只读问题第一次发生在我身上时,我以为问题出在我使用的 Linux 计算机的硬件上。通过研究,我发现有些人报告说他们的 MicroSD 适配器有问题。在尝试了三种不同的 MicroSD 适配器后,我最终获得了成功。前两个适配器连接不良,导致设备只读。
如果您的 MicroSD 卡配有适配器,那可能就是您想要使用的适配器。然而,这并不能保证成功。
图像文件
如果您无法直接安装 SD 卡,那么您仍然可以操作图像文件。这可能是下载的 Raspbian 图像,也可能是一个朋友以某种方式提供给你的。也许他们是从电脑的 SD 卡上创建的图像:
# dd if=/dev/sdb of=/tmp/sdcard.img bs=1024k
这个dd
命令使用 1 MB (1024k)的块大小将输入磁盘(/dev/sdb
)复制到输出文件(/tmp/sdcard.img
)。大块大小用于提高效率。
镜像文件的问题是它包含两个分区。如果它是一个单独的分区,那么它可以直接挂载。分区需要我们做更多的工作。安装kpartx
将使这项任务变得更容易:
# apt-get install kpartx
现在,当我们有一个镜像文件要挂载时,我们可以如下使用它:
# kpartx -v -a /tmp/sdcard.img
add map loop0p1 (254:0): 0 85611 linear /dev/loop0 8192
add map loop0p2 (254:1): 0 30351360 linear /dev/loop0 98304
注意名字loop0p1
和loop0p2
?使用它们在映像文件中挂载文件系统分区:
# mount /dev/mapper/loop0p1 /mnt/1
# mount /dev/mapper/loop0p2 /mnt/2
现在您将在/mnt/1
中找到您的/boot
文件,并且在/mnt/2
中挂载 Pi 根分区。完成更改后,卸载分区:
# umount /mnt/1
# umount /mnt/2
卸载文件系统后,您可以将映像文件复制回 SD 卡。
摘要
本章简要介绍了 SD 卡及其操作。然后研究了在 Raspberry Pi 之外使用 SD 卡文件系统的两种不同方式——直接在 Linux 上挂载 SD 卡和挂载图像文件。两者都有其用途,尤其是在救援行动中。
十、通用非同步收发传输器
Raspberry Pi 有一个 UART 接口,允许它执行串行数据通信。使用的数据线是 3.3 V 逻辑电平信号,应该而不是连接到 TTL 逻辑(+5 V)(它们也是不兼容 RS-232)。要使用 RS-232 与设备通信,您需要一个转换器模块。
RS-232 转换器
虽然勤劳的人可以自己构建 RS-232 转换器,但如果有 pcb 上的廉价转换器,就没什么必要这么做了。
图 10-1 是我用过的一个 MAX232CSE 芯片 pcb。这个特殊的单元只支持 RX 和 TX 线路,没有硬件流量控制。当寻找一个单位,得到一个工作与 3.3 伏逻辑电平。有些单元只能用 TTL (+5 V)逻辑工作,这对你的 Pi 是有害的。当 MAX232CSE 芯片的 VCC 电源引脚连接到+3.3 V 时,该芯片支持 3.3 V 工作电压
图 10-1
MAX232CSE 接口
我还建议你选择一个支持硬件流控制信号的单元。寻找 CTS 和 DTR 信号。完整的 RS-232 转换器还包括 DTR、DSR 和 CD 信号。
注意
在本文中,我们将参考 3 V ,知道它更准确地说是 3.3 V。
TTL 适配器
您也可以使用 TTL 适配器,而不是将信号转换为 RS-232 所需的+/-电压。Pi 要求信号端(TTL)应能在+3.3 V 而不是通常的+5 V 下工作,使用+5 V 适配器可能会损坏 Pi。可以连接+3.3 V 的单元可能会有一个跳线来选择电压。
DTE 或 DCE
选择 RS-232 转换器时,请记住有两种串行连接:
-
DCE :数据通信设备(母接头)
-
DTE :数据终端设备(公接头)
一个普通的 USB 串行适配器(例如,用于笔记本电脑)将提供一个 DTE(公)连接器。这种电缆的布线是这样的,它预计插入到 DCE(阴)连接。当这适用于你的 Raspberry Pi 适配器时,笔记本电脑的串行适配器可以直接插入 DCE(母)连接器,消除了对交叉电缆或零调制解调器的需要。
因此,对于您的 Pi,请选择提供母(DCE)连接器的 RS-232 转换器。同样,确保您为笔记本电脑/台式机购买了一个具有公(DTE)连接的电缆或 USB 设备。连接 DTE 到 DTE 或 DCE 到 DCE 需要一根交叉电缆,根据电缆的不同,还需要一个“性别修理工”。最好从一开始就把事情“搞清楚”。
假设您对 Pi 使用了 DCE 转换器,将 RS-232 转换器的 3 V 逻辑 TX 连接到 Pi 的 TXD0,将 RX 连接到 Pi 的 RXD0 数据线。
所有这些关于 DCE 和 DTR 的事情总是让人很困惑。如果你也觉得这令人困惑,有另一种实用的方法来看待它。从您计划使用的连接器和电缆开始。确保它们在两端匹配,并且串行电缆是已知的直电缆(相对于交叉电缆)。一旦解决了这些物理问题,您就可以正确布线了。将 TX 连接到 RX,并将 RX 连接到 TX。换句话说,你在 RS-232 适配器和 Raspberry Pi 之间自己布线交叉。需要记住的重要一点是,发射端需要向接收端双向发送信号。
注意
在 DB9 或 DB25 电缆上,直串行电缆将引脚 2 连接到引脚 2,引脚 3 连接到引脚 3。一根交叉电缆将穿过这两条信号线以及其他信号线。
RS-232
RS-232 是与串行通信相关的一系列标准的传统名称。它是在 1962 年由 EIA 的无线电部门首次引入的。第一个数据终端是与调制解调器(DCE)通信的电传打字机(DTE)。早期的串行通信受到不兼容的困扰,直到后来的标准发展。
串行链路包括两条数据线,数据从一个终端发送,由同一个终端接收。除了这些数据线,还有几个握手信号(比如 RTS 和 CTS)。默认情况下,Raspberry Pi 不提供这些功能。
图 10-2 显示了一个串行信号传输,时间从左到右。RS-232 设备要求信号在–15V 至+15 V 范围内变化。
图 10-2
串行信号
标准规定,当电压在–3 和–15v 之间时,信号被视为处于标记状态。如果电压在+3 和+15 V 之间,信号被视为处于空间状态。当线路空闲时,RS-232 数据线处于标记状态。
开始位
当要发送数据的异步字符时,该行首先在 1 比特的持续时间内转换到一个空间电平。这就是所谓的起始位 (0)。数据位紧随其后。
异步线路不像同步链路那样使用时钟信号。异步接收器的时钟必须与发送器的波特率匹配。接收器在位单元时间内对线路采样 16 次,以确定其值。采样有助于避免噪声脉冲触发错误的数据读取。
数据位
数据位紧跟在起始位之后,最低有效位在前。空格是 0 数据位,而标记代表 1 位。早期的电传设备使用 5 个数据位发送 5 位博多码中的字符。 11 因此,串行端口可以配置为 5、6、7 或 8 个数据位。在 ASCII 字符集扩展到 8 位之前,通常使用 7 位串行数据。
奇偶校验位
可选的奇偶校验位可以在发送时生成,也可以在接收端检测到。奇偶性可以是奇数、偶数或固定的(标记或空格)。目前最常用的设置是无奇偶校验,这样可以节省 1 位时间,从而加快通信速度。旧设备通常使用奇偶校验来防止来自噪声串行线路的错误。奇数奇偶校验优于偶数奇偶校验,因为它强制字节传输中至少有一次信号转换。这有助于提高数据的可靠性。
传号或空号奇偶校验并不常见,而且用处有限。标记奇偶校验可以与 2 个停止位一起使用,以便为非常慢的电传打字机设备有效地提供 3 个停止位。标记或空间奇偶校验降低了数据的有效吞吐量,但除了可能用于诊断目的之外,没有提供任何好处。表 10-1 总结了各种奇偶校验配置。
表 10-1
RS-232 奇偶校验设置
|平价
|
X
|
笔记
|
| — | — | — |
| 没有人 | 普通 | 无奇偶校验位 |
| 平的 | E | 1 如果偶数个数据 1 位 |
| 奇数的 | O | 1 如果奇数个数据 1 位 |
| 标记 | M | 始终处于标记水平(1) |
| 空间 | S | 总是在空间级别(0) |
停止位
异步通信要求接收机与发射机同步。因此,存在一个或多个停止位,以便接收器可以与下一个起始位的前沿同步。实际上,每个停止位后跟一个开始位,提供了内置的同步。
许多 UARTs 支持 1、1.5 或 2 个停止位。Broadcom SoC 仅支持 1 或 2 个停止位。使用两个停止位在电传打字机设备中很常见,现在可能很少使用了。使用 1 个停止位可提高整体数据吞吐量。表 10-2 总结了停止位配置。
表 10-2
停止位配置
|停止位
|
描述
|
| — | — |
| one | 1 个停止位 |
| One point five | 1.5 个停止位() |
| Two | 2 个停止位 |
?不受树莓派的支持
波特率
波特率是根据每秒位数计算的,包括起始位、数据位、奇偶位和停止位。使用 115200 波特、无奇偶校验和 1 个停止位的链路提供以下数据字节速率:
)
在哪里
B 是波特率。
s 是起始位(总是 1 位)。
d 是数据位数(5、6、7 或 8)。
p 是校验位(0 或 1)。
S 是停止位(1、1.5 或 2)。
115200 波特链路允许每秒 11250 字节。如果添加一个奇偶校验位,吞吐量会降低:
)
添加一个奇偶校验位会将传输速率降低到每秒 10,472.7 字节。
表 10-3 列出了在 Raspberry Pi 上串行链路可以配置的标准波特率。
表 10-3
标准波特率
|速度
|
笔记
|
| — | — |
| Seventy-five | 电传打字机 |
| One hundred and ten | 电传打字机 |
| Three hundred | 低速(声学)调制解调器 |
| One thousand two hundred | |
| Two thousand four hundred | |
| Four thousand eight hundred | |
| Nine thousand six hundred | |
| Nineteen thousand two hundred | |
| Thirty-eight thousand four hundred | |
| Fifty-seven thousand six hundred | |
| One hundred and fifteen thousand two hundred | Raspberry Pi 控制台 |
破裂
通过异步通信,还可以发送和接收中断信号。这是通过将起始位扩展到数据位和停止位之外,并最终将行返回到标记状态来实现的。当接收器看到一个空格而不是停止位的标记时,它会看到一个帧错误。
一些 UARTs 通过记录线路保持在空白状态的时间来区分成帧错误和中断。一个简单的成帧错误可能作为噪声串行线路通信的一部分发生(特别是当使用调制解调器时),通常归因于接收字符错误。在没有中断检测的情况下,当序列中出现几个成帧错误时,可以假定已经接收到中断。然而,短序列的成帧错误也可能只是表明两个端点之间的波特率不匹配。
流控制
任何从一端传输到另一端接收器的链路都存在流量控制问题。想象一条工厂装配线,待装配的零件到达工人工作站的速度比他/她装配它们的速度快。在某些时候,传送带必须暂时停止,否则一些零件将无法组装。或者,如果传送带速度降低,装配工人将能够跟上,但速度低于最佳速度。
除非串行链路接收器能够像数据到达一样快地处理数据的每个字符,否则它将需要流量控制。最简单的方法是简单地降低波特率,这样接收器就能一直跟上。但是这并不总是令人满意的。日志记录应用可能能够快速写入信息,例如,当写入 SD 卡时除外。
一个更好的方法是当接收者陷入困境时,向发送者发出停止发送的信号。一旦接收器赶上,它就可以告诉发送器恢复传输。请注意,串行链路的两端都存在此问题:
-
传输到终端的数据(DTE)
-
数据传输到数据通信设备(DCE)
使用两种形式的流量控制:
-
硬件流控制
-
软件流程控制
硬件流控制
硬件流量控制使用额外的信号线来调节数据流。RS-232 标准定义了一组相当复杂的信号,但流量控制所需的主要信号如表 10-4 所示。与数据线不同,这些信号在空间状态下是无效的,而在标记状态下是有效的。
表 10-4
硬件流控制
|数据终端设备(Data Terminal Equipment)
|
方向
|
数据通信设备(Data Communications Equipment)
|
描述
|
活跃的
|
| — | — | — | — | — |
| 即时战略游戏 | →中 | 即时战略游戏 | 请求发送 | 低的 |
| 同CARPAL TUNNEL SYNDROME | ← | 同CARPAL TUNNEL SYNDROME | 允许发送() |
| 每日业务报告(Daily Service Report) | ← | 每日业务报告(Daily Service Report) | 数据集就绪 | 低的 |
| 负荷型定额(Duty Type Rating) | →中 | 负荷型定额(Duty Type Rating) | 数据终端就绪 |
主要流量控制信号
最重要的信号是表 10-4 中用匕首标记的信号。例如,当 CTS 激活(标记)时,DCE (Pi)表示可以发送数据。如果 DCE 因数据量过大而不堪重负,CTS 信号将变为非活动(空白)状态。看到这个,DTE(桌面)被要求停止发送数据。否则,可能会丢失数据。
类似地,作为 DTE 运行的桌面从 DCE (Pi)接收数据。如果笔记本电脑被输入的大量数据淹没,RTS 信号将变为非活动状态(space)。然后,远端(DCE)将停止传输。当桌面赶上时,它会重新声明,允许 DCE 恢复。
DTR 和 DSR 信号旨在传达两端设备的就绪状态。如果终端被认为没有准备好(DTR),DSR 不会被 DCE 激活。类似地,终端不会断言 DTR,除非它准备好了。在现代串行链路中,DTR 和 DSR 通常被认为是正确的,只留下 CTS 和 RTS 来处理流量控制。
在需要流量控制的地方,硬件流量控制被认为比软件流量控制更可靠。
软件流程控制
为了简化串行通信的布线和支持硬件,可以省略/忽略硬件流控制。取而代之的是使用数据协议。
最初,链路的每一端都假定准备好接收数据。发送数据,直到收到一个XOFF
字符,表示传输应该停止。当接收器准备好再次恢复接收时,发送XON
字符。这些软件流程控制字符如表 10-5 所示。
表 10-5
软件流控制字符
|密码
|
意义
|
美国信息交换标准代码
|
十六进制
|
键盘
|
| — | — | — | — | — |
| 唇 | 暂停传输 | DC3 | Thirteen | 控制-S |
| 发送朴通 | 恢复传输 | DC1 | Eleven | 控制 |
在终端会话中,键盘命令可用于控制串行连接。例如,如果信息显示得太快,用户可以键入 Ctrl-S 来停止传输。按 Ctrl-Q 允许它继续。
软件流控制的缺点包括以下几点:
-
线路噪声会阻止接收器看到
XOFF
字符,并导致数据丢失(由于数据溢出)。 -
线路噪声会使远端看不到
XON
字符,并可能无法恢复传输(导致链路“锁定”)。 -
线路噪声会导致接收到错误的
XON
/XOFF
字符(数据丢失或链路锁定)。 -
如果接收缓冲区已满,远程终端看到发送的
XOFF
字符的延迟会导致数据丢失。 -
XON
和XOFF
字符不能用于传输链路中的数据。
问题 1 至 3 可能会导致链路锁定或数据丢失。如果缓冲区足够早地通知另一端以防止溢出,问题 4 是可以避免的。问题 5 是二进制数据传输的问题。
树莓馅饼
Raspberry Pi 支持两种 UARTs:
|通用非同步收发传输器(Universal Asynchronous Receiver/Transmitter)
|
五金器具
|
结节
|
通用输入输出接口
|
中高音
|
| — | — | — | — | — |
| UART0 | PL011 | /dev/ttyAMA0 | 14 & 15 | Zero |
| UART1 | 迷你 UART | /dev/ttys 0 | 14 & 15 | five |
使用 PL011 还是迷你 UART 取决于 Raspberry Pi 的型号。本来这个问题回答起来很简单。B 型和 A 型 Pi 只是使用 PL011 ( /dev/ttyAMA0
)设备作为控制台。迷你 UART ( /dev/ttyS0
)是一个不同的硬件模块,也是可用的,尽管功能有限。
随着 Pi 3 和 Pi Zero W 上无线和蓝牙功能的增加,PL011 UART 被用于 BT(蓝牙)和 WIFI 支持,而 mini UART 则取代了串行控制台。所有其他型号都使用首选的 PL011 设备作为控制台。
但是,由于使用了设备树覆盖,分配内容的规则变得更加复杂。更多细节可从 raspberrypi 获得。org 。下面的在线文件,不包括日期戳,有更多的血淋淋的细节:
https://www.raspberrypi.org/documentation/configuration/uart.md
哪个在用?
您可以验证正在使用哪个串行设备,如下所示:
$ cat /boot/cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 ...
console=
选项可以在内核命令行中出现多次。在这个 Raspberry Pi 3 Model B 示例中,我们看到定义了两个控制台,但只有一个是串口(serial0
)。列出串行设备显示:
$ ls -l /dev/serial0
lrwxrwxrwx 1 root root 5 Jun 19 22:04 /dev/serial0 -> ttyS0
名称/dev/serial0
是实际设备/dev/ttyS0
的符号链接。
$ ls -l /dev/ttyS0
crw--w---- 1 root tty 4, 64 Jun 19 22:04 /dev/ttyS0
所以这个 Pi 被配置为使用迷你 UART ( /dev/ttyS0
)。
在我的 Raspberry Pi 3 B+上列出引导命令行产生了:
$ cat /boot/cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 ...
它号称用的是/dev/serial0,但是这个配置有没有 serial0
设备:
$ ls /dev/serial0
ls: cannot access '/dev/serial0': No such file or directory
raspberrypi.org 页面还指出:
如果 cmdline.txt 使用别名 serial0 来指代用户可访问的端口,无论是否使用该覆盖,固件都会将其替换为适当的端口。
禁用串行控制台
如果你想将串行设备用于非控制台的目的,那么显然我们必须禁用控制台。最简单的方法是成为 root 用户并使用raspi-config
:
# raspi-config
光标向下选择“界面选项”并按回车键(图 10-3 )
)。
图 10-3
raspi-config 的打开对话框,选择了“接口选项”
然后光标向下(图 10-4 )选择“串行”并按Return
。
图 10-4
在 raspi-config 中选择“串行”,然后按回车键
然后选择<No>
禁用控制台,按Return
(图 10-5 )。
图 10-5
选择,禁用 raspi-config 中的控制台
通过提示您是否要重启,对话框结束(图 10-6 )。这对于内核中的控制台更改是必要的。如果您已经有了选择的设置,可以跳过此提示。
图 10-6
raspi-config 对话框最后会询问您是否要重新启动
Raspberry Pi 3 串行控制台
在连接串口并启用控制台的情况下启动 Raspberry Pi 3时,启动后会显示以下登录提示:
Raspbian GNU/Linux 9 rpi3bplus ttyS0
rpi3bplus login: pi
Password:
注意提示上方的“ttyS0
”。尽管事实上/dev/serial0
并不存在(在 B+上),拉斯边安排/dev/ttyS0
(迷你 UART)用作控制台。
Raspberry Zero 串行控制台
在串行控制台启用的情况下启动 Raspberry Pi Zero(非 W)确认其使用 PL011 (/dev/ttyAMA0
设备作为控制台:
Raspbian GNU/Linux 8 raspberrypi ttyAMA0
raspberrypi login: pi
Password:
图 10-7 所示的 Raspberry Pi Zero 有一个焊接到电路板上的母接头带。串行端口适配器是使用 MAX232CSE IC 的真正 RS232C 转换器。这些连接是:
-
P1-01 (+3.3 伏)至 VCC
-
P1-06 呼叫地面
-
P1-08 (GPIO14)至 TX
-
P1-10 (GPIO15)至 RX
图 10-7
Raspberry Pi Zero,串行控制台连接到 RS232C 适配器。该适配器插入了真正的 RS232C 转 USB 接口。
在这种安排下,没有流量控制。考虑到波特率很高,如果需要提高数据完整性,可以将波特率降低到 9600 波特。
PL011 和微型 UART 的区别
迷你 UART 的 FIFOs 较小,不支持硬件流控制。如果没有流量控制,它将很容易在高数据速率下丢失数据。mini 的波特率也与 VPU 时钟相关,这导致了其他问题。
微型 UART 在以下方面也有缺陷:
-
无中断检测
-
无成帧错误检测
-
不支持奇偶校验位
-
无接收超时中断
-
没有 DCD,DSR,DTR,或里的信号
VPU 时钟给 UART 带来了一个问题,因为 VPU 频率调节器通常会改变内核频率。这将导致 UART 波特率也发生变化。raspberrypi.org 国家:
通过将 enable_uart=1 添加到 config.txt,可以重新启用 Linux 控制台。这也将 core_freq 固定为 250Mhz(除非设置了 force_turbo,否则它将固定为 400Mhz),这意味着 uart 波特率保持一致。
尽可能避免使用微型 UART。
PL011 UART 特性
Broadcom BCM2835 ARM 外设手册声明以下功能不支持:
-
无红外数据协会(IrDA)支持
-
否串行红外(SIR)协议编码器/解码器(endec)
-
否直接内存访问(DMA)
-
不支持DCD、DSR、DTR 和里的信号
然而,支持以下功能*😗
-
独立的 16×8 发送和 16×12 接收 FIFO 缓冲器
-
可编程波特率发生器
-
错误起始位检测
-
断线生成和检测
-
支持控制功能 CTS 和 RTS
-
可编程硬件流量控制
-
完全可编程串行接口特性:
-
数据可以是 5、6、7 或 8 位。
-
偶数、奇数、标记、空白或非奇偶校验位的生成和检测。
-
1 或 2 个停止位生成。
-
波特率生成,DC 高达 UARTCLK/16。
-
Broadcom 还表示,其 UART 和 16C650 UART 的实现存在一些差异。但是这些主要是设备驱动程序的细节:
-
接收 FIFO 触发电平为 1/8、1/4、1/2、3/4 和 7/8。
-
发送 FIFO 触发电平为 1/8、1/4、1/2、3/4 和 7/8。
-
内部寄存器映射地址空间和每个寄存器的位功能不同。
-
1.5 停止位不支持。
-
无独立接收时钟。
应用开发人员唯一真正关心的是 1.5 停止位配置选项是而不是可用的,这在最近很少使用。
如果需要 RS-232 DCD、DSR、DTR 和 RI 信号,可以使用 GPIO 输入和输出引脚(以及适当的 RS-232 线路电平转换器)来实现。这些是变化相对较慢的信号,可以在用户空间中轻松处理。然而,这种方法的一个限制是缺少设备驱动程序提供的挂起 TTY 控制。要改变这种情况,可以修改设备驱动程序源代码,使用 GPIO 来支持这些信号。对此感兴趣的 Raspbian Linux 模块如下:
drivers/tty/serial/amba-pl011.c
UART GPIO 引脚
默认情况下,发送和接收引脚是 GPIO 14 (TX)和 15 (RX),分别是 GPIO 头上的 P1-08 和 P1-10 引脚。当 PL011 器件可用时,当选择备用功能 5 时,硬件流控制信号也可以出现在 GPIO 头上。表 10-6 列出了这些连接。
表 10-6
UART 引脚
|功能
|
通用输入输出接口
|
P1/P5
|
中高音
|
方向
|
描述
|
| — | — | — | — | — | — |
| 数据发送 | Fourteen | P1-08 | Zero | 在外 | DTE 传输数据 |
| 接收数据 | Fifteen | P1-10 | Zero | 在…里 | DTE 接收数据 |
| 即时战略游戏 | Seventeen | P1-11 | five | 在外 | 请求发送 |
| 同CARPAL TUNNEL SYNDROME | Thirty | P1-36 | five | 在…里 | 清除发送 |
RTS/CTS 访问
配置后,硬件流控制 CTS 和 RTS 分别在 GPIO 30 (P1-26)和 17 (P1-11)上可用。默认情况下,这些是 GPIO 输入,但可以重新配置。要访问 UART 的 CTS 和 RTS 信号,将 GPIO 30 和 17 配置为备用功能 5。
摘要
随着 Raspberry Pi 成熟成为新的型号,与串行设备相关的功能变得更加复杂。然而 Raspberry Pi Foundation 已经为您提供了raspi-config
工具来简化串行控制台或专用串行线的配置。
有了提供的信息,您将能够使用串行适配器登录到您的无头 Raspberry Pi Zero。这些信息让你尽可能地利用这一宝贵的资源。