国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

嵌入式開發之linux spi驅動分析

發布時間:2017-7-13 09:40    發布者:技術小白
關鍵詞: 嵌入式 , Linux , SPI驅動
嵌入式開發之linux spi驅動分析

關于spi的學習,最好的方法當然是看Linux的源代碼,主要是driver/spi/spi.c(h),spidev.c(h)。spi dev的示例可以看看at25.c,spi總線的示例可以看omap_uwire或者spi_s3c24xx.c和spi_s3c24xx_gpio.c。在看這些代碼之前,需要對linux的設備模型有一定的了解。

另外,網上有兩篇教程不錯,《linux spi子系統驅動分析》以及《linux spi子系統 驅動分析 續》,如果嵌友有興趣可以百度一下。

下面是我們整理的關于SPI的一些經驗心得。

SPI子系統
spi子系統中,spi設備用struct spi_dev描述,它的驅動程序用struct spi_driver描述。spi總線設備用struct spi_master描述。另外,還有兩個重要的全局變量:
struct bus_type spi_bus_type = {
       .name             = "spi",
       .dev_attrs       = spi_dev_attrs,
       .match           = spi_match_device,
       .uevent           = spi_uevent,
       .suspend  = spi_suspend,
       .resume          = spi_resume,
};

static struct class spi_master_class = {
       .name             = "spi_master",
       .owner           = THIS_MODULE,
       .dev_release    = spi_master_release,
};
spi_bus_type對應sys中的spi bus總線,Linux設備模型對這個結構體有詳細介紹。

所有spi_master對應的spi總線都屬于spi_master_class,也就是說是一個虛擬設備,它的父設備可能是物理設備,比如platform_device等等,s3c2410就是這種情況。

SPI設備

SPI設備的驅動程序通過spi_register_driver注冊進SPI子系統,驅動類型為struct spi_driver。典型例子如at25.c。
static struct spi_driver at25_driver = {
       .driver = {
              .name             = "at25",
              .owner           = THIS_MODULE,
       },
       .probe            = at25_probe,
       .remove          = __devexit_p(at25_remove),
};

因為spi總線不支持SPI設備的自動檢測,所以一般在spi的probe函數中不會檢測設備是否存在,而是做一些spi設備的初始化工作。

spi驅動中可以調用下列函數進行spi的傳輸操作:
static inline int spi_write(struct spi_device *spi, const u8 *buf, size_t len);
static inline int spi_read(struct spi_device *spi, u8 *buf, size_t len);
extern int spi_write_then_read(struct spi_device *spi,        const u8 *txbuf, unsigned n_tx,
              u8 *rxbuf, unsigned n_rx);
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd);
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);

由于spi設備不能被spi總線動態掃描,所以spi子系統使用了另一種方法,就是通過spi_register_board_info函數將spi設備靜態得登記到系統中。
int __init spi_register_board_info(struct spi_board_info const *info, unsigned n);
struct spi_board_info {
       char        modalias[32];                // 設備名
       const void      *platform_data;      // 私有數據,會被設置到spi_device.dev.platform_data
       void        *controller_data;           // 私有數據,會被設置到spi_device.controller_data
       int           irq;                              // 中斷號
       u32         max_speed_hz;             // 最大速率
       u16         bus_num;                            // 用于關聯spi_master
       u16         chip_select;                  // 與片選有關
       u8           mode;                          // spi_device.mode
};

在具體平臺的文件中,可以定義struct spi_board_info的結構體,然后通過spi_register_board_info函數保存這些結構體,最后在scan_boardinfo函數中根據這些保存的結構體創建spi設備(spi_new_device)。

spi_new_device用于登記spi設備,這里面又分兩步,首先是spi_alloc_device,然后是spi_add_device。

struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip)
       spi_dev* pdev = spi_alloc(master);
       proxy->chip_select = chip->chip_select;
       proxy->max_speed_hz = chip->max_speed_hz;
       proxy->mode = chip->mode;
       proxy->irq = chip->irq;
       strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
       proxy->dev.platform_data = (void *) chip->platform_data;
       proxy->controller_data = chip->controller_data;
       proxy->controller_state = NULL;
       spi_add_device(proxy);

struct spi_device *spi_alloc_device(struct spi_master *master)
struct device * dev = master->dev.parent;
       struct spi_dev * spi = kzalloc(sizeof *spi, GFP_KERNEL);
       spi->master = master;
       spi->dev.parent = dev;
       spi->dev.bus = &spi_bus_type;
       spi->dev.release = spidev_release;
       device_initialize(&spi->dev);

這里spi_dev的父設備被指定為master的父設備,而master是spi總線設備,擁有class,是一個虛擬設備。也就是說,spi設備和與之對應的總線設備擁有同一個父設備,這個父設備一般來說是一個物理設備。

int spi_add_device(struct spi_device *spi)
       snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id, "%s.%u", spi->master->dev.bus_id,
                     spi->chip_select);
       status = spi->master->setup(spi);
status = device_add(&spi->dev);

spi總線
struct spi_master {
       struct device   dev;
s16   bus_num;                         // 總線號,如果板子上有多個spi總線,靠這個域區分;另外,spi_dev中也有bus_num,spi_dev通過這個域找到它所屬的總線。
       u16    num_chipselect;         // 片選號,如果一個spi總線有多個設備,
       /* setup mode and clock, etc (spi driver may call many times) */
       int                  (*setup)(struct spi_device *spi);
       int                  (*transfer)(struct spi_device *spi,       struct spi_message *mesg);
       /* called on release() to free memory provided by spi_master */
       void               (*cleanup)(struct spi_device *spi);
};

登記spi總線
struct spi_master *spi_alloc_master(struct device *dev, unsigned size);

