跳到主要内容

40-PIN 功能测试

CANBUS

CANBUS 简介

控制器区域网络(CAN总线)是一种车辆总线标准,旨在允许微控制器和设备相互通信。 它是一种基于消息的协议,最初设计用于汽车内部的多路电气布线,以节省铜,但它也可以用于许多其他环境。 对于每个设备,帧中的数据是串行传输的,但如果多个设备同时传输,则最高优先级设备可以继续传输,而其他设备则后退。 帧被所有设备接收,包括发送设备。

准备

  • 两块带有 CANBUS 的 Radxa 开发板
  • 两个 CANBUS 模块

开启 Overlay

请参照设备树配置 启用 CANBUS 相关 Overlay, eg: "Enable CAN1-M0"。

连接

主设备             CAN 模块            CAN 模块            主设备
3.3V <--> VCC VCC <--> VCC
GND <--> GND GND <--> GND
CAN_TX <--> CTX CTX <--> CAN_TX
CAN_RX <--> CRX CRX <--> CAN_RX
CANH <--> CANH
CANL <--> CANL

安装测试工具

sudo apt update
sudo apt-get install can-utils iproute2

检查 CAN 节点

$ ip a
can0: flags=193<UP,RUNNING,NOARP> mtu 72
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 10 (UNSPEC)
RX packets 144 bytes 1152 (1.1 KiB)
RX errors 175 dropped 0 overruns 0 frame 35
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 62

回还测试

一块板子作为发送端,另一块则作为接收端

  • 发送端
$ sudo ip link set can0 down
$ sudo ip link set can0 type can bitrate 1000000 dbitrate 1000000 loopback on fd on
$ sudo ip link set can0 up
$ sudo cansend can0 123#11223344
  • 接收端
$ sudo ip link set can0 down
$ sudo ip link set can0 type can bitrate 1000000 dbitrate 1000000 loopback on fd on
$ sudo ip link set can0 up
$ sudo candump can0
can0 123 [4] 11 22 33 44

开发参考

GPIO

GPIO 简介

通用输入/输出(GPIO)是集成电路或电子电路(如 MCU/MPU)电路板上的非专用数字信号引脚,可用作输入或输出,或同时用作输入和输出,并可由软件控制。

准备

  • 一块 Radxa ROCK 5B+
  • 一个 LED 灯

连接

如图所示,连接 Radxa ROCK 5B+ 的 PIN_27

gpio connection

测试

输入测试

27 接地或者接 3.3V,


  radxa@rock-5b-plus:~$ gpioget gpiochip4 22
  

如果接的是地,该命令输出 0,如果接的是 3.3V,该命令输出 1。

输出测试


  radxa@rock-5b-plus:~$ sudo gpioset -m signal $(sudo gpiofind PIN_27)=0 # 输出低电平, Led 灭
  radxa@rock-5b-plus:~$ sudo gpioset -m signal $(sudo gpiofind PIN_27)=1 # 输出高电平, Led 亮
  
提示

同一个 GPIO 不允许被两个进程同时使用,否则会提示资源占用。 因此,需要手动按下 'Ctrl + C' 终止后,才可以执行第二个命令。

I2C

I2C 简介

I2C(Inter-Integrated Circuit;发音为 "eye-squared-see "或 "eye-two-see"),又称 I2C 或 IIC,是飞利浦半导体公司于 1982 年发明的一种同步、多控制器/多目标(历史上称为主/从)、单端、串行通信总线。

准备

  • 一块 Radxa ROCK 5B+
  • 一个 OLED

开启 Overlay

请参照设备树配置启用 I2C 相关 Overlay, eg: "Enable I2C8-M2"。

连接

按照以下方式连接 Radxa ROCK 5B+ 和 OLED

Radxa ROCK 5B+<-->OLED
PIN_5<-->SCL
PIN_3<-->SDA
PIN_1<-->VCC
PIN_9<-->GND
i2c connection

测试

  • 打开终端,在终端中输入以下命令安装 python3-periphery 的 Python 库

  • 新建一个名为 Oled.py Python 文件,并将以下代码粘贴到该文件中:

Oled.py
from periphery import I2C
import time

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

i2c = I2C(I2C_BUS)

# SSD1306 init_cmds
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()

提示

该脚本仅为示例,需要根据实际修改变量 I2C_BUS

  • 在终端中,执行以下命令进行测试
sudo python3 Oled.py

执行完以上命令后,Oled 会显示 "Hello, Radxa" 的字符

PWM

PWM 简介

脉宽调制(PWM)是一种调制技术,可产生可变宽度的脉冲来表示模拟输入信号的幅度。 对于高振幅信号,输出开关晶体管更多时间处于导通状态,而对于低振幅信号,输出开关晶体管更多时间处于关断状态。

准备

  • 一块 Radxa ROCK 5B+
  • 一个 LED 灯

开启 Overlay

请参照设备树配置启用 PWM 相关 Overlay。

eg: "Enable PWM7_IR_M3"。

连接

如图所示,连接 Radxa ROCK 5B+27

pwm connection

