1. 程式人生 > >NIOS II SPI詳解 如何使用SPI方式傳輸

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版本。