0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SPI硬件基礎(chǔ)知識(shí)科普

xCb1_yikoulinux ? 來源:一口Linux ? 作者:Jasonangel ? 2022-05-16 16:12 ? 次閱讀

SPI硬件基礎(chǔ)

1、SPI hardware

SPI:Serial Perripheral Interface,串行外圍設(shè)備接口,由 Motorola 公司提出,是一種高速、全雙工、同步通信總線。SPI 以主從方式工作,通常是有一個(gè)主設(shè)備和一個(gè)或多個(gè)從設(shè)備,無應(yīng)答機(jī)制。

本文我們講解標(biāo)準(zhǔn)的 4 線 SPI,四根線如下:

①、CS/SS,Slave Select/Chip Select,片選信號(hào)線,用于選擇需要進(jìn)行通信的從設(shè)備。

②、SCK,Serial Clock,串行時(shí)鐘,和 I2C 的 SCL 一樣,為 SPI 通信提供時(shí)鐘。

③、MOSI/SDO,Master Out Slave In/Serial Data Output,主輸出從輸入。

④、MISO/SDI,Master In Slave Out/Serial Data Input,主輸入從輸出。

c7489992-d3f7-11ec-bce3-dac502259ad0.png

2、SPI 四種工作模式

SPI 有四種工作模式,通過時(shí)鐘極性(CPOL)和時(shí)鐘相位(CPHA)的搭配來得到四種工作模式:

①、CPOL=0,串行時(shí)鐘空閑狀態(tài)為低電平。
②、CPOL=1,串行時(shí)鐘空閑狀態(tài)為高電平。
③、CPHA=0,串行時(shí)鐘的第一個(gè)跳變沿(上升沿或下降沿)采集數(shù)據(jù)。
④、CPHA=1,串行時(shí)鐘的第二個(gè)跳變沿(上升沿或下降沿)采集數(shù)據(jù)。
c77588d0-d3f7-11ec-bce3-dac502259ad0.png

示例波形圖如下:

c7a1857a-d3f7-11ec-bce3-dac502259ad0.png

SPI 是全雙工的,所以讀寫時(shí)序可以一起完成。

3、SPI 傳輸機(jī)制

c7c79b84-d3f7-11ec-bce3-dac502259ad0.png

從圖可以看出,主機(jī)和從機(jī)都有一個(gè)串行移位寄存器,主機(jī)通過向它的 SPI 串行寄存器寫入一個(gè)字節(jié)來發(fā)起一次傳輸。寄存器通過 MOSI 信號(hào)線將字節(jié)傳送給從機(jī),從機(jī)也將自己的移位寄存器中的內(nèi)容通過 MISO 信號(hào)線返回給主機(jī)。這樣,兩個(gè)移位寄存器中的內(nèi)容就被交換。

外設(shè)的寫操作和讀操作是同步完成的。如果只進(jìn)行寫操作,主機(jī)只需忽略接收到的字節(jié);反之,若主機(jī)要讀取從機(jī)的一個(gè)字節(jié),就必須發(fā)送一個(gè)空字節(jié)來引發(fā)從機(jī)的傳輸。

c7eb934a-d3f7-11ec-bce3-dac502259ad0.gifc826bc4a-d3f7-11ec-bce3-dac502259ad0.gifc846a6f4-d3f7-11ec-bce3-dac502259ad0.gif

雖然 SPI 四線制支持讀寫同時(shí)進(jìn)行,但實(shí)際上我們很多時(shí)候并不需要又讀又寫,見以下兩種情況(參考 BMA223 數(shù)據(jù)手冊):

注意:如下三幅圖示均為 CPOL=1,CPHA=1

1、主機(jī)向從機(jī)寫數(shù)據(jù)

c867a4a8-d3f7-11ec-bce3-dac502259ad0.png

主機(jī)發(fā)送先發(fā)送 8 bits,第一個(gè) bit 為 0 代表這次主機(jī)是想寫數(shù)據(jù)到從機(jī),AD6~AD0 表示要寫的寄存器地址。然后,主機(jī)就會(huì)一直寫下去。在這期間 SDO 一直沒用,一直是高阻態(tài),算是一直讀到1。

2、主機(jī)從從機(jī)讀數(shù)據(jù)

c8824ef2-d3f7-11ec-bce3-dac502259ad0.png

這種情況下,主機(jī)先發(fā)送 8 bits,第一位為 1 代表這次是讀,然后 AD6 ~ AD0 是想要讀的寄存器地址,然后 SDO 開始返回?cái)?shù)據(jù)。

4、SPI timing diagram

c8a207d8-d3f7-11ec-bce3-dac502259ad0.png
Tcsb_setup:建立時(shí)間
Tcsb_hold:保持時(shí)間
tsckl:低電平時(shí)間
tsckh:高電平時(shí)間
SCK period :Tsckl + tsckh
一般情況下Tsckl=tsckh