测试

  • 打开终端,在终端中输入以下命令安装 python3-periphery 的 Python 库
sudo apt-get install python3-periphery
  • 新建一个名为 Pwm_led_test.py 的 Python 文件,并将以下代码粘贴到文件中:
Pwm_led_test.py

#!/usr/bin/env python3 # -- encoding: utf-8 --

from periphery import PWM import time

step = 0.05 Range = int(1/0.05)

pwmchip = int(input("pwmchip:")) channel = int(input("channel:"))

pwm = PWM(pwmchip, channel)

try: pwm.frequency = 1e3 pwm.duty_cycle = 0.00 pwm.enable()

while True: for i in range(0,Range): time.sleep(step) pwm.duty_cycle = round(pwm.duty_cycle+step,2)

if pwm.duty_cycle == 1.5: time.sleep(1.5) for i in range(0,Range): time.sleep(step) pwm.duty_cycle = round(pwm.duty_cycle-step,2) except: print("Error !\n")

finally: pwm.duty_cycle = 1.0 pwm.close()

  • 在终端中,执行以下命令进行测试

  radxa@rock-5b-plus:~$ sudo python3 Pwm_led_test.py
  pwmchip:7
  channel:0
  

执行完以上命令后, Led 会有一个渐变的效果(由暗到亮,由亮到暗)。

SPI

SPI 简介

串行外设接口(SPI)是同步串行通信的实际标准(有许多变体),主要用于嵌入式系统中集成电路之间的短距离有线通信。

准备

  • 一块 rock-5b-plus
  • 一根母对母杜邦线

开启 Overlay

请参照设备树配置启用 SPIDEV 相关 Overlay, eg: "Enable spidev on SPI0-M2 over CS0"。

连接

如图所示,短接 1921

spi connection

测试

  • 打开终端,在终端中输入以下命令安装编译工具
sudo apt-get install build-essential
  • 新建一个名为 spidev_test.c 的 C 文件,并将以下代码粘贴到文件中:
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/spidev0.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 FF FF FF FF FF,
0x40 00 00 00 00 95,
0xFF FF FF FF FF FF,
0xFF FF FF FF FF FF,
0xFF FF FF FF FF FF,
0xF0 0D,
};

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;
}
  • 查看新增的 spidev 设备
ls /dev/spidev*
  • 根据具体的 spidev 设备修改 static const char *device = "/dev/spidev0.0"; 这一行

例如,实际的 spidev 是 /dev/spidev1.0, 则 static const char *device = "/dev/spidev1.0";

  • 在终端中,输入以下命令进行编译
gcc spidev_test.c
  • 在终端中,输入以下命令进行测试
sudo ./a.out
  • 检查输出结果

若输出结果和 default_tx 一致(若输出和以下一致),则表示 spi 回环测试成功

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 | ......@......................�.

UART

UART 简介

UART(通用异步接收器/发送器)是一种带有编程功能的微型芯片,用于控制计算机与其连接的串行设备的接口。

准备

  • 一块 rock-5b-plus
  • 两根母对母杜邦线

回环测试

开启 Overlay

请参照设备树配置启用 UART 相关 Overlay, eg: "Enable UART4-M2"。

连接

如图所示,短接 Radxa ROCK 5B+ UART4-M2PIN_23 PIN_19

uart connection

测试

  • 打开终端,在终端中输入以下命令设置串口参数

  radxa@rock-5b-plus:~$ sudo stty -F /dev/ttyS4 speed 115200 cs8 -parenb -cstopb  -echo
  
  • 在终端中,输入一下命令循环发送数据

  radxa@rock-5b-plus:~$ while true ;do echo "sss" > /dev/ttyS4; sleep 1; done;
  
  • 新建一个终端,输入以下命令进行接收数据

  radxa@rock-5b-plus:~$ sudo cat  /dev/ttyS4
  

收发测试

开启 Overlay

请参照设备树配置启用 UART 相关 Overlay, eg: "Enable UART4-M2" 和"Enable UART7-M1"。

连接

按照以下方式连接 UART4-M2UART7-M1

UART4-M2<-->UART7-M1
PIN_23<-->PIN_11
PIN_19<-->PIN_15

如图所示:

uart connection

测试

  • 打开终端,在终端中输入以下命令设置串口参数

  radxa@rock-5b-plus:~$ sudo stty -F /dev/ttyS4 speed 115200 cs8 -parenb -cstopb  -echo
  radxa@rock-5b-plus:~$ sudo stty -F /dev/ttyS7 speed 115200 cs8 -parenb -cstopb  -echo
  
  • 在终端中,输入一下命令循环发送数据

UART4-M2ttyS4 作为发送端


  radxa@rock-5b-plus:~$ sudo stty -F /dev/ttyS4 speed 115200 cs8 -parenb -cstopb  -echo
  
  • 新建一个终端,输入以下命令进行接收数据

UART7-M1ttyS7 作为接收端


  radxa@rock-5b-plus:~$ sudo cat  /dev/ttyS7
  
  • 验证

若发送端成功发送和接收端成功接收字符 "sss",则测试成功。

  • 互换发送端和接收端进行交叉验证