FPGA_Verilog_SPI主机

目录

1 SPI总线协议

2 工作方式

本设计中:CPOL=1,CPHA = 1

 3 Verilog程序

 3.1 时钟产生部分

3.2 数据接收部分

3.3 数据发送部分

4 全部程序

5 测试结果


1 SPI总线协议

SPI总线协议介绍SPI(Serion Perpheral Interface)[3]是一种高速的、全双工、同步的通信总线,并且在芯片的管脚上只占用4根线,节约了芯片的管脚,同时为PCB的布局节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议。SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(用于单向传输时,也就是半双工方式)。也是所有基于SPI的设备共有,分别是MISO(数据输入),MOSI(数据输出),SCK(时钟),NSS(片选)。

2 工作方式

SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL="0",串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设音时钟相位和极性应该一致

CPHA = 1,就表示数据的输出是在一个时钟周期的第一个沿上,至于这个沿是上升沿还是下降沿,这要看CPOL的值而定,CPOL=1那就是下降沿,反之就是上升沿,数据的采样就是在第二个沿上,CPHA = 0,就表示数据的采样是在一个时钟周期的第一个沿上,那么数据的输出就在第二个沿上了。

本设计中:CPOL=1,CPHA = 1

 3 Verilog程序

 3.1 时钟产生部分

 作为SPI通信中的主动方,SPI Master要提供给SPI Slaver一个同步通信时钟SCK,通信的双方都在SCK的上升沿和下降沿进行数据的交换,因此设计SPI Master的第一步应该是产生一个SCK,供双方有一个统一的数据交换时钟。

reg csr;
assign sck = cnt1[2];
assign cs = csr;

