日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[经验] PROTEUS仿真学习笔记05 (SPI 协议 外设)——2014_7_15

發(fā)布時間:2023/12/8 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [经验] PROTEUS仿真学习笔记05 (SPI 协议 外设)——2014_7_15 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
SPI 的概念

***************

對初學(xué)者來說,SPI 應(yīng)該比 I2C 難一些,原因:
1、C51 用SPI 的資料不多,要到STM32?等更高級MCU 資料才會多;
2、SPI 的資料比較生硬,不夠形象;
3、極性和相位的組合。

目前,網(wǎng)上找到的,感覺最好的資料應(yīng)該是,
http://www.niwozhi.net/demo_c487_i35069.html
http://my.oschina.net/freeblues/blog/67400

下面的內(nèi)容有部分摘自上面的鏈接:

SPI 總線實施簡單,僅使用2條數(shù)據(jù)信號線和控制信號線



? ?? ???SCK, Serial Clock, 主要的作用是 Master 設(shè)備往 Slave 設(shè)備傳輸時鐘信號, 控制數(shù)據(jù)交換的時機以及速率;

? ?? ???SS/CS, Slave Select/Chip Select, 用于 Master 設(shè)備片選 Slave 設(shè)備, 使被選中的 Slave 設(shè)備能夠被 Master 設(shè)備所訪問;

? ?? ???SDO/MOSI, Serial Data Output/Master Out Slave In, 在 Master 上面也被稱為 Tx-Channel, 作為數(shù)據(jù)的出口, 主要用于 SPI 設(shè)備發(fā)送數(shù)據(jù);

? ?? ???SDI/MISO, Serial Data Input/Master In Slave Out, 在 Master 上面也被稱為 Rx-Channel, 作為數(shù)據(jù)的入口, 主要用于SPI 設(shè)備接收數(shù)據(jù);

一主多從:



因為圖片是摘錄的,所以有的用 SDO,有的用 MOSI,在理解上可以暫時認為一回事。
同理 SDI MISO
****************
環(huán)形數(shù)據(jù)交換
—— 等價交換
****************
數(shù)據(jù)傳輸通常會包含一次數(shù)據(jù)交換。當(dāng)主節(jié)點向從節(jié)點發(fā)送數(shù)據(jù)時,從節(jié)點也會向主節(jié)點發(fā)送數(shù)據(jù)。為此,主節(jié)點的內(nèi)部移位寄存器和從節(jié)點被設(shè)置成環(huán)形




由SCK提供時鐘脈沖,SDI,SDO則基于此脈沖完成數(shù)據(jù)傳輸。數(shù)據(jù)輸出通過 SDO線,數(shù)據(jù)在時鐘上升沿或下降沿時改變,在緊接著的下降沿或上升沿被讀取。完成一位數(shù)據(jù)傳輸,輸入也使用同樣原理。這樣,在至少8次時鐘信號的改變 (上沿和下沿為一次),就可以完成8位數(shù)據(jù)的傳輸。?



上面的概念 和 原理圖就是 教程的主流,這也是 比較難于理解的原因,還不夠具體。

最好,借鑒一個實例,來觀察一下

SPI協(xié)議舉例

SPI是一個環(huán)形總線結(jié)構(gòu),由ss(cs)、sck、sdi、sdo構(gòu)成,其時序其實很簡單,主要是在sck的控制下,兩個雙向移位寄存器進行數(shù)據(jù)交換。
? ?? ? 假設(shè)下面的8位寄存器裝的是待發(fā)送的數(shù)據(jù)10101010,上升沿發(fā)送、下降沿接收、高位先發(fā)送。
? ?? ? 那么第一個上升沿來的時候 數(shù)據(jù)將會是sdo=1;寄存器=0101010x。下降沿到來的時候,sdi上的電平將所存到寄存器中去,那么這時寄存器=0101010sdi,這樣在 8個時鐘脈沖以后,兩個寄存器的內(nèi)容互相交換一次。這樣就完成里一個spi時序。
舉例:
? ?? ? 假設(shè)主機和從機初始化就緒:并且主機的sbuff=0xaa,從機的sbuff=0x55,下面將分步對spi的8個時鐘周期的數(shù)據(jù)情況演示一遍:假設(shè)上升沿發(fā)送數(shù)據(jù)



這樣就完成了兩個寄存器8位的交換,上面的上表示上升沿、下表示下降沿,sdi、sdo相對于主機而言的。

已經(jīng)很接近理解了,下一步就是把 上面的過程轉(zhuǎn)為動畫,









。。。。。。



有了上面的基礎(chǔ),就可以 來理解 讓人 痛不欲生 的 極性和相位搭配了。

只看該作者?淘帖舉報

?

20?條評論

只看該作者

發(fā)表評論
?

oldbeginner?2014-7-15 18:58:11

沙發(fā)

*********************
極性和相位

*********************

概念 摘錄
http://blog.sina.com.cn/s/blog_69b5d2a50101am99.html

SPI的極性Polarity和相位Phase,最常見的寫法是CPOL和CPHA,不過也有一些其他寫法,簡單總結(jié)如下:
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (時鐘)極性
(2) CKPHA (Clock Phase)? ?= CPHA = PHA = Phase =(時鐘)相位
(3) SCK=SCLK=SPI的時鐘
(4) Edge=邊沿,即時鐘電平變化的時刻,即上升沿(rising edge)或者下降沿(falling edge)
對于一個時鐘周期內(nèi),有兩個edge,分別稱為:
(1)Leadingedge=前一個邊沿=第一個邊沿,對于開始電壓是1,那么就是1變成0的時候,對于開始電壓是0,那么就是0變成1的時候;
(2)Trailingedge=后一個邊沿=第二個邊沿,對于開始電壓是1,那么就是0變成1的時候(即在第一次1變成0之后,才可能有后面的0變成1),對于開始電壓是0,那么就是1變成0的時候;

采用如下用法:
  • 極性=CPOL
  • 相位=CPHA
  • SCLK=時鐘
  • 第一個邊沿和第二個邊沿
CPOL和CPHA,分別都可以是0或時1,對應(yīng)的四種組合就是:





CPOL = 0 時,時鐘在邏輯 0 處空閑:

如果 CPHA = 0,數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。

如果 CPHA = 1,數(shù)據(jù)會在 SCK 的下降沿上讀取,在上升沿上變化。
?

CPOL = 1時,時鐘在邏輯高電平處空閑:
  • 如果 CPHA = 0,數(shù)據(jù)會在 SCK的下降沿上讀取,在上升沿上變化。
  • 如果 CPHA = 1,數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。


下面詳細介紹。
CPOL極性? ?? ???先說什么是SCLK時鐘的空閑時刻,其就是當(dāng)SCLK在發(fā)送8個bit比特數(shù)據(jù)之前和之后的狀態(tài),于此對應(yīng)的,SCLK在發(fā)送數(shù)據(jù)的時候,就是正常的工作的時候,有效active的時刻了。其英文精簡解釋為:ClockPolarity = IDLE state of SCK。
SPI的CPOL,表示當(dāng)SCLK空閑idle的時候,其電平的值是低電平0還是高電平1:
CPOL=0,時鐘空閑idle時候的電平是低電平,所以當(dāng)SCLK有效的時候,就是高電平,就是所謂的active-high;
CPOL=1,時鐘空閑idle時候的電平是高電平,所以當(dāng)SCLK有效的時候,就是低電平,就是所謂的active-low

CPHA相位? ?? ?? ?首先說明一點,capture strobe = latch= read =sample,都是表示數(shù)據(jù)采樣,數(shù)據(jù)有效的時刻。相位,對應(yīng)著數(shù)據(jù)采樣是在第幾個邊沿(edge),是第一個邊沿還是第二個邊沿,0對應(yīng)著第一個邊沿,1對應(yīng)著第二個邊沿。
對于:
CPHA=0,表示第一個邊沿:
對于CPOL=0,idle時候的是低電平,第一個邊沿就是從低變到高,所以是上升沿;
對于CPOL=1,idle時候的是高電平,第一個邊沿就是從高變到低,所以是下降沿;
CPHA=1,表示第二個邊沿:
對于CPOL=0,idle時候的是低電平,第二個邊沿就是從高變到低,所以是下降沿;
對于CPOL=1,idle時候的是高電平,第一個邊沿就是從低變到高,所以是上升沿;

****************************************
極性和相位一共有 4種組合,利用 (極性,相位)來表示,即模式
(0,0)(0,1)(1,0)(1,1)

其中 (0,0)和(1,1)都是最常用的的,尤其是(0,0)

再來看



上圖,表示的是(0,0)或者(1,1)模式,即
數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。

?
贊1回復(fù)

舉報

  • 忘憂草9587?2018-11-21 10:26

    最后這張圖片表示的是上升沿變化,下降沿讀取,對應(yīng)的應(yīng)該是01,01模式吧?

    贊??回復(fù)舉報

?

oldbeginner?2014-7-16 18:37:54

板凳

oldbeginner 發(fā)表于 2014-7-15 18:58
*********************
極性和相位

***********************
W25Q16 的例子
—— 寧波芯動
***********************


W25Q16 是一個 SPI FLASH,C51開發(fā)板上不多,STM32 開發(fā)板上常見;不過 STM32 有自己的 SPI 寄存器,可以進行設(shè)置;而C51 沒有 SPI 寄存器,需要模擬出 SPI 協(xié)議。

看了一些C51 SPI例子,一方面例子真得非常稀少,另一方面 通用性也差。

這里先看一下 芯動開發(fā)板的 W25Q16 實驗。
W25Q16的手冊
http://wenku.baidu.com/view/5e6bcbf604a1b0717fd5dd95.html

其工作模式 0 或模式 3。

根據(jù)SPI 協(xié)議,主從 是同時交換數(shù)據(jù),所以 讀 和 寫可以在 一個函數(shù)內(nèi)完成,但是 實際當(dāng)中,讀數(shù)據(jù) 和寫數(shù)據(jù) 都分成了兩個函數(shù)。


//名稱: SPI寫入一個字節(jié)函數(shù)
void Send_OneByte(unsigned char DATA8) //從SPI發(fā)8位數(shù)
{? ? ? ? ? ? ? ? ? ? ? ?? ?? ???? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ?? ?? ? //上升沿寫入
? ?unsigned char x;? ?? ?
? ?for (x=0;x<8;x++)
? ?{?
? ? ? ???? ? ? ? SCLK=0;
? ? ? ???? ? ? ? if(DATA8&0x80)DIO=1;
? ? ? ???? ? ? ? else DIO=0;
? ? ? ???? ? ? ? SCLK=1;
? ? ? ???? ? ? ? DATA8=DATA8<<1;
? ? ? ? }? ???
}?


寫數(shù)據(jù)的同時,其實也可以接受數(shù)據(jù)。不過,這里 把 接受數(shù)據(jù) 去掉了,但是 動畫還是 展示了 接受數(shù)據(jù)。




再來看 讀一個字節(jié),

//名稱: SPI讀出一個字節(jié)函數(shù)
unsigned char Read_OneByte(void)? ???//從SPI收8位數(shù)
? ?{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ?? ? //下降沿輸出
? ?unsigned char DATA8;
? ?unsigned char x;
? ?SCLK=1;
? ?DATA8=0x00;
? ?for (x=0;x<8;x++)
? ?{?
? ? ? ???? ? ? ? _nop_();
? ? ? ???? ? ? ? SCLK=0;
? ? ? ???? ? ? ? DATA8=DATA8<<1;
? ? ? ???? ? ? ? if(DO)DATA8=DATA8|0x01;
? ? ? ???? ? ? ? SCLK=1;? ? ? ???
? ? ? ? }
? ?return (DATA8);? ?
}



?

?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 19:02:58

3#

oldbeginner 發(fā)表于 2014-7-16 18:37
***********************
W25Q16 的例子
—— 寧波芯動

********************
把 讀和寫 合并成一個函數(shù)

修改芯動的讀函數(shù)
********************

unsigned char Read_OneByte(unsigned char DATA8)? ???//從SPI收8位數(shù),并送出 DATA8
? ?{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ?? ? //下降沿輸出

? ?unsigned char x;
? ?SCLK=1;

? ?for (x=0;x<8;x++)
? ?{?
? ? ? ???? ? ? ? _nop_();
? ? ? ???? ? ? ? SCLK=0;

? ? ? ???? ? ? ? if(DATA8&0x80)DIO=1;
? ? ? ???? ? ? ? else DIO=0;

? ? ? ???? ? ? ? DATA8=DATA8<<1;
? ? ? ???? ? ? ? if(DO)DATA8=DATA8|0x01;
? ? ? ???? ? ? ? SCLK=1;? ? ? ???
? ? ? ? }
? ?return (DATA8);? ?
}


因為?數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。

感覺還是有點怪。
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 19:18:43

4#

本帖最后由 oldbeginner 于 2014-7-17 18:42 編輯 oldbeginner 發(fā)表于 2014-7-16 19:02
********************
把 讀和寫 合并成一個函數(shù)

********************奇怪的變形
——再改?
********************

unsigned char Read_OneByte(unsigned char DATA8)? ???//從SPI收8位數(shù),并送出 DATA8
? ?{? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//下降沿輸出

? ?unsigned char x;
? ?SCLK=0;

? ?for (x=0;x<8;x++)
? ?{?
??? ?? ?? ?? ?? ???SCLK=0;

? ?? ?? ?? ?? ???if(DATA8&0x80)DIO=1;
? ?? ?? ?? ?? ???else DIO=0;

? ?? ?? ?? ?? ???DATA8=DATA8<<1;

? ?? ?? ?? ?? ???SCLK=1;? ???
? ??
? ?? ?? ?? ?? ???if(DO)DATA8=DATA8|0x01;? ?? ??
? ???}
? ?return (DATA8);? ?
}

改成這樣,一樣是可以的。但是 DIO賦值的語句不能 隨便改。
難道 這就是 模式0。

同樣,只能在 下面的方式下工作:

SCLK=0;
for( ; ; ;)
{
? ?SCLK=0;

? ?發(fā)送數(shù)據(jù)();

? ?SCLK=1;

? ? 接收數(shù)據(jù)();
}

同樣,也可以更改成 模式3 (1,1)

unsigned char Read_OneByte(unsigned char DATA8)? ???//從SPI收8位數(shù),并送出 DATA8
? ?{? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//下降沿輸出

? ?unsigned char x;
? ?SCLK=1;

? ?for (x=0;x<8;x++)
? ?{?
? ?? ?? ?? ?? ?? ? SCLK=0;

? ?? ?? ?? ?? ???if(DATA8&0x80)DIO=1;
? ?? ?? ?? ?? ???else DIO=0;

? ?? ?? ?? ?? ???DATA8=DATA8<<1;

? ?? ?? ?? ?? ???SCLK=1;? ???
? ??
? ?? ?? ?? ?? ???if(DO)DATA8=DATA8|0x01;? ?? ??
? ???}
? ?return (DATA8);? ?
}



