一些工业用 Raspberry Pi 设备(如 PL-R5)支持 USB OTG。本研究中使用的 PL-R5 也支持 USB OTG。
USB OTG(On-The-Go)是指单个 USB 端口可同时成为主机和设备。
支持 OTG 的端口的特点是能够根据情况切换角色。
- USB 以太网适配器
- USB 串行通信设备
- USB 存储器
- 使用多种功能进行合成
在工业级 Raspberry Pi 上使用 USB OTG 的示例如下。
- 连接电脑以运行初始设置。
- 通过串行连接进行维护
- 让它运行日志
- 提供网络管理用户界面
Linux 有一个名为 USB 小工具框架的机制。因此,它在标准状态下是受支持的。

| 小工具 | 目录 |
|---|---|
| g_ether | USB 以太网 |
| g_serial | USB 串行 |
| g_mass_storage | USB 存储器 |
| g_hid | 键盘和鼠标 |
| 复合材料 | 同时提供多种功能 |
基于 ConfigFS 的 USB 小工具配置目前是 Linux 的标准。
( 在/sys/kernel/config/usb_gadget/ 下配置 )
虽然 g_ether 等单个模块可由内核自动启动,使用方便,但g_~ 系列的多个功能无法同时配置。因此,有必要在复合配置中进行配置,从实用角度看,复合配置的配置点很多。
从启用 USB OTG 开始,本文将在最终合成中同时设置三种功能。
编写本报告时的说明
在此环境中,我们使用了基于 CM5 的工业级 Raspberry Pi PL-R5,操作系统是 Raspberry Pi OS(书虫)。主机在 Linux/macOS 上进行测试。
验证环境
型号:工业 Raspi “PL-R5” (Raspberry Pi Compute Module 5 Rev 1.0)
OS:Debian GNU/Linux 12 (bookworm)
内核:6.12.62+rpt-rpi-v8
macOS:Tahoe 26.3
当使用 Windows 作为主机时,由于不支持 ECM(以太网控制模型),因此需要与驱动程序一起使用 RNDIS(远程网络驱动程序接口规范)。
此外,虽然这里没有提到,但 Raspberry Pi OS Trixie 包含rpi-usb-gadget 软件包。如果启用它,无需任何手动配置,它就会被识别为 USB 以太网设备,你可以立即通过 SSH 进行连接,而无需配置 Wi-Fi 或有线局域网。
以下内容摘自官方网站。
“从 20.10.2025 或更高版本的 Raspberry Pi OS Trixie 镜像开始,默认情况下会包含一个名为 rpi-usb-gadget 的新软件包。激活后,Raspberry Pi 会显示为 USB 以太网设备,您可以立即通过 SSH 进行连接,而无需配置 Wi-Fi 或以太网。”
https://www.raspberrypi.com/news/usb-gadget-mode-in-raspberry-pi-os-ssh-over-usb/
有许多不同的方法可以一口搞定 USB OTG 的操作和设置。我觉得有点复杂。
因此,如果只是 USB 以太网,现在有了一个额外的机制,可以让它变得更容易。
准备工作将 USB OTG 端口设置为设备模式(手动)
在此,我们将手动设置所有 USB OTG。
要在设备端使用 PL-R5 的 USB2.0 OTG 端口,必须首先在启动时以外设模式启用 USB 控制器。
请注意,您最终会将 Configfs 中的三个函数配置为复合函数,在这种情况下,您需要再次编辑 cmdline.txt。
/boot/firmware/config.txt添加到
dtoverlay=dwc2,dr_mode=peripheral
不过,由于 CM5 系列通常将 dir_mode=host 作为标准(默认)描述,因此在添加之前请注释掉原文。
[cm5]
#dtoverlay=dwc2,dr_mode=host
dtoverlay=dwc2,dr_mode=peripheral
/boot/firmware/cmdline.txt 中还增加了一个内容。
在最后输入以下内容,用一个空格隔开:
规则是 cmdline.txt 应写成一行。不得换行。
modules-load=dwc2
重新启动后,USB OTG 设备模式将启用。
USB 复合接口(在 Configfs 中配置)
使用 USB OTG 时,最好结合使用以太网和串行通信,而不是独立配置。
为了同时提供多种功能,无法在 g_~ 系统中进行配置,因此需要在 configfs 中进行配置。这就是所谓的 USB 复合功能。
– 单一功能(以太网/串行/大容量存储器) → 无法指定多个,由内核自动启动(无需 systemd)
– 组合功能(大容量存储器 + 以太网等) → configfs + 创建 systemd
请注意,同时使用 g_ether 和其他 configfs 设置会导致冲突,因此请务必使用其中之一。
这一次,我们将使其成为一个复合型产品,这样就可以用一根 USB 电缆提供以太网 + 串行端口 + 大容量存储器。
只有每种功能都可以在 Configfs 中进行配置。
注意一点:如果以后要进行其他编辑,必须在设置中启用 UDC 后将其停用,然后再重新启用 UDC,否则可能会出现故障。
我在一次测试中就遇到过。如果你在操作过程中出错,就会发生这种情况。
在这种情况下,我们准备了一个脚本文件,可在一个步骤中设置所有三个功能。
USB 复合设置
首先在您刚刚编辑的/boot/firmware/cmdline.txt中添加以下内容。重新启动以激活。
modules-load=dwc2,libcomposite
加载 libcomposite 后,我们将在/sys/kernel/config/usb_gadget/ 创建必要的配置文件。
在本例中,我们创建了一个名为 CM5 的目录。名称可以是任何你喜欢的,但请相应更改,因为它已在其他地方指定。
如果在该目录下创建一个目录,就会看到已经创建了以下文件。
UDC bDeviceProtocol bMaxPacketSize0 bcdUSB functions idVendor os_desc webusb
bDeviceClass bDeviceSubClass bcdDevice configs idProduct max_speed strings
我们将在这里对文件进行修改。
usb_gadget 配置示例
我们将设置要连接的 USB 小工具。以下是我们这次指定的设置内容。
我们将在脚本中逐一解释如何指定它们,脚本稍后将以脚本形式运行。
sudo modprobe libcomposite
cd /sys/kernel/config/usb_gadget/
sudo mkdir cm5
cd cm5
echo 0x1d6b | sudo tee idVendor # Linux Foundation
echo 0x0104 | sudo tee idProduct # Multifunction Composite
echo 0x0100 | sudo tee bcdDevice # デバイスのリリース番号v1.0.0
echo 0x0200 | sudo tee bcdUSB # USB2.0
sudo mkdir strings/0x409 # en-US 英語文字
echo "0001" | sudo tee strings/0x409/serialnumber
echo "RaspberryPi" | sudo tee strings/0x409/manufacturer
echo "CM5 Composite" | sudo tee strings/0x409/product
首先加载sudo modprobe libcomposite 。为了安全起见,在创建和运行脚本之前只需进行一次加载。
供应商 ID 和沿途产品 ID 最初由各制造商确定,但由于这是一次测试,我们使用 “Linux 基金会 “ID 作为供应商 ID,使用 “Multifunction Composite “ID 作为产品 ID。
bcdDevice 是版本号,因此是 v1.0.0,bcdUSB 是 USB 2.0 值。
此外,strings/0x409指定了英文(en-US)字符,而序列号、制造商和产品则是可选项,但应易于理解。例如,
产品也是主机上显示的部件,因此最好以易于理解的方式命名。
基于这些考虑,我们在脚本文件中对它们进行了指定。
复合配置脚本文件
前提条件:已加载 dwc2 和 libcomposite 的 config.txt 和 cmdline.txt。
内容大致如下
- UDC 发布
- 小玩意一代
- ECM/ACM/MASS_STORAGE 创建
- 配置链接
- UDC 重新绑定
- USB 以太网设置
cm5-usb-gadget.sh
sudo nano /usr/local/sbin/cm5-usb-gadget.sh
#!/bin/bash
set -euo pipefail
G=/sys/kernel/config/usb_gadget/cm5
UDC_DEV=$(ls /sys/class/udc | head -n 1)
[ -z "$UDC_DEV" ] && exit 1
# 既存解除(再実行対策)
if [ -d "$G" ]; then
echo "" > "$G/UDC" || true
rm -rf "$G"
fi
mkdir -p "$G"
cd "$G"
# ===== Device Descriptor =====
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0200 > bcdUSB
echo 0x0100 > bcdDevice
mkdir -p strings/0x409
echo "0123456789" > strings/0x409/serialnumber
echo "Raspberry Pi" > strings/0x409/manufacturer
echo "CM5 USB Gadget" > strings/0x409/product
# ===== Config =====
mkdir -p configs/c.1
mkdir -p configs/c.1/strings/0x409
echo "ECM + ACM + MassStorage" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
# ===== ECM (USB Ethernet) =====
mkdir -p functions/ecm.usb0
echo "02:00:00:00:00:01" > functions/ecm.usb0/dev_addr
echo "02:00:00:00:00:02" > functions/ecm.usb0/host_addr
ln -s functions/ecm.usb0 configs/c.1/
# ===== ACM (USB Serial) =====
mkdir -p functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/
# ===== Mass Storage =====
IMG="/opt/usb/piusb.bin"
LABEL="CM5USB"
mkdir -p /opt/usb
if [ ! -f "$IMG" ]; then
dd if=/dev/zero of="$IMG" bs=1M count=128
mkfs.vfat -n "$LABEL" "$IMG"
fi
fatlabel "$IMG" "$LABEL" || true
mkdir -p functions/mass_storage.usb0
echo 1 > functions/mass_storage.usb0/stall
echo 0 > functions/mass_storage.usb0/lun.0/removable
echo 0 > functions/mass_storage.usb0/lun.0/ro
echo "$IMG" > functions/mass_storage.usb0/lun.0/file
ln -s functions/mass_storage.usb0 configs/c.1/
# ===== Bind =====
echo "$UDC_DEV" > UDC
# ====== USB Ethernet 固定IP設定 ======
sleep 1
if ip link show usb0 >/dev/null 2>&1; then
ip addr flush dev usb0 || true
ip addr add 192.168.7.2/24 dev usb0 || true
ip link set usb0 up || true
fi
虽然每个环境都有不同的部分,但预计不会有任何问题。
在这种情况下,我们在开头写上 “不存在(反重新执行)”。
这是因为程序将在每次启动时执行。这样,即使更改了某些设置值并重新启动系统,这些更改也会反映在新的设置中。
在 “大容量存储器 “部分,我们指定了图像文件的位置和名称及其标签,因为如果在连接时显示标签,会更容易理解。
如果您想随意更改,请在此处更改。
IMG="/opt/usb/piusb.bin"
LABEL="CM5USB"
通过在绑定部分写入echo $UDC_DEV > UDC和 UDC 值,可启用 UDC。
最后,固定 IP 地址。
可以不设置,但在很多方面难以理解,因此我们决定使用固定 IP 地址。(它将是 169.254.x.x/16)
因此,192.168.7.0/24 是安全的,因为它不太可能与典型的家庭局域网重叠。
192.168.7.1 # ホストPCとして
192.168.7.2 # Pi
顺便提一下,我们在启用固定 IP 设置的同时还启用了 usb0(向上),这样就一举两得了。
创建脚本后,授予执行权限。
sudo chmod +x /usr/local/sbin/cm5-usb-gadget.sh
systemd-ize(支持自动启动)
创建服务,使配置自动生效。
sudo nano /etc/systemd/system/cm5-usb-gadget.service
[Unit]
Description=CM5 USB Composite Gadget
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/cm5-usb-gadget.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
换句话说,它每次都会重建配置文件。
如前所述,如果要更改 USB OTG 的某些内容以启用它,只需重新启动即可。
这是因为在 sh 文件的开头删除了 UDC。
RemainAfterExit=yes使服务处于活动(退出)状态,这对希望保持状态的配置服务很有用。它不是常驻服务。
最后,不要忘记激活(启用)您创建的服务。这里不要启动服务,因为 sh 文件将被执行。
sudo systemctl enable cm5-usb-gadget.service
现在重新启动,启用所有三个功能后 USB OTG 就能正常工作了。
帮助您了解的个别方法
我在此提醒大家,在某些情况下,了解这些信息会很有帮助。
停用/启用 UDC
多次提到的 UDC 最初是一个空文件,里面什么都没有。
在向 UDC 写入自己的 UDC 设备名称值(如 1000480000.usb)后,Configfs 方法就会生效。
因此,我们希望每次更改或添加配置时都写入该值,但如果该值已作为 UDC 启用,则需要停用该值(删除该值),然后重新启用。
由于在重写时遇到了一些小麻烦,我们将该命令留给用户手动完成。
使用以下命令中显示的 UDC 名称(如 1000480000.usb)。
ls /sys/class/udc
写入数值的命令。
echo "1000480000.usb" | sudo tee /sys/kernel/config/usb_gadget/cm5/UDC > /dev/null
解锁 UDC 会清空已有值的 UDC。
在进行更改时,例如向 UDC 添加新功能,必须以同样的方式清空 UDC,然后进行配置并重新启用。
空值命令。
echo "" | sudo tee /sys/kernel/config/usb_gadget/cm5/UDC > /dev/null
注意,每个功能都必须参照脚本执行命令。
echo "1000480000.usb" | sudo tee /sys/kernel/config/usb_gadget/cm5/UDC > /dev/null
调查错误
我就不细说了,但我会给你留下一个命令,如果它不起作用,你可以试试。
journalctl -u cm5-usb-gadget.service
在通信调查中试用的命令。所有命令均在 Pi 端执行。
ip route
ip route | grep usb0
ip addr show usb0
cat /sys/class/net/usb0/carrier
ip link show
systemctl status cm5-usb-gadget.service
nmcli dev status
单一功能和组合功能
不过,如果您有详细的设置或开始使用多种功能,请使用 Configfs 方法。
脚本文件很方便,因为只需创建一次,步骤较少,而且由于可列表,易于管理。
如果您想尝试快速 USB OTG,请复制并粘贴脚本文件并尝试。
文章由拉斯必达提供
非工程师也能愉快使用的 Raspberry Pi 信息网站 raspida.com一个非工程师也能享受和使用的 Raspberry Pi 信息网站。他还为 PiLink 网站提供有关工业用 Raspberry Pi 的技术博客文章。

