zoukankan      html  css  js  c++  java
  • linux SPI bus demo hacking

    /**********************************************************************
     *                    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;
    }
  • 相关阅读:
    118/119. Pascal's Triangle/II
    160. Intersection of Two Linked Lists
    168. Excel Sheet Column Title
    167. Two Sum II
    172. Factorial Trailing Zeroes
    169. Majority Element
    189. Rotate Array
    202. Happy Number
    204. Count Primes
    MVC之Model元数据
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/5327821.html
Copyright © 2011-2022 走看看