在树莓派上实现模拟USB设备的功能,并通过USB线与电脑进行通信
全文摘要
本文介绍了一种在树莓派上实现模拟USB设备功能并与其进行通信的方法。该方法使用了gadgetfs和gousb库,其中gadgetfs允许树莓派模拟USB设备,而gousb则用于处理USB通信。具体步骤包括配置树莓派作为USB设备、编写服务端代码和客户端代码等。实验结果表明,该方法能够成功地实现在树莓派上模拟USB设备并与电脑进行通信。需要注意的是,在实际操作中需注意设备权限、设备ID以及端点地址等问题。
简介
要在树莓派上实现模拟 USB 设备的功能,并通过 USB 线与电脑进行通信,可以使用 gadgetfs
和 gousb
库。gadgetfs
允许树莓派模拟 USB 设备,而 gousb
则用于处理 USB 通信。
步骤概述
-
配置树莓派作为 USB 设备:使用
gadgetfs
配置树莓派模拟 USB 设备。 -
编写服务端代码:在树莓派上编写服务端代码,接收来自电脑客户端的数据。
-
编写客户端代码:在电脑上编写客户端代码,发送数据到树莓派。
配置树莓派作为 USB 设备
- 启用 USB Gadget 模式:
- 编辑
/boot/config.txt
文件,添加以下内容以启用 USB Gadget 模式:dtoverlay=dwc2
- 重启树莓派:
sudo reboot
- 编辑
- 配置 USB Gadget:
- 创建一个配置文件,例如
/etc/udev/rules.d/99-gadget.rules
,添加以下内容:SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", MODE="0666"
- 创建一个 gadget 配置文件,例如
/etc/gadget-config.conf
,添加以下内容:ATTRS{idVendor}="1234" ATTRS{idProduct}="5678" ATTRS{bcdDevice}="0100" ATTRS{bDeviceClass}="02" ATTRS{bDeviceSubClass}="00" ATTRS{bDeviceProtocol}="00" ATTRS{bMaxPacketSize0}="64" ATTRS{bNumConfigurations}="1" ATTRS{bmAttributes}="80" ATTRS{bMaxPower}="50"
- 创建一个配置文件,例如
- 加载 USB Gadget 模块:
- 创建一个启动脚本,例如
/usr/local/bin/start-gadget.sh
,添加以下内容:#!/bin/bash modprobe libcomposite cd /sys/kernel/config/usb_gadget/ mkdir -p mygadget cd mygadget echo 0x1234 > idVendor echo 0x5678 > idProduct echo 0x0100 > bcdDevice echo 0x02 > bDeviceClass echo 0x00 > bDeviceSubClass echo 0x00 > bDeviceProtocol echo 0x40 > bMaxPacketSize0 echo 0x01 > bNumConfigurations mkdir -p strings/0x409 echo "fedcba9876543210" > strings/0x409/serialnumber echo "Raspberry Pi" > strings/0x409/manufacturer echo "My USB Gadget" > strings/0x409/product mkdir -p configs/c.1/strings/0x409 echo "Config 1" > configs/c.1/strings/0x409/configuration echo 250 > configs/c.1/MaxPower mkdir -p functions/ncm.usb0 ln -s functions/ncm.usb0 configs/c.1/ ls /sys/class/udc > UDC
- 使脚本可执行:
sudo chmod +x /usr/local/bin/start-gadget.sh
- 运行脚本:
sudo /usr/local/bin/start-gadget.sh
- 创建一个启动脚本,例如
服务端代码(树莓派)
在树莓派上编写服务端代码,接收来自电脑客户端的数据。
package main
import (
"fmt"
"github.com/google/gousb"
"log"
"time"
)
const (
vendorID = 0x1234 // 替换为你的设备的厂商 ID
productID = 0x5678 // 替换为你的设备的产品 ID
)
func main() {
ctx := gousb.NewContext()
defer ctx.Close()
dev, err := ctx.OpenDeviceWithVIDPID(vendorID, productID)
if err != nil {
log.Fatalf("Failed to open device: %v", err)
}
defer dev.Close()
handle, err := dev.Open()
if err != nil {
log.Fatalf("Failed to open device handle: %v", err)
}
defer handle.Close()
intf, err := handle.ClaimInterface(0)
if err != nil {
log.Fatalf("Failed to claim interface: %v", err)
}
defer intf.Release()
// 设置端点
inEndpoint := &gousb.InEndpointInfo{Address: 0x81} // 替换为你的输入端点地址
outEndpoint := &gousb.OutEndpointInfo{Address: 0x02} // 替换为你的输出端点地址
buffer := make([]byte, 64) // 缓冲区大小可以根据需要调整
for {
n, err := inEndpoint.Read(buffer)
if err != nil {
log.Printf("Read error: %v", err)
continue
}
fmt.Printf("Received %d bytes: %x\n", n, buffer[:n])
}
// 保持程序运行
select {}
}
客户端代码(电脑)
在电脑上编写客户端代码,发送数据到树莓派。
package main
import (
"fmt"
"github.com/google/gousb"
"log"
"time"
)
const (
vendorID = 0x1234 // 替换为你的设备的厂商 ID
productID = 0x5678 // 替换为你的设备的产品 ID
)
func main() {
ctx := gousb.NewContext()
defer ctx.Close()
dev, err := ctx.OpenDeviceWithVIDPID(vendorID, productID)
if err != nil {
log.Fatalf("Failed to open device: %v", err)
}
defer dev.Close()
handle, err := dev.Open()
if err != nil {
log.Fatalf("Failed to open device handle: %v", err)
}
defer handle.Close()
intf, err := handle.ClaimInterface(0)
if err != nil {
log.Fatalf("Failed to claim interface: %v", err)
}
defer intf.Release()
// 设置端点
outEndpoint := &gousb.OutEndpointInfo{Address: 0x02} // 替换为你的输出端点地址
data := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
for {
n, err := outEndpoint.Write(data)
if err != nil {
log.Printf("Write error: %v", err)
continue
}
fmt.Printf("Sent %d bytes: %x\n", n, data)
time.Sleep(1 * time.Second) // 每秒发送一次
}
}
运行示例
- 配置树莓派:
- 按照上述步骤配置树莓派,使其模拟 USB 设备。
- 将服务端代码保存为
server.go
并上传到树莓派。 - 在树莓派上运行服务端程序:
go run server.go
- 运行客户端:
- 将客户端代码保存为
client.go
。 - 在电脑上运行客户端程序:
go run client.go
- 将客户端代码保存为
- 查看输出:
- 服务端会输出接收到的数据,客户端会输出发送的数据。
注意事项
-
设备权限:确保你有足够的权限访问 USB 设备。在 Linux 上,你可能需要将用户添加到
usb
组或使用sudo
运行程序。 -
设备 ID:确保
vendorID
和productID
是正确的,对应于你的 USB 设备。 -
端点地址:确保输入和输出端点地址是正确的,可以通过
lsusb -v
命令查看设备的详细信息。
希望这个示例能帮助你实现树莓派作为 USB 设备与电脑进行通信。如果你有更多问题或需要进一步的帮助,请随时提问。