注意:真實(shí)的波形圖如上,高低電平并不是到達(dá)最高點(diǎn)才算,0.3Vdd 以下為低電平,0.7Vdd 以上為高電平,計(jì)算信號(hào)時(shí)間長度的時(shí)候需要注意這個(gè)微小的時(shí)間,硬件設(shè)計(jì)必須注意信號(hào)質(zhì)量風(fēng)險(xiǎn),軟件開發(fā)人員也要會(huì)看波形圖。

這里的參數(shù),一般 spi 驅(qū)動(dòng)不需要設(shè)置,但是半導(dǎo)體廠商提供的 spi 控制器驅(qū)動(dòng)中,可以修改這些參數(shù)。我們寫 SPI 驅(qū)動(dòng)時(shí)候,可以根據(jù)從設(shè)備的要求來修改這些參數(shù)。

5、DMA 與 FIFO

不同平臺(tái)對(duì)于 SPI FIFO 和 DMA 的 buffer size 設(shè)置不同:

c8bc0e8a-d3f7-11ec-bce3-dac502259ad0.png

傳輸 32bytes 以下使用 FIFO,傳輸 32bytes 以上使用 DMA。

DMA 可以自動(dòng)發(fā)起多次傳輸,一次最大 256K 。

6、I2C 與 SPI 對(duì)比

功能 I2C SPI
線數(shù) 2(SDA,SCL) 4(MOSI,MISO,SCLK,CS)
主機(jī)數(shù)量 >=1 ==1
類型 半雙工 全雙工
回應(yīng)機(jī)制 yes no
速度 <=3.4Mbps high
應(yīng)用 重要數(shù)據(jù) 大量數(shù)據(jù)
流控 yes no
設(shè)備地址 yes no
常規(guī)用途 命令 數(shù)據(jù)

I2C 和 SPI 的速率如下:

I2C模式 速度
標(biāo)準(zhǔn) 100KHz
快速 400KHz
快速+ 1MHz
高速 3.4MHz

SPI 速率:幾十 MHz 甚至上百 MHz,速度取決于 CPU 的 SPI 控制器和時(shí)鐘 clock

STM32F103 的 SPI 最高支持 18MHz,imx6ull 的 SPI 最高支持 52MHz,其他芯片一般用不到更高的,因?yàn)樗俣仍娇觳ㄐ钨|(zhì)量越不好,越容易出問題。

具體采用多大速率還和外設(shè)有關(guān),比如 EEPROM 的 W25Q128 的 SPI 最高支持 80MHz,ICM20608 傳感器的 SPI 最高支持8MHz。一般用在 flash 上的速度會(huì)較快。

7、擴(kuò)展

SPI 協(xié)議其實(shí)是包括:Standard SPI、Dual SPI 和 Queued SPI 三種協(xié)議接口。

Dual SPI 還是四線制,只是傳輸線可以變?yōu)橥较?,速度?Standard SPI 的兩倍。

Queued SPI 是六線制,多了兩根數(shù)據(jù)線,傳輸速度是 Standard SPI 的四倍。

SPILinux驅(qū)動(dòng)

1、SPI 驅(qū)動(dòng)源文件目錄

Linux common spi driver

kernel-4.14/drivers/spi/spi.cLinux提供的通用接口封裝層驅(qū)動(dòng)
kernel-4.14/drivers/spi/spidev.clinux提供的SPI通用設(shè)備驅(qū)動(dòng)程序
kernel-4.14/include/linux/spi/spi.hlinux提供的包含SPI的主要數(shù)據(jù)結(jié)構(gòu)和函數(shù)

spi 控制器驅(qū)動(dòng),IC 廠商提供,不同廠商命名不同

kernel-4.14/drivers/spi/spi-mt65xx.cMTKSPI控制器驅(qū)動(dòng)
kernel-4.14/drivers/spi/spi-mt65xx-dev.c
kernel-4.14/include/linux/platform_data/spi-mt65xx.h

dts

kernel-4.14/arch/arm/boot/dts/...
kernel-4.14/arch/arm64/boot/dts/...

以上文件對(duì)應(yīng)如下 SPI 驅(qū)動(dòng)軟件架構(gòu):

c911c0d2-d3f7-11ec-bce3-dac502259ad0.png
SPI 控制器驅(qū)動(dòng)程序

SPI 控制器不用關(guān)心設(shè)備的具體功能,它只負(fù)責(zé)把上層協(xié)議驅(qū)動(dòng)準(zhǔn)備好的數(shù)據(jù)按 SPI 總線的時(shí)序要求發(fā)送給 SPI 設(shè)備,同時(shí)把從設(shè)備收到的數(shù)據(jù)返回給上層的協(xié)議驅(qū)動(dòng),因此,內(nèi)核把 SPI 控制器的驅(qū)動(dòng)程序獨(dú)立出來。

SPI 控制器驅(qū)動(dòng)負(fù)責(zé)控制具體的控制器硬件,諸如 DMA 和中斷操作等等,因?yàn)槎鄠€(gè)上層的協(xié)議驅(qū)動(dòng)可能會(huì)通過控制器請(qǐng)求數(shù)據(jù)傳輸操作,所以,SPI 控制器驅(qū)動(dòng)同時(shí)也要負(fù)責(zé)對(duì)這些請(qǐng)求進(jìn)行隊(duì)列管理,保證先進(jìn)先出的原則。

