日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

转载:Linux kernel SPI驱动解释

發布時間:2024/9/5 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转载:Linux kernel SPI驱动解释 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

From:?http://www.cnblogs.com/liugf05/archive/2012/12/03/2800457.html

下面有兩個大的模塊:

一個是SPI總線驅動的分析????????? ? (研究了具體實現的過程)

另一個是SPI總線驅動的編寫(不用研究具體的實現過程)

SPI總線驅動分析

?

1?SPI概述
????? SPI是英語Serial Peripheral interface的縮寫,顧名思義就是串行外圍設備接口,是Motorola首先在其MC68HCXX系列處理器上定義的。SPI接口主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字信號處理器和數字信號解碼器之間。SPI是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線,節約了芯片的管腳,同時為PCB的布局上節省空間,提供方便。
????? SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要4根線,事實上3根也可以。也是所有基于SPI的設備共有的,它們是SDI(數據輸入),SDO(數據輸出),SCLK(時鐘),CS(片選)。
????? MOSI(SDO):主器件數據輸出,從器件數據輸入。
????? MISO(SDI):主器件數據輸入,從器件數據輸出。
????? SCLK :時鐘信號,由主器件產生。
????? CS:從器件使能信號,由主器件控制。
????? 其中CS是控制芯片是否被選中的,也就是說只有片選信號為預先規定的使能信號時(高電位或低電位),對此芯片的操作才有效,這就允許在同一總線上連接多個SPI設備成為可能。需要注意的是,在具體的應用中,當一條SPI總線上連接有多個設備時,SPI本身的CS有可能被其他的GPIO腳代替,即每個設備的CS腳被連接到處理器端不同的GPIO,通過操作不同的GPIO口來控制具體的需要操作的SPI設備,減少各個SPI設備間的干擾。
????? SPI是串行通訊協議,也就是說數據是一位一位從MSB或者LSB開始傳輸的,這就是SCK時鐘線存在的原因,由SCK提供時鐘脈沖,MISO、MOSI則基于此脈沖完成數據傳輸。 SPI支持4-32bits的串行數據傳輸,支持MSB和LSB,每次數據傳輸時當從設備的大小端發生變化時需要重新設置SPI Master的大小端。

?

2?Linux SPI驅動總體架構
????? 在2.6的linux內核中,SPI的驅動架構可以分為如下三個層次:SPI 核心層、SPI控制器驅動層和SPI設備驅動層。
????? Linux 中SPI驅動代碼位于drivers/spi目錄。
2.1?SPI核心層
????? SPI核心層是Linux的SPI核心部分,提供了核心數據結構的定義、SPI控制器驅動和設備驅動的注冊、注銷管理等API。其為硬件平臺無關層,向下屏蔽了物理總線控制器的差異,定義了統一的訪問策略和接口;其向上提供了統一的接口,以便SPI設備驅動通過總線控制器進行數據收發。
????? Linux中,SPI核心層的代碼位于driver/spi/ spi.c。由于該層是平臺無關層,本文將不再敘述,有興趣可以查閱相關資料。
2.2?SPI控制器驅動層
????? SPI控制器驅動層,每種處理器平臺都有自己的控制器驅動,屬于平臺移植相關層。它的職責是為系統中每條SPI總線實現相應的讀寫方法。在物理上,每個SPI控制器可以連接若干個SPI從設備。
????? 在系統開機時,SPI控制器驅動被首先裝載。一個控制器驅動用于支持一條特定的SPI總線的讀寫。一個控制器驅動可以用數據結構struct spi_master來描述。

? ?在include/liunx/spi/spi.h文件中,在數據結構struct spi_master定義如下:

