当前位置:   article > 正文

FPGA:三大协议(UART、IIC、SPI)之SPI_uart spi iic verilog

uart spi iic verilog

摘要:1、本文介绍SPI物理层面连接(通过哪几条线通信),2、本文介绍SPI时序(通过哪种方式进行器件之间交流)。3、提供主机和从机verilog代码。4、仅供自己参考

一、SPI物理层连接

(1)有四根线连接:CS_N(片选信号--主机发出)、miso(从机发出,主机接收)、mosi(主机发出,从机接收)、SCLK(时钟信号,主机发出,作为数据传输的参考时钟)。

(2)结构图:

(3)总结:

1、SPI是一种全双工,同步通信总线,需要四根信号线(可用于FLASH器件的控制);

2、SPI通信有主从之分,可以实现一主多从或者是一主多从,但是不能实现多主多从,因为从器件只有一根cs_n片选信号线,没办法知道是哪个主机发出的控制信号;

3、SCLK是主机产生的,用来同步数据;

4、SPI是以MSB方式传播(高位先发);

二、SPI协议层时序

(1)SPI要记住以下几个知识(必须死记)

1、miso是从机发送,主机接收的信号线;

2、mosi是主机发送,从机接收的信号线;

3、mosi和mosi可以同时工作;

4、SCLK是主机产生,用来同步数据;且!!!:主机接收和发送数据都是在SCLK的跳变沿

从机接收和发送数据也是在SCLK的跳变沿。

5、根据4可知,数据的接收和发送是在SCLK的上升沿,究竟是在上升沿还是下降沿是由SPI的通信模式决定。注!!!:这个通信模式是双发同时遵守的!!,不是通过什么信号线配置的!!。

6、cs_n片选信号是由主机产生的,控制从机是否开始工作。

(2)通信模式:

解释:什么叫通信模式,就是决定数据的采集是在SCLK的上升沿还是下降沿,并且还决定SCLK在空闲时候是什么状态。

1、由CPOL和CPHA决定(这两个就是两个名称,没有什么用,相当于你的名字,没有实际的用处),所以可以有四种通信模式,00 、01、 10、 11。

2、CPOL        :clock polarity 时钟极性

CPHA             :clock phase 时钟相位,表示奇偶边沿采样。

CPHA = 0      :表示在时钟(SCLK)奇数边沿对数据采样,CPHA = 1表示在SCLK时钟偶数边沿采样。

CPOL = 0       :表示SCLK空闲时低电平,CPOL = 1表示SCLK时钟空闲时是高电平。

总结表格:

时序图示意(提供11模式时序图):

 注!!!:以上的CPOL和CPHA是没有任何物理意义,仅仅用他们来表示这种通信模式,也可以用其他名字来代替CPOL和CPHA,是要双方用同一个模式就可以了。

(3)怎么通信:

1、首先要知道:miso、mosi、cs_n在空闲时是高电平,sclk空闲状态由通信模式决定。

2、由起始信号和停止信号表示传输开始和停止:

 3、sclk怎么产生:用计数器记录一段时间实现SCLK的翻转,就产生了sclk脉冲信号;

4、mosi怎么产生:mosi的产生根据通信模式产生,比如在11模式:sclk空闲为高,在上升沿采样。那么在sclk下降沿的时候更具数据改变mosi信号线的高低;

5、miso怎么接收:比如在11模式。在sclk上升沿来临的时候采集miso,把串行数据变成并行数据;

6、定位数据哪一位接收或者发送:用一个计数器记录这一次接受了好多bit数据,并且放在数据对应位上,实现串并转换。

如上图:当bit为0的时候发送data的第7位(MSB),cnt_bit的变化是当sclk记录完一个周期之后加一。

