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

嵌入式開(kāi)發(fā)之linux spi驅(qū)動(dòng)分析

發(fā)布時(shí)間:2017-7-13 09:40    發(fā)布者:技術(shù)小白
關(guān)鍵詞: 嵌入式 , Linux , SPI驅(qū)動(dòng)
嵌入式開(kāi)發(fā)之linux spi驅(qū)動(dòng)分析

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

另外,網(wǎng)上有兩篇教程不錯(cuò),《linux spi子系統(tǒng)驅(qū)動(dòng)分析》以及《linux spi子系統(tǒng) 驅(qū)動(dòng)分析 續(xù)》,如果嵌友有興趣可以百度一下。

下面是我們整理的關(guān)于SPI的一些經(jīng)驗(yàn)心得。

SPI子系統(tǒng)
spi子系統(tǒng)中,spi設(shè)備用struct spi_dev描述,它的驅(qū)動(dòng)程序用struct spi_driver描述。spi總線(xiàn)設(shè)備用struct spi_master描述。另外,還有兩個(gè)重要的全局變量:
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對(duì)應(yīng)sys中的spi bus總線(xiàn),Linux設(shè)備模型對(duì)這個(gè)結(jié)構(gòu)體有詳細(xì)介紹。

所有spi_master對(duì)應(yīng)的spi總線(xiàn)都屬于spi_master_class,也就是說(shuō)是一個(gè)虛擬設(shè)備,它的父設(shè)備可能是物理設(shè)備,比如platform_device等等,s3c2410就是這種情況。

SPI設(shè)備

SPI設(shè)備的驅(qū)動(dòng)程序通過(guò)spi_register_driver注冊(cè)進(jìn)SPI子系統(tǒng),驅(qū)動(dòng)類(lèi)型為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),
};

因?yàn)閟pi總線(xiàn)不支持SPI設(shè)備的自動(dòng)檢測(cè),所以一般在spi的probe函數(shù)中不會(huì)檢測(cè)設(shè)備是否存在,而是做一些spi設(shè)備的初始化工作。

spi驅(qū)動(dòng)中可以調(diào)用下列函數(shù)進(jìn)行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設(shè)備不能被spi總線(xiàn)動(dòng)態(tài)掃描,所以spi子系統(tǒng)使用了另一種方法,就是通過(guò)spi_register_board_info函數(shù)將spi設(shè)備靜態(tài)得登記到系統(tǒng)中。
int __init spi_register_board_info(struct spi_board_info const *info, unsigned n);
struct spi_board_info {
       char        modalias[32];                // 設(shè)備名
       const void      *platform_data;      // 私有數(shù)據(jù),會(huì)被設(shè)置到spi_device.dev.platform_data
       void        *controller_data;           // 私有數(shù)據(jù),會(huì)被設(shè)置到spi_device.controller_data
       int           irq;                              // 中斷號(hào)
       u32         max_speed_hz;             // 最大速率
       u16         bus_num;                            // 用于關(guān)聯(lián)spi_master
       u16         chip_select;                  // 與片選有關(guān)
       u8           mode;                          // spi_device.mode
};

在具體平臺(tái)的文件中,可以定義struct spi_board_info的結(jié)構(gòu)體,然后通過(guò)spi_register_board_info函數(shù)保存這些結(jié)構(gòu)體,最后在scan_boardinfo函數(shù)中根據(jù)這些保存的結(jié)構(gòu)體創(chuàng)建spi設(shè)備(spi_new_device)。

