1. 程式人生 > >SPI匯流排協議學習筆記

SPI匯流排協議學習筆記

最近正在學習SPI匯流排協議,看了很多網上的相關內容,覺得有必要整理一下,既可以鞏固自己的學習內容,也可以和大家分享,方便以後的學習。

先來對SPI有個大概的瞭解,如下:

SPI是英語Serial Peripheral Interface的縮寫,也就是序列外圍裝置介面。SPI是一種高速的,全雙工,同步的通訊匯流排,並且在晶片的管腳上只佔用四根線,節約了晶片的管腳並且可以節省PCB的佈局空間,故現在越來越多的晶片集成了這種協議。四根線的埠如下:SDI(資料輸入),SDO(資料輸出),SCLK(時鐘訊號),CS(SS,片選)

SPI有四種工作模式(這裡參考了網友的部落格,http://www.linxh.blog.chinaunix.net/uid-23381466-id-257847.html),如下:

四種工作模式是按照 SPI時鐘極性CPOL 和 SPI時鐘相位CPHA 來劃分的。

當 SPI時鐘極性CPOL=0 時,表示沒有資料傳輸時為低電平;當 SPI時鐘極性CPOL=1 時,表示沒有資料傳輸時為高電平

當 SPI時鐘相位CPHA=0 時,表示時鐘的第一個沿採集資料第二個沿輸出資料當 SPI時鐘相位CPHA=1 時,表示時鐘的第一個沿輸出資料第二個沿採集資料

需要注意的是,這裡的第一個沿和第二個沿可以是上升沿或者下降沿,因為時鐘極性不同,所以沿的方向也就跟著發生變化了。

接下來展示一下四種情況下的SPI工作模式圖,可以方便我們的理解,如下: 

通過上面的圖,就能大致瞭解這四種模式了,但是僅僅這樣我覺得還是有點欠缺,有網友的SPI時序圖詳解,我覺得很有參考價值(網址,http://www.docin.com/p-76201096.html)。

其中,在文件的開頭他做了一個假設(這個假設的前提是上升沿輸出資料,空閒時時鐘保持在低電平,也就是對應上圖中的第二個工作模式,CPOL=0,CPHA=1),很好的給我們解釋了在時鐘脈衝作用下,資料傳輸的詳細過程,看完這個過程,我覺得會有更清楚的認識。

接著,在文件中間部分,他介紹了在CPOL=0,CPHA=0(空閒時時鐘保持在低電平,上升沿採集資料,下降沿輸出資料)的情況下,器件的電平變化情況,也很有學習價值,可以好好看一下。

最後,再附上用IO口來模擬的四種SPI模式程式,僅作參考理解用,還要根據實際情況改寫,如下:


//表示相關引腳高低電平,要根據實際引腳修改。
SSEL_D(0) SSEL_D(1)  //片選
SCK_D(0)  SCK_D(1)     //時鐘訊號
MOSI_D(0) MOSI_D(1)   //SDO
MISO_I(0) MISO_I(1)       //SDI


#define _CPOL     1  //時鐘極性
#define _CPHA     0  //時鐘相位


//延時子程式
void delay()
{
 unsigned char m,n;
     for(n=0;n<5;n++);
    for(m=0;m<100;m++);
}


/**********************************************
模式零           寫資料
***********************************************/
#if _CPOL==0&&_CPHA==0          //MODE   0  0   
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 for(n=0;n<8;n++)
 {
  SCK_D(0);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(1);
 }
  SCK_D(0);
}


/*********************************************
模式零         讀資料
*********************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 for(n=0;n<8;n++)
 {
  SCK_D(0);
  dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(1);
 }
  SCK_D(0);
  return dat;
}
#endif


/*********************************************
模式一        寫資料
*********************************************/
#if _CPOL==0&&_CPHA==1           //MODE  0  1
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 SCK_D(0);
 for(n=0;n<8;n++)
 {
  SCK_D(1);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(0);
 }
}
/*********************************************
模式一       讀資料
*********************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 for(n=0;n<8;n++)
 {
  SCK_D(1);
   dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(0);
 }
  SCK_D(0);
  return dat;
}
#endif


/**********************************************
模式二           寫資料
***********************************************/
#if _CPOL==1&&_CPHA==0           //MODE   1  0
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 for(n=0;n<8;n++)
 {
  SCK_D(1);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(0);
 }
  SCK_D(1);
}
/*********************************************
模式二          讀資料
*********************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 for(n=0;n<8;n++)
 {
  SCK_D(1);
  dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(0);
 }
  SCK_D(1);
  return dat;
}


#endif



/**********************************************
模式三          寫資料
***********************************************/
#if _CPOL==1&&_CPHA==1            //MODE  1  1
void SPI_Send_Dat(unsigned char dat)
{
 unsigned char n;
 SCK_D(1);
 for(n=0;n<8;n++)
 {
  SCK_D(0);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(1);
 }
}

/************************************
模式三          讀資料
************************************/
unsigned char SPI_Receiver_Dat(void)
{
 unsigned char n ,dat,bit_t;
 SCK_D(0);
 for(n=0;n<8;n++)
 { SCK_D(0);
  dat<<=1;
  if(MISO_I())dat|=0x01;
  else dat&=0xfe;
  SCK_D(1);
 }
  SCK_D(1);
  return dat;
}
#endif




void main()
{


SPI_Init();
DDRB = 0XFF;
//#if _CPOL
//SCK_D(0);
//#endif
while(1)
{
//SSEL_D(0);
//SPI_Send_Dat(0x01);
//SPI_Send_Dat(0x31);
//SSEL_D(1);
SSEL_D(0);
SPI_Send_Dat(0x81);
PORTB =SPI_Receiver_Dat();
SSEL_D(1);
//delay();
}
}

注:以上所參考的網址為:

http://www.linxh.blog.chinaunix.net/uid-23381466-id-257847.html

http://www.docin.com/p-76201096.html

http://www.51hei.com/mcu/1392.html