?

  • struct?spi_master?{??
  • ????struct?device???dev;??
  • ? ? s16?????????bus_num; ?
  • ? ? u16?????????num_chipselect; ?
  • ? ??int?????????(*setup)(struct?spi_device?*spi); ?
  • ? ??int?????????(*transfer)(struct?spi_device?*spi,?struct?spi_message?*mesg); ?
  • ? ??void? ? ? ? (*cleanup)(struct?spi_device?*spi); ?
  • }; ?
  • ?

    ?????bus_num為該控制器對應的SPI總線號。
    ????? num_chipselect?控制器支持的片選數量,即能支持多少個spi設備?
    ? ? ? setup函數是設置SPI總線的模式,時鐘等的初始化函數,?針對設備設置SPI的工作時鐘及數據傳輸模式等。在spi_add_device函數中調用。?
    ? ? ? transfer函數是實現SPI總線讀寫方法的函數。實現數據的雙向傳輸,可能會睡眠

    ????cleanup注銷時候調用

    2.3?SPI設備驅動層
    ????? SPI設備驅動層為用戶接口層,其為用戶提供了通過SPI總線訪問具體設備的接口。
    ????? SPI設備驅動層可以用兩個模塊來描述,struct spi_driver和struct spi_device。
    ????? 相關的數據結構如下:

    ?

  • struct?spi_driver?{ ?
  • ????int?????????(*probe)(struct?spi_device?*spi);??
  • ????int?????????(*remove)(struct?spi_device?*spi);??
  • ????void????????????(*shutdown)(struct?spi_device?*spi);??
  • ????int?????????(*suspend)(struct?spi_device?*spi,?pm_message_t?mesg);??
  • ????int?????????(*resume)(struct?spi_device?*spi);??
  • ????struct?device_driver????driver;??
  • };?
  • ?

    ? Driver是為device服務的,spi_driver注冊時會掃描SPI bus上的設備,進行驅動和設備的綁定,probe函數用于驅動和設備匹配時被調用。從上面的結構體注釋中我們可以知道,SPI的通信是通過消息隊列機制,而不是像I2C那樣通過與從設備進行對話的方式。

    ?

  • struct?spi_device?{??
  • ????struct?device???????dev;??
  • ????struct?spi_master???*master;??
  • ????u32?????????max_speed_hz;??
  • ????u8??????????chip_select;??
  • ????u8??????????mode; ???
  • ????u8??????????bits_per_word;??
  • ????int?????????irq;??
  • ????void????????????*controller_state;??
  • ????void????????????*controller_data;??
  • ????char????????????modalias[32]; ??
  • };?
  • ?

    ????????.modalias?? = "m25p10",

    ??????? .mode?? =SPI_MODE_0,?? //CPOL=0, CPHA=0 此處選擇具體數據傳輸模式

    ??????? .max_speed_hz??? = 10000000, //最大的spi時鐘頻率

    ??????? /* Connected to SPI-0 as 1st Slave */

    ??????? .bus_num??? = 0,?? //設備連接在spi控制器0上

    ??????? .chip_select??? = 0, //片選線號,在S5PC100的控制器驅動中沒有使用它作為片選的依據,而是選擇了下文controller_data里的方法。

    ? ? ? ? .controller_data = &smdk_spi0_csi[0],??

    通常來說spi_device對應著SPI總線上某個特定的slave。并且spi_device封裝了一個spi_master結構體。spi_device結構體包含了私有的特定的slave設備特性,包括它最大的頻率,片選那個,輸入輸出模式等等

    3?OMAP3630 SPI控制器
    ????? OMAP3630上SPI是一個主/從的同步串行總線,這邊有4個獨立的SPI模塊(SPI1,SPI2,SPI3,SPI4),各個模塊之間的區別在于SPI1支持多達4個SPI設備,SPI2和SPI3支持2個SPI設備,而SPI4只支持1個SPI設備。

    SPI控制器具有以下特征:
    ????? 1.可編程的串行時鐘,包括頻率,相位,極性。
    ????? 2.支持4到32位數據傳輸
    ????? 3.支持4通道或者單通道的從模式
    ????? 4.支持主的多通道模式
    ????????? 4.1全雙工/半雙工
    ????????? 4.2只發送/只接收/收發都支持模式
    ????????? 4.3靈活的I/O端口控制
    ????????? 4.4每個通道都支持DMA讀寫
    ????? 5.支持多個中斷源的中斷時間
    ????? 6.支持wake-up的電源管理
    ????? 7.內置64字節的FIFO

    ?

    4 spi_device以下一系列的操作是在platform板文件中完成!

    spi_device的板信息用spi_board_info結構體來描述: struct spi_board_info { charmodalias[SPI_NAME_SIZE]; const void*platform_data; void*controller_data; intirq; u32max_speed_hz; u16bus_num; u16chip_select; u8mode; };

    ?

    這個結構體記錄了SPI外設使用的主機控制器序號、片選信號、數據比特率、SPI傳輸方式等

    構建的操作是以下的兩個步驟:

    1.

    ?

    static struct spi_board_info s3c_spi_devs[] __initdata = {

    {

    .modalias = "m25p10a",

    .mode = SPI_MODE_0,

    .max_speed_hz = 1000000,

    .bus_num = 0,

    .chip_select = 0,

    .controller_data = &smdk_spi0_csi[SMDK_MMCSPI_CS],

    },

    };

    2.

    ?

    而這個info在init函數調用的時候會初始化:

    spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));

    ?

    spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//注冊spi_board_info。這個代碼會把spi_board_info注冊到鏈表board_list上。spi_device封裝了一個spi_master結構體,事實上spi_master的注冊會在spi_register_board_info之后,spi_master注冊的過程中會調用scan_boardinfo掃描board_list,找到掛接在它上面的spi設備,然后創建并注冊spi_device。

    ?

    至此spi_device就構建并注冊完成了!!!!!!!!!!!!!

    ?

    5 spi_driver的構建與注冊

    ?

    driver有幾個重要的結構體:spi_driver、spi_transfer、spi_message

    driver有幾個重要的函數? ? :spi_message_init、spi_message_add_tail、spi_sync

    ?

    ? ?//spi_driver的構建

    static struct spi_driver ? m25p80_driver = {?

    .driver = {

    ??????? .name?? ="m25p80",

    ??????? .bus??? =&spi_bus_type,

    ??????? .owner? = THIS_MODULE,

    ??? },

    ??? .probe? = m25p_probe,

    ??? .remove =__devexit_p(m25p_remove),

    };

    //spidriver的注冊

    ?

    spi_register_driver(&m25p80_driver);

    在有匹配的spi_device時,會調用m25p_probe

    ?

    probe里完成了spi_transfer、spi_message的構建;

    spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函數的調用

    ?

    例如:

    ?

  • ?*/??
  • static?int?m25p10a_read(?struct?m25p10a?*flash,?loff_t?from,???
  • ????????size_t?len,?char?*buf?)??
  • {??
  • ????int?r_count?=?0,?i;??
  • ? ? struct?spi_transfer?st[2]; ?
  • ????struct?spi_message??msg;??
  • ??????
  • ????spi_message_init(?&msg?);??
  • ????memset(?st,?0,?sizeof(st)?);??
  • ??
  • ????flash->cmd[0]?=?CMD_READ_BYTES;??
  • ????flash->cmd[1]?=?from?>>?16;??
  • ????flash->cmd[2]?=?from?>>?8;??
  • ????flash->cmd[3]?=?from;??
  • ??
  • ????st[?0?].tx_buf?=?flash->cmd;??
  • ????st[?0?].len?=?CMD_SZ;??
  • ????spi_message_add_tail(?&st[0],?&msg?);??
  • ??
  • ????st[?1?].rx_buf?=?buf;??
  • ????st[?1?].len?=?len;??
  • ????spi_message_add_tail(?&st[1],?&msg?);??
  • ??
  • ????mutex_lock(?&flash->lock?);??
  • ??????
  • ????/*?Wait?until?finished?previous?write?command.?*/??
  • ????if?(wait_till_ready(flash))?{??
  • ????????mutex_unlock(?&flash->lock?);??
  • ????????return?-1;??
  • ????}??
  • ??
  • ????spi_sync(?flash->spi,?&msg?);??
  • ????r_count?=?msg.actual_length?-?CMD_SZ;??
  • ????printk(?"in?(%s):?read?%d?bytes\n",?__func__,?r_count?);??
  • ????for(?i?=?0;?i?<?r_count;?i++?)?{??
  • ????????printk(?"0x%02x\n",?buf[?i?]?);??
  • ????}??
  • ??
  • ????mutex_unlock(?&flash->lock?);??
  • ????return?0;??
  • }??
  • static?int?m25p10a_write(?struct?m25p10a?*flash,?loff_t?to,???
  • ????????size_t?len,?const?char?*buf?)??
  • {??
  • ????int?w_count?=?0,?i,?page_offset;
  • ??
  • ????struct?spi_transfer?st[2];?
  • ?
  • ????struct?spi_message??msg;??
  • ????write_enable(?flash?);??//寫使能??
  • ??????
  • ????????spi_message_init(?&msg?); ?

  • ????memset(?st,?0,?sizeof(st)?);??
  • ??
  • ????flash->cmd[0]?=?CMD_PAGE_PROGRAM;??
  • ????flash->cmd[1]?=?to?>>?16;??
  • ????flash->cmd[2]?=?to?>>?8;??
  • ????flash->cmd[3]?=?to;??
  • ??
  • ????st[?0?].tx_buf?=?flash->cmd;??
  • ????st[?0?].len?=?CMD_SZ; ?
  • ??//填充spi_transfer,將transfer放在隊列后面
  • ????spi_message_add_tail(?&st[0],?&msg?);??
  • ??
  • ????st[?1?].tx_buf?=?buf;??
  • ????st[?1?].len?=?len;??
  • ????spi_message_add_tail(?&st[1],?&msg?);??
  • ??
  • ?
  • ????????spi_sync(?flash->spi,?&msg?);???調用spi_master發送spi_message
  • ? ??
  • ????return?0;??
  • }?
  • ?

    ?

  • ??
  • static?int?m25p10a_probe(struct?spi_device?*spi)???
  • {???
  • ????int?ret?=?0;??
  • ????struct?m25p10a??*flash;??
  • ????char?buf[?256?]; ?
  • ????flash?=?kzalloc(?sizeof(struct?m25p10a),?GFP_KERNEL?); ?
  • ????flash->spi?=?spi; ?
  • ????/*?save?flash?as?driver's?private?data?*/??
  • ????spi_set_drvdata(?spi,?flash?);????
  • ????memset(?buf,?0x7,?256?);??
  • ????m25p10a_write(?flash,?0,?20,?buf);?//0地址寫入20個7??
  • ????memset(?buf,?0,?256?);??
  • ????m25p10a_read(?flash,?0,?25,?buf?);?//0地址讀出25個數??
  • ?
  • ????return?0;???
  • } ??
  • ?

    ?

    ?

    ?

    到目前為止,完成了SPI的驅動和應用

    轉載于:https://www.cnblogs.com/super119/archive/2012/12/04/2801159.html

    總結

    以上是生活随笔為你收集整理的转载:Linux kernel SPI驱动解释的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。