跳到主要内容

40 Pin GPIO 接口

瑞莎 CM4 IO 板板载 40 Pin GPIO 接口,为硬件扩展提供了高度灵活的接口支持。

我们可以通过 40-Pin GPIO 接口连接各类传感器、执行器、通信模块、显示屏以及其他嵌入式外设,从而快速实现物联网(IoT)、机器人控制、工业自动化等领域的原型开发与功能验证。

危险

使用 40-Pin GPIO 接口时,请注意引脚和外设的接线,请确保引脚连接正确,不当操作可能导致设备硬件损坏!

GPIO 功能

将瑞莎 CM4 安装到瑞莎 CM4 IO 板上,我们可以通过瑞莎 CM4 IO 板上的 40 Pin GPIO 接口连接外设,支持 UART、I2C、SPI、PWM 等功能。

Function7Function6Function5Function4Function3Function2Function1Pin#Pin#Function1Function2Function3Function4Function5Function6Function7
3V3
1
2
5V
PDM0_SDI1_M2I2C8_SDA_M1UART2_RX_M0GPIO1_C7_d
3
4
5V
PDM0_SDI0_M2I2C8_SCL_M1UART2_TX_M0GPIO1_C6_d
5
6
GND
SPI2_CSN0_M1I2C6_SDA_M1UART4_CTSN_M1GPIO1_C3_u
7
8
GPIO0_D4_uUART0_TX_M0_DEBUG
GND
9
10
GPIO0_D5_uUART0_RX_M0_DEBUG
UART2_RTSN_M0SPI2_MOSI_M1UART4_TX_M1GPIO1_C4_d
11
12
GPIO1_D1_dUART10_RX_M1I3C0_SDA_PU_M1SAI2_SCLK_M0
PWM1_CH0_M2GPIO2_C0_d
13
14
GND
UART2_CTSN_M0SPI2_MISO_M1UART4_RX_M1GPIO1_C5_d
15
16
GPIO2_B6_dUART7_TX_M0I2C8_SCL_M2
3V3
17
18
GPIO2_B7_dUART7_RX_M0I2C8_SDA_M2
SAI3_LRCK_M1PWM1_CH1_M1SPI1_MOSI_M0I2C9_SCL_M1GPIO1_B5_d
19
20
GND
SAI3_SDO_M1SPI1_MISO_M0UART3_CTSN_M2GPIO1_B6_d
21
22
GPIO2_D7_dPWM2_CH7_M2
SAI3_SCLK_M1PWM1_CH0_M1SPI1_CLK_M0I2C9_SDA_M1GPIO1_B4_d
23
24
GPIO1_B7_dSPI1_CSN0_M0SAI3_SDI_M1UART3_RTSN_M2
GND
25
26
GPIO1_C0_dUART3_TX_M2SPI1_CSN1_M0PWM0_CH0_M1PDM0_SDI2_M2
CAN1_RX_M1SAI4_SCLK_M2PWM2_CH3_M1I2C6_SDA_M3GPIO4_C7_d
27
28
GPIO4_C6_dI2C6_SCL_M3PWM2_CH2_M1SAI4_SDI_M2CAN1_TX_M1
CAN1_TX_M3GPIO3_A2_d
29
30
GND
PDM0_CLK0_M2SAI3_MCLK_M1UART3_RX_M2GPIO1_C1_d
31
32
GPIO1_D5_dUART10_CTSN_M1I2C5_SDA_M1SPI2_CLK_M1PDM0_CLK1_M2
UART2_RTSN_M0PWM1_CH2_M1SPI2_CSN1_M1I2C6_SCL_M1UART4_RTSN_M1GPIO1_C2_u
33
34
GND
SAI2_LRCK_M0PWM1_CH3_M1I3C0_SCL_M1GPIO1_D2_d
35
36
GPIO1_D4_dUART10_RTSN_M1I2C5_SCL_M1SAI2_MCLK_M0PDM0_SDI3_M2
CAN1_RX_M3GPIO3_A3_d
37
38
GPIO1_D3_dI3C0_SDA_M1PWM1_CH4_M1SAI2_SDI_M0
GND
39
40
GPIO1_D0_dUART10_TX_M1SAI2_SDO_M0

GPIO 使用

通过板载的 40 Pin GPIO 接口,演示常见的 GPIO 使用方法。