SPI 通用接口封裝層

為了簡化 SPI 驅(qū)動(dòng)程序的編程工作,同時(shí)也為了降低【協(xié)議驅(qū)動(dòng)程序】和【控制器驅(qū)動(dòng)程序】的耦合程度,內(nèi)核把控制器驅(qū)動(dòng)和協(xié)議驅(qū)動(dòng)的一些通用操作封裝成標(biāo)準(zhǔn)的接口,加上一些通用的邏輯處理操作,組成了 SPI 通用接口封裝層。

這樣的好處是,對(duì)于控制器驅(qū)動(dòng)程序,只要實(shí)現(xiàn)標(biāo)準(zhǔn)的接口回調(diào) API,并把它注冊到通用接口層即可,無需直接和協(xié)議層驅(qū)動(dòng)程序進(jìn)行交互。而對(duì)于協(xié)議層驅(qū)動(dòng)來說,只需通過通用接口層提供的 API 即可完成設(shè)備和驅(qū)動(dòng)的注冊,并通過通用接口層的 API 完成數(shù)據(jù)的傳輸,無需關(guān)注 SPI 控制器驅(qū)動(dòng)的實(shí)現(xiàn)細(xì)節(jié)。

SPI 協(xié)議驅(qū)動(dòng)程序

SPI 設(shè)備的具體功能是由 SPI 協(xié)議驅(qū)動(dòng)程序完成的,SPI 協(xié)議驅(qū)動(dòng)程序了解設(shè)備的功能和通信數(shù)據(jù)的協(xié)議格式。向下,協(xié)議驅(qū)動(dòng)通過通用接口層和控制器交換數(shù)據(jù),向上,協(xié)議驅(qū)動(dòng)通常會(huì)根據(jù)設(shè)備具體的功能和內(nèi)核的其它子系統(tǒng)進(jìn)行交互。

例如,和 MTD 層交互以便把 SPI 接口的存儲(chǔ)設(shè)備實(shí)現(xiàn)為某個(gè)文件系統(tǒng),和 TTY 子系統(tǒng)交互把 SPI 設(shè)備實(shí)現(xiàn)為一個(gè) TTY 設(shè)備,和網(wǎng)絡(luò)子系統(tǒng)交互以便把一個(gè) SPI 設(shè)備實(shí)現(xiàn)為一個(gè)網(wǎng)絡(luò)設(shè)備。如果是一個(gè)專有的 SPI 設(shè)備,我們也可以按設(shè)備的協(xié)議要求,實(shí)現(xiàn)自己的專有協(xié)議驅(qū)動(dòng)。

SPI 通用設(shè)備驅(qū)動(dòng)程序

考慮到連接在 SPI 控制器上的設(shè)備的可變性,在內(nèi)核沒有配備相應(yīng)的協(xié)議驅(qū)動(dòng)程序,對(duì)于這種情況,內(nèi)核為我們準(zhǔn)備了通用的 SPI 設(shè)備驅(qū)動(dòng)程序,該通用設(shè)備驅(qū)動(dòng)程序向用戶空間提供了控制 SPI 控制的控制接口,具體的協(xié)議控制和數(shù)據(jù)傳輸工作交由用戶空間根據(jù)具體的設(shè)備來完成,在這種方式中,只能采用同步的方式和 SPI 設(shè)備進(jìn)行通信,所以通常用于一些數(shù)據(jù)量較少的簡單 SPI 設(shè)備。

2、SPI 通用接口層

  1. SPI 通用接口層把具體的 SPI 設(shè)備的協(xié)議驅(qū)動(dòng)和 SPI 控制器驅(qū)動(dòng)連接在一起。
  2. 負(fù)責(zé) SPI 系統(tǒng)與 Linux 設(shè)備模型相關(guān)的初始化工作。
  3. 為協(xié)議驅(qū)動(dòng)和控制器驅(qū)動(dòng)提供一系列的標(biāo)準(zhǔn)接口 API 及其數(shù)據(jù)結(jié)構(gòu)。
  4. SPI 設(shè)備、SPI 協(xié)議驅(qū)動(dòng)、SPI 控制器的數(shù)據(jù)抽象
  5. 協(xié)助數(shù)據(jù)傳輸而定義的數(shù)據(jù)結(jié)構(gòu)

kernel-4.14/drivers/spi/spi.c

staticint__initspi_init(void)
{
intstatus;

buf=kmalloc(SPI_BUFSIZ,GFP_KERNEL);
if(!buf){
status=-ENOMEM;
gotoerr0;
}

//創(chuàng)建/sys/bus/spi節(jié)點(diǎn)
status=bus_register(&spi_bus_type);
if(status0)
gotoerr1;

//創(chuàng)建/sys/class/spi_master節(jié)點(diǎn)
status=class_register(&spi_master_class);
if(status0)
gotoerr2;

if(IS_ENABLED(CONFIG_SPI_SLAVE)){
status=class_register(&spi_slave_class);
if(status0)
gotoerr3;
}
......
}

