当前位置:   article > 正文

SPI总线协议

spi总线协议

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

SPI总线的定义

SPI总线工作方式

SPI驱动Verilog实现

总结


SPI总线的定义

SPI(Serial Peripheral Interface)是一种同步串行通信协议,用于在微控制器、传感器、存储器等外设之间进行数据交换。SPI总线是一种全双工的通信方式,它由一个主设备(Master)和一个或多个从设备(Slave)组成。

spi总线的特点:

1、传输方式:SPI总线使用同步的时钟信号进行通信,主设备控制时钟信号的频率和极性。数据在时钟的边沿上升或下降时转移。SPI总线支持全双工通信,意味着主设备和从设备可以同时发送和接收数据。

2、线路结构:SPI总线具有四条线路:时钟线(SCLK),主设备输出的数据线(MOSI),主设备接收的数据线(MISO)和片选线(CS)。

        时钟线(SCLK):时钟信号线,由主机发出,不同的主机可能具有不同的时钟速率,也就是通信频率。

        主设备输出的数据线(MOSI):主机输出数据,从机接收数据。(M代表Master,S代表Slave)

        主设备接收的数据线(MISO):主机接收数据,从机输出数据。

        片选线(CS):由主机选择与某一从机进行通讯。常为低电平有效

一主一从模式:

一主多从模式:

(PS:借用一下大佬孤独的单刀的图,侵权请联系我,会立刻删除,原文章链接:FPGA实现的SPI协议(一)----SPI驱动_fpga spi_孤独的单刀的博客-CSDN博客)

SPI总线工作方式

SPI的工作模式通常由两个因素决定:一是时钟极性(CPOL,Clock Polarity),二是时钟相位(Clock Phase)。其中时钟极性决定了SCLK在空闲时是低电平还是高电平,时钟相位决定了在什么沿采集数据。

(PS:借用一下大佬孤独的单刀的图,侵权请联系我,会立刻删除,原文章链接:)

        

  • 模式0:CPOL=0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
  • 模式1:CPOL=0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  • 模式2:CPOL=1,CPHA=O。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  • 模式3:CPOL=1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换

通过象限可以将这四种模式进行简要概括:

SPI总线的优缺点:

优点:

        1、灵活:SPI可以使用不同的工作模式,以满足不同场景的需要。SPI主设备可以配置不同的时钟频率,灵活可变。

        2、高速:相比与串口(UART)、IIC接口,SPI具有很高的通信频率

        3、简单的线路结构,只有SCLK、MOSI、MISO、CS 4条线路

        4、不限于8位,一次可以传输任意大小的字

缺点:

        1、仅支持一个主设备,从设备被动响应,不支持从设备主动传输数据。

        2、虽然SPI总线线路相对简单,但每个外设都需要至少四条线路,因此在连接多个外设时,线路数量会增加。(每增加一个从机都会增加一个片选信号)

        3、主/从机没有应答信号

--------------------------------------------------------------------------------------

SPI驱动Verilog实现

说明:模式0,一主一从模式,使用一段式状态机实现。

(PS:不使用状态机的SPI驱动程序请阅读大佬孤独的单刀的文章:

FPGA实现的SPI协议(一)----SPI驱动_fpga spi_孤独的单刀的博客-CSDN博客)