GPIO 输入

硬件准备

  • 主板: 瑞莎 CM4
  • 1 根母对母杜邦线

硬件连接

使用 GPIO_Pin_3 进行 GPIO 输入测试,你可以使用母对母杜邦线将 PIN_3 引脚引出。

读取测试

使用 gpiofind 命令找到 GPIO 引脚对应的设备节点,然后使用 gpioget 命令读取引脚状态。

radxa@device$
gpioget $(gpiofind PIN_3)

我们可以将 PIN3 引出的杜邦线连接到 GND 或者 3.3 V,然后读取引脚状态。

终端输出类似信息:我将引脚连接到 GND 输出的结果(终端输出 0 表示引脚为低电平,输出 1 表示引脚为高电平)。

0

GPIO 输出

硬件准备

  • 主板: 瑞莎 CM4
  • 1 根母对母杜邦线

硬件连接

使用 GPIO_Pin_3 进行 GPIO 输出测试,你可以使用母对母杜邦线将 PIN_3 引脚引出。

输出测试

使用 gpiofind 命令找到 GPIO 引脚对应的设备节点,然后使用 gpioset 命令设置引脚状态。

radxa@device$
# 输出高电平
gpioset -m signal $(gpiofind PIN_3)=1
# 输出低电平
gpioset -m signal $(gpiofind PIN_3)=0
  • -m signal:设置引脚为信号模式
  • $(gpiofind PIN_3):找到 GPIO 引脚对应的设备节点
  • =1:设置引脚状态为高电平

您可以通过万用表测量引脚电压判断引脚状态,或者将引脚连接到其他可控制引脚,来读取引脚状态。

UART 使用

UART(通用异步收发传输器) 是一种广泛使用的串行通信协议,用于在嵌入式系统、计算机和外设之间进行异步串行数据传输。

硬件准备

  • 主板: 瑞莎 CM4
  • 1 根母对母杜邦线

硬件连接

使用 UART4-M1 对应的引脚进行回环测试,我们需要使用母对母杜邦线将 PIN_11 (UART4_TX_M1) 和 PIN_15 (UART4_RX_M1) 进行短接。

提示

我们是通过回环测试 UART 功能,我们只需要连接 TX 和 RX 引脚即可。

若后续外接其他串口模块,最少还需要连接 GND 引脚,确保通讯双方共地。

回环测试

  1. 使能 UART4-M1

使用 rsetup 工具使能 UART4-M1: Overlays --> Manage Overlays --> 勾选 Enable UART4-M1 功能。

使能 UART4-M1 后,需要重启系统才能生效。

提示

有关 rsetup 工具的使用,可以参考 Rsetup 使用 教程

  1. 设置串口权限

我们使能的是 UART4-M1,对应的设备节点是 /dev/ttyS4,我们需要设置串口权限,确保当前用户有权限访问该设备节点。

radxa@device$
sudo chmod 777 /dev/ttyS4
  1. 设置串口参数

使用下面命令设置串口通讯参数:

radxa@device$
sudo stty -F /dev/ttyS4 115200 cs8 -parenb -cstopb -echo
  • 115200 :波特率
  • cs8:8 位数据位
  • -parenb:无校验位
  • -cstopb:1 停止位
  • -echo:禁用回显
  1. 串口发送数据

打开一个终端,在终端输入下面命令:

radxa@device$
while true; do echo "UART4 test" > /dev/ttyS4; sleep 1; done
  1. 串口接收数据

再开一个终端,在终端输入下面命令:

radxa@device$
cat /dev/ttyS4

若回环测试正常,我们可以在串口接收数据的终端看到每隔 1 秒输出一次 UART4 test 的信息。

I2C 使用

I2C 是一种广泛使用的同步串行通信协议,由飞利浦(现恩智浦)开发,主要用于短距离芯片间通信。

硬件准备

  • 主板: 瑞莎 CM4
  • 4 根母对母杜邦线
  • OLED 屏幕 (I2C 通讯)

硬件连接

使用 4 根母对母杜邦线连接 瑞莎 CM4 对应的引脚和 OLED 屏幕。

瑞莎 CM4OLED 屏幕
PIN_1 (3.3V)VCC
PIN_3 (I2C8_SDA_M1)SDA
PIN_5 (I2C8_SCL_M1)SCL
PIN_9 (GND)GND