在這里創(chuàng)建了 SPI 總線,創(chuàng)建 /sys/bus/spi 節(jié)點(diǎn)和 /sys/class/spi_master 節(jié)點(diǎn)。

重要數(shù)據(jù)結(jié)構(gòu):

spi_device
spi_driver
spi_board_info
spi_controller/spi_master
spi_transfer
spi_message

重要 API

spi_message_init
spi_message_add_tail
spi_sync
spi_async
spi_write
spi_read

接下來詳細(xì)解析結(jié)構(gòu)體和API,只講解重點(diǎn)部分,完整解析請(qǐng)參考官方文檔

https://www.kernel.org/doc/html/v4.14//driver-api/spi.html

只有熟悉每個(gè)結(jié)構(gòu)體存儲(chǔ)的是什么東西,才能真正搞懂 SPI 模塊。

spi_master/spi_controller:描述一個(gè) spi 主機(jī)設(shè)備

structspi_master{
//Linux驅(qū)動(dòng)模型中的設(shè)備
structdevicedev;

//此spi_master設(shè)備在全局spi_master鏈表中的節(jié)點(diǎn)
structlist_headlist;

//此spi_master編號(hào)
s16bus_num;

//此spi_master支持的片選信號(hào)數(shù)量
u16num_chipselect;

//dma地址對(duì)齊
u16dma_alignment;

//此spi_master支持傳輸?shù)膍ode
u16mode_bits;
u32bits_per_word_mask;
/*limitsontransferspeed*/
u32min_speed_hz;
u32max_speed_hz;

/*otherconstraintsrelevanttothisdriver*/
u16flags;

/*lockandmutexforSPIbuslocking*/
spinlock_tbus_lock_spinlock;//總線自旋鎖
structmutexbus_lock_mutex;//總線互斥鎖

//總線是否處于lock狀態(tài)
boolbus_lock_flag;

//準(zhǔn)備傳輸,設(shè)置傳輸?shù)膮?shù)
int(*setup)(structspi_device*spi);

//傳輸數(shù)據(jù)
int(*transfer)(structspi_device*spi,
structspi_message*mesg);
//設(shè)備release時(shí)的清除工作
void(*cleanup)(structspi_device*spi);

bool(*can_dma)(structspi_master*master,
structspi_device*spi,
structspi_transfer*xfer);

boolqueued;//是否采用系統(tǒng)的序列化傳輸
structkthread_workerkworker;//序列化傳輸時(shí)的線程worker
structtask_struct*kworker_task;//序列化傳輸?shù)木€程
structkthread_workpump_messages;//序列化傳輸時(shí)的處理函數(shù)
spinlock_tqueue_lock;//序列化傳輸時(shí)的queue_lock
structlist_headqueue;//序列化傳輸時(shí)的msg隊(duì)列頭
structspi_message*cur_msg;//序列化傳輸時(shí)當(dāng)前的msg
boolidling;
boolbusy;//序列化傳輸時(shí)線程是否處于busy狀態(tài)
boolrunning;//序列化傳輸時(shí)線程是否在運(yùn)行
boolrt;//是否實(shí)時(shí)傳輸
......

int(*prepare_transfer_hardware)(structspi_master*master);

//一個(gè)msg的傳輸實(shí)現(xiàn)
int(*transfer_one_message)(structspi_master*master,
structspi_message*mesg);
......

/*gpiochipselect*/
int*cs_gpios;
......
};

spi_device:描述一個(gè) spi 從機(jī)設(shè)備

structspi_device{
//Linux驅(qū)動(dòng)模型中的設(shè)備
structdevicedev;
structspi_master*master;//設(shè)備所連接的spi主機(jī)設(shè)備
u32max_speed_hz;//該設(shè)備最大傳輸速率
u8chip_select;//CS片選信號(hào)編號(hào)
u8bits_per_word;//每次傳輸長度
u16mode;//傳輸模式
......
intirq;//軟件中斷號(hào)
void*controller_state;//控制器狀態(tài)
void*controller_data;//控制參數(shù)
charmodalias[SPI_NAME_SIZE];//設(shè)備名稱
//CS片選信號(hào)對(duì)應(yīng)的GPIOnumber
intcs_gpio;/*chipselectgpio*/

/*thestatistics*/
structspi_statisticsstatistics;
};

spi_driver:描述一個(gè) spi 設(shè)備驅(qū)動(dòng)

structspi_driver{
//此driver所支持的spi設(shè)備list
conststructspi_device_id*id_table;
int(*probe)(structspi_device*spi);
int(*remove)(structspi_device*spi);
//系統(tǒng)shutdown時(shí)的回調(diào)函數(shù)
void(*shutdown)(structspi_device*spi);
structdevice_driverdriver;
};

spi_board_info:描述一個(gè) spi 從機(jī)設(shè)備板級(jí)信息,無設(shè)備樹時(shí)使用

structspi_board_info{
//設(shè)備名稱
charmodalias[SPI_NAME_SIZE];
constvoid*platform_data;//設(shè)備的平臺(tái)數(shù)據(jù)
void*controller_data;//設(shè)備的控制器數(shù)據(jù)
intirq;//設(shè)備的中斷號(hào)
u32max_speed_hz;//設(shè)備支持的最大速率
u16bus_num;//設(shè)備連接的spi總線編號(hào)
u16chip_select;//設(shè)備連接的CS信號(hào)編號(hào)
u16mode;//設(shè)備使用的傳輸mode
};

