SPI是一個環形總線結構,由ss(cs)、sck、sdi、sdo構成,其時序其實很簡單,主要是在sck的控制下,兩個雙向移位寄存器進行數據交換。
上升沿發送、下降沿接收、高位先發送。
1.1. 上升沿到來的時候,sdo上的電平將被髮送到從設備的寄存器中。
1.2. 下降沿到來的時候,sdi上的電平將被接收到主設備的寄存器中。
假設主機和從機初始化就緒:並且主機的sbuff=0xaa(10101010),從機的sbuff=0x55(01010101),下面將分步對spi的8個時鐘週期的數據情況演示一遍(假設上升沿發送數據)。
---------------------------------------------------
脈衝 主機sbuff
從機sbuff sdi sdo
---------------------------------------------------
0 00-0 10101010
01010101 0 0
---------------------------------------------------
1 0--1 0101010x 10101011 0
1
1 1--0 01010100
10101011 0 1
---------------------------------------------------
2 0--1 1010100x 01010110 1
0
2 1--0 10101001
01010110 1 0
---------------------------------------------------
3 0--1 0101001x 10101101 0
1
3 1--0 01010010
10101101 0 1
---------------------------------------------------
4 0--1 1010010x 01011010 1
0
4 1--0 10100101
01011010 1 0
---------------------------------------------------
5 0--1 0100101x 10110101 0
1
5 1--0 01001010
10110101 0 1
---------------------------------------------------
6 0--1 1001010x 01101010 1
0
6 1--0 10010101
01101010 1 0
---------------------------------------------------
7 0--1 0010101x 11010101 0
1
7 1--0 00101010
11010101 0 1
---------------------------------------------------
8 0--1 0101010x 10101010 1
0
8 1--0 01010101 10101010
1 0
---------------------------------------------------
這樣就完成了兩個寄存器8位的交換,上面的0--1表示上升沿、1--0表示下降沿,sdi、 sdo相對於主機而言的。根據以上分析,一個完整的傳送週期是16位,即兩個字節,因爲,首先主機要發送命令過去,然後從機根據主機的名準備數據,主機在下一個8位時鐘週期才把數據讀回來。
SPI總線是Motorola公司推出的三線同步接口,同步串行3線方式進行通信:一條時鐘線SCK,一條數據輸入線MOSI,一條數據輸出線MISO;用於 CPU與各種外圍器件進行全雙工、同步串行通訊。SPI主要特點有:可以同時發出和接收串行數據;可以當作主機或從機工作;提供頻率可編程時鐘;發送結束中斷標誌;寫衝突保護;總線競爭保護等。
SPI模擬具體實現在IO_Spi.c中,其中主要模擬SPI讀和寫兩個過程,主要實現在IO_SpiReadByte()和IO_SpiWriteByte()兩個函數中,如下:(編譯環境CSR,PioSet()設置對應IO口的高低電平)
uint8 IO_SpiReadByte(void)
{
uint8 i,data = 0x00;
PioSet(SCK_PIO,PIO_STATE_LOW);
WAIT_uS(2);
for(i = 0; i < 8; i++)
{
PioSet(SCK_PIO,PIO_STATE_HIGH);
WAIT_uS(2);
if(PioGet(SDI_PIO))
{
data |= 0x01;
}
PioSet(SCK_PIO,PIO_STATE_LOW);
WAIT_uS(2);
data <<= 1;
}
return data;
}
void IO_SpiWriteByte(uint8 byte)
{
uint8 i;
PioSet(SCK_PIO, PIO_STATE_LOW);
WAIT_uS(2);
for(i = 0; i < 8; i++)
{
if(byte & 0x80)
{
PioSet(SDO_PIO,PIO_STATE_HIGH);
}
else
{
PioSet(SDO_PIO,PIO_STATE_LOW);
}
WAIT_uS(2);
PioSet(SCK_PIO,PIO_STATE_HIGH);
WAIT_uS(2);
PioSet(SCK_PIO,PIO_STATE_LOW);
byte <<= 1;
}
}