I2C 测试

  1. 使能 I2C8-M1

使用 rsetup 工具使能 I2C8-M1: Overlays --> Manage Overlays --> 勾选 Enable I2C8-M1 功能。

使能 I2C8-M1 后,需要重启系统才能生效。

  1. 查询 I2C 总线设备

使用 i2cdetect 命令可以查询 I2C 总线设备相关信息。

  • 安装依赖

打开终端,输入下面命令安装 Python3-periphery 和 i2c-tools。

radxa@device$
sudo apt update
sudo apt install python3-periphery i2c-tools
  • 查看 I2C 总线

使用 i2cdetect 命令查看设备所有 I2C 总线。

radxa@device$
sudo i2cdetect -l

终端输出类似信息:

i2c-1   i2c             rk3x-i2c                                I2C adapter
i2c-2 i2c rk3x-i2c I2C adapter
i2c-3 i2c rk3x-i2c I2C adapter
i2c-6 i2c rk3x-i2c I2C adapter
i2c-8 i2c rk3x-i2c I2C adapter
i2c-10 i2c ddc I2C adapter
  • 查看 I2C 总线设备

使用 i2cdetect 命令查看指定 I2C 总线上挂载的设备:其中 * 可以替换成数字,例如填写 8 就表示查看 i2c-8 上挂载的设备。

radxa@device$
sudo i2cdetect -y -r *
# 示例
sudo i2cdetect -y -r 8

终端输出示例:其中 OLED 对应的 I2C 地址是 0x3c。

radxa@device$
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
  1. I2C 通讯测试
  • Python 脚本

新建名称为 oled_test.py 的 Python 脚本,脚本内容如下:其中 I2C_ADDR 是 OLED 对应的 I2C 地址,I2C_BUS 是 OLED 挂载的 I2C 总线,请根据实际情况修改。

oled_test.py

from periphery import I2C
import time

I2C_ADDR = 0x3c
I2C_BUS = "/dev/i2c-8"

i2c = I2C(I2C_BUS)

init_cmds = [
0xAE, # Display off
0x00, # Set lower column address
0x10, # Set higher column address
0x40, # Set display start line
0xB0, # Set page address
0x81, # Set contrast control
0xCF,
0xA1, # Set segment remap
0xA6, # Normal display
0xA8, # Set multiplex ratio
0x3F,
0xC8, # Set COM output scan direction
0xD3, # Set display offset
0x00,
0xD5, # Set display clock divide ratio/oscillator frequency
0x80,
0xD9, # Set pre-charge period
0xF1,
0xDA, # Set COM pins hardware configuration
0x12,
0xDB, # Set VCOMH deselect level
0x40,
0x8D, # Enable charge pump regulator
0x14,
0xAF # Display on
]

for cmd in init_cmds:
i2c.transfer(I2C_ADDR, [I2C.Message([0x00, cmd])])

def oled*clear():
for page in range(8):
i2c.transfer(I2C_ADDR, [I2C.Message([0x00, 0xB0 + page])])
i2c.transfer(I2C_ADDR, [I2C.Message([0x00, 0x00])])
i2c.transfer(I2C_ADDR, [I2C.Message([0x00, 0x10])])
for * in range(128):
i2c.transfer(I2C_ADDR, [I2C.Message([0x40, 0x00])])

char_map = {
"H": [0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F,],
"R": [0x00, 0x7F, 0x09, 0x19, 0x29, 0x46],
"e": [0x00, 0x38, 0x54, 0x54, 0x54, 0x18],
"l": [0x00, 0x00, 0x41, 0x7F, 0x40, 0x00],
"o": [0x00, 0x38, 0x44, 0x44, 0x44, 0x38],
"r": [0x00, 0x7C, 0x08, 0x04, 0x04, 0x08],
"a": [0x00, 0x20, 0x54, 0x54, 0x54, 0x78],
"d": [0x00, 0x38, 0x44, 0x44, 0x48, 0x7F],
"x": [0x00, 0x44, 0x28, 0x10, 0x28, 0x44]
}

def string_to_bytes(string):
bytes_list = []
for char in string:
bytes_list.extend(char_map.get(char, [0x00] \* 4))
bytes_list.append(0x00)
return bytes_list