spi_transfer:描述 spi 傳輸?shù)木唧w數(shù)據(jù)

structspi_transfer{

constvoid*tx_buf;//spi_transfer的發(fā)送buf
void*rx_buf;//spi_transfer的接收buf
unsignedlen;//spi_transfer發(fā)送和接收的長度

dma_addr_ttx_dma;//tx_buf對(duì)應(yīng)的dma地址
dma_addr_trx_dma;//rx_buf對(duì)應(yīng)的dma地址
structsg_tabletx_sg;
structsg_tablerx_sg;

//spi_transfer傳輸完成后是否要改變CS片選信號(hào)
unsignedcs_change:1;
unsignedtx_nbits:3;
unsignedrx_nbits:3;
......
u8bits_per_word;//spi_transfer中一個(gè)word占的bits
u16delay_usecs;//兩個(gè)spi_transfer直接的等待延遲
u32speed_hz;//spi_transfer的傳輸速率

structlist_headtransfer_list;//spi_transfer掛載到的message節(jié)點(diǎn)
};

spi_message:描述一次 spi 傳輸?shù)男畔?/p>

structspi_message{
//掛載在此msg上的transfer鏈表頭
structlist_headtransfers;
//此msg需要通信的spi從機(jī)設(shè)備
structspi_device*spi;
//所使用的地址是否是dma地址
unsignedis_dma_mapped:1;

//msg發(fā)送完成后的處理函數(shù)
void(*complete)(void*context);
void*context;//complete函數(shù)的參數(shù)
unsignedframe_length;
unsignedactual_length;//此msg實(shí)際成功發(fā)送的字節(jié)數(shù)
intstatus;//此 msg 的發(fā)送狀態(tài),0:成功,負(fù)數(shù),失敗

structlist_headqueue;//此msg在所有msg中的鏈表節(jié)點(diǎn)
void*state;//此msg的私有數(shù)據(jù)
};

隊(duì)列化

SPI 數(shù)據(jù)傳輸可以有兩種方式:同步方式和異步方式。

同步方式:數(shù)據(jù)傳輸?shù)陌l(fā)起者必須等待本次傳輸?shù)慕Y(jié)束,期間不能做其它事情,用代碼來解釋就是,調(diào)用傳輸?shù)暮瘮?shù)后,直到數(shù)據(jù)傳輸完成,函數(shù)才會(huì)返回。

異步方式:數(shù)據(jù)傳輸?shù)陌l(fā)起者無需等待傳輸?shù)慕Y(jié)束,數(shù)據(jù)傳輸期間還可以做其它事情,用代碼來解釋就是,調(diào)用傳輸?shù)暮瘮?shù)后,函數(shù)會(huì)立刻返回而不用等待數(shù)據(jù)傳輸完成,我們只需設(shè)置一個(gè)回調(diào)函數(shù),傳輸完成后,該回調(diào)函數(shù)會(huì)被調(diào)用以通知發(fā)起者數(shù)據(jù)傳送已經(jīng)完成。

同步方式簡單易用,很適合處理那些少量數(shù)據(jù)的單次傳輸。但是對(duì)于數(shù)據(jù)量大、次數(shù)多的傳輸來說,異步方式就顯得更加合適。

對(duì)于 SPI 控制器來說,要支持異步方式必須要考慮以下兩種狀況:

  1. 對(duì)于同一個(gè)數(shù)據(jù)傳輸?shù)陌l(fā)起者,既然異步方式無需等待數(shù)據(jù)傳輸完成即可返回,返回后,該發(fā)起者可以立刻又發(fā)起一個(gè) message,而這時(shí)上一個(gè)message還沒有處理完。
  2. 對(duì)于另外一個(gè)不同的發(fā)起者來說,也有可能同時(shí)發(fā)起一次message傳輸請(qǐng)求。

隊(duì)列化正是為了為了解決以上的問題,所謂隊(duì)列化,是指把等待傳輸?shù)?message 放入一個(gè)等待隊(duì)列中,發(fā)起一個(gè)傳輸操作,其實(shí)就是把對(duì)應(yīng)的 message 按先后順序放入一個(gè)等待隊(duì)列中,系統(tǒng)會(huì)在不斷檢測隊(duì)列中是否有等待傳輸?shù)?message,如果有就不停地調(diào)度數(shù)據(jù)傳輸內(nèi)核線程,逐個(gè)取出隊(duì)列中的 message 進(jìn)行處理,直到隊(duì)列變空為止。SPI 通用接口層為我們實(shí)現(xiàn)了隊(duì)列化的基本框架。

c934340a-d3f7-11ec-bce3-dac502259ad0.png

spi_message 就是一次 SPI 數(shù)據(jù)交換的原子操作,不可打斷。

3、SPI 控制器驅(qū)動(dòng)層

