一本精品热在线视频,久久免费视频分类,精品婷婷乱码久久久久久蜜桃,在线可以免费观看的Av

<mark id="vd61v"><dl id="vd61v"></dl></mark>
    <sub id="vd61v"><ol id="vd61v"></ol></sub>

  • <sub id="vd61v"><ol id="vd61v"></ol></sub>
    "); //-->

    博客專欄

    EEPW首頁 > 博客 > spi應用層寫法

    spi應用層寫法

    發(fā)布人:電子禪石 時間:2019-07-21 來源:工程師 發(fā)布文章

    【重點是  傳輸?shù)撵`活應用,想要每次傳輸之間沒有間隔,或者小點】

    1.1     重要的數(shù)據(jù)結(jié)構(gòu)
    1.  spi_device
    雖然用戶空間不需要直接用到spi_device結(jié)構(gòu)體,但是這個結(jié)構(gòu)體和用戶空間的程序有密切的關(guān)系,理解它的成員有助于理解SPI設(shè)備節(jié)點的IOCTL命令,所以首先來介紹它。
    在內(nèi)核中,每個spi_device代表一個物理的SPI設(shè)備。它的成員如程序清單 1.1所示。
    程序清單 1.1 spi_device

    [cpp] view plain copy
    1. struct spi_device {  

    2.          structdevice        dev;  

    3.          structspi_master *master;  

    4.          u32                      max_speed_hz;    /* 通信時鐘最大頻率 */  

    5.          u8                        chip_select;    /* 片選號 */  

    6.          u8                        mode;           /*SPI設(shè)備的模式,下面的宏是它各bit的含義  */  

    7. #define       SPI_CPHA         0x01                /* 采樣的時鐘相位                            */  

    8. #define       SPI_CPOL          0x02               /* 時鐘信號起始相位:高或者是低電平*/  

    9. #define       SPI_MODE_0    (0|0)                      

    10. #define       SPI_MODE_1    (0|SPI_CPHA)  

    11. #define       SPI_MODE_2    (SPI_CPOL|0)  

    12. #define       SPI_MODE_3    (SPI_CPOL|SPI_CPHA)  

    13. #define       SPI_CS_HIGH   0x04                    /* 為1時片選的有效信號是高電平*/  

    14. #define       SPI_LSB_FIRST         0x08            /* 發(fā)送時低比特在前  */  

    15. #define       SPI_3WIRE        0x10                 /* 輸入輸出信號使用同一根信號線 */  

    16. #define       SPI_LOOP         0x20                 /* 回環(huán)模式 */  

    17.          u8                        bits_per_word;    /* 每個通信字的字長(比特數(shù)) */  

    18.          int                        irq;             /*使用到的中斷 */  

    19.          void                     *controller_state;  

    20.          void                     *controller_data;  

    21.          char                     modalias[32];      /* 設(shè)備驅(qū)動的名字*/  

    22. };  





           由于一個SPI總線上可以有多個SPI設(shè)備,因此需要片選號來區(qū)分它們,SPI控制器根據(jù)片選號來選擇不同的片選線,從而實現(xiàn)每次只同一個設(shè)備通信。
           spi_device的mode成員有兩個比特位含義很重要。SPI_CPHA選擇對數(shù)據(jù)線采樣的時機,0選擇每個時鐘周期的第一個沿跳變時采樣數(shù)據(jù),1選擇第二個時鐘沿采樣數(shù)據(jù);SPI_CPOL選擇每個時鐘周期開始的極性,0表示時鐘以低電平開始,1選擇高電平開始。這兩個比特有四種組合,對應SPI_MODE_0~SPI_MODE_3。
           另一個比較重要的成員是bits_per_word。這個成員指定每次讀寫的字長,單位是比特。雖然大部分SPI接口的字長是8或者16,仍然會有一些特殊的例子。需要說明的是,如果這個成員為零的話,默認使用8作為字長。
           最后一個成員并不是設(shè)備的名字,而是需要綁定的驅(qū)動的名字。



    2.   spi_ioc_transfer(用戶層對一些屬性消息的封裝,和內(nèi)核中spi_transfer基本一樣,少了一些成員)
           在用戶使用設(shè)備節(jié)點的IOCTL命令傳輸數(shù)據(jù)的時候,可能需要用到 spi_ioc_transfer結(jié)構(gòu)體,它的成員如程序清單 1.2所示。
    程序清單 1.2  spi_ioc_transfer

    [cpp] view plain copy
    1. struct spi_ioc_transfer {  

    2.             __u64               tx_buf;                   /* 寫數(shù)據(jù)緩沖  */  

    3.             __u64               rx_buf;                   /* 讀數(shù)據(jù)緩沖  */  

    4.    

    5.             __u32               len;                      /* 緩沖的長度 */  

    6.             __u32               speed_hz;                 /* 通信的時鐘頻率 */  

    7.    

    8.             __u16               delay_usecs;    /* 兩個spi_ioc_transfer之間的延時 */  

    9.             __u8                 bits_per_word;           /* 字長(比特數(shù))  */  

    10.             __u8                 cs_change;               /* 是否改變片選 */  

    11.             __u32               pad;                                

    12. };  





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



    1.2     獲得同SPI設(shè)備通信的設(shè)備節(jié)點
    為了在用戶空間獲得和SPI設(shè)備直接通信的設(shè)備節(jié)點,必須有兩個條件要滿足:首先要有SPI控制器驅(qū)動,其次是要在內(nèi)核初始化的時候注冊一個spi_board_info,它的modalias成員必須為“spidev”。有了這兩個條件,就可以和SPI設(shè)備進行通信了??刂破鞯尿?qū)動一般由芯片廠家提供,開發(fā)者只需提供第二個條件。
    spi_board_info的定義如程序清單 1.3所示。
    程序清單 1.3  struct spi_board_info

    [cpp] view plain copy
    1. struct spi_board_info {  

    2.          char            modalias[32];          /* 要綁定的驅(qū)動的名字 */  

    3.          constvoid  *platform_data;                                          

    4.          void            *controller_data;  

    5.          int               irq;              

    6.    

    7.          u32             max_speed_hz;          /* 通信時鐘最大速率 */  

    8.    

    9.          u16             bus_num;              /* 總線編號  */  

    10.          u16             chip_select;           /* 片選號 */  

    11.    

    12.          u8               mode;                 /* 和spi_device中的mode成員類似   */  

    13. };  



           要了解這個結(jié)構(gòu)體各個成員的意義請參考程序清單 1.1。
           定義并注冊structspi_board_info的位置一般是內(nèi)核的arch/xxx/mach-xxxx/board-xxxx.c,比如3250的內(nèi)核,這個文件是arch/arm/mach-lpc32xx/board-smartarm3250.c。定義并注冊struct spi_board_info的代碼如程序清單 1.4所示。
    程序清單 1.4  定義并注冊spi_board_info

    [cpp] view plain copy
    1. static int __init smartarm3250_spi_usp_register(void)  

    2. {  

    3.          structspi_board_info info =  

    4.          {  

    5.                    .modalias= "spidev",  

    6.                    .max_speed_hz= 5000000,  

    7.                    .bus_num= 0,  

    8.                    .chip_select= 0,  

    9.          };  

    10.    

    11.          returnspi_register_board_info(&info, 1);  

    12. }  

    13. arch_initcall(smartarm3250_spi_usp_register);  




           由于3250內(nèi)核代碼在arch/arm/mach-lpc32xx/board-smartarm3250.c已經(jīng)定義了一個smartarm3250_spi_eeprom_register函數(shù),因此在增加程序清單 1.4代碼前先將這個函數(shù)注釋掉。
    程序清單 1.4注冊了一個掛在0號SPI總線上的設(shè)備信息,它的片選號為0。增加完這段代碼后將內(nèi)核重新編譯。在內(nèi)核啟動的時候,會為這個設(shè)備建立一個spi_device并和0號SPI總線的驅(qū)動進行綁定。同時內(nèi)核會為這個設(shè)備申請一個主設(shè)備號為153的的設(shè)備號,次設(shè)備號和注冊的順序有關(guān),最多支持32個同類設(shè)備。
    內(nèi)核重新編譯并重啟之后,如果系統(tǒng)中運行了udev,/dev下就會生成一個spidevX.D設(shè)備節(jié)點,其中X是總線編號,D是片選號。對于程序清單 1.4的代碼應該自動生成的設(shè)備節(jié)點是spidev0.0。
           一般SPI控制器驅(qū)動由芯片廠商提供,開發(fā)者所要在內(nèi)核做的工作就是添加類似程序清單 1.4的內(nèi)容。這樣內(nèi)核空間的工作減少了,用戶空間的工作量加大了,因為用戶空間的開發(fā)者需要全面了解SPI設(shè)備的工作方式和接口協(xié)議。



    1.3     用戶空間同設(shè)備節(jié)點的接口
           對于/dev/spidevX.D設(shè)備節(jié)點,可以進行各種操作,這一小節(jié)介紹它支持的函數(shù)接口。
    1.  open/close
           打開和關(guān)閉設(shè)備節(jié)點沒有特別之處,直接使用open/write就可以了。
    2.  read/write
           讀寫SPI設(shè)備可以直接使用read/write函數(shù),但是每次讀或者寫的大小不能大于4096Byte。
    3.  IOCTL命令
           用戶空間對spidev設(shè)備節(jié)點使用IOCTL命令失敗會返回-1。
    l        SPI_IOC_RD_MODE
    讀取SPI設(shè)備對應的spi_device.mode,mode的含義請參考程序清單 1.1。使用的方法如下:
                       ioctl(fd,SPI_IOC_RD_MODE, &mode);
                  其中第三個參數(shù)是一個uint8_t類型的變量。
    l        SPI_IOC_WR_MODE
    設(shè)置SPI設(shè)備對應的spi_device.mode。使用的方式如下:
                       ioctl(fd,SPI_IOC_WR_MODE, &mode);
    l SPI_IOC_RD_LSB_FIRST
    查看設(shè)備傳輸?shù)臅r候是否先傳輸?shù)捅忍匚?。如果是的話,返?。使用的方式如下:
                       ioctl(fd,SPI_IOC_RD_LSB_FIRST, &lsb);
                  其中l(wèi)sb是一個uint8_t類型的變量。返回的結(jié)果存在lsb中。
    l SPI_IOC_WR_LSB_FIRST
    設(shè)置設(shè)備傳輸?shù)臅r候是否先傳輸?shù)捅忍匚弧.攤魅敕橇愕臅r候,低比特在前,當傳入0的時候高比特在前(默認)。使用的方式如下:
                       ioctl(fd,SPI_IOC_WR_LSB_FIRST, &lsb);
    l SPI_IOC_RD_BITS_PER_WORD
    讀取SPI設(shè)備的字長。使用的方式如下:
                       ioctl(fd,SPI_IOC_RD_BITS_PER_WORD, &bits);
                  其中bits是一個uibt8_t類型的變量。返回的結(jié)果保存在bits中。
    l SPI_IOC_WR_BITS_PER_WORD
    設(shè)置SPI通信的字長。使用的方式如下:
                       ioctl(fd,SPI_IOC_WR_BITS_PER_WORD, &bits);
    l SPI_IOC_RD_MAX_SPEED_HZ
    讀取SPI設(shè)備的通信的最大時鐘頻率。使用的方式如下:
                       ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ, &speed);
                  其中speed是一個uint32_t類型的變量。返回的結(jié)果保存在speed中。
    l SPI_IOC_WR_MAX_SPEED_HZ
    設(shè)置SPI設(shè)備的通信的最大時鐘頻率。使用的方式如下:
                       ioctl(fd,SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    l SPI_IOC_MESSAGE(N)
    一次進行雙向/多次讀寫操作。使用的方式如下:
                       structspi_ioc_transfer  xfer[2];
                       ......
                       status= ioctl(fd, SPI_IOC_MESSAGE(2), xfer);
    其中N是本次通信中xfer的數(shù)組長度。spi_ioc_transfer的信息請參考程序清單 1.2。


    /************************************************************************************/
    如果想要在用戶空間編寫spi驅(qū)動,這就要在內(nèi)核的arch/.../mach-*/board-*.c 中聲明一個spi_board_info,
    它的名字一定要是“spidev”,比如:


    [cpp] view plain copy
    1. struct spi_board_info info =  

    2. {  

    3.  .modalias = "spidev",  

    4.  .max_speed_hz = 5000000,  

    5.  .bus_num = 0,  

    6.  .chip_select = 0,  

    7. };  



     return spi_register_board_info(&info, 1);
    這樣只要控制器驅(qū)動加載了,spidev模塊就會和這個設(shè)備綁定,并為設(shè)備申請一個設(shè)備號,主設(shè)備號為153,次設(shè)備號和設(shè)備加載的次序有關(guān)。
    目前spidev支持最多32個設(shè)備。設(shè)備的名字是spidevX.D,其中X是總線編號,D是設(shè)備的片選號。如果正確安裝并配置了udev,/dev目錄下便會生成spidevX.D
    設(shè)備節(jié)點。直接對這些設(shè)備節(jié)點操作就行了。
     
     
    spidev的設(shè)備節(jié)點的接口包括open/close/read/write/ioctl。
    ~~~~~~~~~~~~~~~~~~~~~~~~~
    其中open/close沒有什么特別之處。
    read/write的話有大小的限制,讀寫的大小默認不能超過4096字節(jié)。這個大小是一個模塊加載參數(shù),可以修改。
    允許多個用戶同時打開設(shè)備節(jié)點,spidev使用mutext進行互斥,多個用戶同時讀寫時只有一個活動的用戶,其他用戶睡眠。
     
     
    spidev的ioctl命令。
    ~~~~~~~~
    SPI_IOC_RD_MODE:讀取spi_device的mode。
    SPI_IOC_RD_LSB_FIRST:如果是SPI_LSB_FIRST的方式則返回1。
    SPI_IOC_RD_BITS_PER_WORD:讀取spi_device的bits_per_word.
    SPI_IOC_RD_MAX_SPEED_HZ:讀取spi_device的max_speed_hz.
    SPI_IOC_WR_MODE:設(shè)置spi_device的mode,并調(diào)用spi_setup立即使設(shè)置生效。
    SPI_IOC_WR_LSB_FIRST:設(shè)置spi使用SPI_LSB_FIRST的傳輸模式。立即生效。
    SPI_IOC_WR_BITS_PER_WORD:讀取字長。
    SPI_IOC_WR_MAX_SPEED_HZ:設(shè)置時鐘速率。
    無論讀取,用戶傳輸?shù)牡谌齻€參數(shù)都被當作緩沖地址指針。讀取時存放結(jié)果,寫入時存放要寫的內(nèi)容。

    SPI_IOC_MESSAGE:這個命令用來進行復雜的通信。參數(shù)涉及到一個結(jié)構(gòu)體。各個成員的意義與spi_transfer一致。

    [cpp] view plain copy
    1. struct spi_ioc_transfer {  

    2.  __u64  tx_buf;  

    3.  __u64  rx_buf;  

    4.  __u32  len;  

    5.  __u32  speed_hz;  

    6.  __u16  delay_usecs;  

    7.  __u8  bits_per_word;  

    8.  __u8  cs_change;  

    9.  __u32  pad;  

    10.  /* If the contents of 'struct spi_ioc_transfer' ever change 

    11.   * incompatibly, then the ioctl number (currently 0) must change; 

    12.   * ioctls with constant size fields get a bit more in the way of 

    13.   * error checking than ones (like this) where that field varies. 

    14.   * 

    15.   * NOTE: struct layout is the same in 64bit and 32bit userspace. 

    16.   */  

    17. };  



    內(nèi)核文檔中一個例子:

    [cpp] view plain copy
    1. static void do_msg(int fd, int len)    

    2. {    

    3.  struct spi_ioc_transfer xfer[2];    

    4.  unsigned char  buf[32], *bp;    

    5.  int   status;    

    6.  memset(xfer, 0, sizeof xfer);    

    7.  memset(buf, 0, sizeof buf);    

    8.  if (len > sizeof buf)    

    9.   len = sizeof buf;    

    10.  buf[0] = 0xaa;    

    11.  xfer[0].tx_buf = (__u64) buf;    

    12.  xfer[0].len = 1;    

    13.  xfer[1].rx_buf = (__u64) buf;    

    14.  xfer[1].len = len;    

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

    16.  if (status < 0) {    

    17.   perror("SPI_IOC_MESSAGE");    

    18.   return;    

    19.  }    

    20.  printf("response(%2d, %2d): ", len, status);    

    21.  for (bp = buf; len; len--)    

    22.   printf(" %02x", *bp++);    

    23.  printf("/n");    

    24. }    




    內(nèi)核在documentation/spi目錄下有spidev的例子。

    注意
    ~~~~
    雖然多個用戶不能同一時刻對spi進行設(shè)置或讀寫,但是同一用戶卻無法組織其他用戶修改同一設(shè)備的設(shè)置。
    舉例來說,usr1打開設(shè)備節(jié)點,然后使用ioctl設(shè)置了時鐘速率,此時usr1線程被調(diào)度出去,然后usr2操作同一個設(shè)備,將它的時鐘設(shè)為另一個值。
    此時usr1重新調(diào)度去使用read函數(shù),則達不到預期的效果。
    建議不要有兩個程序操作spidevX.D設(shè)備節(jié)點。

    *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。



    關(guān)鍵詞: spi

    相關(guān)推薦

    技術(shù)專區(qū)

    關(guān)閉