外设与接口
硬件资源图

| 序号 | 接口 | 序号 | 接口 |
|---|---|---|---|
| 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
引脚分布
下图是 魔方派 3 40-pin 连接器的引脚默认功能,其中大部分引脚和开源开发板的40-pin连接器引脚的默认功能兼容。

下表是 40-pin连接器支持的所有功能,图中蓝色字体表明默认功能。

使用 shell 命令控制
在 魔方派 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。
引脚分布
下图是 魔方派 3 40-pin 连接器的引脚默认功能,其中大部分引脚和开源开发板的40-pin连接器引脚的默认功能兼容。

3 号引脚和 5 号引脚默认已设置配为 I2C1。
下表是 40-pin 连接器支持的所有功能,图中蓝色字体表明默认功能。

使用 shell 命令测试
在 魔方派 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 线制串行总线。
引脚分布
下图是 魔方派 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 传输到 魔方派 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
-