SPI 控制器驅(qū)動(dòng)層負(fù)責(zé)最底層的數(shù)據(jù)收發(fā),主要有以下功能:

  1. 申請(qǐng)必要的硬件資源,比如中斷、DMA 通道、DMA 內(nèi)存緩沖區(qū)等等
  2. 配置 SPI 控制器的工作模式和參數(shù),使之可以和相應(yīng)的設(shè)備進(jìn)行正確的數(shù)據(jù)交換
  3. 向通用接口層提供接口,使得上層的協(xié)議驅(qū)動(dòng)可以通過通用接口層訪問控制器驅(qū)動(dòng)
  4. 配合通用接口層,完成數(shù)據(jù)消息隊(duì)列的排隊(duì)和處理,直到消息隊(duì)列變空為止

SPI 主機(jī)驅(qū)動(dòng)就是 SOC 的 SPI 控制器驅(qū)動(dòng)。Linux 內(nèi)核使用 spi_master/spi_controller 表示 SPI 主機(jī)驅(qū)動(dòng),spi_master 是個(gè)結(jié)構(gòu)體,定義在 include/linux/spi/spi.h 文件中。

SPI 主機(jī)驅(qū)動(dòng)的核心就是申請(qǐng) spi_master,然后初始化 spi_master,最后向 Linux 內(nèi)核注冊 spi_master。

API 如下:

spi_alloc_master 函數(shù):申請(qǐng) spi_master。
spi_master_put 函數(shù):釋放 spi_master。

spi_register_master函數(shù):注冊 spi_master。
spi_unregister_master 函數(shù):注銷 spi_master。

spi_bitbang_start函數(shù):注冊 spi_master。
spi_bitbang_stop 函數(shù):注銷 spi_master。

SPI 主機(jī)驅(qū)動(dòng)的加載

以 MTK 為例,源碼來自于小米開源項(xiàng)目

https://github.com/MiCode/Xiaomi_Kernel_OpenSource

小米每做一個(gè)項(xiàng)目,都會(huì)把 kernel 部分開源,因?yàn)樾枰裱?Linux GPL 開源協(xié)議。

【設(shè)備】聲明在設(shè)備樹中

kernel-4.14/arch/arm64/boot/dts/mediatek/mt6885.dts
c9657024-d3f7-11ec-bce3-dac502259ad0.png

【驅(qū)動(dòng)】

kernel-4.14/drivers/spi/spi-mt65xx.c

c97a6ea2-d3f7-11ec-bce3-dac502259ad0.pngc9a77c3a-d3f7-11ec-bce3-dac502259ad0.png

匹配以后,probe 函數(shù)執(zhí)行,申請(qǐng) spi_master,初始化 spi_master,最后向 Linux 內(nèi)核注冊 spi_master。

c9c3b3f0-d3f7-11ec-bce3-dac502259ad0.pngca1e7c68-d3f7-11ec-bce3-dac502259ad0.png

4、軟件流程

ca391b9a-d3f7-11ec-bce3-dac502259ad0.png

看懂該圖,對(duì) SPI 驅(qū)動(dòng)框架就有完整的了解了。

1、2、3 按順執(zhí)行,首先有 spi 總線的注冊,然后是 spi 控制器驅(qū)動(dòng)加載,然后是設(shè)備驅(qū)動(dòng)加載。

區(qū)別在于,spi 控制器驅(qū)動(dòng)加載時(shí),是靠 platform 總線匹配設(shè)備(控制器)與驅(qū)動(dòng)。spi 設(shè)備驅(qū)動(dòng)加載時(shí),是靠 spi 總線匹配設(shè)備(外設(shè)IC)與驅(qū)動(dòng)。

init flow

ca6d8b28-d3f7-11ec-bce3-dac502259ad0.png

spi_register_master 的調(diào)用序列圖

ca83c424-d3f7-11ec-bce3-dac502259ad0.png

隊(duì)列化的工作機(jī)制及過程

cae4f5c8-d3f7-11ec-bce3-dac502259ad0.pngcb03331c-d3f7-11ec-bce3-dac502259ad0.png

當(dāng)協(xié)議驅(qū)動(dòng)程序通過 spi_async 發(fā)起一個(gè) message 請(qǐng)求時(shí),隊(duì)列化和工作線程被激活,觸發(fā)一些列的操作,最終完成 message 的傳輸操作。

spi_sync 與 spi_async 類似,只是有一個(gè)等待過程。

5、SPI 設(shè)備驅(qū)動(dòng)

【設(shè)備】聲明在設(shè)備樹中

cb42e03e-d3f7-11ec-bce3-dac502259ad0.pngcb55e15c-d3f7-11ec-bce3-dac502259ad0.png

注意:設(shè)備的聲明,slave device node 應(yīng)該包含在你所要掛載的 &spi node 下,將 device 綁定在 master 上。然后通過 pinctrl 方式指定 GPIO,并在驅(qū)動(dòng)中操作 pinctrl 句柄。

【驅(qū)動(dòng)】demo

