FPGA作为从机与STM32进行SPI协议通信---Verilog实现 [转]

2019-04-14 16:06发布

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发送数据与接收数据。 复制代码 1 /*********************************************************************** 2    ****************** name:SPI_Slaver_Driver ************** 3    ********** author:made by zzuxzt ********** 4    ****************** time:2014.4.29 ********************** 5 ***********************************************************************/ 6 //use SPI 3 mode,CHOL = 1,CHAL = 1 7 module spi(input clk, 8 input rst_n, 9 input CS_N, 10 input SCK, 11 input MOSI, 12 input [7:0] txd_data, 13 output reg MISO, 14 output reg [7:0] rxd_data, 15 output rxd_flag);   //recieve done,please transmit data 16 17 //-------------------------capture the sck----------------------------- 18 reg sck_r0,sck_r1; 19 wire sck_n,sck_p; 20 always@(posedge clk or negedge rst_n) 21 begin 22 if(!rst_n) 23 begin 24 sck_r0 <= 1'b1; //sck of the idle state is high 25 sck_r1 <= 1'b1; 26 end 27 else 28 begin 29 sck_r0 <= SCK; 30 sck_r1 <= sck_r0; 31 end 32 end 33 34 assign sck_n = (~sck_r0 & sck_r1)? 1'b1:1'b0; //capture the sck negedge 35 assign sck_p = (~sck_r1 & sck_r0)? 1'b1:1'b0; //capture the sck posedge 36 37 //-----------------------spi_slaver read data------------------------------- 38 reg rxd_flag_r; 39 reg [2:0] rxd_state; 40 always@(posedge clk or negedge rst_n) 41 begin 42 if(!rst_n) 43 begin 44 rxd_data <= 1'b0; 45 rxd_flag_r <= 1'b0; 46 rxd_state <= 1'b0; 47 end 48 else if(sck_p && !CS_N) 49 begin 50 case(rxd_state) 51 3'd0:begin 52 rxd_data[7] <= MOSI; 53 rxd_flag_r <= 1'b0; //reset rxd_flag 54 rxd_state <= 3'd1; 55 end 56 3'd1:begin 57 rxd_data[6] <= MOSI; 58 rxd_state <= 3'd2; 59 end 60 3'd2:begin 61 rxd_data[5] <= MOSI; 62 rxd_state <= 3'd3; 63 end 64 3'd3:begin 65 rxd_data[4] <= MOSI; 66 rxd_state <= 3'd4; 67 end 68 3'd4:begin 69 rxd_data[3] <= MOSI; 70 rxd_state <= 3'd5; 71 end 72 3'd5:begin 73 rxd_data[2] <= MOSI; 74 rxd_state <= 3'd6; 75 end 76 3'd6:begin 77 rxd_data[1] <= MOSI; 78 rxd_state <= 3'd7; 79 end 80 3'd7:begin 81 rxd_data[0] <= MOSI; 82 rxd_flag_r <= 1'b1; //set rxd_flag 83 rxd_state <= 3'd0; 84 end 85 default: ; 86 endcase 87 end 88 end 89 90 91 //--------------------capture spi_flag posedge-------------------------------- 92 reg rxd_flag_r0,rxd_flag_r1; 93 always@(posedge clk or negedge rst_n) 94 begin 95 if(!rst_n) 96 begin 97 rxd_flag_r0 <= 1'b0; 98 rxd_flag_r1 <= 1'b0; 99 end 100 else 101 begin 102 rxd_flag_r0 <= rxd_flag_r; 103 rxd_flag_r1 <= rxd_flag_r0; 104 end 105 end 106 107 assign rxd_flag = (~rxd_flag_r1 & rxd_flag_r0)? 1'b1:1'b0; 108 109 //---------------------spi_slaver send data--------------------------- 110 reg [2:0] txd_state; 111 always@(posedge clk or negedge rst_n) 112 begin 113 if(!rst_n) 114 begin 115 txd_state <= 1'b0; 116 end 117 else if(sck_n && !CS_N) 118 begin 119 case(txd_state) 120 3'd0:begin 121 MISO <= txd_data[7]; 122 txd_state <= 3'd1; 123 end 124 3'd1:begin 125 MISO <= txd_data[6]; 126 txd_state <= 3'd2; 127 end 128 3'd2:begin 129 MISO <= txd_data[5]; 130 txd_state <= 3'd3; 131 end 132 3'd3:begin 133 MISO <= txd_data[4]; 134 txd_state <= 3'd4; 135 end 136 3'd4:begin 137 MISO <= txd_data[3]; 138 txd_state <= 3'd5; 139 end 140 3'd5:begin 141 MISO <= txd_data[2]; 142 txd_state <= 3'd6; 143 end 144 3'd6:begin 145 MISO <= txd_data[1]; 146 txd_state <= 3'd7; 147 end 148 3'd7:begin 149 MISO <= txd_data[0]; 150 txd_state <= 3'd0; 151 end 152 default: ; 153 endcase 154 end 155 end 156 157 endmodule复制代码  六.Modelsim仿真图