———————————————————————————————————————————— SPI匯流排 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ...
————————————————————————————————————————————
SPI匯流排
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
參考鏈接:
http://blog.csdn.net/fly__chen/article/details/52724109
http://blog.csdn.net/skyflying2012/article/details/11910173
http://blog.sina.com.cn/s/blog_9cc7125c0100yk1s.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
簡介:
- 串列外圍設備介面
- 全雙工三線同步,可以同時發出和接收串列數據
- 採用主從(Master Slave)架構,支持多Slave模式應用,一般僅支持單Slave
- 時鐘由Master控制,在時鐘移位脈衝下,數據按位傳輸,高位在前,低位在後
- 目前應用中可以達到幾Mbps的水平
- 優點:與普通的串列設備相比,可以按位傳輸,甚至可以暫停。當沒有時鐘跳變時,從設備不採集和傳送數據。不需要定址操作。全雙工通信。
- 缺點:沒有應答機制確認。
特點:
- 提供頻率可編程時鐘
- 發送結束、中斷標誌;寫衝突保護
- 匯流排競爭保護
- SPI匯流排工作的4種工作方式中,使用最廣泛的是SPI0和SPI3方式
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
信號線情況:
- SCLK提供時鐘脈衝,SDI/SDO基於此脈衝按位傳輸。當處於上升沿模式時,輸出:通過SDO線在時鐘上升沿時輸出,在緊接著的下降沿被讀取。輸入同理。
- SS/CS是片選信號線,只有片選信號為使能信號時,對晶元的操作才有效,所以可以在同一匯流排上連接多個SPI設備
- SDI:slave → master,從機要發送給主機的數據
- SDO:master → slave,主機要發送給從機的數據
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
連接方式:
- 級聯方式:此時所有設備的CS端都連在一起,只要選中一個設備,則全選。可以作為一個設備進行處理。
- 獨立連接方式:設備獨立操作,為被選通的從設備均處於高阻隔離狀態。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
工作模式:
SPI模式 |
CPOL極性 |
CPHA相位 |
說明 |
0 |
0 |
0 |
第一個邊沿上升沿 |
1 |
0 |
1 |
第二個邊沿下降沿 |
2 |
1 |
0 |
第一個邊沿下降沿 |
3 |
1 |
1 |
第二個邊沿上升沿 |
CPOL=0:SCLK有效時為高電平(active-high)
CPOL=1:SCLK有效時為低電平(active-low)
CPHA=0:表示第一個邊沿
CPHA=1:表示第二個邊沿
Toggling edge為切換邊沿,輸出信號
Sampling edge為採樣邊沿,輸入信號
時序圖:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SPI協議舉例
- 主機8位寄存器存放的是1010 1010,從機存放的是0101 0101,將主從機數據交換
- SDI:slave → master
- SDO:master → slave
- 上升沿發送、下降沿接收
初始化就緒狀態:
- 主機SBUFF = 1010 1010
- 從機SBUFF = 0101 0101
操作過程:如圖所示,經過8個脈衝後,master和slave數據交換
SPI的8個時鐘周期的數據:
————————————————————————————————————————————
基於SPI協議,DS1302顯示時鐘實例
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
實現效果:
實現代碼:
1 #include <reg52.h> 2 typedef unsigned char uchar; 3 typedef unsigned int uint; 4 //寫操作控制位元組,D7=1,D0=0 5 uchar code write_address[] = 6 { 7 //秒,分,小時,日,月,星期,年 8 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c 9 }; 10 //讀操作,D7=1,D0=1,地址同寫操作 11 uchar code read_address[] = 12 { 13 0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d 14 }; 15 uchar code table[] = 16 { 17 //0,1,2,3,4,5,6,7,8,9 18 0xfc, 0x60, 0xda, 0xf2, 0x66, 0xb6, 0xbe, 0xe0, 0xfe, 0xf6 19 }; 20 //dat1和dat2存放讀出來的時間,初始值寫入12年5月9日1時1分1秒,dat1存放1234位,dat2存放567位 21 uchar dat1[] = {0x01, 0x01, 0x01, 0x09, 0x05, 0x02, 0x12}; 22 uchar dat2[] = {0x01, 0x01, 0x01, 0x09, 0x05, 0x02, 0x12}; 23 sbit rst = P3 ^ 0; 24 sbit scl = P3 ^ 1; 25 sbit sda = P3 ^ 2; 26 sbit ACC7 = ACC ^ 7; 27 void Delay(uint m) 28 { 29 while(m--); 30 } 31 /* SPI協議操作,讀位元組 */ 32 uchar ReadByte() 33 { 34 uchar i; 35 for (i = 0; i < 8; ++i) 36 { 37 ACC = ACC >> 1; //累加器左移1位,補上未知數x 38 ACC7 = sda; //從sda引腳寫入ACC最高位 39 scl = 1; 40 scl = 0; //時鐘下降沿讀入 41 } 42 return ACC; 43 } 44 /* SPI協議操作,寫位元組 */ 45 void WriteByte(uchar byte) 46 { 47 uchar i; 48 for (i = 0; i < 8; ++i) 49 { 50 byte >>= 1; //byte左移1位存入CY 51 scl = 0; 52 sda = CY; //從CY移入sda,發送給DS102 53 scl = 1; //時鐘上升沿寫入 54 } 55 } 56 void Write1302(uchar address, uchar dat) //寫地址子程式 57 { 58 rst = 0; 59 scl = 0; 60 rst = 1; //rst上升沿開始寫數據 61 WriteByte(address); //先寫入地址控制位元組 62 WriteByte(dat); //再寫入數據位元組 63 rst = 0; 64 } 65 uchar Read1302(uchar address) 66 { 67 uchar temp; 68 rst = 0; 69 scl = 0; 70 rst = 1; //讀過程中保持rst高電平狀態 71 WriteByte(address | 0x01); //寫入地址並置R/W位為1(讀) 72 temp = ReadByte(); //在單片機寫入命令位元組的最後一位的第一個下降沿處即讀出數據 73 scl = 1; 74 rst = 0; 75 return temp; 76 } 77 void SetRST() 78 { 79 uchar i; 80 Write1302(0x8e, 0x00); //向10001110防寫寄存器,寫入指令0x00 81 for (i = 0; i < 7; ++i) 82 Write1302(write_address[i], dat1[i]); //從秒到年各寄存器寫入對應初始值 83 Write1302(0x8e, 0x80); //向防寫寄存器,寫入數據0x80 84 } 85 void ReadTime() 86 { 87 uchar i, temp1, temp2, temp3; 88 temp3 = 0x80; //temp3存放時間寄存器地址 89 for (i = 0; i < 7; ++i) //分別讀出秒分小時日月星期年 90 { 91 temp1 = Read1302(temp3); 92 temp2 = temp1; 93 dat1[i] = (temp1 >> 1) & 0x0f; //讀出的數據1234位存入dat1,屏蔽其他位 94 dat2[i] = (temp2 >> 5) & 0x07; //讀出的數據567位存入dat2,屏蔽其他位 95 temp3 = temp3 + 0x02; //下一個寄存器地址 96 } 97 } 98 void main() 99 { 100 rst = 0; 101 SetRST(); //時鐘建立 102 while(1) 103 { 104 ReadTime(); //讀時間 105 P2 = 0xfe; 106 P1 = table[dat1[0] % 10]; 107 Delay(500); 108 P2 = 0xfd; 109 P1 = table[dat2[0] % 10]; 110 Delay(500); 111 P2 = 0xfb; 112 P1 = 0x02; // - 113 Delay(500); 114 P2 = 0xf7; 115 P1 = table[dat1[1] % 10]; 116 Delay(500); 117 P2 = 0xef; 118 P1 = table[dat2[1] % 10]; 119 Delay(500); 120 P2 = 0xdf; 121 P1 = 0x02; // - 122 Delay(500); 123 P2 = 0xbf; 124 P1 = table[dat1[2] % 10]; 125 Delay(500); 126 P2 = 0x7f; 127 P1 = table[dat2[2] % 10]; 128 Delay(500); 129 } 130 }