linux SPI bus demo hacking

时间:2023-03-08 21:47:47
/**********************************************************************
* linux SPI bus demo hacking
* 说明:
* 本文主要解析linux应用程序如何使用SPI总线和设备通信。
*
* 2016-3-28 深圳 南山平山村 曾剑锋
*********************************************************************/ // 参考文档:
// 1. getopt_long
// http://baike.baidu.com/link?url=6KJogehaQx-6OWyU0882g2P5Fdp-NoKBPJOBGYbx-gQIT6km2myaonw2nOheKsSoMXtDQTqsuVmTwS7trQ5vxq
// 2. Linux Signal (10): abort函数
// http://blog.****.net/yylklshmyt20090217/article/details/4234237
// 3. 用户空间的spi驱动
// http://blog.****.net/liangxiaozhang/article/details/7601880 /*
* SPI testing utility (using spidev driver)
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
*
* 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 <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.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();
} // 默认spi设备节点
static const char *device = "/dev/spidev1.0";
static uint8_t mode;
static uint8_t bits = ;
static uint32_t speed = ;
static uint16_t delay; static void transfer(int fd)
{
int ret;
// 要发送的数据
uint8_t 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,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
// 接收的数据,接收的数据长度和发送的数据长度是一样的,初始化为0。
uint8_t rx[ARRAY_SIZE(tx)] = {, };
// 发送数据结构体
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx, // 写数据缓冲
.rx_buf = (unsigned long)rx, // 读数据缓冲
.len = ARRAY_SIZE(tx), // 缓冲的长度
.delay_usecs = delay, // 两个spi_ioc_transfer之间的延时
.speed_hz = speed, // 通信的时钟频率
.bits_per_word = bits, // 字长(比特数)
}; // 发送数据
ret = ioctl(fd, SPI_IOC_MESSAGE(), &tr);
if (ret < )
pabort("can't send spi message"); // 显示输出发送buf的数据内容
for (ret = ; ret < ARRAY_SIZE(tx); ret++) {
if (!(ret % ))
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
} // 输出显示使用方法
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"
" -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();
} static void parse_opts(int argc, char *argv[])
{
while () { /**
* struct option {
* const char *name;
* int has_arg;
* int *flag;
* int val;
* };
* The meanings of the different fields are:
* name is the name of the long option.
*
* has_arg
* is: no_argument (or 0) if the option does not take an
* argument; required_argument (or 1) if the option requires an
* argument; or optional_argument (or 2) if the option takes an
* optional argument.
*
* flag specifies how results are returned for a long option. If flag
* is NULL, then getopt_long() returns val. (For example, the
* calling program may set val to the equivalent short option
* character.) Otherwise, getopt_long() returns 0, and flag
* points to a variable which is set to val if the option is
* found, but left unchanged if the option is not found.
*
* val is the value to return, or to load into the variable pointed
* to by flag.
*
*/
static const struct option lopts[] = {
{ "device", , , 'D' },
{ "speed", , , 's' },
{ "delay", , , 'd' },
{ "bpw", , , 'b' },
{ "loop", , , 'l' },
{ "cpha", , , 'H' },
{ "cpol", , , 'O' },
{ "lsb", , , 'L' },
{ "cs-high", , , 'C' },
{ "3wire", , , '' },
{ "no-cs", , , 'N' },
{ "ready", , , 'R' },
{ NULL, , , },
};
int c; /**
* 1. 函数中的argc和argv通常直接从main()的两个参数传递而来。
* 2. 字符串optstring可以下列元素:
* 1.单个字符,表示选项,
* 2.单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
* 3 单个字符后跟两个冒号,表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。
* 3. 参数longopts,其实是一个结构的实例。
* 4. 参数longindex,表示当前长参数在longopts中的索引值。
* 5. 给个例子:
* struct option long_options[] = {
* {"a123", required_argument, 0, 'a'},
* {"c123", no_argument, 0, 'c'},
* }
* 现在,如果命令行的参数是-a 123,那么调用getopt_long()将返回字符'a',并且将字符串123由optarg返回(注意注意!字符串123由optarg带回!optarg不需要定义,在getopt.h中已经有定义),那么,如果命令行参数是-c,那么调用getopt_long()将返回字符'c',而此时,optarg是null。最后,当getopt_long()将命令行所有参数全部解析完成后,返回-1。
*/
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL); // 这里表示解析参数已经到了最后一个,会跳出while循环
if (c == -)
break; switch (c) {
case 'D':
device = optarg; // getopt_long函数将参数指针放在optarg中
break;
case 's':
speed = atoi(optarg); // 将optarg字符串转成int型数字
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 '':
mode |= SPI_3WIRE;
break;
case 'N':
mode |= SPI_NO_CS;
break;
case 'R':
mode |= SPI_READY;
break;
default:
print_usage(argv[]);
break;
}
}
} int main(int argc, char *argv[])
{
int ret = ;
int fd; // 参数解析
parse_opts(argc, argv); // 打开设备
fd = open(device, O_RDWR);
if (fd < )
pabort("can't open device"); /*
* spi mode
* 设置spi模式
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -)
pabort("can't set spi mode"); // 读取spi模式
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -)
pabort("can't get spi mode"); /*
* bits per word
* 设置spi字节位数
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -)
pabort("can't set bits per word"); // 读取spi字节位数
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -)
pabort("can't get bits per word"); /*
* max speed hz
* 设置spi最大速率
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -)
pabort("can't set max speed hz"); // 设置spi最大速率
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -)
pabort("can't get max speed hz"); // 显示设置的spi相关参数,这里输出的都是设置了之后,又重新读回来的值,
// 便于确认你设置的值和真是程序运行的值是否有差异。
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/); // 发送数据
transfer(fd); // 关闭设备节点
close(fd); return ret;
}