在 Beagleboard-x15 上配置 spi 和 GPIO

(直接从文档复制过来的,看起来很乱啊,想要具体教程可以留言邮箱我发)

环境与条件

 硬件:Beagleboard-x15

内核版本:linux 4.9.69-ti-rt-r85

其它:sd 卡、杜邦线、示波器、引脚扩展器Enable SPI and GPIO in BeagleBoard-X15

一、准备 u-boot 和 kernel
1.下载 u-boot 并编译
下载:
~/
git clone https://github.com/u-boot/u-boot
cd u-boot/

git checkout v2017.01 -b tmp

~/u-boot
git pull --no-edit git://git.ti.com/ti-u-boot/ti-u-boot.git ti-u-boot-2017.01
git checkout 590c7d7fe15d06a4d708403d3234bcd01cd039e1
wget -c https://rcn-ee.com/repos/git/u-boot-patches/ti-2017.01/0001-beagle_x15-uEnv.txt-
bootz-n-fixes.patch
patch -p1 < 0001-beagle_x15-uEnv.txt-bootz-n-fixes.patch
编译:
~/u-boot
make ARCH=arm CROSS_COMPILE=${CC} distclean
make ARCH=arm CROSS_COMPILE=${CC} am57xx_evm_defconfig

make ARCH=arm CROSS_COMPILE=${CC}

2.下载 kernel 并编译


下载:
~/
git clone https://github.com/RobertCNelson/ti-linux-kernel-dev.git
cd ti-linux-kernel-dev/
下载 TIv4.9.x:
~/ti-linux-kernel-dev/
git checkout origin/ti-linux-4.9.y -b tmp
下载 TIv4.9.x:Real-Time
~/ti-linux-kernel-dev/
git checkout origin/ti-linux-rt-4.9.y -b tmp
编译:~/ti-linux-kernel-dev/
./build_kernel.sh
(参考:http://eewiki.net/display/linuxonarm/BeagleBoard-X15#BeagleBoard-X15-

Bootloader:U-Boot)

我们这里一共需要修改两份文件:
1)~/u-boot/board/ti/am57xx/mux_data.h

2)~/ti-linux-kernel-dev/KERNEL/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts


二、查看需求文档并使用 PINMUX


1.找出一共需要配置的 SPI 和 GPIO从上图可以看到我们这里需要配置 SPI3 和 SPI4,还有其余 20 个 GPIO。
2.使用 PINMUX cloud 对全部进行复用
1)登录 https://dev.ti.com/,选择 PinMux,在 Device 中选择 AM5728_SR2.0,然
后点击 Start 按钮进入配置界面,如下图
然后我们按照上述步骤中的需求图配置好 spi 和 gpio
(1)spi3 和 spi4 配置好后如下图:(2)再加上 20 个 gpio 配置好后如下图
可以看到右上 output 栏出现 8 个文件,前 4 个用于 RT-OS,后三个用于 Linux,最后一
个文件是可以看到你所有配置的表,现在把 genericFileFormatIOdelay.txt 和
genericFileFormatPadConf.txt 下载下来。
3)用脚本生成所需要的 u-boot 配置代码
这里我们需要用到 am57xx_generate_pin_config_data.pl 这个脚本,附录 A 可以得
到。
在命令行输入:
./am57xx_generate_pin_config_data.pl -p
genericFileFormatPadConf.txt -d genericFileFormatIOdelay.txt \
-o iopad >padconf.h
./am57xx_generate_pin_config_data.pl -pgenericFileFormatPadConf.txt -d genericFileFormatIOdelay.txt \
-o iodelay >iodelay.h
而后会生成 padconf.h 和 iodelay.h 两个文件,我们这里 iodelay.h 是空的,所以只

需要 padconf.h。


三、修改 u-boot 和设备树文件

把一、中最后提到的 mux_data.h 和 am57xx-beagle-revc.dts 打开
1)首先修改 mux_data.h
打开 padconf.h,复制内容到 mux_data.h 中 const struct pad_conf_entry
core_padconf_array_essential_x15[] = {} 的最后部分。
(注意:如果不复制到最后会发生被覆盖的可能)
现在我们已经把 spi 和 gpio 都配置好了,但是 spi 还需要生成设备节点,所以还需要在
dts 中进行注册。
2)在 am57xx-beagleboard-revc.dts 中注册 spi 节点
添加如下代码在 dts 的末尾处OK,到这里主要工作就完成了,接下来进行 dtb 和 u-boot 的编译工作。
u-boot:
~/u-boot
make ARCH=arm CROSS_COMPILE=${CC}
该步骤会重新生成 MLO 和 u-boot.img 文件。
dtb:
~/ti-linux-kernel-dev/KERNEL
make ARCH=arm CROSS_COMPILE=${CC} am57xx-beagle-x15-revc.dtb
该步骤会重新生成 am57xx-beagle-x15-revc.dtb 文件。

(注意:如果出现错误可能是没有 root 权限或者编译器没指定)


四、烧录 SD 卡


如果还没对 sd 卡制作启动盘,可以参考
http://eewiki.net/display/linuxonarm/BeagleBoard-X15#BeagleBoard-
X15-Bootloader:U-Boot 中 Setup microSD card 部分进行制作。已经有一张装有 Linux 系统的 sd 卡的情况下,把三、步骤中最后得到的两个文件放在 sd
卡中合适的位置:
1) am57xx-beagle-x15-revc.dtb :替代 /boot/dtb/4.9.69-ti-rt-r85/ 中的同名文件
2 ) MLO 和 u-boot.img :
~/
sudo dd if=./u-boot/MLO of=/dev/sdb count=2 seek=1 bs=128k
sudo dd if=./u-boot/u-boot.img of=/dev/sdb count=4 seek=1 bs=384k
完成后退出 sd 卡,插入板子启动,至此我们的配置过程已经全部完成。接下来是对 spi 和

gpio 的测试部分。


五、在 beagleboard-x15 上对 spi 和 gpio 进行测试


1、对 SPI3 和 SPI4 进行自收发测试
输入 ls /dev/spi*,可以看到 spidev1.0(SPI3),spidev2.0(SPI4)两个设备节
点,说明 spi 已经成功打开,但是能不能收发数据需要进一步的测试。
1)把 SPI3 和 SPI4 的 d0 和 d1 分别使用杜邦线进行连接:SPI3:P16-33 ---- P16-04
SPI4:P16-14 ---- P16-37
2)运行测试程序
使用 spidev_test.c 官方测试程序进行测试,该程序贴在附录 B。
使用 gcc 先对 spidev_test.c 进行编译 gcc spidev_test.c -o spidev_test
对 SPI3 进行测试:
./spidev_test.c -D /dev/spidev1.0
如果结果如上图所示(图来源网络),那么代表测试成功!SPI4 同理。
2、对 GPIO 进行测试
我们这里是在用户空间使用 sysfs 对 GPIO 进行测试。我们这里只进行简单的 in/out 测
试步骤,具体每个步骤和文件的含义可以参考附录 C。
测试 gpio2_27:
1)命令行输入:
cd /sys/class/gpio
echo 59 > export
cd gpio59
echo out > direction
2)使用示波器的探线接上 P16-17,另一边接地。观察当前为高电平还是低电平
3)如果初始为高电平:echo 0 > value
如果初始为低电平:echo 1 > value
4)观察示波器的变化,如果发生高低电平变化则代表 out 成功。
5)echo in > direction
6)cat value 查看当前 value 值
7)如果 value 值为 1,使用杜邦线使 P16-17 引脚接地,在 cat value 查看是否变为
0,然后拔掉一边,再查看 value 是否变回 1。如果一开始 cat value 为 0,则对引脚接
VCC 3.3v 电源,查看 value 是否变为 1,然后拔出电源,是否回 0。

如果上述步骤都成功,那么代表 in 成功。

说明:GPIO 的编号的计算

官方说法是: Board documentation should in most cases cover what GPIOs are used
for what purposes. However, those numbers are not always stable; GPIOs on a
daughtercard might be different depending on the base board being used, or other cards
in the stack. In such cases, you may need to use the gpiochip nodes (possibly in
conjunction with schematics) to determine the correct GPIO number to use for a given
signal.
这里是利用设备树文件 am57xx-beagle-x15-common.dtsi 中对 gpio7_11 进行
了 vtt_fixed 配置,利用 cat /sys/kernel/debug/gpio 可以查看引脚编号 203 被
作为 vtt_fixed 功能,可以得出 203 = (7-1)*32 + 11 的规律猜出其余引脚的编号
数 gpioN_M = (N-1)*32 + M,所以 gpio2_27 编号为 59。但是请注意该计算公式并不

适合所有的系统。

另外

