SPI 用戶空間的讀寫操作


spi_device

雖然用戶空間不需要直接用到spi_device結構體,但是這個結構體和用戶空間的程序有密切的關系,理解它的成員有助於理解SPI設備節點的IOCTL命令,所以首先來介紹它。在內核中,每個spi_device代表一個物理的SPI設備:

struct spi_device {
structdevice dev;
structspi_master *master;
u32 max_speed_hz; /* 通信時鍾最大頻率 */
u8 chip_select; /* 片選號 */
u8 mode; /*SPI設備的模式,下面的宏是它各bit的含義 */
#define SPI_CPHA 0x01 /* 采樣的時鍾相位 */
#define SPI_CPOL 0x02 /* 時鍾信號起始相位:高或者是低電平 */
#define SPI_MODE_0 (0|0)
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* 為1時片選的有效信號是高電平 */
#define SPI_LSB_FIRST 0x08 /* 發送時低比特在前 */
#define SPI_3WIRE 0x10 /* 輸入輸出信號使用同一根信號線 */
#define SPI_LOOP 0x20 /* 回環模式 */
u8 bits_per_word; /* 每個通信字的字長(比特數) */
int irq; /*使用到的中斷 */
void *controller_state;
void *controller_data;
char modalias[32]; /* 設備驅動的名字 */
};

spi_device的mode成員有兩個比特位含義很重要:
SPI_CPHA選擇對數據線采樣的時機,0選擇每個時鍾周期的第一個沿跳變時采樣數據,1選擇第二個時鍾沿采樣數據;
SPI_CPOL選擇每個時鍾周期開始的極性,0表示時鍾以低電平開始,1選擇高電平開始。
這兩個比特有四種組合,對應SPI_MODE_0~SPI_MODE_3。
另一個比較重要的成員是bits_per_word。這個成員指定每次讀寫的字長,單位是比特。雖然大部分SPI接口的字長是8或者16,仍然會有一些特殊的例子。需要說明的是,如果這個成員為零的話,默認使用8作為字長。
最后一個成員並不是設備的名字,而是需要綁定的驅動的名字。

spi_ioc_transfer

linux中,應用開發常用的結構體主要是struct spi_ioc_transfer:

struct spi_ioc_transfer {
    __u64        tx_buf;
    __u64        rx_buf;
 
    __u32        len;
    __u32        speed_hz;
 
    __u16        delay_usecs;
    __u8        bits_per_word;
    __u8        cs_change;
    __u32        pad;
};

每個 spi_ioc_transfer都可以包含讀和寫的請求,其中讀和寫的長度必須相等。所以成員len不是tx_buf和rx_buf緩沖的長度之和,而是它們各自的長度。SPI控制器驅動會先將tx_buf寫到SPI總線上,然后再讀取len長度的內容到rx_buf。如果只想進行一個方向的傳輸,把另一個方向的緩沖置為0就可以了。
speed_hz和bits_per_word這兩個成員可以為每次通信配置不同的通信速率(必須小於spi_device的max_speed_hz)和字長,如果它們為0的話就會使用spi_device中的配置。
delay_usecs可以指定兩個spi_ioc_transfer之間的延時,單位是微妙。一般不用定義。
cs_change指定這個cs_change結束之后是否需要改變片選線。一般針對同一設備的連續的幾個spi_ioc_transfer,只有最后一個需要將這個成員置位。這樣省去了來回改變片選線的時間,有助於提高通信速率。

SPI設備的初始化

void spi_Init()
{
    int ret = 0;
 
 
    spifd = open(device, O_RDWR);
    if (spifd < 0)
        pabort("can't open device");
 
    /*
     * spi mode
     */
    ret = ioctl(spifd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1)
        pabort("can't set spi mode");
 
    ret = ioctl(spifd, SPI_IOC_RD_MODE, &mode);
    if (ret == -1)
        pabort("can't get spi mode");
 
    /*
     * bits per word
     */
    ret = ioctl(spifd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't set bits per word");
 
    ret = ioctl(spifd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't get bits per word");
 
    /*
     * max speed hz
     */
    ret = ioctl(spifd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't set max speed hz");
 
    ret = ioctl(spifd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't get max speed hz");
}

首先open打開SPI的設備,然后通過ioctl函數進行數據位、速率、模式進行配置。

IOCTL命令

用戶空間對spidev設備節點使用IOCTL命令失敗會返回-1。

SPI_IOC_RD_MODE

讀取SPI設備對應的spi_device.mode,使用的方法如下:

ioctl(fd,SPI_IOC_RD_MODE, &mode);

SPI_IOC_WR_MODE

設置SPI設備對應的spi_device.mode。使用的方式如下:

ioctl(fd,SPI_IOC_WR_MODE, &mode);

SPI_IOC_RD_LSB_FIRST

查看設備傳輸的時候是否先傳輸低比特位。如果是的話,返回1。使用的方式如下:

ioctl(fd,SPI_IOC_RD_LSB_FIRST, &lsb);

其中lsb是一個uint8_t類型的變量。返回的結果存在lsb中。

SPI_IOC_WR_LSB_FIRST

設置設備傳輸的時候是否先傳輸低比特位。當傳入非零的時候,低比特在前,當傳入0的時候高比特在前(默認)。使用的方式如下:

ioctl(fd,SPI_IOC_WR_LSB_FIRST, &lsb);

SPI_IOC_RD_BITS_PER_WORD

讀取SPI設備的字長。使用的方式如下:

ioctl(fd,SPI_IOC_RD_BITS_PER_WORD, &bits);

其中bits是一個uibt8_t類型的變量。返回的結果保存在bits中。

SPI_IOC_WR_BITS_PER_WORD

設置SPI通信的字長。使用的方式如下:

ioctl(fd,SPI_IOC_WR_BITS_PER_WORD, &bits);

SPI_IOC_RD_MAX_SPEED_HZ

讀取SPI設備的通信的最大時鍾頻率。使用的方式如下:

ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ, &speed);

SPI_IOC_MESSAGE(N)

一次進行雙向/多次讀寫操作。使用的方式如下:

structspi_ioc_transfer xfer[2];

......

status= ioctl(fd, SPI_IOC_MESSAGE(2), xfer);

其中N是本次通信中xfer的數組長度。

SPI的讀寫

int spi_read()
{
    bt_devide_msg msg;
    unsigned char ucRegVal;
    int ret,i;
    unsigned char tx[20];
    for(i = 0;i<20;i++)
    {
        tx[i] = 0xda;
    }
    unsigned char 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 = udelay,
        .speed_hz = speed,
        .bits_per_word = bits,
    };
    
    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 1)
    {
        printf("can't read spi message\n");
        return -1;
    }
    
    if(rx[0] !=0xAA)
    {
        printf("read spi data: ");    
        for (ret = 0; ret < ARRAY_SIZE(tx); ret++) 
        {
            printf("%02X ", rx[ret]);
        }
        printf("\n");
    }
    
    ucRegVal = rx[ARRAY_SIZE(tx)-1];
    get_data_process(rx);
                            
  return 1;

測試:

void main()
{
    spi_init();
    spi_read();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM