外设与接口
硬件资源图
序号 | 接口 | 序号 | 接口 |
---|---|---|---|
1 | RTC 电池接口 | 10 | 电源 Type-C 接口 |
2 | Micro USB (UART 调试) | 11 | PWR 按键 |
3 | TurboX C6490P SOM | 12 | EDL 按键 |
4 | 3.5mm 耳机接口 | 13 | 摄像头接口 2 |
5 | USB Type-C with DP (USB 3.1) | 14 | 摄像头接口 1 |
6 | USB Type-A (USB 2.0) | 15 | Wi-Fi/蓝牙模块 |
7 | 2 x USB Type-A (USB 3.0) | 16 | 风扇接口 |
8 | 1000M 以太网 | 17 | 40-pin 连接器 |
9 | HDMI OUT | 18 | M.2 Key M 接口 |
40 pin 连接器
GPIO
引脚分布
下图是 RUBIK Pi 3 40-pin 连接器的引脚默认功能,其中大部分引脚和开源开发板的40-pin连接器引脚的默认功能兼容。
下表是 40-pin连接器支持的所有功能,图中蓝色字体表明默认功能。
使用 shell 命令控制
在 RUBIK Pi 3 中执行下面的步骤控制 GPIO。
GPIO 引脚通过 /sys/class/gpio 目录进行管理。运行ls -alt
命令可查看当前目录引脚信息。
GPIO 引脚并非随意分布,而是被组织成多个 GPIO Chip(引脚组),每个 Chip 管理一组连续的引脚,并有唯一的编号。例如上图中,gpiochip336 基地址为 336,管理从引脚 336 开始的引脚。
gpiochip336 由主控制器(pinctrl 控制器)管理,pinctrl 负责配置引脚功能(如 GPIO、SPI 等),适用于通用引脚,而其他 GPIO Chip 则由专用控制器管理,负责特定任务的引脚。
如果想要引用该组中的某个 GPIO 引脚,需要使用基地址加上偏移量(下表中的引脚编号)。
以控制 12 号引脚 GPIO_101 为例,需进行如下操作:
-
进入 /sys/class/gpio 目录:
cd /sys/class/gpio
-
导出要控制的 GPIO。gpio336 基地址 336 + 偏移量 12(即引脚编号)= 348,运行以下命令:
echo 348 > export
-
进入到 gpio348 目录设置 GPIO 属性:
cd gpio 348
ls -alt
-
direction(方向):
-
输入:in
-
输出:out
-
-
value(值):
-
低电平:0
-
高电平:1
-
-
edge (中断边沿):
-
上升沿触发:rising
-
下降沿触发:falling
-
双边沿触发:both
-
禁用中断:none
-
如设置 12 号引脚输出高电平:
echo out > direction
echo 1 > value
取消导出 12 号引脚到用户空间:
echo 348 > unexport
I2C
I2C 是飞利浦公司在 20 世纪 80 年代开发的一种双向 2 线制总线,用于实现高效的 IC 间控制总线。总线上的每个设备都有其唯一的地址(由飞利浦公司领导的 I2C 总机构注册)。I2C 核心支持多控制器模式,以及 10 位目标地址和 10 位可扩展地址。关于 I2C 的更多信息,请参阅 https://www.i2c-bus.org/fileadmin/ftp/i2c_bus_specification_1995.pdf。
引脚分布
下图是 RUBIK Pi 3 40-pin 连接器的引脚默认功能,其中大部分引脚和开源开发板的40-pin连接器引脚的默认功能兼容。
3 号引脚和 5 号引脚默认已设置配为 I2C1。
下表是 40-pin 连接器支持的所有功能,图中蓝色字体表明默认功能。
使用 shell 命令测试
在 RUBIK Pi 3 中执行下面步骤控制 I2C 总线。
-
找到要测试的 I2C 设备
cd /sys/class/i2c-dev/
ls -alt
-
使用 i2cdetect 工具,以下操作以i2c-8为例。
-
查看 I2C8 接口上的设备:
i2cdetect -a -y -r 8
-
每个后缀的含义
-a
:扫描所有可能的 I2C 地址。-y
:跳过交互式确认。-r
:使用 SMBus 的receive byte
命令进行检测。8
:指定要扫描的 I2C 总线号。
-
这里的 UU 代表内核中已经有了I2C8这个驱动,可以进行通信,表示成功使能。
-
SPI
串行外设接口 (SPI) 是在全双工模式下工作的同步串行数据链路。SPI 又称为 4 线制串行总线。
引脚分布
下图是 RUBIK Pi 3 40-pin 连接器的引脚默认功能,其中大部分引脚和开源开发板的40-pin连接器引脚的默认功能兼容。
19 号、21 号、23 号、24 号引脚默认已设置配为 SPI。
下表是 40-pin 连接器支持的所有功能,图中蓝色字体表明默认功能。
使用 C 语言程序 SPI 通信
-
以下代码示例,代码使用 SPI 总线进行数据收发通信:
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <sys/time.h>
#include <signal.h>
#include <string.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev0.0"; /*spi controller device file*/
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 10000000; //5242880hz-->655360B/s-->5mbps
static uint16_t delay;
int flag = 1;
void prompt_info(int signo)
{
flag = 0;
}
void init_sigaction(void)
{
struct sigaction tact;
tact.sa_handler = prompt_info;
tact.sa_flags = 0;
sigemptyset(&tact.sa_mask);
sigaction(SIGALRM, &tact, NULL);
}
void init_time()
{
struct itimerval value;
value.it_value.tv_sec = 5; // Transmission time
value.it_value.tv_usec = 0;
value.it_interval = value.it_value;
setitimer(ITIMER_REAL, &value, NULL);
}
static void transfer(int fd)
{
int ret;
uint8_t temp[] = {
'a', 'a', 'a', 'a', 'a', 'a',
'n', 'o', 'p', 'q', 'r', 's',
'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a',
'b', 'c', 'd', 'e', 'f', 'g',
'b', 'c', 'd', 'e', 'f', 'g',
'b', 'c', 'd', 'e', 'f', 'g',
't', 'u',
'a', 'a', 'a', 'a', 'a', 'a',
'h', 'i', 'j', 'k', 'l', 'm',
'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a',
'a', 'a', 'a', 'a', 'a', 'a',
'b', 'c', 'd', 'e', 'f', 'g',
'b', 'c', 'd', 'e', 'f', 'g',
'b', 'c', 'd', 'e', 'f', 'g',
'v', 'w',
};
uint8_t tx[ARRAY_SIZE(temp)] = {0, }; //Array of data to be sent
memcpy(tx, temp, ARRAY_SIZE(tx));
uint8_t rx[ARRAY_SIZE(tx)] = {0, }; //Received data array
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
unsigned long size = 0;
init_sigaction();
init_time();
FILE *file_write = NULL, *file_read = NULL;
file_write = fopen("writebuf","w+");
file_read = fopen("readbuf","w+");
while (flag)
{
ret = ioctl(fd, SPI_IOC_MESSAGE(1), tr); //ioctl default operation, transfer data
if (ret < 1)
pabort("can't send spi message");
size++;
fwrite(tx, 1, 100, file_write);
fwrite(rx, 1, 100, file_read);
memcpy(tx, temp, ARRAY_SIZE(tx));
memset(rx, 0x00, ARRAY_SIZE(tx));
}
fclose(file_write);
fclose(file_read);
}
static void print_usage(const char *prog) //If the parameter is wrong, print the help information
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev3.0)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word \n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = { //Parameter command table
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ NULL, 0, 0, 0 },
};
int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
if (c == -1)
break;
switch (c) {
case 'D':
device = optarg;
break;
case 's':
speed = atoi(optarg);
break;
case 'd':
delay = atoi(optarg);
break;
case 'b':
bits = atoi(optarg);
break;
case 'l':
mode |= SPI_LOOP;
break;
case 'H':
mode |= SPI_CPHA;
break;
case 'O':
mode |= SPI_CPOL;
break;
case 'L':
mode |= SPI_LSB_FIRST;
break;
case 'C':
mode |= SPI_CS_HIGH;
break;
case '3':
mode |= SPI_3WIRE;
break;
case 'N':
mode |= SPI_NO_CS;
break;
case 'R':
mode |= SPI_READY;
break;
default:
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
parse_opts(argc, argv);
fd = open(device, O_RDWR);
puts(device);
if (fd < 0)
pabort("can't open device");
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
transfer(fd);
close(fd);
return ret;
} -
编译程序:
-
交叉编译
aarch64-linux-gnu-gcc {filename} -o {输出filename} -static
-
使用的交叉编译,需要将 spi 传输到 RUBIK Pi 3 中,如果使用 ADB 传输,命令如下:
adb push spi /dev
-
-
将 19 号引脚和 21 号引脚使用杜邦线短接,验证 SPI 总线通信,如下图所示:
-
设备检查(确认 SPI 设备存在)
- 进入设备终端:
adb shell
- 切换到 root 权限:
su
- 进入 /dev 目录并检查 SPI 设备文件:
cd /dev
ls | grep spidev
预期结果:
存在类似 spidev0.0 的设备节点(名称可能略有差异)。
-
测试文件推送与执行
- 另开终端,推送测试程序:
adb push spi_test /dev
- 回到设备终端,确认文件传输成功:
ls
应看到新文件 spi_test(与 spidev0.0 区分)。
- 赋予 spi_test 执行权限并运行测试:
chmod 777 spi_test
./spi_test
预期结果:
生成 writebuf(发送数据)和 readbuf(接收数据)文件。
- 验证数据传输
对比两个文件内容:
diff writebuf readbuf
预期结果:
无输出(文件完全一致),表示 SPI 传输成功且无丢包。
-
UART
引脚分布
下图是 RUBIK Pi 3 40-pin 连接器的引脚默认功能,其中大部分引脚和开源开发板的40-pin连接器引脚的默认功能兼容。
8 号和 10 号引脚默认已设置配为 UART,设备节点为 /dev/ttyHS3。
下表是 40-pin 连接器支持的所有功能,图中蓝色字体表明默认功能。
使用 C 语言程序 UART 通信
-
以下代码示例,使用 UART 进行数据收发通信:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <getopt.h>
#include <string.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define MAX_SIZE 1024
static int bps_speed = 115200;
static const char *device = "/dev/ttyHS1";
static int data_bits = 8;
static int stop_bits = 1;
static int g_parity = 'N';
static char *type = "read";
static void pabort(const char *s)
{
perror(s);
abort();
}
int speed_arr[] = {B1152000, B1000000, B921600, B576000, B500000, B460800, B230400, B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300};
int name_arr[] = { 1152000, 1000000, 921600, 576000, 500000, 460800, 230400, 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300};
// 设置波特率
void set_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for (i = 0; i < ARRAY_SIZE(speed_arr); i++)
{
if (speed == name_arr[i])
{
// 缓冲区里的数据都废弃
tcflush(fd, TCIOFLUSH);
/* 设置串口的波特率 */
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
pabort("tcsetattr fd");
return;
}
tcflush(fd, TCIOFLUSH);
}
pabort("error speed!");
}
int set_Parity(int fd, int databits, int stopbits, int parity)
{
char cmd_fileclear[40] = {0};
// sprintf(cmd_fileclear, ": > %s", device);
// system(cmd_fileclear);
// memset(cmd_fileclear, 0, ARRAY_SIZE(cmd_fileclear));
sprintf(cmd_fileclear, "stty -F %s -echo", device);
system(cmd_fileclear);
memset(cmd_fileclear, 0, ARRAY_SIZE(cmd_fileclear));
// printf("%s\n", cmd_fileclear);
struct termios options;
if (tcgetattr(fd, &options) != 0)
{
pabort("SetupSerial 1");
}
options.c_cflag &= ~CSIZE;
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
pabort("Unsupported data sizen");
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
pabort("Unsupported parityn");
}
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
pabort("Unsupported stop bits");
}
/* Set input parity option */
if (parity != 'n' && parity != 'N')
options.c_iflag |= INPCK;
options.c_cc[VTIME] = 150; // 15 seconds
options.c_cc[VMIN] = 0;
tcflush(fd, TCIFLUSH); /* Update the options and do it NOW */
if (tcsetattr(fd, TCSANOW, &options) != 0)
{
pabort("SetupSerial 3");
}
return 0;
}
static void print_usage(const char *prog) //If the parameter is wrong, print the help information
{
printf("Usage: %s [-DsbdlHOLC3t]\n", prog);
puts(" -D --device device to use (default /dev/ttyHS3)\n"
" -S --speed bps\n"
" -d --databits \n"
" -s --stopbits \n"
" -p --parity \n"
" -T --type read or write\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1)
{
static const struct option lopts[] = {
//Parameter command table
{"device", 1, 0, 'D'},
{"speed", 1, 0, 'S'},
{"databits", 1, 0, 'd'},
{"stopbits", 1, 0, 's'},
{"parity", 1, 0, 'p'},
{"type", 1, 0, 'T'},
{NULL, 0, 0, 0},
};
int c;
c = getopt_long(argc, argv, "D:S:d:s:p:T:", lopts, NULL);
if (c == -1)
break;
switch (c)
{
case 'D':
device = optarg;
break;
case 'S':
bps_speed = atoi(optarg);
break;
case 'd':
data_bits = atoi(optarg);
break;
case 's':
stop_bits = atoi(optarg);
break;
case 'p':
g_parity = atoi(optarg);
break;
case 'T':
type = optarg;
break;
default:
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
char string1[] = "hello_world\n";
char string2[] = "hello_world_abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz\n";
char *string = string2;
parse_opts(argc, argv);
puts(device);
fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
// 设置波特率
set_speed(fd, bps_speed);
set_Parity(fd, data_bits, stop_bits, g_parity);
close(fd);
fd = open(device, O_RDWR);
int nread = 0,nwrite = 0;
char buff[MAX_SIZE] = {0};
int num = 0;
char send_msg[200] = {0};
printf("%d %d-%d-%c\n", bps_speed, data_bits, stop_bits, g_parity);
printf("type:%s\n", strcmp(type, "read") == 0 ? "read" : "write");
memset(send_msg, 0, ARRAY_SIZE(send_msg));
FILE *read_file;
while (1)
{
if (strcmp(type, "read") == 0)
{
// tcflush(fd, TCIOFLUSH);
nread = read(fd, buff, MAX_SIZE - 2);
if (nread > 1)
{
printf("(Len %d):", nread);
buff[nread] = '\n';
buff[nread + 1] = '\0';
printf("read %s", buff);
if (nread < strlen(string))
{
read_file = fopen("read_file","aw+");
fwrite(buff, strlen(buff), 1, read_file);
fclose(read_file);
}
memset(buff, 0, ARRAY_SIZE(buff));
num++;
}
nread = 0;
}
else
{
sprintf(send_msg, "%d%s", num, string);
nwrite = write(fd, send_msg, strlen(send_msg));
printf("Send test data---%d---%s\n", nwrite, send_msg);
num++;
sleep(1);
}
}
close(fd);
return ret;
} -
编译程序:
-
交叉编译
aarch64-linux-gnu-gcc {filename} -o {输出filename} -static
-
使用交叉编译,需要将编译出的产物 uart 传输到 RUBIK Pi 3 中,如果使用 ADB 传输,命令如下:
adb push uart /opt
-
-
将 8 号引脚和 10 号引脚使用杜邦线短接,验证串口通信,如下图所示
-
测试读功能(接收数据)
- 赋权并启动监听终端
chmod 777 uart_test
./uart -D /dev/ttyHS1- 另开终端发送数据
./uart -D /dev/ttyHS1 -T write
- 另开终端 B 监听数据
./uart -D /dev/ttyHS1
验证:
第一个终端会显示发送的数据,确认数据接收正常。
-
测试写功能(发送数据)
- 在终端 A 发送数据
./uart -D /dev/ttyHS1 -T write
- 另开终端 B 监听数据
./uart -D /dev/ttyHS1
验证:
终端 B 会显示终端 A 发送的数据,确认数据发送正常。
-
USB
RUBIK Pi 3 拥有 4 个 USB 口:
-
两个 USB 3.0 口,只能作为主机模式使用,如下图 7。
-
一个 USB 2.0 口,可以作为主机或设备模式使用,如下图 6。
-
一个 USB 3.1 Gen 1 口,可以作为主机或设备模式,以及 DP 显示使用,如下图 5。
USB 2.0 Type-A 接口
本小节介绍如何通过 USB 2.0 Type-A接口,将 RUBIK Pi 3 配置为 USB 大容量设备(即模拟成 U 盘),来验证安卓 13 设备的 USB Gadget 功能。
权限限制:ADB shell 权限不足以配置 USB Gadget,需使用串口控制台执行所有操作。
请使用 USB 数据线将设备与主机 PC 连接,然后进行以下步骤:
设备模式验证
按以下步骤将设备配置为 USB 大容量存储设备:
-
进入串口控制 台:
-
配置 USB Gadget:
- 进入 USB Gadget 配置目录:
cd /config/usb_gadget/g2
- 创建大容量存储功能:
mkdir functions/mass_storage.0
- 在
/sdcard
创建 2GB ISO 文件:
dd if=/dev/zero of=/sdcard/test.iso bs=1M count=2048
mkfs.ext4 /sdcard/test.iso注意ISO 文件位置:
ISO 文件必须创建在
/sdcard
目录下。其他目录会导致文件访问错误。错误示例(在非
/sdcard
目录创建):-
Ubuntu dmesg 日志显示多次读取错误:
[881331.266781] sd 5:0:0:0: [sdd] tag#0 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE [881331.266782] sd 5:0:0:0: [sdd] tag#0 Sense Key : Medium Error [current] [881331.266783] sd 5:0:0:0: [sdd] tag#0 Add. Sense: Unrecovered read error [881331.287885] sdd: unable to read partition table
-
原因:USB Gadget 框架无法从非
/sdcard
目录读取 ISO 文件,可能是文件系统权限或 SELinux 限制。 -
解决方法:始终使用
/sdcard/test.iso
创建 ISO 文件。
- 将 ISO 文件关联到大容量存储功能:
echo "/sdcard/test.iso" > functions/mass_storage.0/lun.0/file
- 通过创建符号链接,将
mass_storage.0
功能绑定到配置b.1
中。当该配置被激活时,设备会通过 USB 向主机提供大容量存储服务。
ln -s functions/mass_storage.0/ configs/b.1/f1
- 启用 USB 设备控制器(UDC):
echo 8c00000.dwc3 > UDC
-
切换到外设模式:
- 设置 USB 模式为外设:
echo peripheral > /sys/devices/platform/soc/8c00000.hsusb/mode
-
在主机 PC 上验证:
-
在主机 PC 上运行
lsusb
命令, 确认设备已正确连接: -
预期输出:设备显示为
idVendor=0000
,idProduct=0000
,Manufacturer=QUALCOMM
,Product=YUPIKP-IOT-IDP _SN:9F034665
。 -
运行
dmesg
命令,验证设备信息。预期输出如下:[881331.241987] sd 5:0:0:0: [sdd] 4194304 512-byte logical blocks: (2.15 GB/2.00 GiB)
设备在主机 PC 上显示为 2.1 GB USB 磁盘,可访问和挂载(例如 Ubuntu 上的
/dev/sdd
)。注意错误分析:
-
症状:主机 PC 检测到 USB 磁盘但无法读取,
dmesg
显示Medium Error
和Unrecovered read error
。 -
原因:ISO文件创建在非
/sdcard
目录,导致 USB Gadget 框架无法访问。 -
解决方法:在
/sdcard
重新创建 ISO 文件并重复配置步骤。
-
-
切换到主机模式
-
从设备上拔下 USB 线。
-
运 行以下命令,设置 USB 控制器为主机模式:
echo host > /sys/devices/platform/soc/8c00000.hsusb/mode
-
验证:
- 将 USB 设备(例如 U 盘)连接到 USB 2.0 TypeA 端口。
备注USB 2.0 Type-A 端口开机默认为主机模式。请确保使用正确端口进行主机模式验证。
- 运行以下命令,设备可检测连接到 USB Type-A 端口的外部 USB 设备(例如 U 盘):
ls /dev/sd*
USB 3.1 Type-C 接口
Type-C 接口可自动完成主机和设备模式的切换。
-
当 Type-C 接入 PC 时自动切换为设备模式。
-
接入 OTG 线时自动切换为主机模式。
-
接入 DP 显示器时,自动输出 DP 视频信号。
USB 调试
本节提供有关获取调试日志的各种方法的信息。调试方式有 regdumps
、调试 ftraces
、 configfs
节点等。在调试与进入/退出低功耗模式、SMMU 故障、无时钟访问相关的问题时,可通过上述日志查看事件和控制器状态的详细信息。
USB 跟踪
使用 debugfs
跟踪可以更加深入地了解 USB 线上发生的每一个事务。如需查看跟踪列表,可运行以下命令。
确保已挂载
debugfs
。使用
mount | grep /sys/kernel/debug
检查是否已经挂载。如果尚未挂载,可运行以下命令来挂载
debugfs
:
mount -t debugfs none /sys/kernel/debug
ls /sys/kernel/debug/tracing/events/dwc3
以下是可用于验证 xHCI/gadget 协议栈/USB Type-C 连接器系统软件接口 (UCSI) 中的数据传输的跟踪。
dwc3_alloc_request dwc3_event dwc3_gadget_generic_cmd enable
dwc3_complete_trb dwc3_free_request dwc3_gadget_giveback filter
dwc3_ctrl_req dwc3_gadget_ep_cmd dwc3_prepare_trb
dwc3_ep_dequeue dwc3_gadget_ep_disable dwc3_readl
dwc3_ep_queue dwc3_gadget_ep_enable dwc3_writel
要列出 xHCI/主机控制器驱动程序 (HCD) 中的跟踪数据,请运行以下命令。
ls /sys/kernel/debug/tracing/events/xhci-hcd
以下是可用于验证 xHCI/HCD 中数据传输的跟踪。
enable xhci_handle_cmd_config_ep
filter xhci_handle_cmd_disable_slot
xhci_add_endpoint xhci_handle_cmd_reset_dev
xhci_address_ctrl_ctx xhci_handle_cmd_reset_ep
xhci_address_ctx xhci_handle_cmd_set_deq
xhci_alloc_dev xhci_handle_cmd_set_deq_ep
xhci_alloc_virt_device xhci_handle_cmd_stop_ep
xhci_configure_endpoint xhci_handle_command
xhci_configure_endpoint_ctrl_ctx xhci_handle_event
xhci_dbc_alloc_request xhci_handle_port_status
xhci_dbc_free_request xhci_handle_transfer
xhci_dbc_gadget_ep_queue xhci_hub_status_data
xhci_dbc_giveback_request xhci_inc_deq
xhci_dbc_handle_event xhci_inc_enq
xhci_dbc_handle_transfer xhci_queue_trb
xhci_dbc_queue_request xhci_ring_alloc
xhci_dbg_address xhci_ring_ep_doorbell
xhci_dbg_cancel_urb xhci_ring_expansion
xhci_dbg_context_change xhci_ring_free
xhci_dbg_init xhci_ring_host_doorbell
xhci_dbg_quirks xhci_setup_addressable_virt_device
xhci_dbg_reset_ep xhci_setup_device
xhci_dbg_ring_expansion xhci_setup_device_slot
xhci_discover_or_reset_device xhci_stop_device
xhci_free_dev xhci_urb_dequeue
xhci_free_virt_device xhci_urb_enqueue
xhci_get_port_status xhci_urb_giveback
xhci_handle_cmd_addr_dev
请运行以下命令,以便列出 USB 视频类 (UVC) gadget 驱动程序的可用事件。
ls /sys/kernel/debug/tracing/events/gadget
随即显示以下输出。
enable usb_gadget_activate
filter usb_gadget_clear_selfpowered
usb_ep_alloc_request usb_gadget_connect
usb_ep_clear_halt usb_gadget_deactivate
usb_ep_dequeue usb_gadget_disconnect
usb_ep_disable usb_gadget_frame_number
usb_ep_enable usb_gadget_giveback_request
usb_ep_fifo_flush usb_gadget_set_remote_wakeup
usb_ep_fifo_status usb_gadget_set_selfpowered
usb_ep_free_request usb_gadget_vbus_connect
usb_ep_queue usb_gadget_vbus_disconnect
usb_ep_set_halt usb_gadget_vbus_draw
usb_ep_set_maxpacket_limit usb_gadget_wakeup
usb_ep_set_wedge
如需列出 UCSI 驱动程序中的可用事件,可运行以下命令。
ls /sys/kernel/debug/tracing/events/ucsi
随即显示以下输出。
enable ucsi_connector_change ucsi_register_port ucsi_run_command
filter ucsi_register_altmode ucsi_reset_ppm
主机 sysfs 查询
要查看总线详细信息,请运行以下命令。
lsusb
示例输出:
Bus 005 Device 001: ID 1d6b:0003
Bus 003 Device 001: ID 1d6b:0003
Bus 001 Device 001: ID 1d6b:0002
Bus 006 Device 001: ID 1d6b:0002
Bus 004 Device 001: ID 1d6b:0002
Bus 007 Device 003: ID 0b95:1790
Bus 002 Device 001: ID 1d6b:0002
Bus 007 Device 001: ID 1d6b:0003
请运行以下命令,以便列出当前目录的内容。
cd /sys/bus/usb/devices/
ls
示例输出:
1-0:1.0 3-0:1.0 5-0:1.0 7-0:1.0 7-3:1.0 usb2 usb4 usb6
2-0:1.0 4-0:1.0 6-0:1.0 7-3 usb1 usb3 usb5 usb7
要查看有关 USB 设备的详细信息,请运行以下命令。
cat /sys/kernel/debug/usb/devices
示例输出:
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 1
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1
P: Vendor=1d6b ProdID=0002 Rev= 5.04
S: Manufacturer=Linux 5.4.233-qgki-debug dummy_hcd
S: Product=Dummy host controller
S: SerialNumber=dummy_hcd.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 1
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1
P: Vendor=1d6b ProdID=0002 Rev= 5.04
S: Manufacturer=Linux 5.4.233-qgki-debug xhci-hcd
S: Product=xHCI Host Controller
S: SerialNumber=xhci-hcd.2.auto
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=03 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=5000 MxCh= 0
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 3.00 Cls=09(hub ) Sub=00 Prot=03 MxPS= 9 #Cfgs= 1
P: Vendor=1d6b ProdID=0003 Rev= 5.04
S: Manufacturer=Linux 5.4.233-qgki-debug xhci-hcd
S: Product=xHCI Host Controller
S: SerialNumber=xhci-hcd.2.auto
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=(none)
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=04 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 1
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1
P: Vendor=1d6b ProdID=0002 Rev= 5.04
S: Manufacturer=Linux 5.4.233-qgki-debug xhci-hcd
S: Product=xHCI Host Controller
S: SerialNumber=xhci-hcd.3.auto
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=05 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=5000 MxCh= 1
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 3.00 Cls=09(hub ) Sub=00 Prot=03 MxPS= 9 #Cfgs= 1
P: Vendor=1d6b ProdID=0003 Rev= 5.04
S: Manufacturer=Linux 5.4.233-qgki-debug xhci-hcd
S: Product=xHCI Host Controller
S: SerialNumber=xhci-hcd.3.auto
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=06 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 4
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1
P: Vendor=1d6b ProdID=0002 Rev= 5.04
S: Manufacturer=Linux 5.4.233-qgki-debug xhci-hcd
S: Product=xHCI Host Controller
S: SerialNumber=0000:01:00.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=07 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=5000 MxCh= 4
B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0
D: Ver= 3.00 Cls=09(hub ) Sub=00 Prot=03 MxPS= 9 #Cfgs= 1
P: Vendor=1d6b ProdID=0003 Rev= 5.04
S: Manufacturer=Linux 5.4.233-qgki-debug xhci-hcd
S: Product=xHCI Host Controller
S: SerialNumber=0000:01:00.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=07 Lev=01 Prnt=01 Port=02 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0
D: Ver= 3.20 Cls=ff(vend.) Sub=ff Prot=00 MxPS= 9 #Cfgs= 1
P: Vendor=0b95 ProdID=1790 Rev= 2.00
S: Manufacturer=ASIX
S: Product=AX88179B
S: SerialNumber=00536055
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=184mA
I:* If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=ff Prot=00 Driver=ax_usb_nic
E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
E: Ad=82(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=05(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms