<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>
"); //-->

博客專(zhuān)欄

EEPW首頁(yè) > 博客 > spi應用層寫(xiě)法

spi應用層寫(xiě)法

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

【重點(diǎn)是  傳輸的靈活應用,想要每次傳輸之間沒(méi)有間隔,或者小點(diǎn)】

1.1     重要的數據結構
1.  spi_device
雖然用戶(hù)空間不需要直接用到spi_device結構體,但是這個(gè)結構體和用戶(hù)空間的程序有密切的關(guān)系,理解它的成員有助于理解SPI設備節點(diǎn)的IOCTL命令,所以首先來(lái)介紹它。
在內核中,每個(gè)spi_device代表一個(gè)物理的SPI設備。它的成員如程序清單 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;    /* 通信時(shí)鐘最大頻率 */  

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

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

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

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

  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時(shí)片選的有效信號是高電平*/  

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

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

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

  17.          u8                        bits_per_word;    /* 每個(gè)通信字的字長(cháng)(比特數) */  

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

  19.          void                     *controller_state;  

  20.          void                     *controller_data;  

  21.          char                     modalias[32];      /* 設備驅動(dòng)的名字*/  

  22. };  





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



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

[cpp] view plain copy
  1. struct spi_ioc_transfer {  

  2.             __u64               tx_buf;                   /* 寫(xiě)數據緩沖  */  

  3.             __u64               rx_buf;                   /* 讀數據緩沖  */  

  4.    

  5.             __u32               len;                      /* 緩沖的長(cháng)度 */  

  6.             __u32               speed_hz;                 /* 通信的時(shí)鐘頻率 */  

  7.    

  8.             __u16               delay_usecs;    /* 兩個(gè)spi_ioc_transfer之間的延時(shí) */  

  9.             __u8                 bits_per_word;           /* 字長(cháng)(比特數)  */  

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

  11.             __u32               pad;                                

  12. };  





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



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

[cpp] view plain copy
  1. struct spi_board_info {  

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

  3.          constvoid  *platform_data;                                          

  4.          void            *controller_data;  

  5.          int               irq;              

  6.    

  7.          u32             max_speed_hz;          /* 通信時(shí)鐘最大速率 */  

  8.    

  9.          u16             bus_num;              /* 總線(xiàn)編號  */  

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

  11.    

  12.          u8               mode;                 /* 和spi_device中的mode成員類(lèi)似   */  

  13. };  



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



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


/************************************************************************************/
如果想要在用戶(hù)空間編寫(xiě)spi驅動(dòng),這就要在內核的arch/.../mach-*/board-*.c 中聲明一個(gè)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);
這樣只要控制器驅動(dòng)加載了,spidev模塊就會(huì )和這個(gè)設備綁定,并為設備申請一個(gè)設備號,主設備號為153,次設備號和設備加載的次序有關(guān)。
目前spidev支持最多32個(gè)設備。設備的名字是spidevX.D,其中X是總線(xiàn)編號,D是設備的片選號。如果正確安裝并配置了udev,/dev目錄下便會(huì )生成spidevX.D
設備節點(diǎn)。直接對這些設備節點(diǎn)操作就行了。
 
 
spidev的設備節點(diǎn)的接口包括open/close/read/write/ioctl。
~~~~~~~~~~~~~~~~~~~~~~~~~
其中open/close沒(méi)有什么特別之處。
read/write的話(huà)有大小的限制,讀寫(xiě)的大小默認不能超過(guò)4096字節。這個(gè)大小是一個(gè)模塊加載參數,可以修改。
允許多個(gè)用戶(hù)同時(shí)打開(kāi)設備節點(diǎn),spidev使用mutext進(jìn)行互斥,多個(gè)用戶(hù)同時(shí)讀寫(xiě)時(shí)只有一個(gè)活動(dòng)的用戶(hù),其他用戶(hù)睡眠。
 
 
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:設置spi_device的mode,并調用spi_setup立即使設置生效。
SPI_IOC_WR_LSB_FIRST:設置spi使用SPI_LSB_FIRST的傳輸模式。立即生效。
SPI_IOC_WR_BITS_PER_WORD:讀取字長(cháng)。
SPI_IOC_WR_MAX_SPEED_HZ:設置時(shí)鐘速率。
無(wú)論讀取,用戶(hù)傳輸的第三個(gè)參數都被當作緩沖地址指針。讀取時(shí)存放結果,寫(xiě)入時(shí)存放要寫(xiě)的內容。

SPI_IOC_MESSAGE:這個(gè)命令用來(lái)進(jìn)行復雜的通信。參數涉及到一個(gè)結構體。各個(gè)成員的意義與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. };  



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

[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. }    




內核在documentation/spi目錄下有spidev的例子。

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

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



關(guān)鍵詞: spi

相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>