主机代码:

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/09/01 17:04:04
  7. // Design Name:
  8. // Module Name: SPI_derive
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. // SPI总线主机驱动程序
  20. //
  21. module SPI_derive #(
  22. parameter integer DW = 8
  23. )
  24. (
  25. input sys_clk , //系统时钟50M
  26. input sys_rst , //复位信号低电平有效
  27. input[DW-1:0] din , //要发送的数据
  28. input spi_start , //SPI驱动程序启动信号,一个高电平
  29. input spi_end , //SPI驱动程序结束信号
  30. output reg [DW-1:0] dout , //接收的数据
  31. output reg rec_over , //单个字节接收完毕信号
  32. output reg send_over , //单个字节发送结束信号
  33. output reg sclk , //SPI程序的SCLK 本程序是对系统时钟的2分频
  34. output reg cs_n , //SPI片选信号
  35. output reg mosi , //SPI输出,给从机发送数据
  36. input miso //SPI输入,从机发送过来的数据
  37. );
  38. localparam idle = 3'b001 ;
  39. localparam send_recv = 3'b010 ;
  40. localparam over = 3'b100 ;
  41. reg[2:0] state ;
  42. reg[DW-1:0] recv_data = 'd0 ;
  43. reg clk_2d = 'd0 ;
  44. reg[2:0] send_cnt = 'd0 ;
  45. reg[2:0] recv_cnt = 'd0 ;
  46. reg[1:0] cs_n_cnt = 'd0 ;
  47. always@(posedge sys_clk,negedge sys_rst)begin
  48. if(!sys_rst == 1)begin
  49. state <= idle;
  50. cs_n <= 1'b1;
  51. sclk <= 1'b0;
  52. mosi <= 1'b1;
  53. send_cnt<= 'd0 ;
  54. recv_cnt<= 'd0 ;
  55. dout <= 'd0 ;
  56. send_over <= 1'b0;
  57. rec_over <= 1'b0;
  58. cs_n_cnt <= 2'b0;
  59. end
  60. else begin
  61. case(state)
  62. idle:
  63. begin
  64. if(spi_start == 1)begin
  65. state <= send_recv;
  66. end
  67. else begin
  68. state <= state;
  69. end
  70. end
  71. send_recv: //send and receive
  72. begin
  73. if(spi_end == 1)begin
  74. state <= over;
  75. end
  76. else begin
  77. state <= state;
  78. end
  79. if(cs_n_cnt == 2 || sclk == 1)begin //发送 相对于CS的下降沿,发送延迟1个SCLK周期
  80. mosi <= din[DW-1-send_cnt];
  81. if(send_cnt < 3'b111)begin
  82. send_cnt <= send_cnt + 1 ;
  83. end
  84. else begin
  85. send_cnt <= 3'b0;
  86. end
  87. end
  88. else begin
  89. mosi <= mosi;
  90. send_cnt <= send_cnt;
  91. end
  92. if(send_cnt == 3'b111 && sclk == 1)begin
  93. send_over <= 1'b1;
  94. end
  95. else begin
  96. send_over <= 1'b0;
  97. end
  98. ///
  99. if(cs_n_cnt > 2 && sclk == 0)begin //接收
  100. recv_data[DW-1-recv_cnt] <= miso;
  101. if(recv_cnt < 3'b111)begin
  102. recv_cnt <= recv_cnt + 1;
  103. rec_over <= 1'b0;
  104. end
  105. else begin
  106. rec_over <= 1'b1;
  107. recv_cnt <= 3'b0;
  108. end
  109. end
  110. else begin
  111. recv_data<= recv_data;
  112. recv_cnt <= recv_cnt;
  113. rec_over <= rec_over;
  114. end
  115. if(recv_cnt == 3'b000 && sclk == 1)begin
  116. rec_over <= 1'b1;
  117. dout <= recv_data;
  118. end
  119. else begin
  120. rec_over <= 1'b0;
  121. dout <= 'd0;
  122. end
  123. cs_n <= 1'b0;
  124. if(cs_n_cnt < 3)begin
  125. cs_n_cnt <= cs_n_cnt + 1;
  126. end
  127. else begin
  128. cs_n_cnt <= cs_n_cnt;
  129. end
  130. if(cs_n_cnt < 3)begin //空余出1个的SCLK周期,使得主从机收发的数据对其
  131. sclk <= sclk;
  132. end
  133. else begin
  134. sclk <= !sclk;
  135. end
  136. end
  137. over:
  138. begin
  139. state <= idle;
  140. cs_n <= 1'b1;
  141. sclk <= 1'b0;
  142. mosi <= 1'b1;
  143. send_cnt <= 'd0 ;
  144. recv_cnt<= 'd0 ;
  145. dout <= 'd0 ;
  146. send_over <= 1'b0;
  147. rec_over <= 1'b0;
  148. cs_n_cnt <= 2'b0;
  149. end
  150. endcase
  151. end
  152. end
  153. endmodule

从机代码:

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/09/01 17:04:04
  7. // Design Name:
  8. // Module Name: SPI_derive
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. // SPI总线从机驱动程序
  20. //
  21. module spi_slave #(
  22. parameter integer DW = 8
  23. )
  24. (
  25. input sys_clk , //系统时钟50M
  26. input sys_rst , //复位信号低电平有效
  27. input[DW-1:0] din , //要发送的数据
  28. output reg spi_start , //SPI驱动程序启动信号,通知后级SPI程序已经启动
  29. output reg spi_end , //SPI驱动程序结束信号,通知后级SPI程序已经结束
  30. output reg [DW-1:0] dout , //接收的数据
  31. output reg rec_over , //单个字节接收完毕信号
  32. output reg send_over , //单个字节发送结束信号
  33. input sclk , //SPI程序的SCLK 本程序是对系统时钟的2分频
  34. input cs_n , //SPI片选信号
  35. input mosi , //SPI输入,主机发送过来的数据
  36. output reg miso //SPI输出,从机发送的数据
  37. );
  38. localparam idle = 3'b001 ;
  39. localparam send_recv = 3'b010 ;
  40. localparam over = 3'b100 ;
  41. reg[2:0] state ;
  42. reg[DW-1:0] recv_data = 'd0 ;
  43. reg clk_2d = 'd0 ;
  44. reg[2:0] send_cnt = 'd0 ;
  45. reg[2:0] recv_cnt = 'd0 ;
  46. reg sd_rc_flag = 'd0 ;
  47. always@(posedge sys_clk,negedge sys_rst)begin
  48. if(!sys_rst == 1)begin
  49. state <= idle;
  50. send_cnt<= 'd0 ;
  51. recv_cnt<= 'd0 ;
  52. dout <= 'd0 ;
  53. send_over <= 1'b0;
  54. rec_over <= 1'b0;
  55. sd_rc_flag <= 1'b0;
  56. spi_start<= 1'b0;
  57. spi_end <= 1'b0;
  58. end
  59. else begin
  60. case(state)
  61. idle:
  62. begin
  63. send_cnt<= 'd0 ;
  64. recv_cnt<= 'd0 ;
  65. dout <= 'd0 ;
  66. send_over <= 1'b0;
  67. rec_over <= 1'b0;
  68. sd_rc_flag <= 1'b0;
  69. spi_end <= 1'b0;
  70. if(cs_n == 0)begin
  71. state <= send_recv;
  72. spi_start<= 1'b1;
  73. end
  74. else begin
  75. state <= state;
  76. spi_start<= spi_start;
  77. end
  78. end
  79. send_recv: //send and receive
  80. begin
  81. if(cs_n == 1)begin
  82. state <= over;
  83. end
  84. else begin
  85. state <= state;
  86. end
  87. if(sd_rc_flag == 0 || sclk == 1)begin //发送
  88. miso <= din[DW-1-send_cnt];
  89. if(send_cnt < 3'b111)begin
  90. send_cnt <= send_cnt + 1 ;
  91. end
  92. else begin
  93. send_cnt <= 3'b000;
  94. end
  95. end
  96. else begin
  97. send_cnt <= send_cnt;
  98. end
  99. if(send_cnt == 3'b111 && sclk == 1)begin
  100. send_over <= 1'b1;
  101. end
  102. else begin
  103. send_over <= 1'b0;
  104. end
  105. ///
  106. if(sd_rc_flag == 1 && sclk == 0)begin //接收
  107. recv_data[DW-1-recv_cnt] <= mosi;
  108. if(recv_cnt < 3'b111)begin
  109. recv_cnt <= recv_cnt + 1;
  110. rec_over <= 1'b0;
  111. end
  112. else begin
  113. rec_over <= 1'b1;
  114. recv_cnt <= 3'b000;
  115. end
  116. end
  117. else begin
  118. recv_data<= recv_data;
  119. recv_cnt <= recv_cnt;
  120. rec_over <= rec_over;
  121. end
  122. if(recv_cnt == 3'b000 && sclk == 1)begin
  123. rec_over <= 1'b1;
  124. dout <= recv_data;
  125. end
  126. else begin
  127. rec_over <= 1'b0;
  128. dout <= 'd0;
  129. end
  130. spi_start<= 1'b0;
  131. spi_end <= 1'b0;
  132. sd_rc_flag <= 1'b1;
  133. end
  134. over:
  135. begin
  136. state <= idle;
  137. send_cnt <= 'd0 ;
  138. recv_cnt<= 'd0 ;
  139. dout <= 'd0 ;
  140. send_over <= 1'b0;
  141. rec_over <= 1'b0;
  142. sd_rc_flag <= 1'b0;
  143. spi_start<= 1'b0;
  144. spi_end <= 1'b1;
  145. end
  146. endcase
  147. end
  148. end
  149. endmodule

 主机仿真波形

从机仿真波形

Testbench源代码:

  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 2023/09/02 10:38:32
  7. // Design Name:
  8. // Module Name: tb_spi_derive
  9. // Project Name:
  10. // Target Devices:
  11. // Tool Versions:
  12. // Description:
  13. //
  14. // Dependencies:
  15. //
  16. // Revision:
  17. // Revision 0.01 - File Created
  18. // Additional Comments:
  19. //
  20. //
  21. module tb_spi_derive();
  22. reg sys_clk ;
  23. reg sys_rst ;
  24. reg[7:0] din ;
  25. reg dval_i ;
  26. reg spi_start ;
  27. reg spi_end ;
  28. wire[7:0] dout ;
  29. wire dval_o ;
  30. wire rec_over ;
  31. wire send_over ;
  32. wire sclk ;
  33. wire cs_n ;
  34. wire mosi ;
  35. wire miso ;
  36. reg[7:0] din_s ;
  37. wire spi_start_s ;
  38. wire spi_end_s ;
  39. wire[7:0] dout_s ;
  40. wire rec_over_s ;
  41. wire send_over_s ;
  42. reg[3:0] send_cnt ;
  43. reg[3:0] send_cnt_s ;
  44. SPI_derive #(
  45. .DW (8 )
  46. )
  47. u_SPI_derive(
  48. .sys_clk (sys_clk ) ,
  49. .sys_rst (sys_rst ) ,
  50. .din (din ),
  51. .spi_start (spi_start),
  52. .spi_end (spi_end ),
  53. .dout (dout ),
  54. .rec_over (rec_over ),
  55. .send_over (send_over),
  56. .sclk (sclk ),
  57. .cs_n (cs_n ),
  58. .mosi (mosi ),
  59. .miso (miso )
  60. );
  61. spi_slave #(
  62. .DW (8 )
  63. )
  64. u_spi_slave(
  65. .sys_clk (sys_clk ) ,
  66. .sys_rst (sys_rst ) ,
  67. .din (din_s ),
  68. .spi_start (spi_start_s),
  69. .spi_end (spi_end_s ),
  70. .dout (dout_s ),
  71. .rec_over (rec_over_s ),
  72. .send_over (send_over_s),
  73. .sclk (sclk ),
  74. .cs_n (cs_n ),
  75. .mosi (mosi ),
  76. .miso (miso )
  77. );
  78. initial begin
  79. sys_clk = 1;
  80. sys_rst = 0;
  81. spi_start = 1'b0;
  82. din = 8'd0;
  83. dval_i = 1'b0;
  84. spi_end = 1'b0;
  85. #80
  86. sys_rst = 1;
  87. din_s = 8'h51;
  88. #30
  89. spi_start = 1'b1; din = 8'b00110011;
  90. #20
  91. spi_start = 1'b0;
  92. end
  93. always@(posedge sys_clk,negedge sys_rst)
  94. begin
  95. if(!sys_rst)begin
  96. din <= 8'd0;
  97. spi_end <= 1'b0;
  98. send_cnt <= 4'b0;
  99. end
  100. else if(send_over == 1) begin
  101. din <= din + 8'b01;
  102. if(send_cnt < 10)begin
  103. send_cnt <= send_cnt + 1;
  104. spi_end <= 1'b0;
  105. end
  106. else begin
  107. send_cnt <= 4'b0;
  108. spi_end <= 1'b1;
  109. end
  110. end
  111. else begin
  112. spi_end <= 1'b0;
  113. send_cnt <= send_cnt;
  114. din <= din;
  115. end
  116. end
  117. always@(posedge sys_clk,negedge sys_rst)
  118. begin
  119. if(!sys_rst)begin
  120. din_s <= 8'b0;
  121. send_cnt_s<= 4'b0;
  122. end
  123. else if(spi_start_s == 1|| send_over_s ==1) begin
  124. din_s <= din_s + 8'b01;
  125. if(send_cnt_s < 10)begin
  126. send_cnt_s <= send_cnt_s + 1;
  127. end
  128. else begin
  129. send_cnt_s <= 4'b0;
  130. end
  131. end
  132. else begin
  133. send_cnt_s <= send_cnt_s;
  134. din_s <= din_s;
  135. end
  136. end
  137. initial begin
  138. forever #10 sys_clk = !sys_clk;
  139. end
  140. endmodule


总结

本文只是对SPI总线的模式0做初步实现,如有错误欢迎指正。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/746246
推荐阅读
相关标签
  

闽ICP备14008679号