task clk_states;
		     
             if(cnt1==3'b000) begin cnt1 <= 3'b111;cnt <= cnt+1'b1;end
             else cnt1 <= cnt1 - 1'b1;
			 
endtask

always@(posedge clk or negedge rst)
begin
     if(!rst)
	  begin
	  txd_flag <= 1'b0;
	  txd_start<=3'd1;
	  cnt <=4'd0;
	  cnt1<=3'b111;
	  csr <= 1'b1;
	  end
	 else
	  begin
	   if(txd_start==txd_start_outside)
	     begin
		  csr <= 1'b0;
		  txd_flag <=1'b1;//txd_flag 信号与cs信号相反,电路当处于通信时为高电平,表示忙碌状态
	      case(cnt)       //为低电平表示空闲状态
		  0:clk_states(); 
		  1:clk_states();
		  2:clk_states();
		  3:clk_states();
		  4:clk_states();
		  5:clk_states();
		  6:clk_states();
		  7:
		     begin
			   if(cnt1==3'b000) begin
                               cnt1 <= 3'b111;
                               cnt <= 4'd8; 
                                end
		        else cnt1 <= cnt1 - 1'b1;
                  end
                  8://多加一个状态延长cs的低电平时间,给从机足够的时间接受数据
                   begin
                    txd_start <= txd_start+1'b1;
                    cnt <= 4'd0; 
                   end		   
          endcase
        end
       else begin csr <= 1'b1; txd_flag <= 1'b0; end
		 
   end
end 

always@(posedge txd_signal or negedge rst) //外部触发发送脉冲,上升沿触发发送一次
begin                                      //每次触发产生8个周期的SCK脉冲,完成一次8bit数据                                           
   if(!rst)                                //的通信
     txd_start_outside <= 1'b0;
   else
     begin
       if(!txd_flag) txd_start_outside <= txd_start_outside+1'b1; //空闲状态才允许触发通信
       else;
     end 
end

3.2 数据接收部分

     有了固定的SCK,接下来的接收和发送就简单了,只需要在SCK的上升沿或下降沿,从MISO引脚一位一位读取数据,将要发送的数据一位一位的写入到MOSI引脚。

reg[7:0] rxd_outr;
reg [2:0] rec_cnt;
always@(posedge sck or negedge rst) //sck上升沿采样数据
begin
   if(!rst)
     begin
	  rxd_out <= 8'h00;
	  rec_cnt <= 3'd0;
	 end
   else
	begin
		case(rec_cnt)
		0:begin rxd_outr[7] <= miso;rec_cnt<=3'd1;end
		1:begin rxd_outr[6] <= miso;rec_cnt<=3'd2;end
		2:begin rxd_outr[5] <= miso;rec_cnt<=3'd3;end
		3:begin rxd_outr[4] <= miso;rec_cnt<=3'd4;end
		4:begin rxd_outr[3] <= miso;rec_cnt<=3'd5;end
		5:begin rxd_outr[2] <= miso;rec_cnt<=3'd6;end
		6:begin rxd_outr[1] <= miso;rec_cnt<=3'd7;end
		7:begin 
			rxd_outr[0] <= miso;
			rxd_out <= {rxd_outr[7:1],miso};
			rec_cnt<=3'd0;
			end
		default:;
		endcase
    end
end

3.3 数据发送部分

reg [2:0] send_cnt;
always@(negedge sck or negedge rst)//sck下降沿沿发送数据
begin
   if(!rst)
     begin
	  send_cnt <= 3'd0;
	 end
   else
   case(send_cnt)
   0:begin mosi <= txd_in[7];send_cnt<=3'd1;end
   1:begin mosi <= txd_in[6];send_cnt<=3'd2;end
   2:begin mosi <= txd_in[5];send_cnt<=3'd3;end
   3:begin mosi <= txd_in[4];send_cnt<=3'd4;end
   4:begin mosi <= txd_in[3];send_cnt<=3'd5;end
   5:begin mosi <= txd_in[2];send_cnt<=3'd6;end
   6:begin mosi <= txd_in[1];send_cnt<=3'd7;end
   7:begin mosi <= txd_in[0];send_cnt<=3'd0;end
   default:;
   endcase
end

4 全部程序

module SPI_M(
             clk,
             rst,
             txd_flag, //高电平表示为发送完成处于忙碌状态,低电平表示处于空闲状态
             sck,
             cs,
             mosi,
             miso,
             txd_signal,
             rxd_out,
             txd_in
             );
input clk,rst,miso,txd_signal;
output reg mosi,txd_flag;
output wire sck,cs;
output reg[7:0] rxd_out;
input [7:0] txd_in;
reg[2:0] txd_start;
reg[2:0] txd_start_outside;
reg[3:0] cnt;
reg[2:0] cnt1;

reg csr;
assign sck = cnt1[2];
assign cs = csr;

task clk_states;
		     
			   if(cnt1==3'b000) begin cnt1 <= 3'b111;cnt <= cnt+1'b1;end
		        else cnt1 <= cnt1 - 1'b1;
			 
endtask

always@(posedge clk or negedge rst)
begin
     if(!rst)
	  begin
	  txd_flag <= 1'b0;
	  txd_start<=3'd1;
	  cnt <=4'd0;
	  cnt1<=3'b111;
	  csr <= 1'b1;
	  end
	 else
	  begin
	   if(txd_start==txd_start_outside)
	     begin
		  csr <= 1'b0;
		  txd_flag <=1'b1;//txd_flag 信号与cs信号相反,电路当处于通信时为高电平,表示忙碌状态
	      case(cnt)       //为低电平表示空闲状态
		  0:clk_states(); 
		  1:clk_states();
		  2:clk_states();
		  3:clk_states();
		  4:clk_states();
		  5:clk_states();
		  6:clk_states();
		  7:
		     begin
			   if(cnt1==3'b000) begin
                               cnt1 <= 3'b111;
                               cnt <= 4'd8; 
                                end
		        else cnt1 <= cnt1 - 1'b1;
                   end
                  8://多加一个状态延长cs的低电平时间,给从机足够的时间接受数据
                   begin
                    txd_start <= txd_start+1'b1;
                    cnt <= 4'd0; 
                   end		   
          endcase
        end
       else begin csr <= 1'b1; txd_flag <= 1'b0; end
		 
   end
end 

always@(posedge txd_signal or negedge rst) //外部触发发送脉冲,上升沿触发发送一次
begin
   if(!rst)
     txd_start_outside <= 1'b0;
   else 
   begin
    if(!txd_flag) txd_start_outside <= txd_start_outside+1'b1; 
	else;
   end
  
end

reg[7:0] rxd_outr;
reg [2:0] rec_cnt;
always@(posedge sck or negedge rst) //sck上升沿采样数据
begin
   if(!rst)
     begin
	  rxd_out <= 8'h00;
	  rec_cnt <= 3'd0;
	 end
   else
	begin
		case(rec_cnt)
		0:begin rxd_outr[7] <= miso;rec_cnt<=3'd1;end
		1:begin rxd_outr[6] <= miso;rec_cnt<=3'd2;end
		2:begin rxd_outr[5] <= miso;rec_cnt<=3'd3;end
		3:begin rxd_outr[4] <= miso;rec_cnt<=3'd4;end
		4:begin rxd_outr[3] <= miso;rec_cnt<=3'd5;end
		5:begin rxd_outr[2] <= miso;rec_cnt<=3'd6;end
		6:begin rxd_outr[1] <= miso;rec_cnt<=3'd7;end
		7:begin 
			rxd_outr[0] <= miso;
			rxd_out <= {rxd_outr[7:1],miso};
			rec_cnt<=3'd0;
			end
		default:;
		endcase
    end
end

reg [2:0] send_cnt;
always@(negedge sck or negedge rst)//sck下降沿沿发送数据
begin
   if(!rst)
     begin
	  send_cnt <= 3'd0;
	 end
   else
   case(send_cnt)
   0:begin mosi <= txd_in[7];send_cnt<=3'd1;end
   1:begin mosi <= txd_in[6];send_cnt<=3'd2;end
   2:begin mosi <= txd_in[5];send_cnt<=3'd3;end
   3:begin mosi <= txd_in[4];send_cnt<=3'd4;end
   4:begin mosi <= txd_in[3];send_cnt<=3'd5;end
   5:begin mosi <= txd_in[2];send_cnt<=3'd6;end
   6:begin mosi <= txd_in[1];send_cnt<=3'd7;end
   7:begin mosi <= txd_in[0];send_cnt<=3'd0;end
   default:;
   endcase
end
endmodule

5 测试结果

    测试用的从机在FPGA_Verilog_SPI从机_喜欢喝茶的猫的博客-CSDN博客_spi从机 verilog

SPI模块:将SPI主机和SPI从机连接起来

module SPI(
          clk,
          rst,
          M_txd_signal,
          M_rxd_out,
          M_txd_in,
          S_rxd_out,
          M_txd_flag,
          S_rxd_flag,
          sck
          );
input clk,rst,M_txd_signal;
output  M_txd_flag,S_rxd_flag,sck;
output [7:0] S_rxd_out,M_rxd_out;
input [7:0] M_txd_in;
wire sck,cs,mosi,miso;
SPI_M SPIM(
           .clk(clk),
           .rst(rst),
           .txd_flag(M_txd_flag),
           .sck(sck),
           .cs(cs),
           .mosi(mosi),
           .miso(miso),
           .txd_signal(M_txd_signal),
           .rxd_out(M_rxd_out),
           .txd_in(M_txd_in)
            );
spi_slaver_ctrl spi_slaver_ctrl1(
                               .clk(clk),
                               .rst(rst),
                               .cs(cs),
                               .sck(sck),
                               .MOSI(mosi),
                               .MISO(miso),
                               .rxd_out(S_rxd_out),
                               .rxd_flag(S_rxd_flag)
                                );
endmodule
					  

测试脚本程序


`timescale 1 ns/ 1 ps
module SPI_vlg_tst();
reg clk;
reg rst;
reg M_txd_signal;
reg [7:0] M_txd_in;
// wires                                               
wire [7:0] M_rxd_out;
wire [7:0] S_rxd_out;
wire  S_rxd_flag;
wire sck;
// assign statements (if any)                          
SPI i1 (
  
      .clk(clk),
      .rst(rst),
      .M_txd_signal(M_txd_signal),
      .M_rxd_out(M_rxd_out),
      .M_txd_in(M_txd_in),
      .S_rxd_out(S_rxd_out),
      .M_txd_flag(M_txd_flag),
      .S_rxd_flag(S_rxd_flag),
     .sck(sck)
);
initial                                                
begin                                                     
          M_txd_signal = 0;
		  clk = 0;
   		  rst = 1;
		  #100;
		  rst = 0;
		  #50;
		  rst = 1;  //复位完成
		  #50;
		  M_txd_in = 8'ha5;
		  M_txd_signal = 1; //启动发送;
		  #50;
		  M_txd_signal = 0;
		  #1400;
		  
		  M_txd_in = 8'h33;  //正常应该接受到8'ha5+1
		  M_txd_signal = 1; //启动发送;
		  #50;
		  M_txd_signal = 0;
		  #2000;
		  
		  M_txd_in = 8'h66;  //正常应该接受到8'h33+1
		  M_txd_signal = 1; //启动发送;
		  #50;
		  M_txd_signal = 0;
		  #2000;
		  $stop;
                                                       
// --> end                                             
$display("Running testbench");                       
end                                                    
always                                                                 
begin                                                                        
             #10 clk = ~clk; //50M时钟                                          
                                             
end                                                    
endmodule

测试结果

从仿真图可以看出,通信双方都可以正常的收发。

  • 5
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: APB_SPI_Master是一种在Verilog中实现的高级外设控制器。它是一种基于APB(进程内总线)和SPI(串行外设接口)的控制器,可与多种外设进行通信。该控制器功能强大,可实现多种操作,如触发SPI通信、发送和接收数据、设置SPI时序等。APB_SPI_Master支持多种SPI模式和协议,并可与不同的主设备进行通信。它还具有灵活和可配置的特性,可以根据具体的应用场景实现不同的功能。 在实现APB_SPI_Master控制器时,需要使用Verilog语言进行编程。Verilog是一种硬件描述语言,可用于设计数字电路系统。由于APB_SPI_Master控制器是在FPGA或ASIC芯片上实现的,因此需要使用硬件描述语言进行编程。使用Verilog语言可以简化控制器的设计和开发,同时还可以提高控制器的性能和可靠性。Verilog语言可以描述模块的结构、输入输出、时序等信息,控制器的开发者可以通过调整这些信息来修改控制器的功能和性能。 总之,APB_SPI_Master控制器是一种可在Verilog中实现的高级外设控制器,具有灵活和可配置的特性,可以根据应用场景实现不同的功能,使用Verilog语言可以简化控制器开发过程,提高控制器的性能和可靠性。 ### 回答2: apb_spi_master verilog是一个Verilog语言编写的APB SPI主机模块,用于在系统级别上控制外部SPI设备。该模块通过APB总线连接到系统CPU,通过SPI总线连接到外部设备,可以实现数据传输、控制寄存器写入和读取等基本功能。这个模块可以用于各种数字电子系统中,例如嵌入式系统、工业自动化、通信设备等。 该模块的设计实现主要分为两个部分:APB总线和SPI总线。APB总线负责控制器与CPU之间的数据传输,包括地址、读/写控制、数据传输等;SPI总线则负责控制器与外部设备之间的数据传输,包括数据线、时钟线、片选线等。这两个部分通过适当的接口相互结合,实现了APB SPI主机的相关功能。 apb_spi_master verilog模块具有良好的可扩展性和可配置性,可以根据实际需要进行定制和修改。同时,该模块也可以作为其他模块的基础模块,与其他外设模块相结合,构建出更为完整的数字电子系统。在实际应用中,可以根据不同的需求进行调整和优化,以提高系统的性能和稳定性。 总的来说,apb_spi_master verilog模块在数字电子系统中担任着重要的角色,其设计实现的稳定性和可靠性对于系统整体的性能和稳定性具有直接的影响。因此,在进行系统设计时,应该充分考虑到该模块的特点和适用范围,并进行必要的优化和调整。 ### 回答3: apb_spi_master verilog是一种基于Verilog硬件描述语言的模块设计,用于实现在系统中的SPI主设备控制和数据传输。SPI通信是芯片间传输数据的一种常见协议。apb_spi_master verilog通过硬件实现,实现SPI协议的操作和数据传输,提高了数据传输的效率和可靠性。 apb_spi_master verilog通常作为主设备,在SPI系统中与从设备进行通信。它通过发送控制信号,管理数据传输的步骤和时序,并进行数据传输。apb_spi_master verilog具有许多优点,如灵活性、可扩展性、可配置性等。它可以自定义传输速率和协议,并能够适应不同应用场景的需求。 在设计apb_spi_master verilog模块时,需要考虑硬件的复杂性和细节,以确保其功能的正确性和性能的稳定性。同时,需要考虑到测试和调试的可行性,并尽可能简化设计过程,以提高开发效率。apb_spi_master verilog在实际应用中得到广泛的应用,可用于数字签名、数字认证、嵌入式系统等多个领域。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值