int spi_register_master(struct spi_master *master);
       scan_boardinfo(master);

spi_register_master中會調用scan_boardinfo。scan_boardinfo中,會掃描前面保存的boardinfo,看新注冊的master中的bus_num是否與boardinfo中bus_num匹配,如果匹配,那就調用spi_new_device創建spi設備,并登記到spi子系統中。

setup函數

setup函數會做一些初始化工作。比如,根據spi設備的速率,設備paster的位傳輸定時器;設置spi傳輸類型;等等。

spi_add_device函數中,會先調用setup函數,然后再調用device_add。這是因為device_add中會調用到driver的probe函數,而probe函數中可能會對spi設備做IO操作。所以spi子系統就先調用setup為可能的IO操作做好準備。

但是,在代碼中,setup函數似乎也就只在這一個地方被調用。具體傳輸過程中切換spi設備時也要做配置工作,但這里的配置工作就由具體傳輸的實現代碼決定了,可以看看spi_bitbang.c中的函數bitbang_work。

cleanup函數

cleanup函數會在spidev_release函數中被調用,spidev_release被登記為spi dev的release函數。

transfer函數

transfer函數用于spi的IO傳輸。但是,transfer函數一般不會執行真正的傳輸操作,而是把要傳輸的內容放到一個隊列里,然后調用一種類似底半部的機制進行真正的傳輸。這是因為,spi總線一般會連多個spi設備,而spi設備間的訪問可能會并發。如果直接在transfer函數中實現傳輸,那么會產生競態,spi設備互相間會干擾。

所以,真正的spi傳輸與具體的spi控制器的實現有關,spi的框架代碼中沒有涉及。像spi設備的片選、根據具體設備進行時鐘調整等等都在實現傳輸的代碼中被調用。

SPI的傳輸命令都是通過結構體spi_message定義。設備程序調用transfer函數將spi_message交給spi總線驅動,總線驅動再將message傳到底半部排隊,實現串行化傳輸。

struct spi_message {
       struct list_head       transfers;
       struct spi_device    *spi;
       unsigned         is_dma_mapped:1;
       void               (*complete)(void *context);
       void               *context;
       unsigned         actual_length;
       int                  status;
       struct list_head       queue;
       void               *state;
};

spi_message中,有一個transfers隊列,spi_transfer結構體通過這個隊列掛到spi_message中。一個spi_message代表一次傳輸會話,spi_transfer代表一次單獨的IO操作。比如,有些spi設備需要先讀后寫,那么這個讀寫過程就是一次spi會話,里面包括兩個transfer,一個定義寫操作的參數,另一個定義讀操作的參數。

spidev.c

如果不想為自己的SPI設備寫驅動,那么可以用Linux自帶的spidev.c提供的驅動程序。要使用spidev.c的驅動,只要在登記設備時,把設備名設置成spidev就可以。spidev.c會在device目錄下自動為每一個匹配的SPI設備創建設備節點,節點名”spi%d”。之后,用戶程序可以通過字符型設備的通用接口控制SPI設備。

需要注意的是,spidev創建的設備在設備模型中屬于虛擬設備,它的class是spidev_class。它的父設備是在boardinfo中定義的spi設備。


想學習想進步的你和我聯系預約就可以免費聽課了。
以下課程可免費試聽C語言電子PCBSTM32、Linux、FPGA、JAVA、安卓等。
宋工企鵝號:3524-6590-88   Tel/WX:173--1795--1908


本文地址:http://m.qingdxww.cn/thread-453333-1-1.html     【打印本頁】

本站部分文章為轉載或網友發布,目的在于傳遞和分享信息,并不代表本網贊同其觀點和對其真實性負責;文章版權歸原作者及原出處所有,如涉及作品內容、版權和其它問題,我們將根據著作權人的要求,第一時間更正或刪除。
您需要登錄后才可以發表評論 登錄 | 立即注冊

廠商推薦

  • Microchip視頻專區
  • Dev Tool Bits——使用MPLAB® Discover瀏覽資源
  • Dev Tool Bits——使用條件軟件斷點宏來節省時間和空間
  • Dev Tool Bits——使用DVRT協議查看項目中的數據
  • Dev Tool Bits——使用MPLAB® Data Visualizer進行功率監視
  • 貿澤電子(Mouser)專區

相關視頻

關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产精华推荐20212 | 国产在线观看青草视频 | 国产成人aa免费视频 | 久久久国产99久久国产一 | 欧美亚洲国产另类 | 国产精品资源在线播放 | 土地公土地婆免费观看全集 | 国产精品人成 | 国产xxxxx在线播放 | 日本亚洲天堂 | 国模肉肉大尺度啪啪人体150p | 最近韩国高清免费观看 | 久久成年视频 | 成 人在线观看视频网站 | 亚洲国产亚洲片在线观看播放 | 韩国一级黄色大片 | 国产欧美日本亚洲精品五区 | 亚洲欧洲一二三区 | 四虎影视永久地址 | 青青青国产依人在在线观看高 | 亚洲不卡视频在线观看 | 在线天堂新版在线观看 | 欧美视频在线观看免费最新 | 欧美特黄视频在线观看 | 欧美亚洲视频在线观看 | 亚洲国产成人精品激情 | 日本亚洲成高清一区二区三区 | 日韩视频一区二区 | 国产精品91在线播放 | 一区二区三区四区在线观看视频 | 手机看片1024久久 | 久久成人永久免费播放 | 狠狠久久久久久亚洲综合网 | 91精品影视 | 国产在线91精品入口 | 麻豆久久婷婷国产综合五月 | 欧美国产日韩一区 | 美国农夫激情在线综合 | 亚洲一区视频在线播放 | 亚洲精品免播放器在线观看 | 国内精品伊人久久 |