Linux 內(nèi)核使用 spi_driver 結(jié)構(gòu)體來表示 spi 設(shè)備驅(qū)動(dòng),我們在編寫 SPI 設(shè)備驅(qū)動(dòng)的時(shí)候需要實(shí)現(xiàn) spi_driver。spi_driver 結(jié)構(gòu)體定義在 include/linux/spi/spi.h 文件中。

spi_register_driver:注冊 spi_driver
spi_unregister_driver:銷掉 spi_driver
/*probe函數(shù)*/
staticintxxx_probe(structspi_device*spi)
{

/*具體函數(shù)內(nèi)容*/
return0;
}

/*remove函數(shù)*/
staticintxxx_remove(structspi_device*spi)
{

/*具體函數(shù)內(nèi)容*/
return0;
}

/*傳統(tǒng)匹配方式ID列表*/
staticconststructspi_device_idxxx_id[]={

{"xxx",0},
{}
};

/*設(shè)備樹匹配列表*/
staticconststructof_device_idxxx_of_match[]={

{.compatible="xxx"},
{/*Sentinel*/}
};

/*SPI驅(qū)動(dòng)結(jié)構(gòu)體*/
staticstructspi_driverxxx_driver={

.probe=xxx_probe,
.remove=xxx_remove,
.driver={
.owner=THIS_MODULE,
.name="xxx",
.of_match_table=xxx_of_match,
},
.id_table=xxx_id,
};

/*驅(qū)動(dòng)入口函數(shù)*/
staticint__initxxx_init(void)
{

returnspi_register_driver(&xxx_driver);
}

/*驅(qū)動(dòng)出口函數(shù)*/
staticvoid__exitxxx_exit(void)
{

spi_unregister_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);

在驅(qū)動(dòng)入口函數(shù)中調(diào)用 spi_register_driver 來注冊 spi_driver。

在驅(qū)動(dòng)出口函數(shù)中調(diào)用 spi_unregister_driver 來注銷 spi_driver。

spi 讀寫數(shù)據(jù)demo

/*SPI多字節(jié)發(fā)送*/
staticintspi_send(structspi_device*spi,u8*buf,intlen)
{
intret;
structspi_messagem;

structspi_transfert={
.tx_buf=buf,
.len=len,
};

spi_message_init(&m);/*初始化spi_message*/
spi_message_add_tail(t,&m);/*將spi_transfer添加到spi_message隊(duì)列*/
ret=spi_sync(spi,&m);/*同步傳輸*/
returnret;
}
/*SPI多字節(jié)接收*/
staticintspi_receive(structspi_device*spi,u8*buf,intlen)
{
intret;
structspi_messagem;

structspi_transfert={
.rx_buf=buf,
.len=len,
};

spi_message_init(&m);/*初始化spi_message*/
spi_message_add_tail(t,&m);/*將spi_transfer添加到spi_message隊(duì)列*/
ret=spi_sync(spi,&m);/*同步傳輸*/
returnret;
}

除了 init、exit、probe、remove、read、write 函數(shù)外,其他的函數(shù)看需求實(shí)現(xiàn),這幾個(gè)是最基本的。

6、總結(jié)

Linux 是 總線、設(shè)備、驅(qū)動(dòng) 的框架,理解了這個(gè)框架,就能理解所有的模塊驅(qū)動(dòng)框架。

SPI 驅(qū)動(dòng)比 I2C 驅(qū)動(dòng)還是簡單很多的。

end

原文標(biāo)題:SPI 硬件+Linux驅(qū)動(dòng)詳解

文章出處:【微信公眾號(hào):一口Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11171

    瀏覽量

    208472
  • SPI硬件
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    792
  • 傳輸機(jī)制
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    1132

原文標(biāo)題:SPI 硬件+Linux驅(qū)動(dòng)詳解

