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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/***********************************************************************
     ****************** 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仿真图


  • 5
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FPGA(现场可编程门阵列)和STM32(一种基于ARM架构的微控制器)可以通过SPI(串行外设接口)协议进行通信。在这种通信方式中,FPGA作为SPI通信的从,而STM32作为主。 为了实现这种通信,我们需要使用Verilog语言编写从FPGA的代码。首先,我们需要确定FPGASPI接口的基本参数,如数据位宽、时钟频率和时钟极性等。然后,我们可以使用Verilog语言编写从SPI控制器,将其连接到FPGA的其他逻辑电路中。 在Verilog代码中,我们需要实现SPI的Slave模式。在SPI通信中,从始终被动地响应主的指令,并将数据传送给主。从的Verilog代码需要包括两个关键部分:状态和数据传输。 状态是从的控制核心,它根据主的指令进行状态切换,并管理数据传输过程的流程控制。例如,当主发起读取指令时,从会进入接收状态,并将要传输的数据存储到缓冲区中。当主发起写入指令时,从会进入发送状态,并将数据从缓冲区传输给主。 数据传输部分负责实际的数据传输。从需要实现接收和发送两个功能。接收部分负责接收主发送的数据,并将其存储到缓冲区中。发送部分负责从缓冲区中读取数据,并将其传输给主。 在编写Verilog代码时,需要注意时序问题和信号同步。SPI通信需要精确的时钟同步,在从和主之间共享和交换数据需要遵循一定的时序要求。因此,在设计代码时要特别注意时钟同步和数据的正确传输顺序。 最后,我们需要将Verilog代码综合到FPGA芯片中,并进行功能验证和调试。在验证过程中,我们可以通过观察FPGA输出波形和和STM32的通信结果来判断通信是否成功。如果通信出现问题,我们可以通过调试代码和时序分析来进行故障排查和修复。 通过以上步骤,我们可以实现FPGA作为从STM32进行SPI协议通信,并使用Verilog语言完成代码的设计与实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值