首页 > 其他分享 >FPGA 实现SPI 主机双工通信 CS前后肩可调 操作时钟频率可调 ,SPI模式可调,传输位宽可调(最大32位)

FPGA 实现SPI 主机双工通信 CS前后肩可调 操作时钟频率可调 ,SPI模式可调,传输位宽可调(最大32位)

时间:2023-11-29 19:11:06浏览次数:36  
标签:scl 双工 spi 可调 SPI wire CS reg

  1 //testbench
  2 `timescale 1ns/1ns
  3 module  lcd_spi_m_tb();
  4  reg rst_n_i;    
  5  reg spi_clkx_i; 
  6  reg [31:0] spi_data_i;
  7  reg spi_start;  
  8  reg spi_miso_r1,spi_miso_r2;   
  9  wire spi_miso_i;
 10  wire [0:0]  spi_done;
 11  wire [0:0]  spi_busy;
 12  wire [0:0]  spi_cs_o;
 13  wire [0:0]  spi_scl_o;
 14  wire [0:0]  spi_mosi_o;
 15  wire [31:0] spi_data_o;
 16  
 17  localparam CPOL=1'B0 ;
 18  localparam CPHA=1'B1 ;
 19 
 20 
 21 
 22 always #50 spi_clkx_i<=~spi_clkx_i;
 23 initial begin
 24          rst_n_i=0;
 25          spi_clkx_i=0;
 26          #200; 
 27          spi_start=0;        
 28          rst_n_i=1;
 29          @(posedge spi_clkx_i)
 30          spi_data_i=16'h8121;
 31          spi_start=1;
 32          @(posedge spi_clkx_i)
 33          spi_start=0;
 34          @(posedge spi_done)
 35          #200;
 36          @(posedge spi_clkx_i)
 37          spi_data_i=16'h0003;
 38          spi_start=1;
 39          @(posedge spi_clkx_i)
 40          spi_start=0;
 41          @(posedge spi_done) 
 42          #200;
 43          @(posedge spi_clkx_i)
 44          spi_data_i=16'hF227;
 45          spi_start=1;
 46          @(posedge spi_clkx_i)
 47          spi_start=0;
 48          @(posedge spi_done) 
 49          #200;
 50          @(posedge spi_clkx_i)
 51          spi_data_i=16'hA31B;
 52          spi_start=1;
 53          @(posedge spi_clkx_i)
 54          spi_start=0;
 55          @(posedge spi_done) 
 56          $stop;
 57         end
 58         
 59 always @(negedge spi_scl_o or negedge rst_n_i )//要根据CPOL CPHA配置 spi_scl_o采样极性
 60      begin
 61          if(rst_n_i==1'b0)
 62              spi_miso_r1<=1'b1;
 63          else if (spi_cs_o==1'b0 )
 64              spi_miso_r1<=~spi_miso_r1;                         
 65          else
 66              spi_miso_r1<=1'b0;    
 67      end  
 68      
 69 always @(posedge spi_scl_o or negedge rst_n_i )//要根据CPOL CPHA配置 spi_scl_o采样极性
 70      begin
 71          if(rst_n_i==1'b0)
 72              spi_miso_r2<=1'b1;
 73          else if (spi_cs_o==1'b0 )
 74              spi_miso_r2<=~spi_miso_r2;                         
 75          else
 76              spi_miso_r2<=1'b0;    
 77      end            
 78 assign spi_miso_i =(CPOL==1'B0 && CPHA==1'B0 || CPOL==1'B1 && CPHA==1'B1)? spi_miso_r1 :spi_miso_r2;
 79 
 80 lcd_spi_m
 81 #(
 82     .SPI_IN_WIDTH(6'd16),//spi  输入位数
 83     .SPI_OUT_WIDTH(6'd16),//SPI  输出位数
 84     .SPI_CPOL(CPOL),
 85     .SPI_CPHA(CPHA),
 86     .CLK_DIV(6'D5),
 87     .CS_F_DELAY(6'd3),
 88     .CS_B_DELAY(6'd3)
 89     
 90 
 91 
 92 )
 93 lcd_spi_m_inst
 94 (
 95      .rst_n_i            (rst_n_i     )       ,    
 96      .spi_clkx_i        (spi_clkx_i )       ,
 97      .spi_data_i         (spi_data_i  )       ,
 98      .spi_start          (spi_start   )       ,     
 99      .spi_miso_i         (spi_miso_i  )       ,
100      .spi_done           (spi_done    )       ,
101      .spi_busy           (spi_busy    )       ,
102      .spi_cs_o           (spi_cs_o    )       ,
103      .spi_scl_o          (spi_scl_o   )       ,
104      .spi_mosi_o         (spi_mosi_o  )       ,
105      .spi_data_o         (spi_data_o  )    
106              
107 );
108 
109 endmodule
  1 //SPI主程序
  2 //功能:完成32位以内SPI接口的数据双向通信
  3 module  lcd_spi_m
  4 #(
  5     parameter [5:0]SPI_IN_WIDTH  =6'd16,//spi  输入位数
  6     parameter [5:0]SPI_OUT_WIDTH =6'd16,//SPI  输出位数
  7     parameter [0:0]SPI_CPOL=1'b0,//空闲状态SCL电平 0:SCL=0  1:SCL=1
  8     parameter [0:0]SPI_CPHA=1'b0, //SPI数据在哪个SCL边沿有效 0:数据在SCL第一个边沿有效,1:数据在SCL第二个边沿有效,
  9     parameter [5:0]CLK_DIV =6'D3, //设定SPI_SCL半个周期所占的spi_clkx_i时钟个数
 10     parameter [5:0]CS_F_DELAY=6'D1, //CS前延时,相对于spi_scl_o时钟个数
 11     parameter [5:0]CS_B_DELAY=6'D1 //CS后延时,相对于spi_scl_o时钟个数
 12 
 13 
 14 )
 15 (
 16      input  wire  [0:0]  rst_n_i, //复位输入,低电平复位
 17      input  wire  [0:0]  spi_clkx_i,//SPI系统时钟 为SCL输出时钟的倍数 spi_clkx_i=spi_scl_o*2(高电平+低电平)*CLK_DIV
 18      input  wire  [31:0] spi_data_i,//输入32位要从MOSI发送出去的数据
 19      input  wire  [0:0]  spi_start, //单次发送开始,把数据送到spi_data_i 并把spi_start维技一个周期的高电平
 20      input  wire  [0:0]  spi_miso_i,//主机接收从机输出引脚
 21      output reg   [0:0]  spi_done,//SPI完成一次传输并从spi_data_o输出读到的数???
 22      output reg   [0:0]  spi_busy,//SPI忙信号输出,在忙状态时不接收外部数据,高表示忙
 23      output reg   [0:0]  spi_cs_o,//SPI片选信号输出低有效
 24      output wire  [0:0]  spi_scl_o,//SPI 时钟信号输出,请结合CPOL CPHA分析有效???
 25      output reg   [0:0]  spi_mosi_o,//SPI主机输出从机输入接口
 26      output reg   [31:0] spi_data_o//从从机读到的数据在SPI_DONE为高时为有效数据
 27 
 28 
 29 );
 30 localparam [5:0]CS_F_CNT=CS_F_DELAY-6'D1; //CS前面延时
 31 localparam [5:0]CS_B_CNT=CS_B_DELAY-6'D1; //CS前面延时
 32 localparam [5:0] WIDTH_MAX=(SPI_IN_WIDTH>SPI_OUT_WIDTH)? SPI_IN_WIDTH :SPI_OUT_WIDTH;//16
 33 localparam [7:0] SPI_MAX =WIDTH_MAX+CS_F_DELAY+CS_B_DELAY-1'D1;//17
 34 
 35 
 36 reg [7:0] spi_scl_cnt;
 37 reg [7:0 ] clk_cnt;
 38 reg [1:0]  pol_cnt;
 39 reg [0:0]  spi_sclk_r;
 40 wire [0:0] spi_flag;
 41 wire [0:0] pol_flag;
 42 //产生时钟计数
 43 always @(posedge spi_clkx_i  or negedge rst_n_i)
 44      begin
 45          if(rst_n_i==1'b0)
 46              clk_cnt<=8'd0;
 47          else if(spi_busy==1'b1 )
 48              begin
 49                    if(clk_cnt>=CLK_DIV-1'd1)
 50                          clk_cnt<=8'd0;
 51                    else
 52                          clk_cnt<=clk_cnt+1'd1;
 53              end
 54          else
 55                 clk_cnt<=8'd0;
 56      end
 57 
 58 
 59    
 60 //产生SPI_SCL极性计数    
 61 always @(posedge spi_clkx_i  or negedge rst_n_i)
 62      begin
 63          if(rst_n_i==1'b0)
 64              pol_cnt<=2'd0;
 65          else if(pol_cnt>=2'd1 && clk_cnt>=CLK_DIV-1'd1)
 66               pol_cnt<=2'd0;  
 67          else if(clk_cnt>=CLK_DIV-1'd1)
 68                  pol_cnt<=pol_cnt+1'd1;
 69          else 
 70                 pol_cnt<=pol_cnt;
 71      end
 72      
 73 assign pol_flag= (clk_cnt>=CLK_DIV-1'd1)? 1'b1:1'b0;  
 74 
 75 //产生spi_scl时钟计数
 76 always @(posedge spi_clkx_i  or negedge rst_n_i)
 77      begin
 78          if(rst_n_i==1'b0)
 79                   spi_scl_cnt<=15'd0;
 80          else if((spi_scl_cnt>=SPI_MAX) &&(spi_flag==1'b1))
 81                   spi_scl_cnt<=8'd0;
 82          else if(spi_flag==1'b1)
 83                   spi_scl_cnt<=spi_scl_cnt+1'd1;
 84          else 
 85      
 86                 spi_scl_cnt<=spi_scl_cnt;
 87 
 88      end
 89 assign spi_flag=(clk_cnt>=CLK_DIV-1'd1  && pol_cnt>=1'd1)? 1'b1:1'b0;       
 90 
 91 //输出SPI_CS信号
 92 always @(posedge spi_clkx_i  or negedge rst_n_i)
 93      begin
 94          if(rst_n_i==1'b0)
 95              spi_cs_o<=1'b1;
 96          else if(spi_start==1'b1 && spi_busy==1'b0)
 97              spi_cs_o<=1'b0;
 98          else if((spi_scl_cnt>=SPI_MAX) &&(spi_flag==1'b1))
 99              spi_cs_o<=1'b1;
100          else
101             spi_cs_o<=spi_cs_o;
102      end
103 
104 //输出SPI_DONE信号
105 always @(posedge spi_clkx_i  or negedge rst_n_i)
106      begin
107          if(rst_n_i==1'b0)
108              spi_done<=1'b0;
109          else if((spi_scl_cnt>=SPI_MAX) &&(spi_flag==1'b1))
110              spi_done<=1'b1;
111          else 
112              spi_done<=1'b0;
113      end
114 
115 
116 //输出spi_scl信号
117 always @(posedge spi_clkx_i  or negedge rst_n_i)
118      begin
119          if(rst_n_i==1'b0)
120              begin
121                  spi_sclk_r<=1'b0;
122              end
123          else if(pol_flag==1'b1)
124                  spi_sclk_r<=~spi_sclk_r;
125          else
126               begin
127                   spi_sclk_r<=spi_sclk_r;               
128               end                           
129      end
130 assign spi_scl_o =((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)))?((SPI_CPOL)? ~spi_sclk_r:spi_sclk_r):((SPI_CPOL==1'B1)? 1'B1:1'B0);
131 
132      
133 //在spi_start???时捕获数???
134 reg [31:0] temp_data_i;
135 //输出SPI_MOSI信号
136 always @(posedge spi_clkx_i  or negedge rst_n_i)
137      begin
138          if(rst_n_i==1'b0)
139              begin
140                  spi_mosi_o<=1'b0;
141                  spi_busy<=1'b0;
142                  temp_data_i<=32'b0;
143              end
144          else if(spi_start==1'b1 && spi_busy==1'b0)  //在spi_start???时捕获数???
145                 begin
146                     temp_data_i<=spi_data_i;
147                     spi_busy<=1'b1;
148                 end
149         else if(spi_done==1'b1)
150                  spi_busy<=1'b0;
151         else if(SPI_CPHA==1'b0)
152                  begin
153                      if((spi_scl_cnt>=CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)) &&(spi_flag==1'b1) && (pol_flag==1'b1)  )
154                          begin
155                              spi_mosi_o<=temp_data_i[SPI_OUT_WIDTH-1'd1];
156                              temp_data_i<={temp_data_i[(SPI_OUT_WIDTH-2'd2):0],temp_data_i[SPI_OUT_WIDTH-1'd1]};
157                          end
158                      else
159                          begin
160                              spi_mosi_o<=spi_mosi_o;
161                          end
162 
163                  end
164         else 
165                  begin
166                      if((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT))&& (spi_flag==1'b0) && (pol_flag==1'b1))//CPOL=0 CPHA=1->NG
167                          begin
168                              spi_mosi_o<=temp_data_i[SPI_OUT_WIDTH-1'd1];
169                              temp_data_i<={temp_data_i[SPI_OUT_WIDTH-2'd2:0],temp_data_i[SPI_OUT_WIDTH-1'd0]};
170                          end
171                      else
172                          begin
173                              spi_mosi_o<=spi_mosi_o;
174                          end
175                  end
176 
177 
178                                  
179      end
180 
181 //接收SPI_MISO信号
182 always @(posedge spi_clkx_i or negedge rst_n_i)
183      begin
184          if(rst_n_i==1'b0)
185              spi_data_o<=32'b0;
186          else if(SPI_CPHA==1'b0)
187                  begin
188                      if((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)) &&(pol_flag==1'b1)&& (spi_flag==1'b0)  )
189                          begin
190                              spi_data_o<={spi_data_o[30:0],spi_miso_i};
191                          end
192                      else
193                          begin
194                              spi_data_o<=spi_data_o;
195                          end
196                  end
197         else  if(SPI_CPHA==1'b1)
198                  begin
199                      if((spi_scl_cnt>CS_F_CNT) && (spi_scl_cnt<(SPI_MAX-CS_B_CNT)) &&(pol_flag==1'b1)&& (spi_flag==1'b1)  )
200                          begin
201                              spi_data_o<={spi_data_o[30:0],spi_miso_i};
202                          end
203                      else
204                          begin
205                              spi_data_o<=spi_data_o;
206                          end
207                  end
208          else
209                  begin
210                         spi_data_o<=spi_data_o;
211                  end
212 
213      end
214 
215 
216 endmodule

 

 

 

 

标签:scl,双工,spi,可调,SPI,wire,CS,reg
From: https://www.cnblogs.com/xgj-0817/p/17865641.html

相关文章

  • SPI扩展点在业务中的使用及原理分析
    1什么是SPISPI全称ServiceProviderInterface。面向接口编程中,我们会根据不同的业务抽象出不同的接口,然后根据不同的业务实现建立不同规则的类,因此一个接口会实现多个实现类,在具体调用过程中,指定对应的实现类,当业务发生变化时会导致新增一个新的实现类,亦或是导致已经存在的类......
  • spine共享骨骼
    项目中遇到使用多个相同spine的问题:我们需要获取骨骼位置的时候,要拿下面这个类的信息 spine工具给的更新方案是:每个spine在Update中每帧更新,根据当前spine更新骨骼信息。这样比较费,比如我们项目场景中有五个角色,每个角色有五个编制,那光友方单位就是25个spine,很难蚌。优化方......
  • SPI
    概述SPI(ServiceProviderInterface)JDK内置的一种服务提供发现机制;用来启用框架扩展和替换组件; 当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类;当其他的程......
  • P9447 [ICPC2021 WF] Spider Walk 题解
    更好的阅读体验很有意思的一道题。设\(f_i\)表示第\(i\)根线的答案,首先有一个关键结论:任意两根相邻的线答案只差一定小于\(1\)。原因显然,可以在无限远的地方加一根线来构造。该结论可以扩展一下,对于距离为\(d\)的两根线,答案之差不会超过\(d\)。考虑进行倒着加线,考虑加......
  • 12、oracle锁表查询spid进行杀掉
    oracle锁表查询spid进行杀掉1、查询SELECTA.SPID,B.SID,B.SERIAL#,B.USERNAMEFROMV$PROCESSA,V$SESSIONBWHEREA.ADDR=B.PADDRANDB.STATUS='KILLED';2、linuxOs执行杀掉进程kill-9SPID......
  • Reference and inspiration from China's strategy for addressing water pollution i
     AccordingtoChina'sthreelineonepermitmeasures,webelievethatthishasacertainreferencevalueforwaterpollutionissuesinAfrica.The"threelines"referstotheecologicalprotectionredline,theenvironmentalqualitybottom......
  • Spin 基于rust 开发的开源运行基于webassembly serverless 工具
    spin是基于rust开发的,可以用来开发以及运行基于webassemblyserverless服务的工具包含的特性提供了周边扩展 默认wasm只提供了基本类型的支持,wasm提供了不少扩展可以方便的支持不同语言的调用(比如网络,数据库访问)提供了快速应该开发的cli提供了服务部署的能力 包含了本......
  • 实现 利用SPI 接口 发送 显示屏 初始化代码
    1`timescale1ns/1ns2modulelcd_spi_top_tb();34regclk_x2_i;5regrst_i;6wirespi_cs;7wirespi_scl;8wirespi_mosi;9wirespi_miso;10wirespi_init_done;111213initialbegin14clk_x2_i=0;15rst_i=0;16......
  • 在ASP.NET Core 中使用 .NET Aspire 消息传递组件
    前言云原生应用程序通常需要可扩展的消息传递解决方案,以提供消息队列、主题和订阅等功能。.NETAspire组件简化了连接到各种消息传递提供程序(例如Azure服务总线)的过程。在本教程中,小编将为大家介绍如何创建一个ASP.NETCore应用并将提交的消息将发送到服务总线主题以供订阅......
  • QT实战 之自定义控件(QSpinBox+QSlider)
    QT实战之自定义控件(QSpinBox+QSlider)创建自定义控件(QT设计师界面类)打开ui文件,拖拽需要用到的基础控件(QSpinBox+QSlider)事件绑定(注意QSpinBox::valueChanged有重载,需要用函数指针指定信号函数)使用自定义控件(拖拽添加Widget容器控件,右键菜单--提升为,打开提升对话框,填写提升的......