FPGA:SPI接口
SPI可以用作FPGA和其他芯片之間簡單有效的通信方式。
本文引用地址:http://www.ex-cimer.com/article/202401/454461.htmSPI 1 - 什么是SPI?
SPI是一個(gè)簡單的接口,允許一個(gè)芯片與一個(gè)或多個(gè)其他芯片進(jìn)行通信。
它看起來如何?
讓我們從一個(gè)簡單的例子開始,其中只有兩個(gè)芯片必須一起通信。
SPI 需要在兩個(gè)芯片之間使用 4 根線。
如您所見,這些電線被稱為 SCK、MOSI、MISO 和 SSEL,其中一個(gè)芯片是“主芯片”,另一個(gè)芯片是“從芯片”。
SPI基礎(chǔ)知識
基本上:
它是同步的。
它是全雙工串行。
它不是即插即用的。
有一個(gè)(也只有一個(gè))主站和一個(gè)或多個(gè)(或多個(gè))從站。
更多細(xì)節(jié):
同步:時(shí)鐘由主機(jī)生成。
全雙工串行:數(shù)據(jù)被串行化,在每個(gè)時(shí)鐘周期內(nèi)向每個(gè)方向傳輸一位數(shù)據(jù),因此使用兩條數(shù)據(jù)線(MOSI 和 MISO)。
非即插即用:主站和從站事先知道通信的細(xì)節(jié)(位順序、交換的數(shù)據(jù)字長度等)。
一個(gè)主站:從站不能發(fā)起通信,只有主站可以。從屬設(shè)備傾聽并做出回應(yīng)。
簡單轉(zhuǎn)移
假設(shè)主站和從站需要 8 位數(shù)據(jù)傳輸,首先傳輸 MSB。
以下是單個(gè) 8 位數(shù)據(jù)傳輸?shù)臉幼印?/p>
MOSI線是“主輸出”,而MISO是“從輸出”。 由于SPI是全雙工的,兩條線同時(shí)切換,從主到從,從從到主,從到主。
更多細(xì)節(jié):
主站下拉 SSEL 以向從站指示通信正在啟動(dòng)(SSEL 處于低電平有效狀態(tài))。
主機(jī)切換時(shí)鐘八次,并在其 MOSI 線路上發(fā)送 8 個(gè)數(shù)據(jù)位。同時(shí),它從MISO線路上的從站接收8個(gè)數(shù)據(jù)位。
主站向上拉 SSEL 以指示傳輸已結(jié)束。
如果主服務(wù)器有多個(gè) 8 位數(shù)據(jù)要發(fā)送/接收,則它只能在發(fā)送/接收完成后繼續(xù)發(fā)送/接收并取消置位 SSEL。
多個(gè)從站
SPI主機(jī)可以通過兩種方式與多個(gè)從機(jī)通信:并行連接大多數(shù)信號并添加SSEL線,或者通過鏈接從機(jī)。
使用多 SSEL 線路技術(shù)時(shí),一次只能激活一條 SSEL 線路,并且未選擇的從站不得驅(qū)動(dòng) MISO 線路。
它有多快?
SPI可以輕松實(shí)現(xiàn)幾Mbps(兆比特/秒)。 這意味著它可以用于未壓縮的音頻或壓縮的視頻。
SPI 2 - 簡單的實(shí)現(xiàn)
ARM 處理器
為了有機(jī)會(huì)測試我們新獲得的 SPI 知識,我們使用了 Saxo-L 板。 它有一個(gè) ARM7 處理器 (LPC2138) 和一個(gè) Cyclone FPGA (EP1C3),通過 SPI 總線連接。
ARM 用作 SPI 主機(jī),而 FPGA 用作 SPI 從機(jī)。 Saxo-L ARM 處理器實(shí)際上有兩個(gè) SPI 接口,一個(gè)稱為 SPI0,另一個(gè)更高級的稱為 SPI1/SSP。 它們都同樣易于使用。我們在 Saxo-L 上使用 SPI1/SSP,因?yàn)樗杨A(yù)先接線在電路板上。
SPI 主機(jī) - C ARM 代碼
使用 SSP 只需初始化幾個(gè)寄存器,然后寫入/讀取數(shù)據(jù)以自動(dòng)發(fā)送/接收。
void main(void)
{
// initialize SSP
SSP0CPSR = 0x02; // SSP max speed
SSP0CR0 = 0x07; // SSP max speed, 8 bits
SSP0CR1 = 0x02; // SSP master mode
PINSEL1 = 0x2A8; // SSP mode for pins P0.17 to P0.20
while(1)
{
// send two bytes
SSP0DR = 0x55; // one nice thing about the SSP is that it has a 8-words deep FIFO
SSP0DR = 0x54; // so here we write the data to be sent without worrying
// now wait until both bytes are sent
while(!(SSP0SR & 0x01));
// now we can read the two bytes received... and do anything with them
int data1 = SSP0DR;
int data2 = SSP0DR;
// ...
}
}
SPI 從機(jī) - HDL FPGA 代碼
現(xiàn)在是FPGA中的SPI從機(jī)。
由于SPI總線通常比FPGA工作時(shí)鐘速度慢得多,因此我們選擇使用FPGA時(shí)鐘對SPI總線進(jìn)行過采樣。 這使得從機(jī)代碼稍微復(fù)雜一些,但其優(yōu)點(diǎn)是SPI邏輯在FPGA時(shí)鐘域中運(yùn)行,這將使以后的工作變得更容易。
首先是模塊聲明。
module SPI_slave(clk, SCK, MOSI, MISO, SSEL, LED);
input clk;
input SCK, SSEL, MOSI;
output MISO;
output LED;
請注意,我們有“clk”(FPGA 時(shí)鐘)和一個(gè) LED 輸出......一個(gè)不錯(cuò)的小調(diào)試工具。 “clk”需要比 SPI 總線更快。Saxo-L 的默認(rèn)時(shí)鐘為 24MHz,在這里工作正常。
我們使用 FPGA 時(shí)鐘和移位寄存器對 SPI 信號(SCK、SSEL 和 MOSI)進(jìn)行采樣/同步。
// sync SCK to the FPGA clock using a 3-bits shift register
reg [2:0] SCKr;
always @(posedge clk) SCKr <= {SCKr[1:0], SCK};
wire SCK_risingedge = (SCKr[2:1]==2'b01); // now we can detect SCK rising edges
wire SCK_fallingedge = (SCKr[2:1]==2'b10); // and falling edges
// same thing for SSEL
reg [2:0] SSELr;
always @(posedge clk) SSELr <= {SSELr[1:0], SSEL};
wire SSEL_active = ~SSELr[1]; // SSEL is active low
wire SSEL_startmessage = (SSELr[2:1]==2'b10); // message starts at falling edge
wire SSEL_endmessage = (SSELr[2:1]==2'b01); // message stops at rising edge
// and for MOSI
reg [1:0] MOSIr;
always @(posedge clk) MOSIr <= {MOSIr[0], MOSI};
wire MOSI_data = MOSIr[1];
現(xiàn)在,從 SPI 總線接收數(shù)據(jù)很容易。
// we handle SPI in 8-bits format, so we need a 3 bits counter to count the bits as they come in
reg [2:0] bitcnt;reg byte_received; // high when a byte has been received
reg [7:0] byte_data_received;
always @(posedge clk)begin
if(~SSEL_active)
bitcnt <= 3'b000; else
if(SCK_risingedge) begin
bitcnt <= bitcnt + 3'b001;
// implement a shift-left register (since we receive the data MSB first)
byte_data_received <= {byte_data_received[6:0], MOSI_data}; end
end
always @(posedge clk) byte_received <= SSEL_active && SCK_risingedge && (bitcnt==3'b111);
// we use the LSB of the data received to control an LEDreg LED;
always @(posedge clk) if(byte_received) LED <= byte_data_received[0];
最后是傳動(dòng)部分。
reg [7:0] byte_data_sent;
reg [7:0] cnt;
always @(posedge clk) if(SSEL_startmessage) cnt<=cnt+8'h1; // count the messages
always @(posedge clk)if(SSEL_active)begin
if(SSEL_startmessage)
byte_data_sent <= cnt; // first byte sent in a message is the message count else
if(SCK_fallingedge) begin
if(bitcnt==3'b000)
byte_data_sent <= 8'h00; // after that, we send 0s else
byte_data_sent <= {byte_data_sent[6:0], 1'b0};
end
end
assign MISO = byte_data_sent[7]; // send MSB first
// we assume that there is only one slave on the SPI bus
// so we don't bother with a tri-state buffer for MISO
// otherwise we would need to tri-state MISO when SSEL is inactiveendmodule
我們已經(jīng)在 ARM 和 FPGA 之間建立了通信!
運(yùn)行代碼
當(dāng)我們逐步執(zhí)行 ARM 代碼時(shí),我們可以看到 LED 的狀態(tài)發(fā)生變化,以及 FPGA 返回的數(shù)據(jù)。
現(xiàn)在讓我們看看我們是否可以用SPI做一些有用的事情。
SPI 3 - 應(yīng)用
LCD接口
由于我們已經(jīng)知道如何驅(qū)動(dòng)圖形 LCD 面板,尤其是在文本模式下,讓我們嘗試從 LPC 中寫出文本。
從FPGA的角度來看,LCD控制器使用幾個(gè)blockram來保存字體,字符顯示等... 因此,我們只需要確保SPI數(shù)據(jù)進(jìn)入模塊即可。
從ARM的角度來看,將數(shù)據(jù)發(fā)送到LCD模塊的功能稱為“SSP_WriteBlock”。
// function used to write in the LCD blockrams
void SSP_WriteBlock(char* ob, int len, int addr);
void LCD_PutString(char* s, int x, int y)
{
// the blockram that holds the characters starts at address 0, and have 80 characters per line
SSP_WriteBlock(s, strlen(s), x+y*80);
}
void main(void)
{
SSP_init();
LCD_PutString("Hello world!", 0, 0);
LCD_PutString("FPGA4FUN.COM - where FPGAs are fun.", 4, 3);
LCD_PutString("Char set:", 0, 7);
int i; for(i=0; i<128; i++) LCD_PutChar(i, i, 8);
LCD_Cursor_off();
}
使用 LCD 控制器配置 FPGA 并運(yùn)行 ARM 代碼后,我們得到以下結(jié)果:
評論