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;
    }
  • 相关阅读:
    华为交换机中hybrid、access、trunk的区别
    debug调试
    网络基础--ICMP
    HCNA---ARP协议
    Python之字典
    python之元组
    网络基础--Telnet、SSH、FTP
    学习Cisco切换为华为的第一天---Telnet
    学思科,考华为,用华三。这是多少人的现况,快来听听我的经历~
    [P3385]【模板】负环 (spfa / bellman-ford)
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/5327821.html
Copyright © 2011-2022 走看看