文章出處:【微信號(hào):yikoulinux,微信公眾號(hào):一口Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    C語言基礎(chǔ)知識(shí)科普

    C語言是單片機(jī)開發(fā)中的必備基礎(chǔ)知識(shí),本文列舉了部分STM32學(xué)習(xí)中比較常見的一些C語言基礎(chǔ)知識(shí)。
    發(fā)表于 07-21 10:58 ?1844次閱讀

    科普一下CAN總線的基礎(chǔ)知識(shí)

    CAN總線是一種常用的總線,對(duì)于剛開始接觸CAN總線的,面對(duì)著各式各樣的資料,可能不知道從何看起,今天科普一下CAN總線的基礎(chǔ)知識(shí)。CAN2.0協(xié)議分為A版本和B版本,A版本協(xié)議為11位標(biāo)識(shí)符(標(biāo)準(zhǔn)幀),B版本在兼容11位ID標(biāo)識(shí)符的同時(shí),向上擴(kuò)展到29位ID標(biāo)識(shí)符。
    發(fā)表于 05-16 09:49 ?3072次閱讀
    <b class='flag-5'>科普</b>一下CAN總線的<b class='flag-5'>基礎(chǔ)知識(shí)</b>

    手機(jī)硬件設(shè)計(jì)基礎(chǔ)知識(shí)

    從別處得來的,基礎(chǔ)知識(shí)講解,純分享{:4_95:}
    發(fā)表于 01-17 15:38

    硬件設(shè)計(jì)基礎(chǔ)知識(shí)

    硬件設(shè)計(jì)基礎(chǔ)知識(shí)
    發(fā)表于 02-25 21:28

    ecs700硬件基礎(chǔ)知識(shí)

    ecs700硬件基礎(chǔ)知識(shí),目錄1、電磁干擾1、電磁干擾
    發(fā)表于 07-14 06:35

    示波器基礎(chǔ)知識(shí)

    第1章 示波器基礎(chǔ)知識(shí)本章的內(nèi)容整理自網(wǎng)絡(luò),主要講解示波器的基礎(chǔ)知識(shí)。如果初學(xué)的話非常有必要對(duì)這部分知識(shí)有一個(gè)了解。因?yàn)槭静ㄆ魇?b class='flag-5'>硬件調(diào)試必不可少的設(shè)備。1.1 什么是示波器1.2 示波
    發(fā)表于 08-09 07:21

    五分鐘讀懂WiFi基礎(chǔ)知識(shí)

    家1、嵌入式技術(shù)常識(shí)科普【物聯(lián)網(wǎng)】WiFi基礎(chǔ)知識(shí)五分鐘讀懂TCP/IP;協(xié)議STM32開發(fā) -- Keil基本使用如何看懂時(shí)序圖(以SPI/I2C為例)ESP8266配網(wǎng)思路(不使用...
    發(fā)表于 12-01 06:36

    SPI通信協(xié)議的基礎(chǔ)知識(shí)解析

    SPI通信協(xié)議詳解寫在最前: 本文講述了SPI通信協(xié)議的基本內(nèi)容包括如下SPI基礎(chǔ)知識(shí)SPI的讀寫時(shí)序本文重點(diǎn)參考 英文維基百科 中文維基
    發(fā)表于 12-13 08:05

    記錄一下SPI基礎(chǔ)知識(shí)與軟件開發(fā)環(huán)境

    的,畢竟速率夠高,數(shù)據(jù)量能傳的比較大。1. 準(zhǔn)備1.1 SPI基礎(chǔ)知識(shí)1.2 開發(fā)環(huán)境1.2.1 軟件開發(fā)環(huán)境1.2.2 硬件環(huán)境SPI2. Demo...
    發(fā)表于 02-17 06:29

    科普】卷積神經(jīng)網(wǎng)絡(luò)基礎(chǔ)知識(shí)

    本文的主要目的,是簡單介紹時(shí)下流行的深度學(xué)習(xí)算法的基礎(chǔ)知識(shí),本人也看過許多其他教程,感覺其中大部分講的還是太過深?yuàn)W,于是便有了寫一篇科普文的想法。博主也是現(xiàn)學(xué)現(xiàn)賣,文中如有不當(dāng)之處,請(qǐng)各位指出
    發(fā)表于 11-10 14:49 ?1676次閱讀
    【<b class='flag-5'>科普</b>】卷積神經(jīng)網(wǎng)絡(luò)<b class='flag-5'>基礎(chǔ)知識(shí)</b>

    電源管理基礎(chǔ)知識(shí)電源管理基礎(chǔ)知識(shí)電源管理基礎(chǔ)知識(shí)

    電源管理基礎(chǔ)知識(shí)電源管理基礎(chǔ)知識(shí)電源管理基礎(chǔ)知識(shí)
    發(fā)表于 09-15 14:36 ?76次下載
    電源管理<b class='flag-5'>基礎(chǔ)知識(shí)</b>電源管理<b class='flag-5'>基礎(chǔ)知識(shí)</b>電源管理<b class='flag-5'>基礎(chǔ)知識(shí)</b>

    硬件工程師必備要了解哪些基礎(chǔ)知識(shí)

    硬件工程師必備基礎(chǔ)知識(shí) 目的:基于實(shí)際經(jīng)驗(yàn)與實(shí)際項(xiàng)目詳細(xì)理解并掌握成為合格的硬件工程師的最基本知識(shí)。
    發(fā)表于 10-30 08:00 ?0次下載

    單片機(jī)基礎(chǔ)知識(shí)學(xué)習(xí)筆記

    單片機(jī)基礎(chǔ)知識(shí)學(xué)習(xí)筆記有關(guān)總線1.IIC總線2.SPI總線
    發(fā)表于 11-14 16:51 ?26次下載
    單片機(jī)<b class='flag-5'>基礎(chǔ)知識(shí)</b>學(xué)習(xí)筆記

    電氣基本知識(shí)科普

    電氣基本知識(shí)科普
    的頭像 發(fā)表于 09-09 10:23 ?5990次閱讀
    電氣基本<b class='flag-5'>知識(shí)</b><b class='flag-5'>科普</b>

    SPI協(xié)議基礎(chǔ)知識(shí)

    電子發(fā)燒友網(wǎng)站提供《SPI協(xié)議基礎(chǔ)知識(shí).pdf》資料免費(fèi)下載
    發(fā)表于 11-16 10:32 ?1次下載
    <b class='flag-5'>SPI</b>協(xié)議<b class='flag-5'>基礎(chǔ)知識(shí)</b>