NIOS II SPI詳解 如何使用SPI方式傳輸
http://bbs.ic37.com/bbsview-34086.htm
1、說明
本文是依據筆者閱讀《Embedded Peripherals (ver 9.0, Mar 2009, 4 MB).pdf》參考文件所作的個人理解,可以看做是筆記吧。
本文只講NIOS II嵌入式外設SPI的原理與使用,關於IP-CORE的使用,請讀者參考 《SPI Slave JTAG to Avalon Master.pdf》。
在下一篇文章中將用例項說明如何用SPI驅動ADS1256。
2、功能描述
Spi通訊包括兩條資料線(進、出)、一條同步時鐘線和一條控制線。
(1)Master Out Slave In (mosi)—主裝置輸入資料到從裝置的資料線。
(2)Master In Slave Out (miso)—從裝置輸出資料到主裝置的資料線。
(3)Serial Clock (sclk)—主裝置驅動從裝置的同步時鐘。
(4)Slave SELECT (ss_n)—主裝置驅動,用於選擇從裝置。置低時有效。最多可以設定32個從裝置。
Sclk與Avalon—MM是同步的。當配置為主裝置,spi-core將Avalon—MM的時鐘分頻得到sclk。若配置為從裝置,接收邏輯是與sclk的輸入同步的。
3、傳送邏輯
傳送邏輯包含傳送保持暫存器(txdata)和傳送移位暫存器,各有n位寬度,可配置1~32位。當某主外設寫txdata,值會自動被複制到移位暫存器,並在下個操作開始時傳輸。
Txdata和移位暫存器提供兩個儲存空間。當有資料在移位暫存器傳輸時,可以往txdata中寫入資料,並且在移位暫存器處理完當前資料時會自動載入txdata中資料,併發送。
在主裝置模式傳輸移位暫存器直接驅動mosi,在從裝置模式,傳輸移位暫存器直接驅動miso。
資料傳輸時,LSB或者MSB在前,由配置時使用者指定。指定之後,不可軟體更改。
4、接收邏輯
接收邏輯包含接收保持暫存器(rxdata)和接收移位暫存器,各有n位寬度,可配置1~16位。在接收移位暫存器捕獲滿n位資料後,主外設可以從rxdata中讀取接收到的資料。
同傳送邏輯類似,接收邏輯同樣具有提供兩個儲存空間的功能。Rxdata可以保持前一個接收到的資料,同時接收移位暫存器亦在接收資料。當一個傳輸完畢時,接收邏輯自動將接收移位暫存器的資料更新至rxdata。
在主裝置模數時,接收移位暫存器直接受miso驅動。在從裝置模式 ,受mosi驅動。同樣,資料傳輸時,LSB或者MSB在前,由配置時使用者指定。指定之後,不可軟體更改。
5、在SOPC中安裝SPI-CORE
5.1 主從設定
使用者可以選擇主裝置模式或者從裝置模式。當選擇主裝置模式時,下面的選擇是有效的:NUMBER of select (SS_n) signals、SPI clock rate、 和Specify delay.
(1)Number of Select (SS_n) Signals
設定從裝置的個數。取值範圍是1~32。該選擇會在生成NIOS II時產生n個SS_n引腳,用於片選。
(2)SPI Clock (sclk) Rate
該時鐘是有Avalon-MM分頻產生,其數學關係是:
/ [2, 4, 6, 8, ...]
在SOPC設定時,可以輸入所需要的時鐘,軟體會自動選擇一個低於或者等於所輸入時鐘的最近符合值。
時鐘的設定注意結合從裝置的要求。
(3)Specify Delay
該延時是指片選訊號有效(置低)之後,需要多長的時間才能對從裝置發起操作。
5.2 資料暫存器設定
該設定影響資料暫存器的大小和行為特性。
(1)Width—設定txdata、rxdata、傳送移位暫存器、接收移位暫存器的大小,可設定為1~32位。
(2)Shift direction—指定資料傳輸時LSB或者MSB在前。使用者需瞭解從裝置所需的模式。
5.3 時序設定
該設定涉及ss_n, sclk, mosi 和 miso 訊號的設定。共有兩處設定,可設定4種模式。
(1)Clock polarity—設定為0時,sclk空閒時為低;設定為1時,sclk空閒時為高。
(2)Clock phase—設定為0時,資料在sclk的上升沿鎖存;設定為1時,資料在sclk的下降沿鎖存。
具體見如下時序圖:
1. Clock Polarity = 0, Clock Phase = 0
2. Clock Polarity = 0, Clock Phase = 1
3. Clock Polarity = 1, Clock Phase = 0
4. Clock Polarity = 1, Clock Phase = 1
6、NIOS II軟體驅動模組
有兩種方式:
1. 使用有ALTERA提供的alt_avalon_spi_command()函式,使用非常簡單。具體可見筆者下一篇驅動例項。
2. 讀者自己編寫。
這裡先將讀者自己DIY的驅動編寫方法。
6.1 驅動方式一
必須包含:
#include "altera_avalon_spi.h"
#include "altera_avalon_pio_regs.h"
這兩個標頭檔案。
檔案中包含有暫存器地址與API函式。
IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(); //從裝置選擇
IOWR_ALTERA_AVALON_SPI_CONTROL(base, data); //控制暫存器
IORD_ALTERA_AVALON_SPI_RXDATA(base); //讀接收暫存器
IOWR_ALTERA_AVALON_SPI_TXDATA(base, data); //寫傳送暫存器
IORD_ALTERA_AVALON_SPI_STATUS(base); //讀狀態暫存器
為了準確實現方式,首先列出SPI暫存器對映圖
(1)若rxdata和txdata少於16位,則[31:16]位無效;
(2)寫status會清除ROE、TOE和E位;
(3)ss0只在作為主裝置時有效;用來啟動傳送或者接受資料。例如:
4)rxdata:
// Force SS_n ACTIVE:
na_spi_0-> np_spicontrol |= np_spicontrol_sso_mask;
for (i = 0; i < 3; ++i)
{
// Transmit a byte:
while (!(na_spi_0->np_spistatus & np_spistatus_trdy_mask));
na_spi_0->np_spitxdata = data;
// Read and throw away the received data:
while (!(na_spi_0->np_spistatus & np_spistatus_rrdy_mask));
na_spi_0->np_spirxdata;
}
// Wait until the last byte is transmitted:
while (!(na_spi_0->np_spistatus & np_spistatus_tmt_mask));
// Release SS_n:
na_spi_0-> np_spicontrol &=~np_spicontrol_sso_mask;
(
(a)當接收暫存器接收到所設定的n位且將資料移入rxdata後,status中的RRDY設為1;軟體可設查詢此位來判斷是否有新資料接收。如:
while(IORD_ALTERA_AVALON_SPI_STATUS(base) & ALTERA_AVALON_SPI_STATUS_RRDY_MSK == 1);
(b)讀rxdata會自動清除RRDY位;
(c)新資料總是不斷讀入rxdata,若RRDY=1; rxdata的資料未讀出時,有新的資料傳入rxdata,則ROE=1;此時,rxdata儲存的資料未定義。
(5)txdata:
(a)當TRDY=1時,表示傳送暫存器準備好接受下一個傳送資料。如:
while(IORD_ALTERA_AVALON_SPI_STATUS(base) & ALTERA_AVALON_SPI_STATUS_TRDY_MSK == 1);
(b)寫入rxdata使TRDY=0;
(c)當txdata中的資料傳入傳送暫存器時,TRDY=1;
(d)若TRDY=0時寫入資料至txdata會導致TOE=1;此時新資料被忽略。
6.2驅動方式二
該方式使用官方的函式:(下篇文章使用的是該方式。)
int alt_avalon_spi_command(alt_u32 base, alt_u32 slave,
alt_u32 write_length,
const alt_u8* wdata,
alt_u32 read_length,
alt_u8* read_data,
alt_u32 flags)
該函式不支援8位或者8位以上的傳輸。但是,可以呼叫多次實現高於8位的傳輸。該函式原始碼位於“<安裝路徑>/altera/81/ip/altera/sopc_builder_ip/altera_avalon_spi/HAL/src”。
alt_u32 base, alt_u32 slave, //選擇從裝置
alt_u32 write_length, //設定寫入長度。8位為一個長度。
const alt_u8* wdata, //將要寫入資料的陣列地址。該函式自動將陣列中的資料依次發出直到發出所設定的write_length長度。
alt_u32 read_length, //設定讀出長度。8位為一個長度
alt_u8* read_data, //將讀出的資料儲存到陣列中,直到存滿所設定的read_length長度
alt_u32 flags, //如果flag=ALT_AVALON_SPI_COMMAND_MERGE,則訪問完從裝置之後不會釋放從裝置,一般用於需多次訪問的情況。如果flag=0,則每次訪問完從裝置都會釋放從裝置,即當讀取read_length個位元組數,或者寫完write_length個位元組數,會釋放從裝置。
7、總結
筆者測試使用環境均為8.1版本。