?? 9991.txt
字號:
串行接口SPI 接口應用設計
作者:馬潮老師 / 整理:armok / 2005-01-17/ www.OurAVR.com
使用的同步串行三線SPI 接口,可以方便的連接采用SPI 通信協議的外圍或另一片AVR 單片機,實現在
短距離內的高速同步通信。ATmega128 的SPI 采用硬件方式實現面向字節的全雙工3 線同步通信,支持主
機、從機和2 種不同極性的SPI 時序,通信速率有7 種選擇,主機方式的最高速率為1/2 系統時鐘,從機方式
最高速率為1/4 系統時鐘。
ATmega128 單片機內部的SPI 接口也被用于程序存儲器和數據E2PROM 的編程下載和上傳。但特別需要
注意的是,此時SPI 的MOSI 和MISO 接口不再對應PB2、PB3 引腳,而是轉換到PE0、PE1 引腳上(PDI、
PDO),其詳見第二章中關于程序存儲器的串行編程和校驗部分的內容。
ATmega128 的SPI 為硬件接口和傳輸完成中斷申請,所以使用SPI 傳輸數據的有效方法是采用中斷方式+
數據緩存器的設計方法。在對SPI 初始化時,應注意以下幾點:
.正確選擇和設置主機或從機,以及工作模式(極性),數據傳輸率;
.注意傳送字節的順序,是低位優先(LSB First)還是高位優先(MSB Frist);
.正確設置MOSI 和MISO 接口的輸入輸出方向,輸入引腳使用上拉電阻,可以節省總線上的吊高電阻。
下面一段是SPI 主機方式連續發送(接收)字節的例程:
#define SIZE 100
unsigned char SPI_rx_buff[SIZE];
unsigned char SPI_tx_buff[SIZE];
unsigned char rx_wr_index,rx_rd_index,rx_counter,rx_buffer_overflow;
unsigned char tx_wr_index,tx_rd_index,tx_counter;
#pragma interrupt_handler spi_stc_isr:18
void spi_stc_isr(void)
{
SPI_rx_buff[rx_wr_index] = SPDR; //從ISP 口讀出收到的字節
if (++rx_wr_index == SIZE) rx_wr_index = 0; //放入接收緩沖區,并調整隊列指針
if (++rx_counter == SIZE)
{
rx_counter = 0;
rx_buffer_overflow = 1;
}
if (tx_counter) //如果發送緩沖區中有待發的數據
{
--tx_counter;
SPDR = SPI_tx_buff[tx_rd_index]; //發送一個字節數據,并調整指針
if (++tx_rd_index == SIZE) tx_rd_index = 0;
}
}
unsigned char getSPIchar(void)
{
unsigned char data;
while (rx_counter == 0); //無接收數據,等待
data = SPI_rx_buff[rx_rd_index]; //從接收緩沖區取出一個SPI 收到的數據
if (++rx_rd_index == SIZE) rx_rd_index = 0; //調整指針
CLI();
--rx_counter;
SEI();
return data;
}
void putSPIchar(char c)
{
while (tx_counter == SIZE);//發送緩沖區滿,等待
CLI();
if (tx_counter || ((SPSR & 0x80) == 0))//發送緩沖區已中有待發數據
{ //或SPI 正在發送數據時
SPI_tx_buffer[tx_wr_index] = c; //將數據放入發送緩沖區排隊
if (++tx_wr_index == SIZE) tx_wr_index = 0; //調整指針
++tx_counter;
}
else
SPDR = c; //發送緩沖區中空且SPI 口空閑,直接放入SPDR 由SIP 口發送
SEI();
}
void spi_init(void)
{
unsigned chat temp;
DDRB |= 0x080; //MISO=input and MOSI,SCK,SS = output
PORTB |= 0x80; //MISO 上拉電阻有效
SPCR = 0xD5; //SPI 允許,主機模式,MSB,允許SPI 中斷,極性方式01,1/16 系統時鐘速率
SPSR = 0x00;
temp = SPSR;
temp = SPDR; //清空SPI,和中斷標志,使SPI 空閑
}
void main(void)
{
unsigned char I;
CLI(); //關中斷
spi_init(); //初始化SPI 接口
SEI(); //開中斷
while()
{
putSPIchat(i); //發送一個字節
i++;
getSPIchar(); //接收一個字節(第一個字節為空字節)
………
}
}
這個典型的SPI 例程比較簡單,主程序中首先對ATmega128 的硬件SPI 進行初始化。在初始化過程中,
將PORTB 的MOSI、SCLK 和SS 引腳作為輸出,同時將MISO 作為輸入引腳,并打開上拉電阻。接著對SPI
的寄存器進行初始化設置,并空讀一次SPSR、SPDR 寄存器(讀SPSR 后再對SPDR 操作將自動清零SPI 中斷
標志自動清零),使ISP 空閑等待發送數據。
AVR 的SPI 由一個16 位的循環移位寄存器構成,當數據從主機方移出時,從機的數據同時也被移入,因
此SPI 的發送和接收在一個中斷服務中完成。在SPI 中斷服務程序中,先從SPDR 中讀一個接收的字節存入接
收數據緩沖器中,再從發送數據緩沖器取出一個字節寫入SPDR 中,由ISP 發送到從機。數據一旦寫入
SPDR,ISP 硬件開始發送數據。下一次ISP 中斷時,表示發送完成,并同時收到一個數據。類似本章介紹的
USART 接口的使用,程序中putSPIchar()和getSPIchar()為應用程序的底層接口函數(SPI 驅動程序是SPI 中斷
服務程序),同時也使用了兩個數據緩沖器,分別構成循環隊列。這種程序設計的思路,不但程序的結構性完
整,同時也適當的解決了高速MCU 和低速串口之間的矛盾,實現程序中任務的并行運行,提高了MCU 的運
行效率。
本例程是通過SPI 批量輸出、輸入數據的示例,用戶可以使用一片ATmega128,將其MOSI 和MISO 兩
個引腳連接起來,構成一個ISP 接口自發自收的系統,對程序進行演示驗證。需要注意,實際接收到的字節為
上一次中斷時發出的數據,即第一個收到的字節是空字節。
讀懂和了解程序的處理思想,讀者可以根據需要對程序進行改動,適合實際系統的使用。如在實際應用中
外接的從機是一片SPI 接口的溫度芯片,協議規程為:主機先要連續發送3 個字節的命令,然后從機才返回一
個字節的數據。那么用戶程序可以先循環調用putSPIchar()函數4 次,將3 個字節的命令和一個字節的空數據
發送到從機,然后等待一段時間,或處理一些其它的操作后,再循環調用getSPIchar()函數4 次,從接收數據
緩沖器中連續讀取4 個字節,放棄前3 個空字節,第4 個字節即為從機的返回數據了。
原文出處: OurAVR.com 技術論壇,歡迎參加討論
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -