zoukankan      html  css  js  c++  java
  • FPGA作为从机与STM32进行SPI协议通信---Verilog实现

    一.SPI协议简要介绍

    SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。
      SPI总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

    SPI总线有四种工作方式(SP0, SP1, SP2, SP3),其中使用的最为广泛的是SPI0和SPI3方式。SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。

    SPI主模块和与之通信的外设时钟相位和极性应该一致。

    以下是SPI时序图:


       主要讲解一下广泛使用的两种方式设置:

    SPI0方式:CPOL=0,CPHA=0;SCK空闲状态为低电平,第一个跳变沿(上升沿)采样数据,无论对Master还是Slaver都是如此。

    SPI3方式:CPOL=1,CPHA=1;SCK空闲状态为高电平,第二个跳变沿(上升沿采样数据,无论对Master还是Slaver都是如此。

    其实对于SPI0和SPI1发送与接收数据,可以总结为一句话:上升沿采样数据,下降沿发送数据。全双工同时进行,当然,必须在CS拉低使能情况下。

     

    二.FPGA作为Slaver实现SPI3方式与STM32通信

    1.STM32方面:用库函数配置SPI1,设置CPOL=1,CPHA=1.

    2.FPGA方面:

    (1)通过边沿检测技术得出SCK上升沿与下降沿标志,用于下面状态机中的数据采样及发送。

    (2)根据时序图,采用2个状态机分别在SCK上升沿实现数据采样,下降沿实现数据发送。无论是采样还是发送,都是高位在前,从Bit[7]到Bit[0],共8位数据。

    (3)最后通过边沿检测技术得出数据采样完成标志,用于用户操作。

    以下是SPI3的时序图:


    .Verilog代码部分

    测试工程代码:实现了STM32每隔200ms发送流水灯数据给FPGA,使FPGA系统板上的4个LED灯实现流水操作;同时,FPGA每隔1s发送计数数据给STM32,并在STM32系统板上的LCD屏出来,即:显示0-9循环计数。

    但下面的代码只是SPI作为从机的驱动部分,包括SPI发送数据与接收数据。

     

    /***********************************************************************
         ****************** name:SPI_Slaver_Driver **************
                ********** author:made by zzuxzt **********
         ****************** time:2014.4.29 **********************
    ***********************************************************************/
    //use SPI 3 mode,CHOL = 1,CHAL = 1
    module spi(input clk,
    			  input rst_n,
    			  input CS_N,
    			  input SCK,
    			  input MOSI,
    			  input [7:0] txd_data,
    			  output reg MISO,
    			  output reg [7:0] rxd_data,
    			  output rxd_flag);
    
    //-------------------------capture the sck-----------------------------		  
    reg sck_r0,sck_r1;
    wire sck_n,sck_p;
    always@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    		begin
    			sck_r0 <= 1'b1;   //sck of the idle state is high 
    			sck_r1 <= 1'b1;
    		end
    	else
    		begin
    			sck_r0 <= SCK;
    			sck_r1 <= sck_r0;
    		end
    end
    
    assign sck_n = (~sck_r0 & sck_r1)? 1'b1:1'b0;   //capture the sck negedge
    assign sck_p = (~sck_r1 & sck_r0)? 1'b1:1'b0;   //capture the sck posedge
    
    //-----------------------spi_slaver read data-------------------------------
    reg rxd_flag_r;
    reg [2:0] rxd_state;
    always@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    		begin
    			rxd_data <= 1'b0;
    			rxd_flag_r <= 1'b0;
    			rxd_state <= 1'b0;
    		end
    	else if(sck_p && !CS_N)   
    		begin
    			case(rxd_state)
    				3'd0:begin
    						rxd_data[7] <= MOSI;
    						rxd_flag_r <= 1'b0;   //reset rxd_flag
    						rxd_state <= 3'd1;
    					  end
    				3'd1:begin
    						rxd_data[6] <= MOSI;
    						rxd_state <= 3'd2;
    					  end
    				3'd2:begin
    						rxd_data[5] <= MOSI;
    						rxd_state <= 3'd3;
    					  end
    				3'd3:begin
    						rxd_data[4] <= MOSI;
    						rxd_state <= 3'd4;
    					  end
    				3'd4:begin
    						rxd_data[3] <= MOSI;
    						rxd_state <= 3'd5;
    					  end
    				3'd5:begin
    						rxd_data[2] <= MOSI;
    						rxd_state <= 3'd6;
    					  end
    				3'd6:begin
    						rxd_data[1] <= MOSI;
    						rxd_state <= 3'd7;
    					  end
    				3'd7:begin
    						rxd_data[0] <= MOSI;
    						rxd_flag_r <= 1'b1;  //set rxd_flag
    						rxd_state <= 3'd0;
    					  end
    				default: ;
    			endcase
    		end
    end
    
    
    //--------------------capture spi_flag posedge--------------------------------
    reg rxd_flag_r0,rxd_flag_r1;
    always@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    		begin
    			rxd_flag_r0 <= 1'b0;
    			rxd_flag_r1 <= 1'b0;
    		end
    	else
    		begin
    			rxd_flag_r0 <= rxd_flag_r;
    			rxd_flag_r1 <= rxd_flag_r0;
    		end
    end
    
    assign rxd_flag = (~rxd_flag_r1 & rxd_flag_r0)? 1'b1:1'b0;   
    
    //---------------------spi_slaver send data---------------------------
    reg [2:0] txd_state;
    always@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    		begin
    			txd_state <= 1'b0;
    		end
    	else if(sck_n && !CS_N)
    		begin
    			case(txd_state)
    				3'd0:begin
    						MISO <= txd_data[7];
    						txd_state <= 3'd1;
    					  end
    				3'd1:begin
    						MISO <= txd_data[6];
    						txd_state <= 3'd2;
    					  end
    				3'd2:begin
    						MISO <= txd_data[5];
    						txd_state <= 3'd3;
    					  end
    				3'd3:begin
    						MISO <= txd_data[4];
    						txd_state <= 3'd4;
    					  end
    				3'd4:begin
    						MISO <= txd_data[3];
    						txd_state <= 3'd5;
    					  end
    				3'd5:begin
    						MISO <= txd_data[2];
    						txd_state <= 3'd6;
    					  end
    				3'd6:begin
    						MISO <= txd_data[1];
    						txd_state <= 3'd7;
    					  end
    				3'd7:begin
    						MISO <= txd_data[0];
    						txd_state <= 3'd0;
    					  end
    				default: ;
    			endcase
    		end
    end
    
    endmodule
    



     

    六.Modelsim仿真图

  • 相关阅读:
    leetcode 131. Palindrome Partitioning
    leetcode 526. Beautiful Arrangement
    poj 1852 Ants
    leetcode 1219. Path with Maximum Gold
    leetcode 66. Plus One
    leetcode 43. Multiply Strings
    pytorch中torch.narrow()函数
    pytorch中的torch.repeat()函数与numpy.tile()
    leetcode 1051. Height Checker
    leetcode 561. Array Partition I
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/7627065.html
Copyright © 2011-2022 走看看