SCLK=1;
for( ; ; ;)
{
? ?SCLK=0;

? ?發(fā)送數(shù)據(jù)();

? ?SCLK=1;

? ? 接收數(shù)據(jù)();
}


**************************

SCLK=0; //模式0
// SCLK=1; //模式1

for( ; ; ;)
{
? ?SCLK=0;

? ?發(fā)送數(shù)據(jù)();

? ?SCLK=1;

? ? 接收數(shù)據(jù)();
}

原理如此,還是比較好理解的。
?
贊回復(fù)

舉報

?

折羽燕?2014-7-16 20:10:19

5#

講的很透徹,謝謝了,辛苦了~~
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 20:22:12

6#

oldbeginner 發(fā)表于 2014-7-16 19:18
********************奇怪的變形
——再改?
********************

************
NRF24L01 的SPI
************

這個看起來就很簡單了,也更加清晰。

uchar SPI_RW(uchar byte)
{
? ?? ???uchar bit_ctr;
? ?? ???for(bit_ctr=0;bit_ctr<8;bit_ctr++)??// 輸出8位
? ?? ???{
? ?? ?? ?? ?? ? NRF_MOSI=(byte&0x80);? ?? ?? ?? ?? ?? ?? ?? ? // MSB TO MOSI
? ?? ?? ?? ?? ? byte=(byte<<1);? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? // shift next bit to MSB
? ?? ?? ?? ?? ? NRF_SCK=1;
? ?? ?? ?? ?? ? byte|=NRF_MISO;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???// capture current MISO bit
? ?? ?? ?? ?? ? NRF_SCK=0;
? ?? ???}
? ?? ???return byte;
}

SCK??0 ---> 1 之間,MOSI 賦值;
SCK??1 ---->0 之間,byte 賦值。

?
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 20:45:43

7#

本帖最后由 oldbeginner 于 2014-7-17 16:06 編輯 oldbeginner 發(fā)表于 2014-7-16 20:22
************
NRF24L01 的SPI
************

******************
93C66 的例子
******************

http://wenku.baidu.com/view/1f8e9d06eff9aef8941e06bc.html

// 函數(shù)名稱: SPISendByte
// 入口參數(shù): ch
// 函數(shù)功能: 發(fā)送一個字節(jié)
//--------------------------------------------------------------------------------------------------
void SPISendByte(unsigned char ch)
{?
? ?? ???unsigned char idata n=8;? ?? ?// 向SDA上發(fā)送一位數(shù)據(jù)字節(jié),共八位? ?? ?
? ?? ?? ?SCK = 1 ;? ?? ?? ?? ?? ?? ? //時鐘置高
? ?? ?? ?SS1 = 0 ;? ?? ?? ?? ?? ?? ? //選擇從機

? ?? ???while(n--)
? ?? ???{?
? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?SCK = 0 ;? ?? ?? ?? ?? ?? ? //時鐘置低
? ?? ?? ?? ?? ?? ? if((ch&0x80) == 0x80)? ???// 若要發(fā)送的數(shù)據(jù)最高位為1則發(fā)送位1
? ?? ?? ?? ?? ?? ? {? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ?? ?MOSI = 1;? ???// 傳送位1
? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ? else
? ?? ?? ?? ?? ?? ? {??
? ?? ?? ?? ?? ?? ?? ?? ?? ? MOSI = 0;? ???// 否則傳送位0
? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ???
? ?? ?? ?? ?? ?? ?ch = ch<<1;? ?? ?? ?// 數(shù)據(jù)左移一位
? ?? ?? ?? ?? ?? ???SCK = 1 ;? ?? ?? ?? ?? ?? ? //時鐘置高?
? ?? ?? ? }
}



發(fā)送和讀取也是分成了兩個函數(shù),

// 函數(shù)名稱: SPIreceiveByte
// 返回接收的數(shù)據(jù)
// 函數(shù)功能: 接收一字節(jié)子程序
//--------------------------------------------------------------------------------------------------
unsigned char SPIreceiveByte()
{
? ?? ???unsigned char idata n=8;? ???// 從MISO線上讀取一上數(shù)據(jù)字節(jié),共八位
? ?? ???unsigned char tdata;

? ?? ?? ?SCK = 1;? ?? ?? ?? ?? ?? ???//時鐘為高
? ?? ?? ?SS1 = 0;? ?? ?? ?? ?? ?? ???//選擇從機

? ?? ?? ?? ?? ? while(n--)
? ?? ?? ?? ?? ? {? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ? SCK = 0;? ?? ?? ?? ?? ?? ???//時鐘為低

? ?? ?? ?? ?? ?? ?? ?? ?? ?tdata = tdata<<1;? ???// 左移一位,或_crol_(temp,1)
? ?? ?? ?? ?? ?? ?? ?? ?? ?if(MISO == 1)
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?tdata = tdata|0x01;? ???// 若接收到的位為1,則數(shù)據(jù)的最后一位置1
? ?? ?? ?? ?? ?? ?? ?? ?? ?else?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???tdata = tdata&0xfe;? ???// 否則數(shù)據(jù)的最后一位置0
? ?? ?? ?? ?? ?? ?? ?? ?? ?SCK=1;
? ?? ?? ?? ?? ? }
? ?? ???return(tdata);
}


很奇怪,讀和寫 都是 在 SCK 位于 0 1 之間賦值的。

SPI 協(xié)議 真亂。
7-17 補充:感覺這篇文章的代碼有問題,

修改如下并合并,
unsigned char SPISendByte(unsigned char ch)
{?
? ?? ???unsigned char idata n=8;? ?? ?// 向SDA上發(fā)送一位數(shù)據(jù)字節(jié),共八位? ?? ?

? ?? ???while(n--)
? ?? ???{?
? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?SCK = 0 ;? ?? ?? ?? ?? ?? ? //時鐘置低
? ?? ?? ?? ?? ?? ? if((ch&0x80) == 0x80)? ???// 若要發(fā)送的數(shù)據(jù)最高位為1則發(fā)送位1
? ?? ?? ?? ?? ?? ? {? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ?? ?MOSI = 1;? ???// 傳送位1
? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ? else
? ?? ?? ?? ?? ?? ? {??
? ?? ?? ?? ?? ?? ?? ?? ?? ? MOSI = 0;? ???// 否則傳送位0
? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ???
? ?? ?? ?? ?? ?? ?ch = ch<<1;? ?? ?? ?// 數(shù)據(jù)左移一位


? ?? ?? ???? ?? ? tdata = tdata<<1;? ???// 左移一位,或_crol_(temp,1)

? ?? ?? ?? ?? ?? ???SCK = 1 ;? ?? ?? ?? ?? ?? ? //時鐘置高?
? ?? ?? ?? ?? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ?? ?if(MISO == 1)
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?tdata = tdata|0x01;? ???// 若接收到的位為1,則數(shù)據(jù)的最后一位置1
? ?? ?? ?? ?? ?? ?? ?? ?? ?else?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???tdata = tdata&0xfe;? ???// 否則數(shù)據(jù)的最后一位置0

? ?? ?? ? }
? ?? ???return(tdata);
}

?
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 21:15:13

8#

oldbeginner 發(fā)表于 2014-7-16 20:45
******************
93C66 的例子
******************

*******************
常用的SPI C51 模擬
重復(fù)
*******************

http://www.dz863.com/Microprocessors/MCS-8051/SPI-C51.htm

SPI接口有一個特點,即在時鐘SCK的上升沿打入數(shù)據(jù)MOSI,在下降沿讀入數(shù)據(jù)MISO.?
片選信號CS有正負區(qū)別.在硬件上MOSI與MISO是可以短路變?yōu)镾IO可讀寫IO的.?
故SPI可為(不包括CS)?
三線(SCK,MOSI,MISO)協(xié)議,兩線(SCK,SIO)協(xié)議?
再者,SPI一般為雙向同時高速收發(fā)數(shù)據(jù)的,方向由時鐘SCK的跳變沿決定。?

根據(jù)以上所述,模擬SPI讀寫模塊編制成為一體化模塊是必要的。?
而且調(diào)用規(guī)則只需注意讀數(shù)據(jù)時要寫入0xff即可。非常方便好用。?

例如:?
res = SpiReadWrite(val);//模塊寫?
SpiReadWrite(0xff);//模塊讀?
對于具體器件,由于涉及到命令、地址及數(shù)據(jù)等,故一個完整的SPI讀或?qū)懖僮?
可能需要幾個模擬SPI讀寫一體化模塊來完成。?

所以一般完整的SPI讀或?qū)懖僮餍枰韵潞瘮?shù)組合完成?

