Linux核心SPI支援概述¶
2012年2月2日
什麼是SPI?¶
“序列外設介面”(SPI)是一種同步的四線序列鏈路,用於將微控制器連線到感測器、儲存器和外設。它是一個簡單的“事實上的”標準,不夠複雜到需要一個標準化機構。SPI使用主/從配置。
三根訊號線包含一個時鐘(SCK,通常在10 MHz左右)和並行資料線,具有“主出從入”(MOSI)或“主入從出”(MISO)訊號。(也使用其他名稱。)有四種時鐘模式,透過這些模式交換資料;模式0和模式3是最常用的。每個時鐘週期移出資料並移入資料;只有當有資料位要移位時,時鐘才會迴圈。但並非所有資料位都被使用;並非每個協議都使用這些全雙工功能。
SPI主機使用第四根“片選”線來啟用給定的SPI目標裝置,因此這三根訊號線可以並行連線到多個晶片。所有SPI目標都支援片選;它們通常是低電平有效的訊號,標記為目標“x”的nCSx(例如nCS0)。一些裝置有其他訊號,通常包括到主機的中斷。
與USB或SMBus等序列匯流排不同,即使是SPI目標功能的低階協議,通常在供應商之間也不可互操作(除了SPI儲存晶片等商品)。
SPI可以用於請求/響應式裝置協議,例如觸控式螢幕感測器和儲存晶片。
它也可以用於在任一方向(半雙工)或同時在兩個方向(全雙工)流式傳輸資料。
一些裝置可能使用8位字。其他裝置可能使用不同的字長,例如12位或20位數字取樣的流。
字通常以其最高有效位(MSB)優先發送,但有時最低有效位(LSB)優先發送。
有時SPI用於菊花鏈連線裝置,例如移位暫存器。
同樣,SPI目標很少支援任何型別的自動發現/列舉協議。從給定的SPI主機控制器可訪問的目標裝置的樹通常是手動設定的,帶有配置表。
SPI只是這種四線協議使用的名稱之一,大多數控制器都可以毫無問題地處理“MicroWire”(可以將其視為用於請求/響應協議的半雙工SPI)、SSP(“同步序列協議”)、PSP(“可程式設計序列協議”)和其他相關協議。
一些晶片透過組合MOSI和MISO來消除訊號線,並將自己限制在硬體級別的半雙工。事實上,一些SPI晶片將這種訊號模式作為跨接選項。可以使用與SPI相同的程式設計介面訪問這些晶片,但當然它們不能處理全雙工傳輸。您可能會發現這些晶片被描述為使用“三線”信令:SCK、資料、nCSx。(該資料線有時稱為MOMI或SISO。)
微控制器通常支援SPI協議的主機端和目標端。本文件(和Linux)支援SPI互動的主機端和目標端。
誰使用它?在什麼型別的系統上?¶
使用SPI的Linux開發人員可能正在為嵌入式系統板編寫裝置驅動程式。SPI用於控制外部晶片,它也是每個MMC或SD儲存卡支援的協議。(較舊的“DataFlash”卡,早於MMC卡,但使用相同的聯結器和卡形狀,僅支援SPI。)一些PC硬體使用SPI快閃記憶體來儲存BIOS程式碼。
SPI目標晶片的範圍從用於模擬感測器和編解碼器的數字/模擬轉換器,到儲存器,到USB控制器或乙太網介面卡等外設;以及更多。
大多數使用SPI的系統將在主機板上整合一些裝置。一些在擴充套件聯結器上提供SPI鏈路;在沒有專用SPI控制器的情況下,可以使用GPIO引腳來建立低速“位敲擊”介面卡。極少數系統會“熱插拔”SPI控制器;使用SPI的原因集中在低成本和簡單操作上,如果動態重新配置很重要,USB通常是更合適的低引腳數外設匯流排。
許多可以執行Linux的微控制器集成了具有SPI模式的一個或多個I/O介面。有了SPI支援,它們就可以使用MMC或SD卡,而無需專用的MMC/SD/SDIO控制器。
我感到困惑。這四種SPI“時鐘模式”是什麼?¶
很容易在這裡感到困惑,而且您會發現的供應商文件不一定有幫助。這四種模式結合了兩個模式位
CPOL指示初始時鐘極性。CPOL=0表示時鐘從低電平開始,因此第一個(前沿)是上升沿,第二個(後沿)是下降沿。CPOL=1表示時鐘從高電平開始,因此第一個(前沿)是下降沿。
CPHA指示用於取樣資料的時鐘相位;CPHA=0表示在前沿取樣,CPHA=1表示在後沿取樣。
由於訊號需要在取樣前穩定下來,因此CPHA=0意味著其資料在第一個時鐘沿之前半個時鐘寫入。片選可能使其變為可用。
晶片規格不一定會以這麼多字說“使用SPI模式X”,但它們的時序圖會使CPOL和CPHA模式清晰。
在SPI模式編號中,CPOL是高位,CPHA是低位。因此,當晶片的時序圖顯示時鐘從低電平開始(CPOL=0)並且資料在後沿時鐘沿(CPHA=1)期間穩定以進行取樣時,那是SPI模式1。
請注意,一旦片選變為有效,時鐘模式就相關了。因此,主機必須在選擇目標之前將時鐘設定為非活動狀態,並且目標可以透過在其選擇線變為有效時取樣時鐘電平來判斷所選極性。這就是為什麼許多裝置支援例如模式0和模式3:它們不關心極性,並且始終在上升時鐘沿時鐘輸入/輸出資料。
這些驅動程式程式設計介面如何工作?¶
<linux/spi/spi.h> 標頭檔案包括 kerneldoc,主要原始碼也包括,您肯定應該閱讀核心 API 文件的該章節。這只是一個概述,因此您可以在瞭解這些細節之前獲得全域性概念。
SPI請求始終進入I/O佇列。給定SPI裝置的請求始終按FIFO順序執行,並透過完成回撥非同步完成。對於這些呼叫,還有一些簡單的同步包裝器,包括用於常見事務型別(如寫入命令然後讀取其響應)的包裝器。
有兩種型別的SPI驅動程式,這裡稱為
- 控制器驅動程式 ...
控制器可以構建到片上系統處理器中,並且通常支援控制器和目標角色。這些驅動程式觸控硬體暫存器並可能使用DMA。或者它們可以是PIO位敲擊器,只需要GPIO引腳。
- 協議驅動程式 ...
這些驅動程式透過控制器驅動程式傳遞訊息,以便與SPI鏈路另一端的目標或控制器裝置通訊。
因此,例如,一個協議驅動程式可能會與MTD層通訊,以便將資料匯出到儲存在SPI快閃記憶體(如DataFlash)上的檔案系統;而其他協議驅動程式可能會控制音訊介面、將觸控式螢幕感測器呈現為輸入介面,或者在工業處理期間監視溫度和電壓級別。而這些可能都共享同一個控制器驅動程式。
“struct spi_device”封裝了這兩種型別的驅動程式之間的控制器端介面。
存在一個最小的SPI程式設計介面核心,專注於使用驅動程式模型來連線控制器和協議驅動程式,使用主機板特定初始化程式碼提供的裝置表。SPI在sysfs中的多個位置顯示
/sys/devices/.../CTLR ... physical node for a given SPI controller
/sys/devices/.../CTLR/spiB.C ... spi_device on bus "B",
chipselect C, accessed through CTLR.
/sys/bus/spi/devices/spiB.C ... symlink to that physical
.../CTLR/spiB.C device
/sys/devices/.../CTLR/spiB.C/modalias ... identifies the driver
that should be used with this device (for hotplug/coldplug)
/sys/bus/spi/drivers/D ... driver for one or more spi*.* devices
/sys/class/spi_master/spiB ... symlink to a logical node which could hold
class related state for the SPI host controller managing bus "B".
All spiB.* devices share one physical SPI bus segment, with SCLK,
MOSI, and MISO.
/sys/devices/.../CTLR/slave ... virtual file for (un)registering the
target device for an SPI target controller.
Writing the driver name of an SPI target handler to this file
registers the target device; writing "(null)" unregisters the target
device.
Reading from this file shows the name of the target device ("(null)"
if not registered).
/sys/class/spi_slave/spiB ... symlink to a logical node which could hold
class related state for the SPI target controller on bus "B". When
registered, a single spiB.* device is present here, possible sharing
the physical SPI bus segment with other SPI target devices.
此時,唯一的類特定狀態是匯流排編號(“spiB”中的“B”),因此這些/sys/class條目僅可用於快速識別匯流排。
主機板特定的初始化程式碼如何宣告SPI裝置?¶
Linux需要多種資訊才能正確配置SPI裝置。即使對於支援某些自動發現/列舉的晶片,該資訊通常也由主機板特定的程式碼提供。
宣告控制器¶
第一種資訊是現有SPI控制器的列表。對於基於片上系統(SOC)的主機板,這些通常是平臺裝置,並且控制器可能需要一些platform_data才能正常執行。“struct platform_device”將包括控制器第一個暫存器的物理地址及其IRQ等資源。
平臺通常會抽象“註冊SPI控制器”操作,可能會將其與初始化引腳配置的程式碼耦合,以便幾個主機板的arch/.../mach-*/board-*.c檔案都可以共享相同的基本控制器設定程式碼。這是因為大多數SOC都有多個支援SPI的控制器,通常只有給定主機板上實際可用的控制器才應設定和註冊。
因此,例如,arch/.../mach-*/board-*.c檔案可能具有如下程式碼
#include <mach/spi.h> /* for mysoc_spi_data */
/* if your mach-* infrastructure doesn't support kernels that can
* run on multiple boards, pdata wouldn't benefit from "__init".
*/
static struct mysoc_spi_data pdata __initdata = { ... };
static __init board_init(void)
{
...
/* this board only uses SPI controller #2 */
mysoc_register_spi(2, &pdata);
...
}
SOC特定的實用程式程式碼可能如下所示
#include <mach/spi.h>
static struct platform_device spi2 = { ... };
void mysoc_register_spi(unsigned n, struct mysoc_spi_data *pdata)
{
struct mysoc_spi_data *pdata2;
pdata2 = kmalloc(sizeof *pdata2, GFP_KERNEL);
*pdata2 = pdata;
...
if (n == 2) {
spi2->dev.platform_data = pdata2;
register_platform_device(&spi2);
/* also: set up pin modes so the spi2 signals are
* visible on the relevant pins ... bootloaders on
* production boards may already have done this, but
* developer boards will often need Linux to do it.
*/
}
...
}
請注意,即使使用相同的SOC控制器,主機板的platform_data也可能不同。例如,在一個主機板上,SPI可能使用外部時鐘,而在另一個主機板上,SPI時鐘來自某個主時鐘的當前設定。
宣告目標裝置¶
第二種資訊是目標板上存在的SPI目標裝置的列表,通常帶有驅動程式正確工作所需的一些主機板特定資料。
通常,您的arch/.../mach-*/board-*.c檔案會提供一個小表,列出每個主機板上的SPI裝置。(這通常只有少數幾個。)可能如下所示
static struct ads7846_platform_data ads_info = {
.vref_delay_usecs = 100,
.x_plate_ohms = 580,
.y_plate_ohms = 410,
};
static struct spi_board_info spi_board_info[] __initdata = {
{
.modalias = "ads7846",
.platform_data = &ads_info,
.mode = SPI_MODE_0,
.irq = GPIO_IRQ(31),
.max_speed_hz = 120000 /* max sample rate at 3V */ * 16,
.bus_num = 1,
.chip_select = 0,
},
};
同樣,請注意如何提供主機板特定資訊;每個晶片可能需要幾種型別。此示例顯示了通用約束,如允許的最快SPI時鐘(在這種情況下是主機板電壓的函式)或IRQ引腳的接線方式,以及晶片特定的約束,如重要延遲,該延遲由一個引腳上的電容改變。
(還有“controller_data”,可能對控制器驅動程式有用的資訊。一個例子是外圍裝置特定的DMA調整資料或片選回撥。它儲存在spi_device中。)
board_info應提供足夠的資訊,以便系統在不載入晶片驅動程式的情況下工作。這方面最麻煩的是spi_device.mode欄位中的SPI_CS_HIGH位,因為與以“向後”方式解釋片選的裝置共享匯流排是不可能的,直到基礎結構知道如何取消選擇它。
然後,您的主機板初始化程式碼會將該表註冊到SPI基礎結構,以便稍後在註冊SPI主機控制器驅動程式時可用
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
與其他靜態主機板特定設定一樣,您不會取消註冊這些設定。
廣泛使用的“卡”式計算機將記憶體、cpu以及其他一些元件捆綁到一張可能只有30平方釐米的卡上。在此類系統上,您的 arch/.../mach-.../board-*.c 檔案主要提供有關插入此類卡的主機板上的裝置的資訊。這當然包括透過卡聯結器連線的SPI裝置!
非靜態配置¶
當Linux透過SPI包括對MMC/SD/SDIO/DataFlash卡的支援時,這些配置也將是動態的。幸運的是,此類裝置都支援基本的裝置識別探測,因此它們應該可以正常熱插拔。
如何編寫“SPI協議驅動程式”?¶
大多數SPI驅動程式當前都是核心驅動程式,但也支援使用者空間驅動程式。在這裡,我們只討論核心驅動程式。
SPI協議驅動程式有點類似於平臺裝置驅動程式
static struct spi_driver CHIP_driver = {
.driver = {
.name = "CHIP",
.pm = &CHIP_pm_ops,
},
.probe = CHIP_probe,
.remove = CHIP_remove,
};
驅動程式核心將自動嘗試將此驅動程式繫結到任何board_info的modalias為“CHIP”的SPI裝置。除非您正在建立管理匯流排(顯示在/sys/class/spi_master下)的裝置,否則您的probe()程式碼可能如下所示。
static int CHIP_probe(struct spi_device *spi)
{
struct CHIP *chip;
struct CHIP_platform_data *pdata;
/* assuming the driver requires board-specific data: */
pdata = &spi->dev.platform_data;
if (!pdata)
return -ENODEV;
/* get memory for driver's per-chip state */
chip = kzalloc(sizeof *chip, GFP_KERNEL);
if (!chip)
return -ENOMEM;
spi_set_drvdata(spi, chip);
... etc
return 0;
}
一旦進入probe(),驅動程式就可以使用“struct spi_message”向SPI裝置發出I/O請求。當remove()返回時,或者在probe()失敗後,驅動程式保證它不會再提交任何此類訊息。
spi_message是一系列協議操作,作為一個原子序列執行。SPI驅動程式控制元件包括
雙向讀取和寫入何時開始 ... 透過其spi_transfer請求序列的排列方式;
使用哪些I/O緩衝區 ... 每個spi_transfer包裝一個用於每個傳輸方向的緩衝區,支援全雙工(兩個指標,在兩種情況下可能相同)和半雙工(一個指標為NULL)傳輸;
可選地定義傳輸後的短延遲 ... 使用spi_transfer.delay.value設定(如果緩衝區長度為零,則此延遲可能是唯一的協議效果) ... 在指定此延遲時,預設的spi_transfer.delay.unit是微秒,但是如果需要,可以將其調整為時鐘週期或納秒;
在傳輸和任何延遲後,片選是否變為非活動狀態 ... 透過使用spi_transfer.cs_change標誌;
提示下一個訊息是否可能轉到同一裝置 ... 透過在該原子組中的最後一個傳輸上使用spi_transfer.cs_change標誌,並可能節省晶片取消選擇和選擇操作的成本。
遵循標準的核心規則,並在您的訊息中提供DMA安全的緩衝區。這樣,使用DMA的控制器驅動程式就不會被迫進行額外的複製,除非硬體需要它(例如,解決強制使用反彈緩衝的硬體錯誤)。
基本的I/O原語是
spi_async()。非同步請求可以在任何上下文(irq處理程式、任務等)中發出,並且使用訊息提供的回撥報告完成情況。在檢測到任何錯誤後,晶片將被取消選擇,並且該spi_message的處理將被中止。還有同步包裝器,例如
spi_sync(),以及包裝器,例如spi_read(),spi_write(), 和spi_write_then_read()。這些只能在可能休眠的上下文中發出,並且它們都是乾淨的(並且小而“可選”)層,位於spi_async()之上。
spi_write_then_read()呼叫及其周圍的便利包裝器,只應用於少量資料,在這種情況下,可以忽略額外的複製成本。它旨在支援常見的RPC風格的請求,例如寫入一個八位命令並讀取一個十六位響應 --spi_w8r16()是它的一個包裝器,完全可以做到這一點。
有些驅動程式可能需要修改spi_device特性,如傳輸模式、字大小或時鐘速率。這可以透過 spi_setup() 完成,它通常在probe()中在對裝置進行第一次I/O之前呼叫。但是,這也可以在沒有為該裝置掛起任何訊息時隨時呼叫。
雖然“spi_device”將是驅動程式的下限,但上限可能包括sysfs(特別是對於感測器讀數)、輸入層、ALSA、網路、MTD、字元裝置框架或其他Linux子系統。
請注意,作為與SPI裝置互動的一部分,您的驅動程式必須管理兩種型別的記憶體。
I/O緩衝區使用常用的Linux規則,並且必須是DMA安全的。您通常會從堆或空閒頁面池中分配它們。不要使用堆疊或任何宣告為“static”的內容。
spi_message和spi_transfer元資料用於將這些I/O緩衝區粘合到一組協議事務中。這些可以方便地在任何地方分配,包括作為其他一次性分配驅動程式資料結構的一部分。將這些初始化為零。
如果您願意,可以使用spi_message_alloc()和spi_message_free()便利例程來分配和零初始化具有多個傳輸的spi_message。
如何編寫“SPI控制器驅動程式”?¶
SPI控制器可能會在platform_bus上註冊;編寫一個驅動程式來繫結到裝置,無論涉及哪個匯流排。
此型別驅動程式的主要任務是提供“spi_controller”。使用spi_alloc_host()分配主機控制器,並使用spi_controller_get_devdata()獲取為該裝置分配的驅動程式專用資料。
struct spi_controller *ctlr;
struct CONTROLLER *c;
ctlr = spi_alloc_host(dev, sizeof *c);
if (!ctlr)
return -ENODEV;
c = spi_controller_get_devdata(ctlr);
驅動程式將初始化該spi_controller的欄位,包括匯流排編號(可能與平臺裝置ID相同)和用於與SPI核心和SPI協議驅動程式互動的三種方法。它還將初始化自己的內部狀態。(請參閱下面的匯流排編號和這些方法。)
初始化spi_controller後,使用 spi_register_controller() 將其釋出到系統的其餘部分。此時,將提供控制器和任何預先宣告的spi裝置的裝置節點,並且驅動程式模型核心將負責將它們繫結到驅動程式。
如果您需要刪除SPI控制器驅動程式,spi_unregister_controller() 將反轉 spi_register_controller() 的效果。
匯流排編號¶
匯流排編號很重要,因為這是Linux識別給定SPI匯流排(共享SCK、MOSI、MISO)的方式。有效的匯流排編號從零開始。在SOC系統上,匯流排編號應與晶片製造商定義的編號匹配。例如,硬體控制器SPI2將是匯流排編號2,並且連線到它的裝置的spi_board_info將使用該編號。
如果您沒有此類硬體分配的匯流排編號,並且由於某種原因您無法簡單地分配它們,請提供一個負匯流排編號。然後,它將被動態分配的編號替換。然後,您需要將其視為非靜態配置(參見上文)。
SPI主機控制器方法¶
ctlr->setup(struct spi_device *spi)這設定了裝置時鐘速率、SPI模式和字大小。驅動程式可以更改board_info提供的預設值,然後呼叫spi_setup(spi)來呼叫此例程。它可能會休眠。
除非每個SPI目標都有自己的配置暫存器,否則不要立即更改它們 ... 否則驅動程式可能會損壞正在為其他SPI裝置進行的I/O。
注意
錯誤警報:由於某種原因,許多spi_controller驅動程式的第一個版本似乎都弄錯了。在您編寫setup()時,假設控制器正在主動處理另一個裝置的傳輸。
ctlr->cleanup(struct spi_device *spi)您的控制器驅動程式可以使用spi_device.controller_state來儲存其動態與該裝置關聯的狀態。如果您這樣做,請務必提供cleanup()方法來釋放該狀態。
ctlr->prepare_transfer_hardware(struct spi_controller *ctlr)佇列機制將呼叫此方法來向驅動程式發出訊號,指示即將收到訊息,因此子系統請求驅動程式透過發出此呼叫來準備傳輸硬體。它可能會休眠。
ctlr->unprepare_transfer_hardware(struct spi_controller *ctlr)佇列機制將呼叫此方法來向驅動程式發出訊號,指示佇列中不再有任何掛起的訊息,並且它可以放鬆硬體(例如,透過電源管理呼叫)。它可能會休眠。
ctlr->transfer_one_message(struct spi_controller *ctlr, struct spi_message *mesg)子系統呼叫驅動程式來傳輸單個訊息,同時對同時到達的傳輸進行排隊。當驅動程式完成此訊息時,它必須呼叫
spi_finalize_current_message(),以便子系統可以發出下一個訊息。它可能會休眠。ctrl->transfer_one(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *transfer)子系統呼叫驅動程式來傳輸單個傳輸,同時對同時到達的傳輸進行排隊。當驅動程式完成此傳輸時,它必須呼叫
spi_finalize_current_transfer(),以便子系統可以發出下一個傳輸。它可能會休眠。注意:transfer_one和transfer_one_message是互斥的;當兩者都設定時,通用子系統不會呼叫您的transfer_one回撥。返回值
negative errno:錯誤
0:傳輸已完成
1:傳輸仍在進行中
ctrl->set_cs_timing(struct spi_device *spi, u8 setup_clk_cycles, u8 hold_clk_cycles, u8 inactive_clk_cycles)此方法允許SPI客戶端驅動程式請求SPI主機控制器來配置裝置特定的CS建立、保持和非活動時序要求。
已棄用的方法¶
ctrl->transfer(struct spi_device *spi, struct spi_message *message)這不得休眠。它的責任是安排發生傳輸併發出其complete()回撥。這兩者通常會在稍後發生,在其他傳輸完成後,如果控制器空閒,則需要重新啟動它。此方法未在排隊的控制器上使用,如果實現了transfer_one_message()和(un)prepare_transfer_hardware(),則必須為NULL。
SPI訊息佇列¶
如果您對SPI子系統提供的標準排隊機制感到滿意,只需實現上面指定的排隊方法。使用訊息佇列的優點是集中了大量程式碼並提供了方法的純程序上下文執行。訊息佇列也可以在高優先順序SPI流量上提升到即時優先順序。
除非選擇了SPI子系統中的排隊機制,否則驅動程式的大部分將管理現在已棄用的函式transfer()提供的I/O佇列。
該佇列可能純粹是概念性的。例如,僅用於低頻感測器訪問的驅動程式可能可以很好地使用同步PIO。
但是佇列可能非常真實,使用message->queue、PIO、通常是DMA(特別是如果根檔案系統位於SPI快閃記憶體中),以及執行上下文(如IRQ處理程式、tasklet或工作佇列(如keventd))。您的驅動程式可以根據需要進行花哨或簡單操作。這樣的transfer()方法通常只是將訊息新增到佇列,然後啟動一些非同步傳輸引擎(除非它已經在執行)。
SPI 協議的擴充套件¶
由於 SPI 沒有正式的規範或標準,晶片製造商可以以略有不同的方式實現 SPI 協議。在大多數情況下,來自不同供應商的 SPI 協議實現是彼此相容的。例如,在 SPI 模式 0 (CPOL=0, CPHA=0) 中,匯流排線路的行為可能如下所示
nCSx ___ ___
\_________________________________________________________________/
• •
• •
SCLK ___ ___ ___ ___ ___ ___ ___ ___
_______/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \_____
• : ; : ; : ; : ; : ; : ; : ; : ; •
• : ; : ; : ; : ; : ; : ; : ; : ; •
MOSI XXX__________ _______ _______ ________XXX
0xA5 XXX__/ 1 \_0_____/ 1 \_0_______0_____/ 1 \_0_____/ 1 \_XXX
• ; ; ; ; ; ; ; ; •
• ; ; ; ; ; ; ; ; •
MISO XXX__________ _______________________ _______ XXX
0xBA XXX__/ 1 \_____0_/ 1 1 1 \_____0__/ 1 \____0__XXX
圖例
• marks the start/end of transmission;
: marks when data is clocked into the peripheral;
; marks when data is clocked into the controller;
X marks when line states are not specified.
在少數情況下,晶片透過指定其他 SPI 協議不具備的線路行為(例如,CS 未置位時的資料線路狀態)來擴充套件 SPI 協議。這些不同的 SPI 協議、模式和配置由不同的 SPI 模式標誌支援。
MOSI 空閒狀態配置¶
常見的 SPI 協議實現沒有為控制器不時鐘輸出資料時 MOSI 線路的任何狀態或行為指定。但是,確實存在外設要求在未時鐘輸出資料時 MOSI 線路處於特定狀態。例如,如果外設期望控制器不時鐘輸出資料時 MOSI 線路為高電平 (SPI_MOSI_IDLE_HIGH),那麼 SPI 模式 0 中的傳輸將如下所示
nCSx ___ ___
\_________________________________________________________________/
• •
• •
SCLK ___ ___ ___ ___ ___ ___ ___ ___
_______/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \_____
• : ; : ; : ; : ; : ; : ; : ; : ; •
• : ; : ; : ; : ; : ; : ; : ; : ; •
MOSI _____ _______ _______ _______________ ___
0x56 \_0_____/ 1 \_0_____/ 1 \_0_____/ 1 1 \_0_____/
• ; ; ; ; ; ; ; ; •
• ; ; ; ; ; ; ; ; •
MISO XXX__________ _______________________ _______ XXX
0xBA XXX__/ 1 \_____0_/ 1 1 1 \_____0__/ 1 \____0__XXX
圖例
• marks the start/end of transmission;
: marks when data is clocked into the peripheral;
; marks when data is clocked into the controller;
X marks when line states are not specified.
在這種對常用 SPI 協議的擴充套件中,MOSI 線路狀態被指定為:當 CS 被置位但控制器未向外設時鐘輸出資料時,以及當 CS 未被置位時,都保持高電平。
需要此擴充套件的外設必須透過將 SPI_MOSI_IDLE_HIGH 位設定到它們的 struct spi_device 的 mode 屬性中,並呼叫 spi_setup() 來請求它。 支援此擴充套件的控制器應透過在它們的 struct spi_controller 的 mode_bits 屬性中設定 SPI_MOSI_IDLE_HIGH 來指示它。 將 MOSI 配置為空閒低電平是類似的,但使用 SPI_MOSI_IDLE_LOW 模式位。
感謝¶
Linux-SPI 討論的貢獻者包括(按姓氏字母順序排列):
Mark Brown
David Brownell
Russell King
Grant Likely
Dmitry Pervushin
Stephen Street
Mark Underwood
Andrew Victor
Linus Walleij
Vitaly Wool