1. 硬件原理
1.1 SPI通信協(xié)議
-
SPI(Serial Peripheral Interface)是由Motorola公司開發(fā)的一種通用數(shù)據(jù)總線
-
四根通信線:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
-
同步,全雙工
-
支持總線掛載多設(shè)備(一主多從)
1.2 硬件連接
- 多NSS獨立片選方式
- 菊花鏈方式
引腳 | 含義 |
---|---|
DO(MOSI) | Master Output, Slave Input, SPI主控用來發(fā)出數(shù)據(jù),SPI從設(shè)備用來接收數(shù)據(jù),輸出引腳設(shè)置為推挽輸出 |
DI(MISO) | Master Input, Slave Output, SPI主控用來發(fā)出數(shù)據(jù),SPI從設(shè)備用來接收數(shù)據(jù),輸入引腳設(shè)置為浮空或上拉輸入,從機不輸出時為高阻態(tài)。 |
SCK | Serial Clock,時鐘 |
CS | Chip Select,芯片選擇引腳,NSS 信號線由高變低,是 SPI 通訊的起始信號。NSS 信號由低變高,是 SPI 通訊的停止信號,表示本次通訊結(jié)束,從機的選中狀態(tài)被取消。 |
移位寄存器示意圖
1.3 時序
- 起始:CS從高到低
- 終止:CS從低到高
SPI的時鐘極性CPOL和時鐘相位CPHA可以分別為0或1,由此構(gòu)成了四種組合:
CPOL | CPHA | 模式 | 含義 |
---|---|---|---|
0 | 0 | 0 | CLK初始電平為低電平,在第一個時鐘沿采樣數(shù)據(jù) |
0 | 1 | 1 | CLK初始電平為低電平,在第二個時鐘沿采樣數(shù)據(jù) |
1 | 0 | 2 | CLK初始電平為高電平,在第一個時鐘沿采樣數(shù)據(jù) |
1 | 1 | 3 | CLK初始電平為高電平,在第二個時鐘沿采樣數(shù)據(jù) |
常用的是模式0和模式3,因為它們都是在上升沿采樣數(shù)據(jù),不用去在乎時鐘的初始電平是什么,只要在上升沿采集數(shù)據(jù)就行。
IIC有效字節(jié)流數(shù)據(jù)第一個字節(jié)是寄存器地址,之后是讀寫的數(shù)據(jù),使用的是讀寫寄存器模型
SPI用指令碼加讀寫數(shù)據(jù)模型,發(fā)送指令字節(jié)的方式來讀取,從機中有指令集對應(yīng),
發(fā)送指令。
指定地址讀。
指定地址寫。
1.4 代碼
IO模擬
void MySPI_Start(void)
{
MySPI_W_SS(0);
}
void MySPI_Stop(void)
{
MySPI_W_SS(1);
}
uint8_t MySPI_SwapByte(uint8_t ByteSend) //交換字節(jié),發(fā)送的話就不需要讀取返回值,接收的話就發(fā)送0XFF接收返回的數(shù)據(jù)。
{
uint8_t i, ByteReceive = 0x00;
for (i = 0; i < 8; i ++) //模式0,其他模式對著時序圖換一下MySPI_W_SCK就行
{
MySPI_W_MOSI(ByteSend & (0x80 >> i));
MySPI_W_SCK(1);
if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
MySPI_W_SCK(0);
}
return ByteReceive;
}
1.5 SPI FLASH W25Qxx
1.5.1 硬件原理
-
W25Qxx系列是一種低成本、小型化、使用簡單的非易失性存儲器,常應(yīng)用于數(shù)據(jù)存儲、字庫存儲、固件程序存儲等場景
-
存儲介質(zhì):Nor Flash(閃存)
-
時鐘頻率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
-
存儲容量(24位地址):
W25Q40: 4Mbit / 512KByte
W25Q80: 8Mbit / 1MByte
W25Q16: 16Mbit / 2MByte
W25Q32: 32Mbit / 4MByte
W25Q64: 64Mbit / 8MByte
W25Q128: 128Mbit / 16MByte
W25Q256: 256Mbit / 32MByte
spi flash hold
spi flash hold
該引腳接收到低電平時,且 /CS=0,數(shù)據(jù)引腳為高阻態(tài),芯片可以屏蔽總線的數(shù)據(jù)和時鐘信號,當(dāng)引腳為高電平時,可以繼續(xù)恢復(fù)對芯片的操作,適用于多設(shè)備SPI控制,分時使用。這個引腳的意義是引進(jìn)了3種設(shè)備情況:設(shè)備不被選中,被選中但不工作,被選中且工作;沒有這個引腳功能時,芯片只有兩種情況:不被選中,選中且工作。
該引腳通過控制寄存器可以有復(fù)用功能,作為數(shù)據(jù)引腳。
SPI Flash有三種工作模式
SPI Flash有三種工作模式:Standard SPI,Dual SPI,Quad SPI。這三種模式的區(qū)別在于數(shù)據(jù)引腳的數(shù)量和功能不一樣。
Standard SPI
標(biāo)準(zhǔn)SPI,也就是我們常說的四線:片選 (/CS),時鐘 (CLK),輸入數(shù)據(jù) (DI),輸出數(shù)據(jù) (DO)。另外配有寫保護(hù) (/WP) 和維持 (/HOLD) 功能。
Dual SPI
這種工作模式就是對標(biāo)準(zhǔn)SPI進(jìn)行了改進(jìn),將DO,DI改成IO1和IO2,變成了雙向IO口,這樣一個時鐘周期可以讀寫2位數(shù)據(jù)。寫保護(hù)(/WP)和維持(/HOLD)功能仍然保留。
Quad SPI
這種工作模式是對Dual SPI模式進(jìn)行改進(jìn),就是上面講的,將寫保護(hù) (/WP) 和維持 (/HOLD) 引腳復(fù)用為IO口,標(biāo)記為IO3,IO4,這樣總共就是四個IO口,數(shù)據(jù)傳送速度更快。
- 空間劃分:塊64KB,扇區(qū)4KB,頁256B
- SPI控制邏輯
- 狀態(tài)寄存器
- 頁緩存,會對一次性寫入的數(shù)據(jù)緩存
1.5.2 Flash操作注意事項
寫入操作時:
-
寫入操作前,必須先進(jìn)行寫使能
-
每個數(shù)據(jù)位只能由1改寫為0,不能由0改寫為1(不能覆蓋改寫,要先擦除,發(fā)送擦除指令)
-
寫入數(shù)據(jù)前必須先擦除,擦除后,所有數(shù)據(jù)位變?yōu)?
-
擦除必須按最小擦除單元進(jìn)行
-
連續(xù)寫入多字節(jié)時,最多寫入一頁的數(shù)據(jù),超過頁尾位置的數(shù)據(jù),會回到頁首覆蓋寫入
-
寫入操作結(jié)束后,芯片進(jìn)入忙狀態(tài),不響應(yīng)新的讀寫操作
讀取操作時:
- 直接調(diào)用讀取時序,無需使能,無需額外操作,沒有頁的限制,讀取操作結(jié)束后不會進(jìn)入忙狀態(tài),但不能在忙狀態(tài)時讀取
BUSY
- 擦除和寫入會變?yōu)槊顟B(tài),每次操作都要先讀是不是忙狀態(tài)再操作。
WEL
- 任何寫入都要先寫使能
1.5.3 代碼
void W25Q64_Init(void)
{
MySPI_Init();
}
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
MySPI_Start();
MySPI_SwapByte(W25Q64_JEDEC_ID); //發(fā)送讀ID指令
*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交換讀取8位廠商ID
*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交換讀取高8位設(shè)備ID
*DID <<= 8;
*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//交換讀取低8位設(shè)備ID
MySPI_Stop();
}
void W25Q64_WriteEnable(void)
{
MySPI_Start();
MySPI_SwapByte(W25Q64_WRITE_ENABLE);
MySPI_Stop();
}
void W25Q64_WaitBusy(void)
{
uint32_t Timeout;
MySPI_Start();
MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); //讀狀態(tài)寄存器
Timeout = 100000;
while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01) //查看是否忙狀態(tài)
{
Timeout --;
if (Timeout == 0) //超時判斷
{
break;
}
}
MySPI_Stop();
}
//頁編程:頁的某個地址寫一堆數(shù)據(jù)
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
uint16_t i;
W25Q64_WriteEnable();
MySPI_Start();
MySPI_SwapByte(W25Q64_PAGE_PROGRAM);
MySPI_SwapByte(Address >> 16); //先發(fā)地址最高位
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
for (i = 0; i < Count; i ++) //循環(huán)發(fā)送數(shù)據(jù)
{
MySPI_SwapByte(DataArray[i]);
}
MySPI_Stop();
W25Q64_WaitBusy(); //事后等待,保證出了這個函數(shù)是不忙的,會浪費資源。
//也可以放在最前面,事前等待
}
//扇區(qū)擦除
void W25Q64_SectorErase(uint32_t Address)
{
W25Q64_WriteEnable();
MySPI_Start();
MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
MySPI_Stop();
W25Q64_WaitBusy();
}
//讀數(shù)據(jù)
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
uint32_t i;
MySPI_Start();
MySPI_SwapByte(W25Q64_READ_DATA);
MySPI_SwapByte(Address >> 16);
MySPI_SwapByte(Address >> 8);
MySPI_SwapByte(Address);
for (i = 0; i < Count; i ++)
{
DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
}
MySPI_Stop();
}
1.6 常見面試問題
SPI總線的工作頻率
SPI是一種事實標(biāo)準(zhǔn),由Motorola開發(fā),并沒有一個官方標(biāo)準(zhǔn)。已知的有的器件SPI已達(dá)到50Mbps。具體到產(chǎn)品中SPI的速率主要看主從器件SPI控制器的性能限制。
SPI最大傳輸速率受以下幾個條件影響:
1)SPI的最大時鐘頻率;
2)CPU處理SPI數(shù)據(jù)的能力;
3)輸出端驅(qū)動能力(PCB所允許的最大信號傳輸速率);
W25Qxx系列時鐘頻率:80MHz/160MHz(Dual SPI)/320MHz(Quad SPI)
SPI優(yōu)缺點
SPI的優(yōu)點
- 全雙工串行通信;
- 高速數(shù)據(jù)傳輸速率。
- 簡單的軟件配置;
- 極其靈活的數(shù)據(jù)傳輸,不限于8位,它可以是任意大小的字;
- 非常簡單的硬件結(jié)構(gòu)。從站不需要唯一地址(與I2C不同)。從機使用主機時鐘,不需要精密時鐘振蕩器/晶振(與UART不同)。不需要收發(fā)器(與CAN不同)。
SPI的缺點
- 沒有硬件從機應(yīng)答信號(主機可能在不知情的情況下無處發(fā)送);
- 通常僅支持一個主設(shè)備;
- 需要更多的引腳(與I2C不同);
- 沒有定義硬件級別的錯誤檢查協(xié)議;
- 與RS-232和CAN總線相比,只能支持非常短的距離;
2. 應(yīng)用編程–spidev應(yīng)用程序分析
內(nèi)核提供的測試程序:tools\spi\spidev_fdx.c
2.1 使用方法
spidev_fdx [-h] [-m N] [-r N] /dev/spidevB.D
- -h: 打印用法
- -m N:先寫1個字節(jié)0xaa,再讀N個字節(jié),**注意:**不是同時寫同時讀
- -r N:讀N個字節(jié)
2.2 代碼分析
2.2.1 顯示設(shè)備屬性
2.2.2 讀數(shù)據(jù)
2.2.3 先寫再讀
2.2.4 同時讀寫
2.3 spidev的缺點
使用read、write函數(shù)時,只能讀、寫,這是半雙工方式。
使用ioctl可以達(dá)到全雙工的讀寫。
但是spidev有2個缺點:
- 不支持中斷
- 只支持同步操作,不支持異步操作:就是read/write/ioctl這些函數(shù)只能執(zhí)行完畢才可返回
3.內(nèi)核驅(qū)動
總線-設(shè)備-驅(qū)動模型
SPI子系統(tǒng)中:SPI控制器、SPI設(shè)備-驅(qū)動。
在設(shè)備樹里,會有一個節(jié)點用來表示SPI控制器。
在這個SPI控制器下面,連接有哪些SPI設(shè)備?會在設(shè)備樹里使用子節(jié)點來描述SPI設(shè)備。
官方的通用dev驅(qū)動spidev驅(qū)動程序
自己編寫設(shè)備spi驅(qū)動程序,如DAC4822,TLC5615,OLED SD1306
SPI_Master驅(qū)動程序框架文章來源:http://www.zghlxwxcb.cn/news/detail-571056.html
SPI_Slave_Mode驅(qū)動程序框架文章來源地址http://www.zghlxwxcb.cn/news/detail-571056.html
4. 總體框架
到了這里,關(guān)于【嵌入式Linux內(nèi)核驅(qū)動】SPI子系統(tǒng) | 硬件原理 | 應(yīng)用編程 | 內(nèi)核驅(qū)動 | 總體框架的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!