oled_clear()
hello_world_bytes = string_to_bytes("Hello Radxa")
i2c.transfer(I2C_ADDR, [I2C.Message([0x00, 0xB0])])
i2c.transfer(I2C_ADDR, [I2C.Message([0x00, 0x00])])
i2c.transfer(I2C_ADDR, [I2C.Message([0x00, 0x10])])
for byte in hello_world_bytes:
i2c.transfer(I2C_ADDR, [I2C.Message([0x40, byte])])

i2c.close()

  • 运行脚本

打开终端,输入下面命令运行脚本:

radxa@device$
python3 oled_test.py

若一切正常,我们可以在 OLED 屏幕上看到 Hello Radxa 的输出。

SPI 使用

SPI(串行外设接口)是一种高速、全双工、同步串行通信协议,由 Motorola(现为 NXP )开发,主要用于短距离芯片间通信,常见于传感器、存储器(如 Flash )、显示屏等设备与微控制器之间的数据传输。

硬件准备

  • 主板: 瑞莎 CM4
  • 1 根母对母杜邦线

硬件连接

使用 SPI1-M0 对应的引脚进行回环测试,我们需要使用母对母杜邦线将 PIN_19 (SPI1_MOSI_M0) 和 PIN_21 (SPI1_MISO_M0) 进行短接。

提示

我们是通过回环测试 SPI 功能,我们只需要连接 MOSI 和 MISO 引脚即可。

若后续外接其他 SPI 模块,最少还需要连接 GND 引脚,确保通讯双方共地。

回环测试

  1. 使能 SPI1-M0

使用 rsetup 工具使能 SPI1-M0: Overlays --> Manage Overlays --> 勾选 Enable spidev on SPI1-M0 over CS0 功能。

使能 SPI1-M0 后,需要重启系统才能生效。

  1. 查询 SPI 总线设备

使用 ls /dev/spidev* 命令可以查询 SPI 总线设备相关信息。

radxa@device$
ls /dev/spidev\*

终端输出类似信息:

/dev/spidev1.0
  1. SPI 通讯测试
  • C 代码

新建名称为 spidev_test.c 的 C 代码,代码内容如下:其中 static const char *device = "/dev/spidev1.0"; 是 SPI 总线设备节点,请根据实际情况修改。

spidev_test.c

/\*

- SPI testing utility (using spidev driver)
-
- Copyright (c) 2007 MontaVista Software, Inc.
- Copyright (c) 2007 Anton Vorontsov <[email protected]>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License.
-
- Cross-compile with cross-gcc -I/path/to/cross-kernel/include
\*/

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static void pabort(const char \*s)
{
perror(s);
abort();
}

static const char *device = "/dev/spidev1.0";
static uint32_t mode;
static uint8_t bits = 8;
static char *input_file;
static char \*output_file;
static uint32_t speed = 500000;
static uint16_t delay;
static int verbose;

uint8_t default_tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0x0D,
};

uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
char \*input_tx;

static void hex_dump(const void *src, size_t length, size_t line_size,
char *prefix)
{
int i = 0;
const unsigned char *address = src;
const unsigned char *line = address;
unsigned char c;

printf("%s | ", prefix);
while (length-- > 0) {
printf("%02X ", *address++);
if (!(++i % line_size) || (length == 0 && i % line_size)) {
if (length == 0) {
while (i++ % line_size)
printf("__ ");
}
printf(" | "); /* right close */
while (line < address) {
c = *line++;
printf("%c", (c < 33 || c == 255) ? 0x2E : c);
}
printf("\n");
if (length > 0)
printf("%s | ", prefix);
}
}

}

/\*

- Unescape - process hexadecimal escape character
- converts shell input "\x23" -> 0x23
_/
static int unescape(char _\_dst, char *\_src)
{
int ret = 0;
int match;
char *src = \_src;
char \*dst = \_dst;
unsigned int ch;

while (*src) {
if (*src == '\\' && *(src+1) == 'x') {
match = sscanf(src + 2, "%2x", &ch);
if (!match)
pabort("malformed input string");

src += 4;
*dst++ = (unsigned char)ch;
} else {
*dst++ = *src++;
}
ret++;
}
return ret;

}

static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
int ret;
int out_fd;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};

