SPI總線是我們常用的串行設(shè)備接口,一般情況下我們都會(huì)適應(yīng)硬件SPI接口,但有些時(shí)候當(dāng)硬件端口不足時(shí),我們也希望可以使用軟件來(lái)模擬SPI硬件接口,特別是要求不是很高的時(shí)候。在這一篇中我們將來(lái)討論如何使用GPIO和軟件來(lái)模擬SPI通訊接口。
1、功能概述
??SPI即串行外設(shè)接口,是一種同步串行通訊接口,用于微處理器及控制器和外圍擴(kuò)展芯片之間的串行連接,現(xiàn)已發(fā)展成為一種工業(yè)標(biāo)準(zhǔn)。
1.1、物理層
??SPI總線在物理層通常使用3條總線及1條片選線,3條總線分別為 SCK、 MOSI、 MISO,片選線為NSS,它們的作用介紹如下:
??NSS( Slave Select),從設(shè)備選擇信號(hào)線,常稱為片選信號(hào)線。在SPI協(xié)議中沒(méi)有設(shè)備地址,所以需要使用NSS信號(hào)線來(lái)尋址,當(dāng)主機(jī)要選擇從設(shè)備時(shí),把該從設(shè)備的NSS信號(hào)線設(shè)置為低電平,該從設(shè)備即被選中,即片選有效,接著主機(jī)開(kāi)始與被選中的從設(shè)備進(jìn)行SPI通訊。所以SPI通訊以NSS線置低電平為開(kāi)始信號(hào),以NSS線被拉高作為結(jié)束信號(hào)。
??SCK (Serial Clock),時(shí)鐘信號(hào)線,用于通訊數(shù)據(jù)同步。它由通訊主機(jī)產(chǎn)生,決定了通訊的速率,不同的設(shè)備支持的最高時(shí)鐘頻率不一樣,如 STM32 的 SPI 時(shí)鐘頻率最大為fpclk/2,兩個(gè)設(shè)備之間通訊時(shí),通訊速率受限于低速設(shè)備。
??MOSI (Master Output, Slave Input),主設(shè)備輸出/從設(shè)備輸入引腳。主機(jī)的數(shù)據(jù)從這條信號(hào)線輸出,從機(jī)由這條信號(hào)線讀入主機(jī)發(fā)送的數(shù)據(jù),即這條線上數(shù)據(jù)的方向?yàn)橹鳈C(jī)到從機(jī)。
??MISO(Master Input,, Slave Output),主設(shè)備輸入/從設(shè)備輸出引腳。主機(jī)從這條信號(hào)線讀入數(shù)據(jù),從機(jī)的數(shù)據(jù)由這條信號(hào)線輸出到主機(jī),即在這條線上數(shù)據(jù)的方向?yàn)閺臋C(jī)到主機(jī)。
??對(duì)于使用SPI總線來(lái)進(jìn)行通訊的設(shè)備,一臺(tái)主機(jī)可以與多臺(tái)從機(jī)進(jìn)行通訊,時(shí)鐘與數(shù)據(jù)總線為公用,片選線每臺(tái)從機(jī)都是獨(dú)立的,具體連接方式如下圖所示:
??也就是說(shuō),有多個(gè)SPI從設(shè)備與SPI主機(jī)通訊時(shí),設(shè)備的時(shí)鐘線和總線數(shù)據(jù)線 SCK、MOSI及MISO同時(shí)并聯(lián)到相同的SPI總線上,即無(wú)論有多少個(gè)從設(shè)備,都共同只使用這 3 條總線。而每個(gè)從設(shè)備都擁有獨(dú)立的一條NSS信號(hào)線,即有多少個(gè)從設(shè)備,就有多少條片選信號(hào)線。
1.2、協(xié)議層
??我們已經(jīng)簡(jiǎn)述了SPI總線的物理連接方式,接下來(lái)我們?cè)賮?lái)了解一下具體的通訊協(xié)議。在了解協(xié)議前,我們需要清楚兩個(gè)概念,即時(shí)鐘的極性和時(shí)鐘的相位。
??所謂時(shí)鐘極性,通常稱作CPOL,即是指在空閑狀態(tài)下,時(shí)鐘所處的電平狀態(tài)。如果SCLK在數(shù)據(jù)發(fā)送之前和之后的空閑狀態(tài)是高電平,那么就是CPOL=1;如果空閑狀態(tài)SCLK是低電平,那么就是 CPOL=0。
??而時(shí)鐘的相位,通常稱作CPHA,就是指數(shù)據(jù)采樣是在時(shí)鐘脈沖的第1個(gè)跳變沿還是在第2個(gè)跳變沿。如果在SCK的第1個(gè)跳變沿進(jìn)行數(shù)據(jù)采樣,則CPHA=0;如果在SCK的第2個(gè)跳變沿采樣,則CPHA=1。
??在通訊協(xié)議中,根據(jù)CPOL和CPHA的取值不同存在4種不同的配置方式,不同的配置方式對(duì)應(yīng)不同的通訊模式。
??(1)當(dāng)CPOL=0,CPHA=0時(shí),空閑狀態(tài)時(shí)鐘SCK的電平要保持在低電平,而數(shù)據(jù)的采樣時(shí)刻在時(shí)鐘脈沖的奇數(shù)跳變邊沿,其時(shí)序圖如下:
??(2)當(dāng)CPOL=0,CPHA=1時(shí),空閑狀態(tài)時(shí)鐘SCK的電平要保持在低電平,而數(shù)據(jù)的采樣時(shí)刻在時(shí)鐘脈沖的偶數(shù)跳變邊沿,其時(shí)序圖如下:
??(3)當(dāng)CPOL=1,CPHA=0時(shí),空閑狀態(tài)時(shí)鐘SCK的電平要保持在高電平,而數(shù)據(jù)的采樣時(shí)刻在時(shí)鐘脈沖的奇數(shù)跳變邊沿,其時(shí)序圖如下:
??(4)當(dāng)CPOL=1,CPHA=1時(shí),空閑狀態(tài)時(shí)鐘SCK的電平要保持在高電平,而數(shù)據(jù)的采樣時(shí)刻在時(shí)鐘脈沖的偶數(shù)跳變邊沿,其時(shí)序圖如下:
??根據(jù)這時(shí)鐘情況,將SPI總線的工作模式分為4種,如下圖所示:
??而只有主機(jī)與從機(jī)擁有相同的工作模式時(shí),主從機(jī)之間才可以正常通訊。在實(shí)際應(yīng)用中,“模式 0”與“模式 3”是比較常見(jiàn)的工作模式,但在我們的驅(qū)動(dòng)設(shè)計(jì)中應(yīng)該兼顧這4種模式,以具備更廣泛的適應(yīng)性。
2、驅(qū)動(dòng)設(shè)計(jì)與實(shí)現(xiàn)
??我們已經(jīng)簡(jiǎn)要的描述了SPI通訊總線的物理連接和通訊協(xié)議,接下來(lái)我們將根據(jù)其協(xié)議的特性設(shè)計(jì)并實(shí)現(xiàn)基于GPIO模擬的SPI總線驅(qū)動(dòng)。
2.1、對(duì)象定義
??我們依然使用基于對(duì)象的思想來(lái)實(shí)現(xiàn)基于GPIO模擬的SPI總線驅(qū)動(dòng)。既然是基于對(duì)象,那么在使用一個(gè)對(duì)象之前我們需要先獲得這個(gè)對(duì)象。所以我們必須先定義一個(gè)基于GPIO模擬的SPI總線的對(duì)象。
2.1.1、對(duì)象的抽象
??我們要得到基于GPIO模擬的SPI總線的對(duì)象,需要先分析其基本特性。一般來(lái)說(shuō),一個(gè)對(duì)象至少包含屬性與操作兩方面的特性。接下來(lái)我們就來(lái)從這兩個(gè)方面思考一下基于GPIO模擬的SPI總線的對(duì)象。
??我們首先來(lái)考慮對(duì)象的屬性,作為屬性肯定是用于標(biāo)識(shí)或記錄對(duì)象特征的東西。我們?cè)谇懊嬉呀?jīng)了解到SPI總線的一些特點(diǎn)和獨(dú)特設(shè)定,那么這些特點(diǎn)和設(shè)定是否能成為對(duì)象的屬性呢?
??我們考慮到作為同步總線,SPI總線的控制需要時(shí)鐘,這關(guān)系到SPI總線的通訊速率,這一通訊速率不僅配置SPI總線的工作方式也標(biāo)識(shí)當(dāng)前的工作狀態(tài),所以我們將工作速率作為模擬SPI總線對(duì)象的一個(gè)屬性。在前面我們討論過(guò)SPI總線的工作模式,工作模式由CPOL和CPHA決定,所以在初始化總線時(shí)就會(huì)確定工作模式,所以我們需要記錄CPOL和CPHA,我們將CPOL和CPHA也作為對(duì)象的屬性。
??接下來(lái)我們考慮GPIO模擬SPI總線的操作問(wèn)題。我們將那些對(duì)象要實(shí)現(xiàn)的,并且依賴于具體的平臺(tái)的行為實(shí)現(xiàn)定義為對(duì)象的操作。對(duì)于GPIO模擬SPI總線來(lái)說(shuō),我們需要通過(guò)總線發(fā)送數(shù)據(jù)和接收數(shù)據(jù),而接收和發(fā)送的實(shí)現(xiàn)都依賴于具體的軟硬件平臺(tái),所以我們將發(fā)送數(shù)據(jù)和接收數(shù)據(jù)定義為對(duì)象的操作。SPI總線作為同步總線需要時(shí)鐘,而時(shí)鐘操作也是依賴于具體的軟硬件平臺(tái)來(lái)實(shí)現(xiàn),所以我們將始終的控制也作為對(duì)象的操作。
??根據(jù)上述我們對(duì)GPIO模擬SPI總線的分析,我們可以定義GPIO模擬SPI總線對(duì)象類型如下:
/*定義GPIO模擬SPI接口對(duì)象*/typedef struct SimuSPIObject{
uint16_t CPOL:1; uint16_t CPHA:1; uint16_t period:14; //確定速度為大于0K小于等于400K的整數(shù),默認(rèn)為100K
void (*SetSCKPin)(SimuSPIPinValueType op); //設(shè)置SCL引腳
void (*SetMOSIPin)(SimuSPIPinValueType op); //設(shè)置SDA引腳
uint8_t (*ReadMISOPin)(void); //讀取SDA引腳位
void (*Delayus)(volatile uint32_t period); //速度延時(shí)函數(shù)}SimuSPIObjectType;
2.1.2、對(duì)象初始化
??我們知道,在使用一個(gè)對(duì)象之前需要先對(duì)其進(jìn)行初始化,所以這里我們來(lái)考慮GPIO模擬SPI對(duì)象的初始化函數(shù)。一般來(lái)說(shuō),初始化函數(shù)需要處理幾個(gè)方面的問(wèn)題。一是檢查輸入參數(shù)是否合理;二是為對(duì)象的屬性賦初值;三是對(duì)對(duì)象做必要的初始化配置。據(jù)此我們?cè)O(shè)計(jì)GPIO模擬SPI對(duì)象的初始化函數(shù)如下:
/* GPIO模擬SPI通訊初始化 */void SimuSPIInitialization(SimuSPIObjectType *simuSPIInstance,//初始化的模擬SPI對(duì)象
uint32_t speed, //時(shí)鐘頻率
SimuSPICPOLType CPOL, //時(shí)鐘極性
SimuSPICPHAType CPHA, //時(shí)鐘頻率
SimuSPISetSCKPin setSCK, //SCK時(shí)鐘操作函數(shù)指針
SimuSPISetMOSIPin setMOSI, //MOSI操作函數(shù)指針
SimuSPIReadMISOPin getMISO, //MISO操作函數(shù)指針
SimuSPIDelayus delayus //微秒延時(shí)操作函數(shù)指針
){ if((simuSPIInstance==NULL)||(setSCK==NULL)||(setMOSI==NULL)||(getMISO==NULL)||(delayus==NULL))
{ return;
}
simuSPIInstance->SetSCKPin=setSCK;
simuSPIInstance->SetMOSIPin=setMOSI;
simuSPIInstance->ReadMISOPin=getMISO;
simuSPIInstance->Delayus=delayus;
/*初始化速度,默認(rèn)100K*/
if((speed>0)&&(speed<=500))
{
simuSPIInstance->period=500/speed;
} else
{
simuSPIInstance->period=5;
}
simuSPIInstance->CPOL=CPOL;
simuSPIInstance->CPHA=CPHA;
/*拉高總線,使處于空閑狀態(tài)*/
if(simuSPIInstance->CPOL==SimuSPI_POLARITY_LOW)
{
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
} else
{
simuSPIInstance->SetSCKPin(SimuSPI_Set);
}
}
2.2、對(duì)象操作
??我們已經(jīng)定義了對(duì)象類型,也實(shí)現(xiàn)了對(duì)象的初始化函數(shù),但我們還沒(méi)有實(shí)現(xiàn)對(duì)象的具體操作,所以接下來(lái)我們就來(lái)實(shí)現(xiàn)對(duì)象的具體操作。
2.2.1、數(shù)據(jù)的發(fā)送
??在我們使用SPI來(lái)實(shí)現(xiàn)數(shù)據(jù)通訊時(shí),免不了要發(fā)送數(shù)據(jù),所以在我們使用GPIO模擬SPI端口時(shí)就需要解決數(shù)據(jù)發(fā)送的問(wèn)題。這里我們考慮使用模擬SPI發(fā)送一個(gè)字節(jié)的問(wèn)題,因?yàn)榘l(fā)送多個(gè)字節(jié)無(wú)非是多重復(fù)幾次。根據(jù)前面分析的在不同模式下的時(shí)序圖我們可以編寫GPIO模擬SPI發(fā)送一個(gè)字節(jié)的函數(shù)如下:
/* 通過(guò)模擬SPI發(fā)送一個(gè)字節(jié) */static void SendByteBySimuSPI(SimuSPIObjectType *simuSPIInstance,uint8_t byte){// uint8_t length[2]={8,16};
if(simuSPIInstance->CPOL==SimuSPI_POLARITY_LOW)
{ /*拉低SCL引腳準(zhǔn)備數(shù)據(jù)傳輸*/
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
if(simuSPIInstance->CPHA==SimuSPI_PHASE_1EDGE) //模式0
{ for(uint8_t count = 0; count < 8; count++)
{ if(byte & 0x80) //每次發(fā)送最高位
{
simuSPIInstance->SetMOSIPin(SimuSPI_Set);
} else
{
simuSPIInstance->SetMOSIPin(SimuSPI_Reset);
}
byte <<= 1; //發(fā)送一位后,左移一位
simuSPIInstance->Delayus(simuSPIInstance->period);
simuSPIInstance->SetSCKPin(SimuSPI_Set);
simuSPIInstance->Delayus(simuSPIInstance->period);
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
}
} else //模式1
{ for(uint8_t count = 0; count < 8; count++)
{ if(byte & 0x80) //每次發(fā)送最高位
{
simuSPIInstance->SetMOSIPin(SimuSPI_Set);
} else
{
simuSPIInstance->SetMOSIPin(SimuSPI_Reset);
}
byte <<= 1; //發(fā)送一位后,左移一位
simuSPIInstance->SetSCKPin(SimuSPI_Set);
simuSPIInstance->Delayus(simuSPIInstance->period);
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period);
}
}
} else
{ /*拉低SCL引腳準(zhǔn)備數(shù)據(jù)傳輸*/
simuSPIInstance->SetSCKPin(SimuSPI_Set);
if(simuSPIInstance->CPHA==SimuSPI_PHASE_1EDGE) //模式2
{ for(uint8_t count = 0; count < 8; count++)
{ if(byte & 0x80) //每次發(fā)送最高位
{
simuSPIInstance->SetMOSIPin(SimuSPI_Set);
} else
{
simuSPIInstance->SetMOSIPin(SimuSPI_Reset);
}
byte <<= 1; //發(fā)送一位后,左移一位
simuSPIInstance->Delayus(simuSPIInstance->period);
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period);
simuSPIInstance->SetSCKPin(SimuSPI_Set);
}
} else //模式3
{ for(uint8_t count = 0; count < 8; count++)
{ if(byte & 0x80) //每次發(fā)送最高位
{
simuSPIInstance->SetMOSIPin(SimuSPI_Set);
} else
{
simuSPIInstance->SetMOSIPin(SimuSPI_Reset);
}
byte <<= 1; //發(fā)送一位后,左移一位
simuSPIInstance->Delayus(simuSPIInstance->period);
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period);
simuSPIInstance->SetSCKPin(SimuSPI_Set);
}
}
}
}
2.2.2、數(shù)據(jù)的接收
??對(duì)于SPI端口來(lái)說(shuō)不光需要發(fā)送數(shù)據(jù),也需要從對(duì)方接收數(shù)據(jù),同樣的我們?cè)俅慰紤]接收一個(gè)字節(jié)的情況。同樣我們根據(jù)前面對(duì)不同模式下,接收數(shù)據(jù)的時(shí)序要求可以編寫接收一個(gè)字節(jié)的函數(shù)如下:
/* 通過(guò)模擬SPI接收一個(gè)字節(jié) */static uint8_t RecieveByteBySimuSPI(SimuSPIObjectType *simuSPIInstance){ uint8_t receive = 0;
if(simuSPIInstance->CPOL==SimuSPI_POLARITY_LOW)
{ /*拉低SCL引腳準(zhǔn)備數(shù)據(jù)傳輸*/
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
if(simuSPIInstance->CPHA==SimuSPI_PHASE_1EDGE) //模式0
{ for(uint8_t count = 0; count < 8; count++ )
{
simuSPIInstance->SetSCKPin(SimuSPI_Set);
simuSPIInstance->Delayus(simuSPIInstance->period);
receive <<= 1;
if(simuSPIInstance->ReadMISOPin())
{
receive++;
}
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period);
}
} else //模式1
{
simuSPIInstance->SetSCKPin(SimuSPI_Set);
simuSPIInstance->Delayus(simuSPIInstance->period); for(uint8_t count = 0; count < 8; count++ )
{
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period);
receive <<= 1;
if(simuSPIInstance->ReadMISOPin())
{
receive++;
}
simuSPIInstance->SetSCKPin(SimuSPI_Set);
simuSPIInstance->Delayus(simuSPIInstance->period);
}
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
}
} else
{ /*拉低SCL引腳準(zhǔn)備數(shù)據(jù)傳輸*/
simuSPIInstance->SetSCKPin(SimuSPI_Set);
if(simuSPIInstance->CPHA==SimuSPI_PHASE_1EDGE) //模式2
{ for(uint8_t count = 0; count < 8; count++ )
{
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period);
receive <<= 1;
if(simuSPIInstance->ReadMISOPin())
{
receive++;
}
simuSPIInstance->SetSCKPin(SimuSPI_Set);
simuSPIInstance->Delayus(simuSPIInstance->period);
}
} else //模式3
{
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period); for(uint8_t count = 0; count < 8; count++ )
{
simuSPIInstance->SetSCKPin(SimuSPI_Set);
simuSPIInstance->Delayus(simuSPIInstance->period);
receive <<= 1;
if(simuSPIInstance->ReadMISOPin())
{
receive++;
}
simuSPIInstance->SetSCKPin(SimuSPI_Reset);
simuSPIInstance->Delayus(simuSPIInstance->period);
}
simuSPIInstance->SetSCKPin(SimuSPI_Set);
}
}
return receive;
}
3、驅(qū)動(dòng)的使用
??我們已經(jīng)設(shè)計(jì)并實(shí)現(xiàn)了GPIO模擬SPI總線的驅(qū)動(dòng)程序,接下來(lái)我們將基于這一驅(qū)動(dòng)設(shè)計(jì)一個(gè)簡(jiǎn)單的用例,以驗(yàn)證驅(qū)動(dòng)程序的正確性。
3.1、聲明并初始化對(duì)象
??我們是基于對(duì)象來(lái)實(shí)現(xiàn)GPIO模擬SPI驅(qū)動(dòng)的,所以在開(kāi)始之前我們需要聲明一個(gè)模擬SPI對(duì)象如下:
SimuSPIObjectType simuSPI;
??聲明了這一對(duì)象之后,我們還需要對(duì)這一變量進(jìn)行初始化才能使用。前面我們已經(jīng)實(shí)現(xiàn)了對(duì)象變量的初始化函數(shù),使用這一函數(shù)就可方便的初始化對(duì)象變量,該函數(shù)有多個(gè)輸入數(shù):
SimuSPIObjectType *simuSPIInstance,//初始化的模擬SPI對(duì)象uint32_t speed, //時(shí)鐘頻率SimuSPICPOLType CPOL, //時(shí)鐘極性SimuSPICPHAType CPHA, //時(shí)鐘相位SimuSPIDataSizeType dataSize,//數(shù)據(jù)長(zhǎng)度SimuSPISetSCKPin setSCK, //SCK時(shí)鐘操作函數(shù)指針SimuSPISetMOSIPin setMOSI, //MOSI操作函數(shù)指針SimuSPIReadMISOPin getMISO, //MISO操作函數(shù)指針SimuSPIDelayus delayus //微秒延時(shí)操作函數(shù)指針
??在這些參數(shù)中simuSPIInstance為我們想要初始化的對(duì)象變量的指針。時(shí)鐘極性、時(shí)鐘相位以及數(shù)據(jù)長(zhǎng)度都是枚舉量,我們根據(jù)實(shí)際的使用要求選擇輸入即可。時(shí)鐘頻率為我們希望的時(shí)鐘速度,最大500K。而余下的幾個(gè)參數(shù)則都是回調(diào)函數(shù)的指針。而這幾個(gè)函數(shù)則是我們?cè)趹?yīng)用程序中需要實(shí)現(xiàn)的,它們的原型如下:
//設(shè)置SCL引腳typedef void (*SimuSPISetSCKPin)(SimuSPIPinValueType op);//設(shè)置SDA引腳typedef void (*SimuSPISetMOSIPin)(SimuSPIPinValueType op);//讀取SDA引腳位typedef uint8_t (*SimuSPIReadMISOPin)(void);//速度延時(shí)函數(shù)typedef void (*SimuSPIDelayus)(volatile uint32_t period);
??這些函數(shù)的實(shí)現(xiàn)與具體的應(yīng)用平臺(tái)有關(guān),我們?cè)赟TM32F407上基于HAL庫(kù)實(shí)現(xiàn)的這些函數(shù)如下:
//設(shè)置SCL引腳void SPISCKOperation(SimuSPIPinValueType op){
GPIO_PinState PinState=(GPIO_PinState)op;
HAL_GPIO_WritePin(GPIOSPI, SPISCK, PinState);
}//設(shè)置SDA引腳void SPIMOSIOperation(SimuSPIPinValueType op){
GPIO_PinState PinState=(GPIO_PinState)op;
HAL_GPIO_WritePin(GPIOSPI, SPIMOSI, PinState);
}//讀取SDA引腳位uint8_t SPIMISORead(void){ if(HAL_GPIO_ReadPin(GPIOSPI, SPIMISO))
{ return 1;
}
return 0;
}
??而延時(shí)操作函數(shù)則采用我們系統(tǒng)中通用的Delayus。有了這些參數(shù)后我們合一調(diào)用初始化函數(shù)對(duì)對(duì)象變量進(jìn)行初始化如下:
/* GPIO模擬SPI通訊初始化 */
SimuSPIInitialization(&simuSPI,//初始化的模擬SPI對(duì)象
500, //時(shí)鐘頻率
SimuSPI_POLARITY_LOW, //時(shí)鐘極性
SimuSPI_PHASE_1EDGE, //時(shí)鐘頻率
SimuSPI_DataSize_8Bit,//數(shù)據(jù)長(zhǎng)度
SPISCKOperation, //SCK時(shí)鐘操作函數(shù)指針
SPIMOSIOperation, //MOSI操作函數(shù)指針
SPIMISORead, //MISO操作函數(shù)指針
Delayus //微秒延時(shí)操作函數(shù)指針
);
3.2、基于對(duì)象進(jìn)行操作
??初始化這個(gè)對(duì)象變量后,我們就可以基于它操作這一對(duì)象了。我們基于驅(qū)動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的讀寫數(shù)據(jù)的操作如下:
/* 使用模擬SPI讀寫數(shù)據(jù)*/void SimuSPIDataExchange(void){ uint8_t wDatas[3]; uint8_t rDatas[3];
/* 通過(guò)模擬SPI向從站寫數(shù)據(jù) */
WriteDataBySimuSPI(&simuSPI,wDatas,3,1000);
HAL_Delay(10);
/* 通過(guò)模擬SPI自從站讀數(shù)據(jù) */
ReadDataBySimuSPI(&simuSPI,rDatas, 3,1000);
HAL_Delay(10);
/* 通過(guò)模擬SPI實(shí)現(xiàn)對(duì)從站先寫數(shù)據(jù)緊接讀數(shù)據(jù)組合操作 */
WriteReadDataBySimuSPI(&simuSPI, wDatas,3,rDatas, 3,1000);
HAL_Delay(10);
/* 通過(guò)模擬SPI實(shí)現(xiàn)對(duì)從站同時(shí)寫和讀數(shù)據(jù)組合操作*/
WriteWhileReadDataBySimuSPI(&simuSPI, wDatas,rDatas,3,1000);
}
??我們分別測(cè)試了讀取數(shù)據(jù)、下發(fā)數(shù)據(jù)、同時(shí)寫和讀數(shù)據(jù)以及寫完后再讀等幾種情況的測(cè)試,效果還是比較理想的。
4、應(yīng)用總結(jié)
??在這一篇中,我們?cè)O(shè)計(jì)并實(shí)現(xiàn)了基于GPIO模擬的SPI接口驅(qū)動(dòng)程序。并在此基礎(chǔ)上設(shè)計(jì)了一個(gè)簡(jiǎn)單的測(cè)試應(yīng)用。我們通過(guò)GPIO模擬的SPI接口向SPI接口的Flash中寫數(shù)據(jù)、讀數(shù)據(jù)、同時(shí)讀寫和先寫后讀試驗(yàn)都沒(méi)有問(wèn)題。
??在使用驅(qū)動(dòng)程序時(shí)需要注意,由于是使用GPIO模擬的SPI端口,其速度是受到限制的,目前最快能夠支持到500K,再快就不能支持了。所以這個(gè)驅(qū)動(dòng)程序只能應(yīng)用于通訊速度小于500K的設(shè)備。
評(píng)論
查看更多