SPI Signal and Timing 參考
此規格相較於 IIC 而單純。後附以前寫過的 spi timing。
原則上,spi timing 就是遵循以上的 timing 資料。實際的運用將看該 device 所定義的,原則上是可對應到以上四種模式的其中一/幾種。
另常見的狀況補充,例如,daisy-chain,單一支 CS 已成為被各 devices 所共享,故需有 device address 以定址到某一個特定的 device。故而,如以下程式所使用到,MISO pin 在 master 定址時,可被告知 slave 處於 busy 與否。(但 daisy-chain 有時序延遲的附帶影響與所該因應)。
其次,SPI 的可應用時脈相較於 IIC 的高。再因例如 ESP8266 的 GPIO toggle 有 1us 的延遲與中斷有 4us 的 payload。故 nonblocking mode 應是不合適。
因此假若實作 timer-triggered nonblocking mode,應是較適合用在 gpio 擴展上。相反地說,或許可實作 shift-register IC 類別,來套用各種 digital-signal timing specs。
#define MOSI_OUTPUT(bit) ((bit)?MOSI_HIGH:MOSI_LOW)
#define MISO_INPUT ((P1IN&0x04)?1:0)
#define CS_LOCK (P1OUT&=0xF7)
#define CS_UNLOCK (P1OUT|=0x08)
#define SPI_DELAY_TRANSIENT (Delay_us(3))
#define SPI_DELAY_LOW (Delay_us(158))
#define SPI_DELAY_HIGH (Delay_us(87))
#define SPI_SET_READ(addr) ((addr)=((addr)&0x3F|0x00))
#define SPI_SET_WRITE(addr) ((addr)=((addr)&0x3F|0x80))
#define SPI_SIGNAL_START (CS_LOCK,SCLK_LOW)
int SPI_ReadByte(unsigned char addr, unsigned char *r_data){
int i;
SPI_SET_READ(addr);
SPI_SIGNAL_START;
SPI_DELAY_TRANSIENT;
MOSI_OUTPUT(addr&0x80);
for (i=0; i<7; i++){
if (!MISO_INPUT){
SCLK_HIGH;
MOSI_HIGH;
CS_UNLOCK;
return 0;
}
SPI_DELAY_LOW;
SCLK_TOGGLE;
SPI_DELAY_HIGH;
SCLK_LOW;
SPI_DELAY_TRANSIENT;
addr<<=1;
MOSI_OUTPUT(addr&0x80);
}
SPI_DELAY_LOW;
SCLK_TOGGLE;
SPI_DELAY_HIGH;
for (++i; i<16; i++){
SCLK_LOW;
SPI_DELAY_TRANSIENT;
*r_data<<=1;
*r_data|=MISO_INPUT;
SPI_DELAY_LOW;
SCLK_TOGGLE;
SPI_DELAY_HIGH;
}
CS_UNLOCK;
MOSI_HIGH;
SPI_DELAY_LOW; SPI_DELAY_HIGH;
return 1;
}
int SPI_WriteByte(unsigned char addr, unsigned char data){
int i;
long int j;
SPI_SET_WRITE(addr);
j=((addr<<8)&0xFF00|data);
SPI_SIGNAL_START;
SPI_DELAY_TRANSIENT;
MOSI_OUTPUT(j&0x8000);
for (i=0; i<15; i++){
if (0 && !MISO_INPUT){ // because MISO floating@1st write(before enabled)
SCLK_HIGH;
MOSI_HIGH;
CS_UNLOCK;
return 0;
}
SPI_DELAY_LOW;
SCLK_TOGGLE;
SPI_DELAY_HIGH;
SCLK_LOW;
SPI_DELAY_TRANSIENT;
j<<=1;
MOSI_OUTPUT(j&0x8000);
}
SPI_DELAY_LOW;
SCLK_HIGH;
CS_UNLOCK;
MOSI_HIGH;
SPI_DELAY_LOW; SPI_DELAY_HIGH;
return 1;
}