1、基于FPGA的SPI接口设计 SPI是一种在FPGA和其他芯片之间传输数据的简单有效的接口方式。 SPI项目 第一部分:什么是SPI 第二部分:SPI的简单实现 第三部分:应用 第一部分:什么是SPI SPI是允许一个器件同其他一个或多个器件进行通讯的简单接口。 SPI是什么样的? 首先让我们来看看两个芯片之间的SPI接口是如何连接的。 在两个芯片时间通讯时,SPI需要4条连线。 正如你所看到的,他们是SCK、MISO、MOSI以及SSEL。其中一个芯片叫做主控芯片,另一个叫从芯片。 SPI基础 基本特点: 1.同步 2.串行 3.全双工 4.非即插即
2、用 5.一主多从 更多细节: 1.同步时钟有主控芯片产生,每个时钟传输一位数据 2.数据在传输前,首先许要进行并转串,才能用一条线传输 3.两条数据线,一条输入、一条输出 4.主从双方有关于SPI传输的先验知识,如比特顺序、数据长度等 5.数据传输有主控芯片发起,每次只与一个从芯片通讯 SPI是一种同步全双工的通讯接口,每个时钟在两条数据线上各传输一比特数据。 简单的传输 假设在主从芯片之间进行的是8位长度的,高位数据在前的SPI传输,则单个字节的传输在波形上看起来是这样的。 MOSI是主输出线,而MISO则是从输出线。由于SPI是全双工的,所以在时钟沿上两条线同时传
3、输数据。MOSI将数据从主控芯片传输至从芯片,MISO则将从芯片的数据传输到主控芯片。 详细的说是这样的: 1,首先主控芯片使能相应的SSEL信号,通知相应的从芯片数据传输要开始了; 2,主控芯片产生8个SPI时钟周期,并将数据在每个时钟沿发送出去,同时从芯片在也每个时钟沿将数据发送到MISO线上。 3,主控芯片撤销SSEL信号,一次SPI传输结束 多个从芯片的情况 通过扩展SSEL信号,一个主控芯片可以和多个从芯片进行SPI通讯。下图是有三个从芯片的情况: 主控芯片有3条SSEL线,每次只使能条,和其中一个从芯片进行SPI通讯。由于所有芯片的MISO都连接在一起,所以不
4、允许同时有多个从芯片驱动MISO线。 SPI有多快 SPI可以很轻易的做到几Mbps的传输速率,这就意味着SPI可以用来进行非压缩的音频和和压缩的视频信号传输。 相关链接:Wikipedia的SPI接口总线 第二部分 SPI接口的FPGA简单实现 ARM 处理器 为了检验我们刚学得的关于SPI的知识,我们使用一个带有SPI接口的ARM7板和FPGA板,板间由SPI总线连接。 ARM处理器作为主控器,FPGA作为SPI从机。下图为他们之间的连接方式。 SPI 主控机 - C语言程序 使用ARM的SPI接口,只需要初始化一些寄存器,然后对SPI接口进行写数和读数操作即
5、可让SPI接口自动完成发送和接收数据。 void main(void) { // 初始化SPI接口,因处理器而异 SSP0CPSR = 0x02; SSP0CR0 = 0x07; SSP0CR1 = 0x02; PINSEL1 = 0x2A8; while(1) { // 发送两个字节 SSP0DR = 0x55; SSP0DR = 0x54; // 等待数据发送完毕 while(!(SSP0SR & 0x01)); // 读数 int data1 = SSP0DR;
6、 int data2 = SSP0DR; // ... } } SPI 从机 - HDL 代码 再来考虑FPGA端SPI从机的设计。 Since the SPI bus is typically much slower than the FPGA operating clock speed, we choose to over-sample the SPI bus using the FPGA clock. That makes the slave code slightly more complicated, but has the advantage of ha
7、ving the SPI logic run in the FPGA clock domain, which will make things easier afterwards. First the module declaration. module SPI_slave(clk, SCK, MOSI, MISO, SSEL, LED); input clk; input SCK, SSEL, MOSI; output MISO; output LED; Note that we have "clk" (the FPGA clock) and an LED output...
8、a nice little debug tool. "clk" needs to be faster than the SPI bus. Saxo-L has a default clock of 24MHz, which works fine here. We sample/synchronize the SPI signals (SCK, SSEL and MOSI) using the FPGA clock and shift registers. // sync SCK to the FPGA clock using a 3-bits shift register reg [
9、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}
10、 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};
11、 wire MOSI_data = MOSIr[1]; Now receiving data from the SPI bus is easy. reg [2:0] bitcnt; // we handle SPI in 8-bits format, so we need a 3 bits counter to count the bits as they come in reg byte_received; // high when a byte has been received reg [7:0] byte_data_received; always @(posedge
12、clk) begin if(~SSEL_active) bitcnt <= 3'b000; else if(SCK_risingedge) begin bitcnt <= bitcnt + 3'b001; byte_data_received <= {byte_data_received[6:0], MOSI_data}; // implement a shift-left register (since we receive the data MSB first) end end always @(posedge clk)
13、byte_received <= SSEL_active && SCK_risingedge && (bitcnt==3'b111); // we use the LSB of the data received to control an LED reg LED; always @(posedge clk) if(byte_received) LED <= byte_data_received[0]; Finally the transmission part. reg [7:0] byte_data_sent; reg [7:0] cnt; always @(posedge
14、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_
15、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 w
16、ould need to tri-state MISO when SSEL is inactive endmodule We have established communication between the ARM and the FPGA! Running the code As we step through the ARM code, we can see the LED changing state, and the data returned by the FPGA. Now let's see if we can do useful things with SPI.






