zoukankan      html  css  js  c++  java
  • Am335x SPI 驱动测试

    内核版本:3.14.65

    CPU:Am335x

    1、编译内核:

    make menuconfig

     Device Drivers -> 
    
                <*>SPI support ->
                 <*>McSPI driver for omap
                 <*>User mode SPI device driver support

    2、添加设备树

    bb_spi0_pins: pinmux_bb_spi0_pins {
            pinctrl-single,pins = <
                0x150 0x30    /* spi0_sclk.spi0_sclk, INPUT_PULLUP | MODE0 */
                0x154 0x30    /* spi0_d0.spi0_d0, INPUT_PULLUP | MODE0 */
                0x158 0x10    /* spi0_d1.spi0_d1, OUTPUT_PULLUP | MODE0 */
                0x15c 0x10    /* spi0_cs0.spi0_cs0, OUTPUT_PULLUP | MODE0 */
            >;
        };

    &spi0 {
    #address-cells = <1>;
    #size-cells = <0>;

    
    

    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&bb_spi0_pins>;

    
    


    channel@0 {
    #address-cells = <1>;
    #size-cells = <0>;

    
    

    compatible = "spidev";

    
    

    reg = <0>;
    spi-max-frequency = <16000000>;
    spi-cpha;
    };

    
    


    channel@1 {
    #address-cells = <1>;
    #size-cells = <0>;

    
    

    compatible = "spidev";

    
    

    reg = <1>;
    spi-max-frequency = <16000000>;
    };
    };

     

    编译内核和设备树,下载入开发板。

    3、查看SPI是否被注册入Dev中:

    ls /dev

    console             ram8                tty40
    cpu_dma_latency     ram9                tty41
    full                random              tty42
    hwrng               root                tty43
    i2c-0               rtc0                tty44
    input               snd                 tty45
    kmem                spidev1.0           tty46
    kmsg                spidev1.1           tty47
    log                 tty                 tty48
    loop-control        tty0                tty49

    可以看到spidev已经被注册入设备中啦。

    3、测试程序的编写:可以借鉴或者直接用Doucment/spi/spi_text.c下的文件。

    /*
     * 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]))
    
    #define WREN  0x06  //set write enable Latch
    #define WRDI  0x04  // write disable
    #define RDSR  0x05  // read status register
    #define WRSR  0x01  // Write Status Register
    #define READ  0x03  // Read memory Data
    #define WRITE 0x02  // Write memory Data  
    
    static void pabort(const char *s)
    {
        perror(s);
        abort();
    }
    
    static const char *device = "/dev/spidev1.0";
    static uint8_t mode;
    static uint8_t bits = 8;
    static uint32_t speed = 10000;
    static uint16_t delay;
    
    static void transfer_read(int fd)
    {
        int ret;
        uint8_t tx[] = {
            0x03, 0x10, 0x00, 
        };
        
        uint8_t rx[6] = {0, };
        struct spi_ioc_transfer tr[2] = {
            {
                .tx_buf = (unsigned long)tx,
                .rx_buf = (unsigned long)rx,
                .len = ARRAY_SIZE(tx),
                .delay_usecs = delay,
                .speed_hz = speed,
                .bits_per_word = bits,
            },
            
            {
                .tx_buf = (unsigned long)tx,
                .rx_buf = (unsigned long)rx,
                .len = ARRAY_SIZE(rx),
                .delay_usecs = delay,
                .speed_hz = speed,
                .bits_per_word = bits,
            },
        };
    
        ret = ioctl(fd, SPI_IOC_MESSAGE(2), &tr);
        if (ret < 1)
            pabort("can't send spi message");
    
        for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
            if (!(ret % 2))
                puts("");
            printf("%.5X ", rx[ret]);
        }
        puts("");
    }
    
    static void transfer(int fd)
    {
        int ret;
        uint8_t tx[] = {
            0x06, 0x02, 0x10, 0x00, 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,
        };
        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,
            .speed_hz = speed,
            .bits_per_word = bits,
        };
    
        ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        if (ret < 1)
            pabort("can't send spi message");
    
        for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
            if (!(ret % 2))
                puts("");
            printf("%.5X ", 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) {
            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;
    
            c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", 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 '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");
            
        //mode = mode | 0 | SPI_LSB_FIRST;
    
        /*
         * spi mode
         */
        ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
        if (ret == -1)
            pabort("can't set spi mode");
    
        ret = ioctl(fd, SPI_IOC_RD_MODE, &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: %d
    ", mode);
        printf("bits per word: %d
    ", bits);
        printf("max speed: %d Hz (%d KHz)
    ", speed, speed/1000);
    
        //transfer(fd);
        
        while(1)
        {
            //transfer(fd);
            transfer_read(fd);
            //write(fd,buf_me,1);
        }
        
        close(fd);
    
        return ret;
    }

    需要注意:

    是内核中SPI的数据发送和接收都是以byte为单位的。所以假如设置per_bits_word不是8or16or32时用上面的程序会出错的。假如设置的per_bits_word为9(https://blog.csdn.net/yasin_lee/article/details/54632982),那么在设置

    struct spi_ioc_transfer tr 这个结构体时.len的长度一定要注意了。因为驱动在判断per_bits_word>8时,会用两个字节来存放要发送的数据的,大于16就会用4个字节来存放要发送的数据。
    .len = ARRAY_SIZE(tx),ARRAY_SIZE只能计算tx数组的的元素的个数,并不会计算tx数组所占的字节数。所以当per_bits_word%8 > 时(也就是非8的整数倍),若是还用.len = ARRAY_SIZE(tx)
    来计算SPI发送数据的长度,会引起内核崩溃的。

    可以用以下方法来计算长度:
    .len = sizeof(tx),
    .len = ARRAY_SIZE(tx)*2//(16>per_bits_word>8)
    .len = ARRAY_SIZE(tx)*4//(32>per_bits_word>16)

    根据测试在用这个函数发送数据时:
    ioctl(fd, SPI_IOC_MESSAGE(1), &tr)
    每配置一个 struct spi_ioc_transfer tr 结构体就相当于拉低一次CS,当然前提是配置CS低有效。
    也就是SPI每发送一个SPI_IOC_MESSAGE,CS被拉低一次,可以通过这个来编写不同的SPI的接口芯片访问方式(暂时是这样理解的,等spi芯片到了,可以测试一下)。

    还有就是此版本的SPI驱动并不支持低字节优先发送,假如设置了低字节优先发送的模式,会报以下错误:
    spidev spi1.0: setup: unsupported mode bits 8
    can't set spi mode: Invalid argument
    Aborted

    可以从内核源码中了解相关信息:linux-3.14.65/drivers/spi/spi.c

     * Note that this call will fail if the protocol driver specifies an option
     * that the underlying controller or its driver does not support.  For
     * example, not all hardware supports wire transfers using nine bit words,
     * LSB-first wire encoding, or active-high chipselects.
     */
    int spi_setup(struct spi_device *spi)
    {
        unsigned    bad_bits, ugly_bits;
        int        status = 0;

     若是没有SPI芯片可以用来测试的话,可已将AM335x的Tx与Rx脚短接,这样也可以测试驱动及驱动测试程序的好坏。

  • 相关阅读:
    关于 Android 平台开发相关的有哪些推荐书籍?
    spring: 在表达式中使用类型
    spring: ?.运算符
    spring:使用会话和请求作用域
    spring:bean的作用域
    spring:自定义限定符注解@interface, 首选bean
    spring: 使用Spring提供的JDBC模板(使用profiles选择数据源/使用基于JDBC驱动的数据源)
    spring: 在Spring应用中使用JDBC(使用profiles选择数据源/使用基于JDBC驱动的数据源)
    python接口自动化-token参数关联登录(二)
    代码
  • 原文地址:https://www.cnblogs.com/BigOBlue/p/9025133.html
Copyright © 2011-2022 走看看