主机verilog代码:

  1. module spi_master(
  2. input clk , //system clock 50MHz
  3. input rst_n , //reset, low valid
  4. input start , //按键控制数据发送
  5. /* input [7:00] send_data , //要发送的数据 */
  6. input spi_miso,//从机发送,主机接收
  7. output send_done , //这一次发送结束
  8. output reg [7:0] rx_data,
  9. output reg sclk, //
  10. output reg spi_mosi,//主发
  11. output reg spi_cs //片选信号
  12. );
  13. //Parameter Declarations
  14. localparam
  15. IDLE = 4'b0001,
  16. START = 4'b0010,
  17. WR_RD_DATA = 4'b0100,
  18. STOP = 4'b1000;
  19. //Internal wire/reg declarations
  20. reg [3:00] state_c, state_n; //
  21. //跳转条件定义
  22. wire idle2start;
  23. wire start2wr_rd_data;
  24. wire wr_rd_data2stop;
  25. wire stop2dile;
  26. reg [7:0] data_tmp;//输入数据暂存
  27. reg [7:0] data_tmp_rx;
  28. reg [5:00] cnt_sclk ; //Counter
  29. wire add_cnt_sclk ; //Counter Enable
  30. wire end_cnt_sclk ; //Counter Reset
  31. reg [3:00] cnt_bit ; //Counter
  32. wire add_cnt_bit ; //Counter Enable
  33. wire end_cnt_bit ; //Counter Reset
  34. //Module instantiations , self-build module
  35. //缓存数据
  36. always @(posedge clk or negedge rst_n)begin
  37. if(!rst_n)begin
  38. data_tmp <= 8'd20;
  39. end
  40. else if(start)begin
  41. data_tmp <= data_tmp + 3;
  42. end
  43. else begin
  44. data_tmp <= data_tmp;
  45. end
  46. end //always end
  47. //Logic Description
  48. //第一段设置状态转移空间
  49. always @(posedge clk or negedge rst_n)begin
  50. if(!rst_n)begin
  51. state_c <= IDLE;
  52. end
  53. else begin
  54. state_c <= state_n;
  55. end
  56. end //always end
  57. //第二段、组合逻辑定义状态转移
  58. always@(*)begin
  59. case(state_c)
  60. IDLE:begin
  61. if(idle2start)begin
  62. state_n = START;
  63. end
  64. else begin
  65. state_n = state_c;
  66. end
  67. end
  68. START:begin
  69. if(start2wr_rd_data)begin
  70. state_n = WR_RD_DATA;
  71. end
  72. else begin
  73. state_n = state_c;
  74. end
  75. end
  76. WR_RD_DATA:begin
  77. if(wr_rd_data2stop)begin
  78. state_n = STOP;
  79. end
  80. else begin
  81. state_n = state_c;
  82. end
  83. end
  84. STOP:begin
  85. if(stop2dile)begin
  86. state_n = IDLE;
  87. end
  88. else begin
  89. state_n = state_c;
  90. end
  91. end
  92. default: begin
  93. state_n = IDLE;
  94. end
  95. endcase
  96. end //always end
  97. assign idle2start = state_c == IDLE && start;
  98. assign start2wr_rd_data = state_c == START && spi_cs == 0;
  99. assign wr_rd_data2stop = state_c == WR_RD_DATA && end_cnt_bit ;
  100. assign stop2dile = state_c == STOP && spi_cs == 1;
  101. //计数器
  102. //cnt_sclk[5:00]
  103. always @(posedge clk or negedge rst_n)begin
  104. if(!rst_n)begin
  105. cnt_sclk <= 6'd0;
  106. end
  107. else if(add_cnt_sclk)begin
  108. if(end_cnt_sclk)begin
  109. cnt_sclk <= 6'd0;
  110. end
  111. else begin
  112. cnt_sclk <= cnt_sclk + 1'b1;
  113. end
  114. end
  115. else begin
  116. cnt_sclk <= 6'd0;
  117. end
  118. end
  119. assign add_cnt_sclk = state_c == WR_RD_DATA;
  120. assign end_cnt_sclk = add_cnt_sclk && cnt_sclk >= 9; //周期是10个时钟周期,当cnt_sclk加到5的时候翻转
  121. //cnt_bit
  122. always @(posedge clk or negedge rst_n)begin
  123. if(!rst_n)begin
  124. cnt_bit <= 4'd0;
  125. end
  126. else if(wr_rd_data2stop)begin
  127. cnt_bit <= 4'd0;
  128. end
  129. else if(add_cnt_bit)begin
  130. if(end_cnt_bit)begin
  131. cnt_bit <= 4'd0;
  132. end
  133. else begin
  134. cnt_bit <= cnt_bit + 1'b1;
  135. end
  136. end
  137. else begin
  138. cnt_bit <= cnt_bit;
  139. end
  140. end
  141. assign add_cnt_bit = state_c == WR_RD_DATA && end_cnt_sclk; //在计数器为0的时候加一,表示数据变化是在下降沿,采集是在上升沿
  142. assign end_cnt_bit = add_cnt_bit && cnt_bit >= 7;
  143. //第三段,定义状态机输出情况,可以时序逻辑,也可以组合逻辑
  144. always @(posedge clk or negedge rst_n)begin
  145. if(!rst_n)begin
  146. sclk <= 1'b1;
  147. spi_cs <= 1'b1;
  148. spi_mosi <= 1'b1;
  149. rx_data <= 8'd0;
  150. end
  151. else begin
  152. case(state_c)
  153. IDLE :begin//全高
  154. sclk <= 1'b1;
  155. spi_cs <= 1'b1;
  156. spi_mosi <= 1'b1;
  157. end
  158. START :begin
  159. spi_cs <= 1'b0;
  160. sclk <= 1'b1;
  161. spi_mosi <= 1'b1;
  162. end
  163. WR_RD_DATA :begin
  164. //发送
  165. spi_cs <= 1'b0;
  166. if(cnt_sclk < 5)begin
  167. sclk <= 1'b0;
  168. end
  169. else begin
  170. sclk <= 1'b1;
  171. end
  172. spi_mosi <= data_tmp[7-cnt_bit];
  173. //接收
  174. if(sclk == 1'b1)begin
  175. data_tmp_rx[7-cnt_bit] <= spi_miso;
  176. end
  177. end
  178. STOP :begin
  179. rx_data <= data_tmp_rx;
  180. sclk <= 1'b1;
  181. spi_cs <= 1'b1;
  182. spi_mosi <= 1'b1;
  183. end
  184. default:;
  185. endcase
  186. end
  187. end //always end
  188. assign send_done = stop2dile;
  189. endmodule

 从机verilog代码:

  1. module spi_slave(
  2. input sclk , //
  3. input spi_cs , //
  4. input spi_mosi,//接收的数据
  5. output reg spi_miso, //输出的无数据
  6. output [7:0] rx_data,
  7. output data_vld//信号有效
  8. );
  9. //Parameter Declarations
  10. reg [7:0] rx_data_tmp;
  11. reg [2:0] cnt_bit;
  12. reg [7:0] tx_data = 8'd0;
  13. //Internal wire/reg declarations
  14. //Module instantiations , self-build module
  15. //Logic Description
  16. always @(posedge sclk)begin
  17. if(spi_cs == 1'b1)begin
  18. rx_data_tmp <= 8'd0;
  19. spi_miso <= 1'b1;
  20. cnt_bit <= 3'b111;
  21. end
  22. else begin
  23. cnt_bit = cnt_bit + 1;
  24. if(sclk)begin
  25. rx_data_tmp[7-cnt_bit] <= spi_mosi;
  26. end
  27. else ;
  28. if(cnt_bit == 1'b1)begin
  29. tx_data <= tx_data + 1;
  30. end
  31. else ;
  32. end
  33. end
  34. always @(*)begin
  35. if(spi_cs == 1'b1)begin
  36. spi_miso = 1'b1;
  37. end
  38. else begin
  39. if(sclk == 1'b0)begin
  40. spi_miso = tx_data[7 - cnt_bit];
  41. end
  42. else begin
  43. spi_miso = spi_miso;
  44. end
  45. end
  46. end
  47. assign rx_data = rx_data_tmp;
  48. assign data_vld = cnt_bit == 3'd7;
  49. endmodule

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

闽ICP备14008679号