void SpiOpen(void); //打開片選 CS=0或CS=1?
void SpiClose(void); //關(guān)閉片選 CS=1或CS=0?
unsigned char SpiReadWrite(val); //模擬SPI讀寫一體化模塊?
void SpiWriteEnable(void); //使能寫操作?
void SpiWriteDISAble(void); //禁止寫操作?
unsigned char SpiReadStatus(void); //讀狀態(tài)?
void SpiWriteStatus(unsigned char val); //寫狀態(tài)?
void SpiWriteWait(void); //等待寫入完成?
void SpiWriteByte(unsigned int addr, unsigned char val);//寫一個字節(jié)?
void SpiWriteWord(unsigned char addr, unsigned int val);//寫一個字?
unsigned char SpiReadByte(unsigned int addr); //讀一個字節(jié)?
unsigned int SpiReadWord(unsigned char addr); //讀一個字?
/*----------------------------------------------------------------------------?

/*-----------------------------------------------?
例:?
X5045模擬SPI讀寫一體化模塊?
PTR905模擬SPI讀寫一體化模塊?
------------------------------------------------*?
unsigned char SpiReadWrite(unsigned char val)?
{?
unsigned char i;?
ACC = val;?
for (i = 8; i > 0; i --)?
{?
CY = MISO;//取數(shù)據(jù)SO?
_rlca_();//存數(shù)據(jù)ACC.0讀數(shù)據(jù)ACC.7同時進行?
MOSI = CY;//送數(shù)據(jù)SI?
SCK = 1;//上升沿打入數(shù)據(jù)?
_nop_();//延時?
SCK = 0;//下降沿讀入數(shù)據(jù)?
}?
return ACC;?
}?

/*-----------------------------------------------?
例:?
X5045模擬SPI讀寫一體化模塊?
ISD4004模擬SPI讀寫一體化模塊?
------------------------------------------------*?
unsigned char SpiReadWrite(unsigned char val)?
{?
unsigned char i;?
ACC = val;?
for (i = 8; i > 0; i --)?
{?
SCK = 0;//下降沿讀入數(shù)據(jù)?
_nop_();//延時?
CY = MISO;//取數(shù)據(jù)SO?
_rlca_();//存數(shù)據(jù)ACC.0讀數(shù)據(jù)ACC.7同時進行?
MOSI = CY;//送數(shù)據(jù)SI?
_nop_();//延時?
SCK = 1;//上升沿打入數(shù)據(jù)?
MOSI = 1;//釋放總線SI?
}?
return ACC;?
}?

/*-----------------------------------------------?
例:?
AT93C46模擬SPI讀寫模塊?
------------------------------------------------*?
sbit ACC_7 = ACC^7;?
unsigned char SpiReadWrite(unsigned char val)?
{?
unsigned char i;?
ACC = val;?
for (i = 8; i > 0; i --)?
{?
CY = ACC_7;//讀數(shù)據(jù)ACC.7?
MOSI = CY;//送數(shù)據(jù)SI?
SCK = 1;//上升沿打入數(shù)據(jù)?
CY = MISO;//取數(shù)據(jù)SO?
_rlca_();//存數(shù)據(jù)ACC.0?
SCK = 0;//下降沿?
}?
return ACC;?
}?

?
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-17 15:47:44

9#

oldbeginner 發(fā)表于 2014-7-16 21:15
*******************
常用的SPI C51 模擬
重復(fù)

********************
SD卡 的 SPI 模式

********************

SD 的 SPI 模式 寫出來 是如此簡單。

//寫一字節(jié)到SD卡,模擬SPI總線方式
void SdWrite(unsigned char n)
{

unsigned char i;

for(i=8;i;i--)
{
SD_CLK=0;
SD_DI=(n&0x80);
n<<=1;
SD_CLK=1;
}
SD_DI=1;?
}?
下降沿 發(fā)出數(shù)據(jù)。

//===========================================================
//從SD卡讀一字節(jié),模擬SPI總線方式
unsigned char SdRead()
{
unsigned char n,i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_CLK=1;
n<<=1;
if(SD_DO) n|=1;

}
return n;
}

上升沿 接收數(shù)據(jù)。
?
贊回復(fù)

舉報

?

slim443?2014-7-18 16:16:08

10#

協(xié)議介紹的很詳細
?
贊回復(fù)

舉報

?

gh037?2014-7-21 01:15:03

11#

{:1:}
?
贊回復(fù)

舉報

?

家瑞?2014-9-25 11:28:15

12#

頂樓主,寫的不錯
?
贊回復(fù)

舉報

?

我是流氓羽?2015-6-17 10:57:06

13#

講的太好了,默默的收藏
?
贊回復(fù)

舉報

?

shunbaiwang?2016-2-14 21:39:06

14#

看起來挺有用的,感謝樓主
?
贊回復(fù)

舉報

?

qq543538634?2016-8-31 14:23:27

15#

這么深入淺出 通俗易懂的教程居然沒人頂????
謝謝樓主啦!
?
贊回復(fù)

舉報

?

我是你的唯一?2016-11-11 21:02:45

16#

不錯,確實很有用的 一定好好學(xué)習(xí)
?
贊回復(fù)

舉報

?
SPI 的概念

***************

對初學(xué)者來說,SPI 應(yīng)該比 I2C 難一些,原因:
1、C51 用SPI 的資料不多,要到STM32?等更高級MCU 資料才會多;
2、SPI 的資料比較生硬,不夠形象;
3、極性和相位的組合。

目前,網(wǎng)上找到的,感覺最好的資料應(yīng)該是,
http://www.niwozhi.net/demo_c487_i35069.html
http://my.oschina.net/freeblues/blog/67400

下面的內(nèi)容有部分摘自上面的鏈接:

SPI 總線實施簡單,僅使用2條數(shù)據(jù)信號線和控制信號線



? ?? ???SCK, Serial Clock, 主要的作用是 Master 設(shè)備往 Slave 設(shè)備傳輸時鐘信號, 控制數(shù)據(jù)交換的時機以及速率;

? ?? ???SS/CS, Slave Select/Chip Select, 用于 Master 設(shè)備片選 Slave 設(shè)備, 使被選中的 Slave 設(shè)備能夠被 Master 設(shè)備所訪問;

? ?? ???SDO/MOSI, Serial Data Output/Master Out Slave In, 在 Master 上面也被稱為 Tx-Channel, 作為數(shù)據(jù)的出口, 主要用于 SPI 設(shè)備發(fā)送數(shù)據(jù);

? ?? ???SDI/MISO, Serial Data Input/Master In Slave Out, 在 Master 上面也被稱為 Rx-Channel, 作為數(shù)據(jù)的入口, 主要用于SPI 設(shè)備接收數(shù)據(jù);

一主多從:



因為圖片是摘錄的,所以有的用 SDO,有的用 MOSI,在理解上可以暫時認為一回事。
同理 SDI MISO
****************
環(huán)形數(shù)據(jù)交換
—— 等價交換
****************
數(shù)據(jù)傳輸通常會包含一次數(shù)據(jù)交換。當(dāng)主節(jié)點向從節(jié)點發(fā)送數(shù)據(jù)時,從節(jié)點也會向主節(jié)點發(fā)送數(shù)據(jù)。為此,主節(jié)點的內(nèi)部移位寄存器和從節(jié)點被設(shè)置成環(huán)形




由SCK提供時鐘脈沖,SDI,SDO則基于此脈沖完成數(shù)據(jù)傳輸。數(shù)據(jù)輸出通過 SDO線,數(shù)據(jù)在時鐘上升沿或下降沿時改變,在緊接著的下降沿或上升沿被讀取。完成一位數(shù)據(jù)傳輸,輸入也使用同樣原理。這樣,在至少8次時鐘信號的改變 (上沿和下沿為一次),就可以完成8位數(shù)據(jù)的傳輸。?



上面的概念 和 原理圖就是 教程的主流,這也是 比較難于理解的原因,還不夠具體。

最好,借鑒一個實例,來觀察一下

SPI協(xié)議舉例

SPI是一個環(huán)形總線結(jié)構(gòu),由ss(cs)、sck、sdi、sdo構(gòu)成,其時序其實很簡單,主要是在sck的控制下,兩個雙向移位寄存器進行數(shù)據(jù)交換。
? ?? ? 假設(shè)下面的8位寄存器裝的是待發(fā)送的數(shù)據(jù)10101010,上升沿發(fā)送、下降沿接收、高位先發(fā)送。
? ?? ? 那么第一個上升沿來的時候 數(shù)據(jù)將會是sdo=1;寄存器=0101010x。下降沿到來的時候,sdi上的電平將所存到寄存器中去,那么這時寄存器=0101010sdi,這樣在 8個時鐘脈沖以后,兩個寄存器的內(nèi)容互相交換一次。這樣就完成里一個spi時序。
舉例:
? ?? ? 假設(shè)主機和從機初始化就緒:并且主機的sbuff=0xaa,從機的sbuff=0x55,下面將分步對spi的8個時鐘周期的數(shù)據(jù)情況演示一遍:假設(shè)上升沿發(fā)送數(shù)據(jù)



這樣就完成了兩個寄存器8位的交換,上面的上表示上升沿、下表示下降沿,sdi、sdo相對于主機而言的。

已經(jīng)很接近理解了,下一步就是把 上面的過程轉(zhuǎn)為動畫,









。。。。。。



有了上面的基礎(chǔ),就可以 來理解 讓人 痛不欲生 的 極性和相位搭配了。

只看該作者?淘帖舉報

?

20?條評論

只看該作者

發(fā)表評論
?

oldbeginner?2014-7-15 18:58:11

沙發(fā)

*********************
極性和相位

*********************

概念 摘錄
http://blog.sina.com.cn/s/blog_69b5d2a50101am99.html

SPI的極性Polarity和相位Phase,最常見的寫法是CPOL和CPHA,不過也有一些其他寫法,簡單總結(jié)如下:
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (時鐘)極性
(2) CKPHA (Clock Phase)? ?= CPHA = PHA = Phase =(時鐘)相位
(3) SCK=SCLK=SPI的時鐘
(4) Edge=邊沿,即時鐘電平變化的時刻,即上升沿(rising edge)或者下降沿(falling edge)
對于一個時鐘周期內(nèi),有兩個edge,分別稱為:
(1)Leadingedge=前一個邊沿=第一個邊沿,對于開始電壓是1,那么就是1變成0的時候,對于開始電壓是0,那么就是0變成1的時候;
(2)Trailingedge=后一個邊沿=第二個邊沿,對于開始電壓是1,那么就是0變成1的時候(即在第一次1變成0之后,才可能有后面的0變成1),對于開始電壓是0,那么就是1變成0的時候;

采用如下用法:
  • 極性=CPOL
  • 相位=CPHA
  • SCLK=時鐘
  • 第一個邊沿和第二個邊沿
CPOL和CPHA,分別都可以是0或時1,對應(yīng)的四種組合就是:





CPOL = 0 時,時鐘在邏輯 0 處空閑:

如果 CPHA = 0,數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。

如果 CPHA = 1,數(shù)據(jù)會在 SCK 的下降沿上讀取,在上升沿上變化。
?

CPOL = 1時,時鐘在邏輯高電平處空閑:
  • 如果 CPHA = 0,數(shù)據(jù)會在 SCK的下降沿上讀取,在上升沿上變化。
  • 如果 CPHA = 1,數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。


下面詳細介紹。
CPOL極性? ?? ???先說什么是SCLK時鐘的空閑時刻,其就是當(dāng)SCLK在發(fā)送8個bit比特數(shù)據(jù)之前和之后的狀態(tài),于此對應(yīng)的,SCLK在發(fā)送數(shù)據(jù)的時候,就是正常的工作的時候,有效active的時刻了。其英文精簡解釋為:ClockPolarity = IDLE state of SCK。
SPI的CPOL,表示當(dāng)SCLK空閑idle的時候,其電平的值是低電平0還是高電平1:
CPOL=0,時鐘空閑idle時候的電平是低電平,所以當(dāng)SCLK有效的時候,就是高電平,就是所謂的active-high;
CPOL=1,時鐘空閑idle時候的電平是高電平,所以當(dāng)SCLK有效的時候,就是低電平,就是所謂的active-low

CPHA相位? ?? ?? ?首先說明一點,capture strobe = latch= read =sample,都是表示數(shù)據(jù)采樣,數(shù)據(jù)有效的時刻。相位,對應(yīng)著數(shù)據(jù)采樣是在第幾個邊沿(edge),是第一個邊沿還是第二個邊沿,0對應(yīng)著第一個邊沿,1對應(yīng)著第二個邊沿。
對于:
CPHA=0,表示第一個邊沿:
對于CPOL=0,idle時候的是低電平,第一個邊沿就是從低變到高,所以是上升沿;
對于CPOL=1,idle時候的是高電平,第一個邊沿就是從高變到低,所以是下降沿;
CPHA=1,表示第二個邊沿:
對于CPOL=0,idle時候的是低電平,第二個邊沿就是從高變到低,所以是下降沿;
對于CPOL=1,idle時候的是高電平,第一個邊沿就是從低變到高,所以是上升沿;

****************************************
極性和相位一共有 4種組合,利用 (極性,相位)來表示,即模式
(0,0)(0,1)(1,0)(1,1)

其中 (0,0)和(1,1)都是最常用的的,尤其是(0,0)

再來看



上圖,表示的是(0,0)或者(1,1)模式,即
數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。

?
贊1回復(fù)

舉報

  • 忘憂草9587?2018-11-21 10:26

    最后這張圖片表示的是上升沿變化,下降沿讀取,對應(yīng)的應(yīng)該是01,01模式吧?

    贊??回復(fù)舉報

?

oldbeginner?2014-7-16 18:37:54

板凳

oldbeginner 發(fā)表于 2014-7-15 18:58
*********************
極性和相位

***********************
W25Q16 的例子
—— 寧波芯動
***********************


W25Q16 是一個 SPI FLASH,C51開發(fā)板上不多,STM32 開發(fā)板上常見;不過 STM32 有自己的 SPI 寄存器,可以進行設(shè)置;而C51 沒有 SPI 寄存器,需要模擬出 SPI 協(xié)議。

看了一些C51 SPI例子,一方面例子真得非常稀少,另一方面 通用性也差。

這里先看一下 芯動開發(fā)板的 W25Q16 實驗。
W25Q16的手冊
http://wenku.baidu.com/view/5e6bcbf604a1b0717fd5dd95.html

其工作模式 0 或模式 3。

根據(jù)SPI 協(xié)議,主從 是同時交換數(shù)據(jù),所以 讀 和 寫可以在 一個函數(shù)內(nèi)完成,但是 實際當(dāng)中,讀數(shù)據(jù) 和寫數(shù)據(jù) 都分成了兩個函數(shù)。


//名稱: SPI寫入一個字節(jié)函數(shù)
void Send_OneByte(unsigned char DATA8) //從SPI發(fā)8位數(shù)
{? ? ? ? ? ? ? ? ? ? ? ?? ?? ???? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ?? ?? ? //上升沿寫入
? ?unsigned char x;? ?? ?
? ?for (x=0;x<8;x++)
? ?{?
? ? ? ???? ? ? ? SCLK=0;
? ? ? ???? ? ? ? if(DATA8&0x80)DIO=1;
? ? ? ???? ? ? ? else DIO=0;
? ? ? ???? ? ? ? SCLK=1;
? ? ? ???? ? ? ? DATA8=DATA8<<1;
? ? ? ? }? ???
}?


寫數(shù)據(jù)的同時,其實也可以接受數(shù)據(jù)。不過,這里 把 接受數(shù)據(jù) 去掉了,但是 動畫還是 展示了 接受數(shù)據(jù)。




再來看 讀一個字節(jié),

//名稱: SPI讀出一個字節(jié)函數(shù)
unsigned char Read_OneByte(void)? ???//從SPI收8位數(shù)
? ?{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ?? ? //下降沿輸出
? ?unsigned char DATA8;
? ?unsigned char x;
? ?SCLK=1;
? ?DATA8=0x00;
? ?for (x=0;x<8;x++)
? ?{?
? ? ? ???? ? ? ? _nop_();
? ? ? ???? ? ? ? SCLK=0;
? ? ? ???? ? ? ? DATA8=DATA8<<1;
? ? ? ???? ? ? ? if(DO)DATA8=DATA8|0x01;
? ? ? ???? ? ? ? SCLK=1;? ? ? ???
? ? ? ? }
? ?return (DATA8);? ?
}



?

?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 19:02:58

3#

oldbeginner 發(fā)表于 2014-7-16 18:37
***********************
W25Q16 的例子
—— 寧波芯動

********************
把 讀和寫 合并成一個函數(shù)

修改芯動的讀函數(shù)
********************

unsigned char Read_OneByte(unsigned char DATA8)? ???//從SPI收8位數(shù),并送出 DATA8
? ?{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?? ?? ? //下降沿輸出

? ?unsigned char x;
? ?SCLK=1;

? ?for (x=0;x<8;x++)
? ?{?
? ? ? ???? ? ? ? _nop_();
? ? ? ???? ? ? ? SCLK=0;

? ? ? ???? ? ? ? if(DATA8&0x80)DIO=1;
? ? ? ???? ? ? ? else DIO=0;

? ? ? ???? ? ? ? DATA8=DATA8<<1;
? ? ? ???? ? ? ? if(DO)DATA8=DATA8|0x01;
? ? ? ???? ? ? ? SCLK=1;? ? ? ???
? ? ? ? }
? ?return (DATA8);? ?
}


因為?數(shù)據(jù)會在 SCK 的上升沿上讀取,在下降沿上變化。

感覺還是有點怪。
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 19:18:43

4#

本帖最后由 oldbeginner 于 2014-7-17 18:42 編輯 oldbeginner 發(fā)表于 2014-7-16 19:02
********************
把 讀和寫 合并成一個函數(shù)

********************奇怪的變形
——再改?
********************

unsigned char Read_OneByte(unsigned char DATA8)? ???//從SPI收8位數(shù),并送出 DATA8
? ?{? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//下降沿輸出

? ?unsigned char x;
? ?SCLK=0;

? ?for (x=0;x<8;x++)
? ?{?
??? ?? ?? ?? ?? ???SCLK=0;

? ?? ?? ?? ?? ???if(DATA8&0x80)DIO=1;
? ?? ?? ?? ?? ???else DIO=0;

? ?? ?? ?? ?? ???DATA8=DATA8<<1;

? ?? ?? ?? ?? ???SCLK=1;? ???
? ??
? ?? ?? ?? ?? ???if(DO)DATA8=DATA8|0x01;? ?? ??
? ???}
? ?return (DATA8);? ?
}

改成這樣,一樣是可以的。但是 DIO賦值的語句不能 隨便改。
難道 這就是 模式0。

同樣,只能在 下面的方式下工作:

SCLK=0;
for( ; ; ;)
{
? ?SCLK=0;

? ?發(fā)送數(shù)據(jù)();

? ?SCLK=1;

? ? 接收數(shù)據(jù)();
}

同樣,也可以更改成 模式3 (1,1)

unsigned char Read_OneByte(unsigned char DATA8)? ???//從SPI收8位數(shù),并送出 DATA8
? ?{? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//下降沿輸出

? ?unsigned char x;
? ?SCLK=1;

? ?for (x=0;x<8;x++)
? ?{?
? ?? ?? ?? ?? ?? ? SCLK=0;

? ?? ?? ?? ?? ???if(DATA8&0x80)DIO=1;
? ?? ?? ?? ?? ???else DIO=0;

? ?? ?? ?? ?? ???DATA8=DATA8<<1;

? ?? ?? ?? ?? ???SCLK=1;? ???
? ??
? ?? ?? ?? ?? ???if(DO)DATA8=DATA8|0x01;? ?? ??
? ???}
? ?return (DATA8);? ?
}



SCLK=1;
for( ; ; ;)
{
? ?SCLK=0;

? ?發(fā)送數(shù)據(jù)();

? ?SCLK=1;

? ? 接收數(shù)據(jù)();
}


**************************

SCLK=0; //模式0
// SCLK=1; //模式1

for( ; ; ;)
{
? ?SCLK=0;

? ?發(fā)送數(shù)據(jù)();

? ?SCLK=1;

? ? 接收數(shù)據(jù)();
}

原理如此,還是比較好理解的。
?
贊回復(fù)

舉報

?

折羽燕?2014-7-16 20:10:19

5#

講的很透徹,謝謝了,辛苦了~~
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 20:22:12

6#

oldbeginner 發(fā)表于 2014-7-16 19:18
********************奇怪的變形
——再改?
********************

************
NRF24L01 的SPI
************

這個看起來就很簡單了,也更加清晰。

uchar SPI_RW(uchar byte)
{
? ?? ???uchar bit_ctr;
? ?? ???for(bit_ctr=0;bit_ctr<8;bit_ctr++)??// 輸出8位
? ?? ???{
? ?? ?? ?? ?? ? NRF_MOSI=(byte&0x80);? ?? ?? ?? ?? ?? ?? ?? ? // MSB TO MOSI
? ?? ?? ?? ?? ? byte=(byte<<1);? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? // shift next bit to MSB
? ?? ?? ?? ?? ? NRF_SCK=1;
? ?? ?? ?? ?? ? byte|=NRF_MISO;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???// capture current MISO bit
? ?? ?? ?? ?? ? NRF_SCK=0;
? ?? ???}
? ?? ???return byte;
}

SCK??0 ---> 1 之間,MOSI 賦值;
SCK??1 ---->0 之間,byte 賦值。

?
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 20:45:43

7#

本帖最后由 oldbeginner 于 2014-7-17 16:06 編輯 oldbeginner 發(fā)表于 2014-7-16 20:22
************
NRF24L01 的SPI
************

******************
93C66 的例子
******************

http://wenku.baidu.com/view/1f8e9d06eff9aef8941e06bc.html

// 函數(shù)名稱: SPISendByte
// 入口參數(shù): ch
// 函數(shù)功能: 發(fā)送一個字節(jié)
//--------------------------------------------------------------------------------------------------
void SPISendByte(unsigned char ch)
{?
? ?? ???unsigned char idata n=8;? ?? ?// 向SDA上發(fā)送一位數(shù)據(jù)字節(jié),共八位? ?? ?
? ?? ?? ?SCK = 1 ;? ?? ?? ?? ?? ?? ? //時鐘置高
? ?? ?? ?SS1 = 0 ;? ?? ?? ?? ?? ?? ? //選擇從機

? ?? ???while(n--)
? ?? ???{?
? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?SCK = 0 ;? ?? ?? ?? ?? ?? ? //時鐘置低
? ?? ?? ?? ?? ?? ? if((ch&0x80) == 0x80)? ???// 若要發(fā)送的數(shù)據(jù)最高位為1則發(fā)送位1
? ?? ?? ?? ?? ?? ? {? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ?? ?MOSI = 1;? ???// 傳送位1
? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ? else
? ?? ?? ?? ?? ?? ? {??
? ?? ?? ?? ?? ?? ?? ?? ?? ? MOSI = 0;? ???// 否則傳送位0
? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ???
? ?? ?? ?? ?? ?? ?ch = ch<<1;? ?? ?? ?// 數(shù)據(jù)左移一位
? ?? ?? ?? ?? ?? ???SCK = 1 ;? ?? ?? ?? ?? ?? ? //時鐘置高?
? ?? ?? ? }
}



發(fā)送和讀取也是分成了兩個函數(shù),

// 函數(shù)名稱: SPIreceiveByte
// 返回接收的數(shù)據(jù)
// 函數(shù)功能: 接收一字節(jié)子程序
//--------------------------------------------------------------------------------------------------
unsigned char SPIreceiveByte()
{
? ?? ???unsigned char idata n=8;? ???// 從MISO線上讀取一上數(shù)據(jù)字節(jié),共八位
? ?? ???unsigned char tdata;

? ?? ?? ?SCK = 1;? ?? ?? ?? ?? ?? ???//時鐘為高
? ?? ?? ?SS1 = 0;? ?? ?? ?? ?? ?? ???//選擇從機

? ?? ?? ?? ?? ? while(n--)
? ?? ?? ?? ?? ? {? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ? SCK = 0;? ?? ?? ?? ?? ?? ???//時鐘為低

? ?? ?? ?? ?? ?? ?? ?? ?? ?tdata = tdata<<1;? ???// 左移一位,或_crol_(temp,1)
? ?? ?? ?? ?? ?? ?? ?? ?? ?if(MISO == 1)
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?tdata = tdata|0x01;? ???// 若接收到的位為1,則數(shù)據(jù)的最后一位置1
? ?? ?? ?? ?? ?? ?? ?? ?? ?else?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???tdata = tdata&0xfe;? ???// 否則數(shù)據(jù)的最后一位置0
? ?? ?? ?? ?? ?? ?? ?? ?? ?SCK=1;
? ?? ?? ?? ?? ? }
? ?? ???return(tdata);
}


很奇怪,讀和寫 都是 在 SCK 位于 0 1 之間賦值的。

SPI 協(xié)議 真亂。
7-17 補充:感覺這篇文章的代碼有問題,

修改如下并合并,
unsigned char SPISendByte(unsigned char ch)
{?
? ?? ???unsigned char idata n=8;? ?? ?// 向SDA上發(fā)送一位數(shù)據(jù)字節(jié),共八位? ?? ?

? ?? ???while(n--)
? ?? ???{?
? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?SCK = 0 ;? ?? ?? ?? ?? ?? ? //時鐘置低
? ?? ?? ?? ?? ?? ? if((ch&0x80) == 0x80)? ???// 若要發(fā)送的數(shù)據(jù)最高位為1則發(fā)送位1
? ?? ?? ?? ?? ?? ? {? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ?? ?MOSI = 1;? ???// 傳送位1
? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ? else
? ?? ?? ?? ?? ?? ? {??
? ?? ?? ?? ?? ?? ?? ?? ?? ? MOSI = 0;? ???// 否則傳送位0
? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ???
? ?? ?? ?? ?? ?? ?ch = ch<<1;? ?? ?? ?// 數(shù)據(jù)左移一位


? ?? ?? ???? ?? ? tdata = tdata<<1;? ???// 左移一位,或_crol_(temp,1)

? ?? ?? ?? ?? ?? ???SCK = 1 ;? ?? ?? ?? ?? ?? ? //時鐘置高?
? ?? ?? ?? ?? ?? ?? ?? ??
? ?? ?? ?? ?? ?? ?? ?? ?? ?if(MISO == 1)
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?tdata = tdata|0x01;? ???// 若接收到的位為1,則數(shù)據(jù)的最后一位置1
? ?? ?? ?? ?? ?? ?? ?? ?? ?else?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???tdata = tdata&0xfe;? ???// 否則數(shù)據(jù)的最后一位置0

? ?? ?? ? }
? ?? ???return(tdata);
}

?
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-16 21:15:13

8#

oldbeginner 發(fā)表于 2014-7-16 20:45
******************
93C66 的例子
******************

*******************
常用的SPI C51 模擬
重復(fù)
*******************

http://www.dz863.com/Microprocessors/MCS-8051/SPI-C51.htm

SPI接口有一個特點,即在時鐘SCK的上升沿打入數(shù)據(jù)MOSI,在下降沿讀入數(shù)據(jù)MISO.?
片選信號CS有正負區(qū)別.在硬件上MOSI與MISO是可以短路變?yōu)镾IO可讀寫IO的.?
故SPI可為(不包括CS)?
三線(SCK,MOSI,MISO)協(xié)議,兩線(SCK,SIO)協(xié)議?
再者,SPI一般為雙向同時高速收發(fā)數(shù)據(jù)的,方向由時鐘SCK的跳變沿決定。?

根據(jù)以上所述,模擬SPI讀寫模塊編制成為一體化模塊是必要的。?
而且調(diào)用規(guī)則只需注意讀數(shù)據(jù)時要寫入0xff即可。非常方便好用。?

例如:?
res = SpiReadWrite(val);//模塊寫?
SpiReadWrite(0xff);//模塊讀?
對于具體器件,由于涉及到命令、地址及數(shù)據(jù)等,故一個完整的SPI讀或?qū)懖僮?
可能需要幾個模擬SPI讀寫一體化模塊來完成。?

所以一般完整的SPI讀或?qū)懖僮餍枰韵潞瘮?shù)組合完成?

void SpiOpen(void); //打開片選 CS=0或CS=1?
void SpiClose(void); //關(guān)閉片選 CS=1或CS=0?
unsigned char SpiReadWrite(val); //模擬SPI讀寫一體化模塊?
void SpiWriteEnable(void); //使能寫操作?
void SpiWriteDISAble(void); //禁止寫操作?
unsigned char SpiReadStatus(void); //讀狀態(tài)?
void SpiWriteStatus(unsigned char val); //寫狀態(tài)?
void SpiWriteWait(void); //等待寫入完成?
void SpiWriteByte(unsigned int addr, unsigned char val);//寫一個字節(jié)?
void SpiWriteWord(unsigned char addr, unsigned int val);//寫一個字?
unsigned char SpiReadByte(unsigned int addr); //讀一個字節(jié)?
unsigned int SpiReadWord(unsigned char addr); //讀一個字?
/*----------------------------------------------------------------------------?

/*-----------------------------------------------?
例:?
X5045模擬SPI讀寫一體化模塊?
PTR905模擬SPI讀寫一體化模塊?
------------------------------------------------*?
unsigned char SpiReadWrite(unsigned char val)?
{?
unsigned char i;?
ACC = val;?
for (i = 8; i > 0; i --)?
{?
CY = MISO;//取數(shù)據(jù)SO?
_rlca_();//存數(shù)據(jù)ACC.0讀數(shù)據(jù)ACC.7同時進行?
MOSI = CY;//送數(shù)據(jù)SI?
SCK = 1;//上升沿打入數(shù)據(jù)?
_nop_();//延時?
SCK = 0;//下降沿讀入數(shù)據(jù)?
}?
return ACC;?
}?

/*-----------------------------------------------?
例:?
X5045模擬SPI讀寫一體化模塊?
ISD4004模擬SPI讀寫一體化模塊?
------------------------------------------------*?
unsigned char SpiReadWrite(unsigned char val)?
{?
unsigned char i;?
ACC = val;?
for (i = 8; i > 0; i --)?
{?
SCK = 0;//下降沿讀入數(shù)據(jù)?
_nop_();//延時?
CY = MISO;//取數(shù)據(jù)SO?
_rlca_();//存數(shù)據(jù)ACC.0讀數(shù)據(jù)ACC.7同時進行?
MOSI = CY;//送數(shù)據(jù)SI?
_nop_();//延時?
SCK = 1;//上升沿打入數(shù)據(jù)?
MOSI = 1;//釋放總線SI?
}?
return ACC;?
}?

/*-----------------------------------------------?
例:?
AT93C46模擬SPI讀寫模塊?
------------------------------------------------*?
sbit ACC_7 = ACC^7;?
unsigned char SpiReadWrite(unsigned char val)?
{?
unsigned char i;?
ACC = val;?
for (i = 8; i > 0; i --)?
{?
CY = ACC_7;//讀數(shù)據(jù)ACC.7?
MOSI = CY;//送數(shù)據(jù)SI?
SCK = 1;//上升沿打入數(shù)據(jù)?
CY = MISO;//取數(shù)據(jù)SO?
_rlca_();//存數(shù)據(jù)ACC.0?
SCK = 0;//下降沿?
}?
return ACC;?
}?

?
?
贊回復(fù)

舉報

?

oldbeginner?2014-7-17 15:47:44

9#

oldbeginner 發(fā)表于 2014-7-16 21:15
*******************
常用的SPI C51 模擬
重復(fù)

********************
SD卡 的 SPI 模式

********************

SD 的 SPI 模式 寫出來 是如此簡單。

//寫一字節(jié)到SD卡,模擬SPI總線方式
void SdWrite(unsigned char n)
{

unsigned char i;

for(i=8;i;i--)
{
SD_CLK=0;
SD_DI=(n&0x80);
n<<=1;
SD_CLK=1;
}
SD_DI=1;?
}?
下降沿 發(fā)出數(shù)據(jù)。

//===========================================================
//從SD卡讀一字節(jié),模擬SPI總線方式
unsigned char SdRead()
{
unsigned char n,i;
for(i=8;i;i--)
{
SD_CLK=0;
SD_CLK=1;
n<<=1;
if(SD_DO) n|=1;

}
return n;
}

上升沿 接收數(shù)據(jù)。
?
贊回復(fù)

舉報

?

slim443?2014-7-18 16:16:08

10#

協(xié)議介紹的很詳細
?
贊回復(fù)

舉報

?

gh037?2014-7-21 01:15:03

11#

{:1:}
?
贊回復(fù)

舉報

?

家瑞?2014-9-25 11:28:15

12#

頂樓主,寫的不錯
?
贊回復(fù)

舉報

?

我是流氓羽?2015-6-17 10:57:06

13#

講的太好了,默默的收藏
?
贊回復(fù)

舉報

?

shunbaiwang?2016-2-14 21:39:06

14#

看起來挺有用的,感謝樓主
?
贊回復(fù)

舉報

?

qq543538634?2016-8-31 14:23:27

15#

這么深入淺出 通俗易懂的教程居然沒人頂????
謝謝樓主啦!
?
贊回復(fù)

舉報

?

我是你的唯一?2016-11-11 21:02:45

16#

不錯,確實很有用的 一定好好學(xué)習(xí)
?
贊回復(fù)

舉報

?

我是你的唯一?2016-11-11 21:13:32

17#

666666666666666666666
?
?
贊回復(fù)

舉報

?

wenzi122255?2017-2-21 10:25:37

18#

非常好,值得學(xué)習(xí)啊!
?
贊回復(fù)

舉報

?

769295513?2017-12-17 11:29:31

19#

不好意思想請問一下,關(guān)于相位的那個動畫,數(shù)據(jù)采樣是指把SDI的值存入寄存器中,存入的過程中整個8位數(shù)據(jù)已經(jīng)改變;數(shù)據(jù)輸出是指8位數(shù)據(jù)向左移1位,將最左端的數(shù)據(jù)放在SDO中,是這樣么?
如果是的話,那在(0,0)的時候,應(yīng)該是先采樣,改變8位數(shù)據(jù);再移位,把最左端的一位數(shù)取出來。
那最后一個相位的動畫是不是反了呢?

我是你的唯一?2016-11-11 21:13:32

17#

666666666666666666666
?
?
贊回復(fù)

舉報

?

wenzi122255?2017-2-21 10:25:37

18#

非常好,值得學(xué)習(xí)啊!
?
贊回復(fù)

舉報

?

769295513?2017-12-17 11:29:31

19#

不好意思想請問一下,關(guān)于相位的那個動畫,數(shù)據(jù)采樣是指把SDI的值存入寄存器中,存入的過程中整個8位數(shù)據(jù)已經(jīng)改變;數(shù)據(jù)輸出是指8位數(shù)據(jù)向左移1位,將最左端的數(shù)據(jù)放在SDO中,是這樣么?
如果是的話,那在(0,0)的時候,應(yīng)該是先采樣,改變8位數(shù)據(jù);再移位,把最左端的一位數(shù)取出來。
那最后一個相位的動畫是不是反了呢?

總結(jié)

以上是生活随笔為你收集整理的[经验] PROTEUS仿真学习笔记05 (SPI 协议 外设)——2014_7_15的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。