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]\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(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\n", mode);
    printf("bits per word: %d\n", bits);
    printf("max speed: %d Hz (%d KHz)\n", 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脚短接,这样也可以测试驱动及驱动测试程序的好坏。

 

posted @ 2018-05-11 15:56  竹主  阅读(2339)  评论(0编辑  收藏  举报