/********************************************************************** * 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.csdn.net/yylklshmyt20090217/article/details/4234237 // 3. 用户空间的spi驱动 // http://blog.csdn.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 = 8; static uint32_t speed = 500000; 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)] = {0, }; // 发送数据结构体 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(1), &tr); if (ret < 1) pabort("can't send spi message"); // 显示输出发送buf的数据内容 for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { if (!(ret % 6)) puts(""); printf("%.2X ", rx[ret]); } puts(""); } // 输出显示使用方法 static void print_usage(const char *prog) { printf("Usage: %s [-DsbdlHOLC3] ", prog); puts(" -D --device device to use (default /dev/spidev1.1) " " -s --speed max speed (Hz) " " -d --delay delay (usec) " " -b --bpw bits per word " " -l --loop loopback " " -H --cpha clock phase " " -O --cpol clock polarity " " -L --lsb least significant bit first " " -C --cs-high chip select active high " " -3 --3wire SI/SO signals shared "); exit(1); } static void parse_opts(int argc, char *argv[]) { while (1) { /** * 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", 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; /** * 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 == -1) 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 '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); if (fd < 0) pabort("can't open device"); /* * spi mode * 设置spi模式 */ ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); if (ret == -1) pabort("can't set spi mode"); // 读取spi模式 ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); if (ret == -1) pabort("can't get spi mode"); /* * bits per word * 设置spi字节位数 */ ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); if (ret == -1) pabort("can't set bits per word"); // 读取spi字节位数 ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); if (ret == -1) pabort("can't get bits per word"); /* * max speed hz * 设置spi最大速率 */ ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); if (ret == -1) pabort("can't set max speed hz"); // 设置spi最大速率 ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); if (ret == -1) pabort("can't get max speed hz"); // 显示设置的spi相关参数,这里输出的都是设置了之后,又重新读回来的值, // 便于确认你设置的值和真是程序运行的值是否有差异。 printf("spi mode: %d ", mode); printf("bits per word: %d ", bits); printf("max speed: %d Hz (%d KHz) ", speed, speed/1000); // 发送数据 transfer(fd); // 关闭设备节点 close(fd); return ret; }