SPI 2 - A simple implementation

举报
李锐博恩 发表于 2021/07/15 04:06:24 2021/07/15
【摘要】 ARM processor To get an opportunity to test our newly acquired SPI knowledge, we use a Saxo-L board. It has an ARM7 processor (LPC2138) and a Cyclone FPGA (EP1C3), connected by a...

ARM processor

To get an opportunity to test our newly acquired SPI knowledge, we use a Saxo-L board. It has an ARM7 processor (LPC2138) and a Cyclone FPGA (EP1C3), connected by a SPI bus.

The ARM is used as a SPI master, while the FPGA is used as a SPI slave. The Saxo-L ARM processor has actually two SPI interfaces, one called SPI0, and a more advanced one called SPI1/SSP. They are both equally easy to use. We are using SPI1/SSP on Saxo-L, as it is pre-wired on the board.

SPI master - C ARM code

Using SSP is just a matter of initializing a few registers, and then writing/reading data to send/receive automatically.


  
  1. void main(void)
  2. {
  3. // initialize SSP
  4. SSP0CPSR = 0x02; // SSP max speed
  5. SSP0CR0 = 0x07; // SSP max speed, 8 bits
  6. SSP0CR1 = 0x02; // SSP master mode
  7. PINSEL1 = 0x2A8; // SSP mode for pins P0.17 to P0.20
  8. while(1)
  9. {
  10. // send two bytes
  11. SSP0DR = 0x55; // one nice thing about the SSP is that it has a 8-words deep FIFO
  12. SSP0DR = 0x54; // so here we write the data to be sent without worrying
  13. // now wait until both bytes are sent
  14. while(!(SSP0SR & 0x01));
  15. // now we can read the two bytes received... and do anything with them
  16. int data1 = SSP0DR;
  17. int data2 = SSP0DR;
  18. // ...
  19. }
  20. }

SPI slave - HDL FPGA code

Now for the SPI slave in the FPGA.

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 having the SPI logic run in the FPGA clock domain, which will make things easier afterwards.

First the module declaration.


  
  1. module SPI_slave(clk, SCK, MOSI, MISO, SSEL, LED);
  2. input clk;
  3. input SCK, SSEL, MOSI;
  4. output MISO;
  5. output LED;

Note that we have "clk" (the FPGA clock) and an LED output... 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.


  
  1. // sync SCK to the FPGA clock using a 3-bits shift register
  2. reg [2:0] SCKr; always @(posedge clk) SCKr <= {SCKr[1:0], SCK};
  3. wire SCK_risingedge = (SCKr[2:1]==2'b01); // now we can detect SCK rising edges
  4. wire SCK_fallingedge = (SCKr[2:1]==2'b10); // and falling edges
  5. // same thing for SSEL
  6. reg [2:0] SSELr; always @(posedge clk) SSELr <= {SSELr[1:0], SSEL};
  7. wire SSEL_active = ~SSELr[1]; // SSEL is active low
  8. wire SSEL_startmessage = (SSELr[2:1]==2'b10); // message starts at falling edge
  9. wire SSEL_endmessage = (SSELr[2:1]==2'b01); // message stops at rising edge
  10. // and for MOSI
  11. reg [1:0] MOSIr; always @(posedge clk) MOSIr <= {MOSIr[0], MOSI};
  12. wire MOSI_data = MOSIr[1];

Now receiving data from the SPI bus is easy.


  
  1. // we handle SPI in 8-bits format, so we need a 3 bits counter to count the bits as they come in
  2. reg [2:0] bitcnt;
  3. reg byte_received; // high when a byte has been received
  4. reg [7:0] byte_data_received;
  5. always @(posedge clk)
  6. begin
  7. if(~SSEL_active)
  8. bitcnt <= 3'b000;
  9. else
  10. if(SCK_risingedge)
  11. begin
  12. bitcnt <= bitcnt + 3'b001;
  13. // implement a shift-left register (since we receive the data MSB first)
  14. byte_data_received <= {byte_data_received[6:0], MOSI_data};
  15. end
  16. end
  17. always @(posedge clk) byte_received <= SSEL_active && SCK_risingedge && (bitcnt==3'b111);
  18. // we use the LSB of the data received to control an LED
  19. reg LED;
  20. always @(posedge clk) if(byte_received) LED <= byte_data_received[0];

Finally the transmission part.


  
  1. reg [7:0] byte_data_sent;
  2. reg [7:0] cnt;
  3. always @(posedge clk) if(SSEL_startmessage) cnt<=cnt+8'h1; // count the messages
  4. always @(posedge clk)
  5. if(SSEL_active)
  6. begin
  7. if(SSEL_startmessage)
  8. byte_data_sent <= cnt; // first byte sent in a message is the message count
  9. else
  10. if(SCK_fallingedge)
  11. begin
  12. if(bitcnt==3'b000)
  13. byte_data_sent <= 8'h00; // after that, we send 0s
  14. else
  15. byte_data_sent <= {byte_data_sent[6:0], 1'b0};
  16. end
  17. end
  18. assign MISO = byte_data_sent[7]; // send MSB first
  19. // we assume that there is only one slave on the SPI bus
  20. // so we don't bother with a tri-state buffer for MISO
  21. // otherwise we would need to tri-state MISO when SSEL is inactive
  22. 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.

转:https://www.fpga4fun.com/SPI2.html

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/90020591

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。