if (mode & SPI_TX_QUAD)
tr.tx_nbits = 4;
else if (mode & SPI_TX_DUAL)
tr.tx_nbits = 2;
if (mode & SPI_RX_QUAD)
tr.rx_nbits = 4;
else if (mode & SPI_RX_DUAL)
tr.rx_nbits = 2;
if (!(mode & SPI_LOOP)) {
if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
tr.rx_buf = 0;
else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
tr.tx_buf = 0;
}

ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");

if (verbose)
hex_dump(tx, len, 32, "TX");

if (output_file) {
out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (out_fd < 0)
pabort("could not open output file");

ret = write(out_fd, rx, len);
if (ret != len)
pabort("not all bytes written to output file");

close(out_fd);
}

if (verbose || !output_file)
hex_dump(rx, len, 32, "RX");

}

static void print_usage(const char \*prog)
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev0.0)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\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"
" -v --verbose Verbose (show tx buffer)\n"
" -p Send data (e.g. \"1234\\xde\\xad\")\n"
" -N --no-cs no chip select\n"
" -R --ready slave pulls low to pause\n"
" -2 --dual dual transfer\n"
" -4 --quad quad transfer\n");
exit(1);
}

static void parse_opts(int argc, char \*argv[])
{
while (1) {
static const struct option lopts[] = {
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "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' },
{ "dual", 0, 0, '2' },
{ "verbose", 0, 0, 'v' },
{ "quad", 0, 0, '4' },
{ NULL, 0, 0, 0 },
};
int c;

c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v",
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 'i':
input_file = optarg;
break;
case 'o':
output_file = 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 'v':
verbose = 1;
break;
case 'R':
mode |= SPI_READY;
break;
case 'p':
input_tx = optarg;
break;
case '2':
mode |= SPI_TX_DUAL;
break;
case '4':
mode |= SPI_TX_QUAD;
break;
default:
print_usage(argv[0]);
break;
}
}
if (mode & SPI_LOOP) {
if (mode & SPI_TX_DUAL)
mode |= SPI_RX_DUAL;
if (mode & SPI_TX_QUAD)
mode |= SPI_RX_QUAD;
}

}

static void transfer_escaped_string(int fd, char *str)
{
size_t size = strlen(str);
uint8_t *tx;
uint8_t \*rx;

tx = malloc(size);
if (!tx)
pabort("can't allocate tx buffer");

rx = malloc(size);
if (!rx)
pabort("can't allocate rx buffer");

size = unescape((char *)tx, str);
transfer(fd, tx, rx, size);
free(rx);
free(tx);

}

static void transfer_file(int fd, char *filename)
{
ssize_t bytes;
struct stat sb;
int tx_fd;
uint8_t *tx;
uint8_t \*rx;

if (stat(filename, &sb) == -1)
pabort("can't stat input file");

tx_fd = open(filename, O_RDONLY);
if (tx_fd < 0)
pabort("can't open input file");

tx = malloc(sb.st_size);
if (!tx)
pabort("can't allocate tx buffer");

rx = malloc(sb.st_size);
if (!rx)
pabort("can't allocate rx buffer");

bytes = read(tx_fd, tx, sb.st_size);
if (bytes != sb.st_size)
pabort("failed to read input file");

transfer(fd, tx, rx, sb.st_size);
free(rx);
free(tx);
close(tx_fd);

}

int main(int argc, char \*argv[])
{
int ret = 0;
int fd;

parse_opts(argc, argv);

fd = open(device, O_RDWR);
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: 0x%x\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

if (input_tx && input_file)
pabort("only one of -p and --input may be selected");

if (input_tx)
transfer_escaped_string(fd, input_tx);
else if (input_file)
transfer_file(fd, input_file);
else
transfer(fd, default_tx, default_rx, sizeof(default_tx));

close(fd);

return ret;

}

  • 编译工具

打开终端,输入下面命令安装编译工具:

radxa@device$

sudo apt update
sudo apt install build-essential

  • 编译代码

打开终端,输入下面命令编译代码:

radxa@device$

gcc -o spidev_test spidev_test.c

  • 运行代码

打开终端,输入下面命令运行代码:

radxa@device$

sudo ./spidev_test

若回环测试正常,我们可以在终端看到输出以下信息:


spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 KHz)
RX | FF FF FF FF FF FF 40 00 00 00 00 95 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF F0 0D | ......@....�..................�.