spi_new_device用于登記spi設(shè)備,這里面又分兩步,首先是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的父設(shè)備被指定為master的父設(shè)備,而master是spi總線(xiàn)設(shè)備,擁有class,是一個(gè)虛擬設(shè)備。也就是說(shuō),spi設(shè)備和與之對(duì)應(yīng)的總線(xiàn)設(shè)備擁有同一個(gè)父設(shè)備,這個(gè)父設(shè)備一般來(lái)說(shuō)是一個(gè)物理設(shè)備。

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總線(xiàn)
struct spi_master {
       struct device   dev;
s16   bus_num;                         // 總線(xiàn)號(hào),如果板子上有多個(gè)spi總線(xiàn),靠這個(gè)域區(qū)分;另外,spi_dev中也有bus_num,spi_dev通過(guò)這個(gè)域找到它所屬的總線(xiàn)。
       u16    num_chipselect;         // 片選號(hào),如果一個(gè)spi總線(xiàn)有多個(gè)設(shè)備,
       /* 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總線(xiàn)
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中會(huì)調(diào)用scan_boardinfo。scan_boardinfo中,會(huì)掃描前面保存的boardinfo,看新注冊(cè)的master中的bus_num是否與boardinfo中bus_num匹配,如果匹配,那就調(diào)用spi_new_device創(chuàng)建spi設(shè)備,并登記到spi子系統(tǒng)中。

setup函數(shù)

setup函數(shù)會(huì)做一些初始化工作。比如,根據(jù)spi設(shè)備的速率,設(shè)備paster的位傳輸定時(shí)器;設(shè)置spi傳輸類(lèi)型;等等。

spi_add_device函數(shù)中,會(huì)先調(diào)用setup函數(shù),然后再調(diào)用device_add。這是因?yàn)閐evice_add中會(huì)調(diào)用到driver的probe函數(shù),而probe函數(shù)中可能會(huì)對(duì)spi設(shè)備做IO操作。所以spi子系統(tǒng)就先調(diào)用setup為可能的IO操作做好準(zhǔn)備。

但是,在代碼中,setup函數(shù)似乎也就只在這一個(gè)地方被調(diào)用。具體傳輸過(guò)程中切換spi設(shè)備時(shí)也要做配置工作,但這里的配置工作就由具體傳輸?shù)膶?shí)現(xiàn)代碼決定了,可以看看spi_bitbang.c中的函數(shù)bitbang_work。

cleanup函數(shù)

cleanup函數(shù)會(huì)在spidev_release函數(shù)中被調(diào)用,spidev_release被登記為spi dev的release函數(shù)。

transfer函數(shù)

transfer函數(shù)用于spi的IO傳輸。但是,transfer函數(shù)一般不會(huì)執(zhí)行真正的傳輸操作,而是把要傳輸?shù)膬?nèi)容放到一個(gè)隊(duì)列里,然后調(diào)用一種類(lèi)似底半部的機(jī)制進(jìn)行真正的傳輸。這是因?yàn)椋瑂pi總線(xiàn)一般會(huì)連多個(gè)spi設(shè)備,而spi設(shè)備間的訪(fǎng)問(wèn)可能會(huì)并發(fā)。如果直接在transfer函數(shù)中實(shí)現(xiàn)傳輸,那么會(huì)產(chǎn)生競(jìng)態(tài),spi設(shè)備互相間會(huì)干擾。

所以,真正的spi傳輸與具體的spi控制器的實(shí)現(xiàn)有關(guān),spi的框架代碼中沒(méi)有涉及。像spi設(shè)備的片選、根據(jù)具體設(shè)備進(jìn)行時(shí)鐘調(diào)整等等都在實(shí)現(xiàn)傳輸?shù)拇a中被調(diào)用。

SPI的傳輸命令都是通過(guò)結(jié)構(gòu)體spi_message定義。設(shè)備程序調(diào)用transfer函數(shù)將spi_message交給spi總線(xiàn)驅(qū)動(dòng),總線(xiàn)驅(qū)動(dòng)再將message傳到底半部排隊(duì),實(shí)現(xiàn)串行化傳輸。

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中,有一個(gè)transfers隊(duì)列,spi_transfer結(jié)構(gòu)體通過(guò)這個(gè)隊(duì)列掛到spi_message中。一個(gè)spi_message代表一次傳輸會(huì)話(huà),spi_transfer代表一次單獨(dú)的IO操作。比如,有些spi設(shè)備需要先讀后寫(xiě),那么這個(gè)讀寫(xiě)過(guò)程就是一次spi會(huì)話(huà),里面包括兩個(gè)transfer,一個(gè)定義寫(xiě)操作的參數(shù),另一個(gè)定義讀操作的參數(shù)。

spidev.c

如果不想為自己的SPI設(shè)備寫(xiě)驅(qū)動(dòng),那么可以用Linux自帶的spidev.c提供的驅(qū)動(dòng)程序。要使用spidev.c的驅(qū)動(dòng),只要在登記設(shè)備時(shí),把設(shè)備名設(shè)置成spidev就可以。spidev.c會(huì)在device目錄下自動(dòng)為每一個(gè)匹配的SPI設(shè)備創(chuàng)建設(shè)備節(jié)點(diǎn),節(jié)點(diǎn)名”spi%d”。之后,用戶(hù)程序可以通過(guò)字符型設(shè)備的通用接口控制SPI設(shè)備。

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


想學(xué)習(xí)想進(jìn)步的你和我聯(lián)系預(yù)約就可以免費(fèi)聽(tīng)課了。
以下課程可免費(fèi)試聽(tīng)C語(yǔ)言電子PCBSTM32、Linux、FPGA、JAVA、安卓等。
宋工企鵝號(hào):3524-6590-88   Tel/WX:173--1795--1908


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

本站部分文章為轉(zhuǎn)載或網(wǎng)友發(fā)布,目的在于傳遞和分享信息,并不代表本網(wǎng)贊同其觀(guān)點(diǎn)和對(duì)其真實(shí)性負(fù)責(zé);文章版權(quán)歸原作者及原出處所有,如涉及作品內(nèi)容、版權(quán)和其它問(wèn)題,我們將根據(jù)著作權(quán)人的要求,第一時(shí)間更正或刪除。
您需要登錄后才可以發(fā)表評(píng)論 登錄 | 立即注冊(cè)

廠(chǎng)商推薦

  • Microchip視頻專(zhuān)區(qū)
  • Dev Tool Bits——使用MPLAB® Discover瀏覽資源
  • Dev Tool Bits——使用條件軟件斷點(diǎn)宏來(lái)節(jié)省時(shí)間和空間
  • Dev Tool Bits——使用DVRT協(xié)議查看項(xiàng)目中的數(shù)據(jù)
  • Dev Tool Bits——使用MPLAB® Data Visualizer進(jìn)行功率監(jiān)視
  • 貿(mào)澤電子(Mouser)專(zhuān)區(qū)

相關(guān)視頻

關(guān)于我們  -  服務(wù)條款  -  使用指南  -  站點(diǎn)地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權(quán)所有   京ICP備16069177號(hào) | 京公網(wǎng)安備11010502021702
快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 99视频在线观看视频 | 国产亚洲成在线播放va | 国产三级高清 | 国产高清视频一区二区 | 免费一区二区三区在线视频 | 国产在线一区二区三区在线 | 亚洲理论在线观看 | 国产成人精品免费视频 | 在线精品国内视频秒播 | 欧美视频一区二区三区 | 在线成人免费观看国产精品 | 亚洲欧美在线观看视频 | 亭亭色| 欧美黑人一区 | 亚洲三级国产 | 国产激情对白一区二区三区四 | 九九99香蕉在线视频免费 | 午夜拍拍福利视频蜜桃视频 | 免费精品国产自在在线app | 中文字幕在线日韩 | 高清不卡免费一区二区三区 | 精品肉动漫资源在线观看 | 日韩欧美亚 | 国产一区二区三区亚洲综合 | 欧美高清不卡 | 欧美日本一本线在线观看 | 国产在线观看一区二区三区四区 | 凹凸世界第4季完整免费 | 日韩精品视频在线免费观看 | 久久成年片色大黄全免费网站 | 99在线观看国产 | 久久99精品久久久66 | 九九小视频 | 亚洲欧美v视色一区二区 | 国产黄色片在线免费观看 | 久久久久久久久久免免费精品 | 国产极品在线观看视频 | 国产三片高清在线观看 | 1插菊花综合 | 亚洲激情区 | 综合精品视频 |