测试到 gpio2_8 时候,当 echo 40 > export,产生 echo write Error: Device
or resource busy 错误,然后 cat /sys/kernel/debug/gpio 时发现引脚 40 已经被使用。
查找设备数中发现 gpio2_8 被用作 PCIe RC 和 EP
然后修改其改为 gpio4_10,重新编译启动,未发现任何异常且 gpio2_8 可以使用。

特此记录。

附录 A

am57xx_generate_pin_config_data.pl
#!/usr/bin/perl -w
#
# (C) Copyright 2016
# Texas Instruments, &lt;ti.com&gt;#
# SPDX-License-Identifier:
GPL-2.0+
#
use strict;
use warnings;
use Getopt::Std;
#---------------------------------------- Data
my %options = ();
my $iodelay_file;
my $padconf_file;
my $output_format;
my %iodelay_array;
my %iopad_array;
# For New operations: update the two arrays
my %operations_help = (
iopad =>
"Generate IO Pad header data",
iodelay =>
"Generate IO Delay header data",
);
my %operations_array = (
iopad
=> \&operation_iopad,
iodelay => \&operation_iodelay,
);
#---------------------------------------- Main flow
getopts( "hp:d:o:", \%options );
if ( $options{h} ) {
do_help("Usage:");
}
$iodelay_file = $options{d} if defined $options{d};
$padconf_file = $options{p} if defined $options{p};
$output_format = $options{o} if defined $options{o};
# check for sanity
do_help("Error: Missing file parameters!")
if !defined $iodelay_file && !defined $padconf_file;
do_help("Error: Missing output format!") if !defined $output_format;
do_help("Error: iodelay file '$iodelay_file' is not readable")
if defined $iodelay_file && !-r "$iodelay_file";
do_help("Error: padconf file '$padconf_file' is not readable")
if defined $padconf_file && !-r "$padconf_file";
do_help("Error: Unknown Output format '$output_format'")
if !exists( $operations_array{$output_format} );
# read input files
do_read_iopad($padconf_file)
if defined $padconf_file;
do_read_iodelay($iodelay_file) if defined $iodelay_file;
# Now, execute the corresponding operation
$operations_array{$output_format}->();
exit 0;#---------------------------------------- Subroutines
# Help subroutine.. uses the argument of some error or print message..
sub do_help
{
my $operation;
print "@_\n";
print "$0 [-h] -p padconf_file -d iodelay_file -o output_format\n";
print "Where";
print "\t-h provides this help text\n";
print
"\t-p padconf_file is the generic pad config output file provided by PMT(PinMux
Tool)\n";
print
"\t-d iodelay_file is the generic iodelay output file provided by PMT(PinMux
Tool)\n";
print "\t-o output_format , where output_format is one of:\n";
}
foreach $operation ( keys %operations_array ) {
print "\t\t $operation - $operations_help{$operation}\n";
}
exit 0;
# read into an associative array of array indexed by register address
# allows us to do operations and offsets and sorts. unfortunately with ballname,
# we dont have exactly an unique key considering that multiple idoelay registers
# have the same ballname
sub do_read_file
{
my $file = $_[0];
my $iopad = $_[1];
my $fh;
my $row;
my @iopad_row;
my $register;
my $skip = 0;
open( $fh, '<', $file )
or do_help("Error: unable to open IOPAD $file for read");
while ( $row = <$fh> ) {
chomp($row);
# get rid of commented lines including single line and multiline
next if $skip == 1;
if ( $row =~ /\/\*/ ) { $skip = 1 if !$row =~ /\*\//; next; }
if ( $row =~ /\*\// ) { $skip = 0; next; }
# get rid of Empty lines
next if $row =~ /^$/;
# Now, Human readable to CSV
$row =~ s/\s\s*/,/g;
@iopad_row = split( ',', $row );
$register = $iopad_row[0];
@iopad_row = splice @iopad_row, 1, $#iopad_row;
if ($iopad) {
$iopad_array{$register} = [@iopad_row];
} else {
$iodelay_array{$register} = [@iopad_row];
}}
}
close($fh);
# tiny lil wrapper
sub do_read_iopad
{
do_read_file( $_[0], 1 );
}
# tiny lil wrapper
sub do_read_iodelay
{
do_read_file( $_[0], 0 );
}
#---- the various operations----
sub operation_iopad()
{
my $register;
do_help("Error: I need iodelay file for this option")
if !defined $iodelay_file;
do_help("Error: I need padconf file for this option")
if !defined $padconf_file;
foreach $register ( sort keys %iopad_array ) {
my @val;
my $reg_val;
my $reg_name;
my $ball_name;
my $mux0;
my $mux;
my $reg_dec;
my $slew_control;
my $input_en;
my $pull_active;
my $pull_up;
my $compare_val;
my $compare_hex;
my $val_mux0_name;
my $val_mux_mode;
my $val_pull;
my $val_delay_mode;
my $val_virtual_mode;
my $val_manual_mode;
@val = @{ $iopad_array{$register} };
# register_address(hex)
register_value(hex)
ball_name(string)
register_name(string)
mux_mode0_name(string) muxed_mode_name(string)
( $reg_val, $ball_name, $reg_name, $mux0, $mux ) = @val;
$reg_dec = hex($reg_val);
#pulls and mux mode
$slew_control = $reg_dec & ( 1 << 19 );
$input_en
= $reg_dec & (1 << 18 );
$pull_up
= $reg_dec & (1 << 17 );
$pull_active = $reg_dec & (1 << 16 );
$compare_val = $slew_control | $input_en | $pull_up | $pull_active;
$compare_hex = sprintf( "0x%08x", $compare_val );
if ( $compare_hex =~ /0x00010000/ ) { $val_pull = "PIN_OUTPUT"; }elsif ( $compare_hex =~ /0x00030000/ ) {
$val_pull = "PIN_OUTPUT";
} elsif ( $compare_hex =~ /0x00020000/ ) {
$val_pull = "PIN_OUTPUT_PULLUP";
} elsif ( $compare_hex =~ /0x00000000/ ) {
$val_pull = "PIN_OUTPUT_PULLDOWN";
} elsif ( $compare_hex =~ /0x00080000/ ) {
$val_pull = "PIN_OUTPUT_PULLDOWN | SLEWCONTROL";
} elsif ( $compare_hex =~ /0x00050000/ ) {
$val_pull = "PIN_INPUT";
} elsif ( $compare_hex =~ /0x000c0000/ ) {
$val_pull = "PIN_INPUT_SLEW";
} elsif ( $compare_hex =~ /0x00060000/ ) {
$val_pull = "PIN_INPUT_PULLUP";
} elsif ( $compare_hex =~ /0x00040000/ ) {
$val_pull = "PIN_INPUT_PULLDOWN";
} elsif ( $compare_hex =~ /0x000e0000/ ) {
$val_pull = "PIN_INPUT_PULLUP | SLEWCONTROL";
}
# Uggh.. unknown definition?
else { $val_pull = $compare_hex; }
# virtual mode definition
$val_virtual_mode = ( $reg_dec >> 4 ) & 15;
$val_delay_mode = "MODESELECT" if $reg_dec & (1 << 8 );
# Am i Manual mode with VIRTUAL_MODE0 ?
if ( defined $val_delay_mode && $val_virtual_mode == 0 ) {
my $iodr;
#This could be manual mode!
foreach $iodr ( keys %iodelay_array ) {
my @iodv;
my $iod_a_delay;
my $iod_g_delay;
my $iod_reg_name;
my $iod_ball;
my $na = "N/A";
@iodv = @{ $iodelay_array{$iodr} };
( $iod_a_delay, $iod_g_delay, $iod_reg_name, $iod_ball )
=
@iodv;
if ( !$ball_name =~ /$iod_ball/ ) { next; }
if ( $iod_a_delay =~ /$na/ && $iod_g_delay =~ /$na/ )
{ next; }
# if either one is defined, we are manual mode
$val_manual_mode = "MANUAL_MODE";
}
}
# mux mode
$val_mux_mode = $reg_dec & 15;
# register defines is mux_mode0 name
# CTRL_CORE_PAD_GPMC_AD0 -> is GPMC_AD0
$val_mux0_name = substr $reg_name, 14;
print "\{$val_mux0_name, (M$val_mux_mode | $val_pull";
if ( defined $val_delay_mode ) {
if ( defined $val_manual_mode ) {
print " | $val_manual_mode";} else {
printf " | VIRTUAL_MODE$val_virtual_mode";
}
}
}
printf(")},\t/* $mux0.$mux */\n");
}
sub operation_iodelay()
{
my $register;
my @val;
do_help("Error: I need iodelay file for this option")
if !defined $iodelay_file;
foreach $register ( sort keys %iodelay_array ) {
my @iodv;
my $iodelay_base = 0x4844A000;
my $reg_dec;
my $offset;
my $iod_a_delay;
my $iod_g_delay;
my $iod_reg_name;
my $iod_ball;
my $na = "N/A";
my $val_a;
my $val_g;
my $val_offset;
}
}
@iodv = @{ $iodelay_array{$register} };
( $iod_a_delay, $iod_g_delay, $iod_reg_name, $iod_ball ) = @iodv;
next if $iod_a_delay =~ /$na/ && $iod_g_delay =~ /$na/;
if ( $iod_a_delay =~ /$na/ ) {
$val_a = 0;
} else {
$val_a = $iod_a_delay;
}
if ( $iod_g_delay =~ /$na/ ) {
$val_g = 0;
} else {
$val_g = $iod_g_delay;
}
$reg_dec
= hex($register);
$offset
= $reg_dec - $iodelay_base;
$val_offset = sprintf( "0x%04X", $offset );

print "{$val_offset, $val_a, $val_g},\t/* $iod_reg_name */\n";

附录 B

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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<stdint.h>
<unistd.h>
<stdio.h>
<stdlib.h>
<string.h>
<getopt.h>
<fcntl.h>
<sys/ioctl.h>
<linux/ioctl.h>
<sys/stat.h>
<linux/types.h>
<linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static
static
static
static
static
static
static
static
const char *device = "/dev/spidev1.1";
uint32_t mode;
uint8_t bits = 8;
char *input_file;
char *output_file;
uint32_t speed = 500000;
uint16_t delay;
int verbose;
uint8_t default_tx[] = {
0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0x0D,
};
0xFF,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0x95,
0xFF,
0xFF,
0xFF,
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, size_t len)
{
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/spidev1.1)\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, size);
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 (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_MODE32, &mode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &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;

附录 C

用户空间访问 GPIO
用户空间访问 gpio,即通过 sysfs 接口访问 gpio,下面是/sys/class/gpio 目录下的三种文
件:
--export/unexport 文件
--gpioN 指代具体的 gpio 引脚
--gpio_chipN 指代 gpio 控制器
必须知道以上接口没有标准 device 文件和它们的链接。
(1) export/unexport 文件接口:
/sys/class/gpio/export,该接口只能写不能读
用户程序通过写入 gpio 的编号来向内核申请将某个 gpio 的控制权导出到用户空间
当然前提是没有内核代码申请这个 gpio 端口
比如 echo 19 > export
上述操作会为 19 号 gpio 创建一个节点 gpio19,此时/sys/class/gpio 下边生成一个
gpio19 的目录
/sys/class/gpio/unexport 和导出的效果相反。
比如 echo 19 > unexport
上述操作将会移除 gpio19 这个节点。
(2) /sys/class/gpio/gpioN
指代某个具体的 gpio 端口,里边有如下属性文件
direction 表示 gpio 端口的方向,读取结果是 in 或 out。该文件也可以写,写入 out 时该
gpio 设为输出同时电平默认为低。写入 low 或 high 则不仅可以设置为输出还可以设置输出
的电平,当然如果内核不支持或者内核代码不愿意,将不会存在这个属性,比如内核调用了
gpio_export(N,0)表示内核不愿意修改 gpio 端口方向属性value 表示 gpio 引脚的电平,0(低电平)1(高电平),如果 gpio 被配置为输出,这个值是可写
的,记住任何非零的值都将输出高电平, 如果某个引脚能并且已经被配置为中断,则可以调
用 poll(2)函数监听该中断,中断触发后 poll(2)函数就会返回。
edge 表示中断的触发方式,edge 文件有如下四个值:none, rising, falling,both。
none 表示引脚为输入,不是中断引脚
rising 表示引脚为中断输入,上升沿触发
falling 表示引脚为中断输入,下降沿触发
both 表示引脚为中断输入,边沿触发
这个文件节点只有在引脚被配置为输入引脚的时候才存在。 当值是 none 时可以通过如下方
法将变为中断引脚
echo "both" > edge;对于是 both,falling 还是 rising 依赖具体硬件的中断的触发方式。
此方法即用户态 gpio 转换为中断引脚的方式
active_low 不怎么明白,也木有用过
(3)/sys/class/gpio/gpiochipN
gpiochipN 表示的就是一个 gpio_chip,用来管理和控制一组 gpio 端口的控制器,该目录下
存在一下属性文件:
base 和 N 相同,表示控制器管理的最小的端口编号。
lable 诊断使用的标志(并不总是唯一的)
ngpio 表示控制器管理的 gpio 端口数量(端口范围是:N ~ N+ngpio-1)

猜你喜欢

转载自blog.csdn.net/a249093278/article/details/80582381