将 PL-R5 作为 USB 设备运行时可做的事情
以太网、串行接口、通过 OTG 实现的存储功能

一些工业用 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_etherUSB 以太网
g_serialUSB 串行
g_mass_storageUSB 存储器
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 的技术博客文章