赞
踩
摘要:1、本文介绍SPI物理层面连接(通过哪几条线通信),2、本文介绍SPI时序(通过哪种方式进行器件之间交流)。3、提供主机和从机verilog代码。4、仅供自己参考
一、SPI物理层连接
(1)有四根线连接:CS_N(片选信号--主机发出)、miso(从机发出,主机接收)、mosi(主机发出,从机接收)、SCLK(时钟信号,主机发出,作为数据传输的参考时钟)。
(2)结构图:
(3)总结:
1、SPI是一种全双工,同步通信总线,需要四根信号线(可用于FLASH器件的控制);
2、SPI通信有主从之分,可以实现一主多从或者是一主多从,但是不能实现多主多从,因为从器件只有一根cs_n片选信号线,没办法知道是哪个主机发出的控制信号;
3、SCLK是主机产生的,用来同步数据;
4、SPI是以MSB方式传播(高位先发);
二、SPI协议层时序
(1)SPI要记住以下几个知识(必须死记)
1、miso是从机发送,主机接收的信号线;
2、mosi是主机发送,从机接收的信号线;
3、mosi和mosi可以同时工作;
4、SCLK是主机产生,用来同步数据;且!!!:主机接收和发送数据都是在SCLK的跳变沿
从机接收和发送数据也是在SCLK的跳变沿。
5、根据4可知,数据的接收和发送是在SCLK的上升沿,究竟是在上升沿还是下降沿是由SPI的通信模式决定。注!!!:这个通信模式是双发同时遵守的!!,不是通过什么信号线配置的!!。
6、cs_n片选信号是由主机产生的,控制从机是否开始工作。
(2)通信模式:
解释:什么叫通信模式,就是决定数据的采集是在SCLK的上升沿还是下降沿,并且还决定SCLK在空闲时候是什么状态。
1、由CPOL和CPHA决定(这两个就是两个名称,没有什么用,相当于你的名字,没有实际的用处),所以可以有四种通信模式,00 、01、 10、 11。
2、CPOL :clock polarity 时钟极性
CPHA :clock phase 时钟相位,表示奇偶边沿采样。
CPHA = 0 :表示在时钟(SCLK)奇数边沿对数据采样,CPHA = 1表示在SCLK时钟偶数边沿采样。
CPOL = 0 :表示SCLK空闲时低电平,CPOL = 1表示SCLK时钟空闲时是高电平。
总结表格:
时序图示意(提供11模式时序图):
注!!!:以上的CPOL和CPHA是没有任何物理意义,仅仅用他们来表示这种通信模式,也可以用其他名字来代替CPOL和CPHA,是要双方用同一个模式就可以了。
(3)怎么通信:
1、首先要知道:miso、mosi、cs_n在空闲时是高电平,sclk空闲状态由通信模式决定。
2、由起始信号和停止信号表示传输开始和停止:
3、sclk怎么产生:用计数器记录一段时间实现SCLK的翻转,就产生了sclk脉冲信号;
4、mosi怎么产生:mosi的产生根据通信模式产生,比如在11模式:sclk空闲为高,在上升沿采样。那么在sclk下降沿的时候更具数据改变mosi信号线的高低;
5、miso怎么接收:比如在11模式。在sclk上升沿来临的时候采集miso,把串行数据变成并行数据;
6、定位数据哪一位接收或者发送:用一个计数器记录这一次接受了好多bit数据,并且放在数据对应位上,实现串并转换。
如上图:当bit为0的时候发送data的第7位(MSB),cnt_bit的变化是当sclk记录完一个周期之后加一。
主机verilog代码:
- module spi_master(
- input clk , //system clock 50MHz
- input rst_n , //reset, low valid
-
- input start , //按键控制数据发送
- /* input [7:00] send_data , //要发送的数据 */
-
- input spi_miso,//从机发送,主机接收
-
- output send_done , //这一次发送结束
- output reg [7:0] rx_data,
- output reg sclk, //
- output reg spi_mosi,//主发
- output reg spi_cs //片选信号
- );
- //Parameter Declarations
- localparam
- IDLE = 4'b0001,
- START = 4'b0010,
- WR_RD_DATA = 4'b0100,
- STOP = 4'b1000;
- //Internal wire/reg declarations
- reg [3:00] state_c, state_n; //
- //跳转条件定义
- wire idle2start;
- wire start2wr_rd_data;
- wire wr_rd_data2stop;
- wire stop2dile;
-
- reg [7:0] data_tmp;//输入数据暂存
- reg [7:0] data_tmp_rx;
-
- reg [5:00] cnt_sclk ; //Counter
- wire add_cnt_sclk ; //Counter Enable
- wire end_cnt_sclk ; //Counter Reset
-
- reg [3:00] cnt_bit ; //Counter
- wire add_cnt_bit ; //Counter Enable
- wire end_cnt_bit ; //Counter Reset
-
- //Module instantiations , self-build module
- //缓存数据
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- data_tmp <= 8'd20;
- end
- else if(start)begin
- data_tmp <= data_tmp + 3;
- end
- else begin
- data_tmp <= data_tmp;
- end
- end //always end
- //Logic Description
-
- //第一段设置状态转移空间
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- state_c <= IDLE;
- end
- else begin
- state_c <= state_n;
- end
- end //always end
- //第二段、组合逻辑定义状态转移
- always@(*)begin
- case(state_c)
- IDLE:begin
- if(idle2start)begin
- state_n = START;
- end
- else begin
- state_n = state_c;
- end
- end
- START:begin
- if(start2wr_rd_data)begin
- state_n = WR_RD_DATA;
- end
- else begin
- state_n = state_c;
- end
- end
- WR_RD_DATA:begin
- if(wr_rd_data2stop)begin
- state_n = STOP;
- end
- else begin
- state_n = state_c;
- end
- end
- STOP:begin
- if(stop2dile)begin
- state_n = IDLE;
- end
- else begin
- state_n = state_c;
- end
- end
- default: begin
- state_n = IDLE;
- end
- endcase
- end //always end
-
- assign idle2start = state_c == IDLE && start;
- assign start2wr_rd_data = state_c == START && spi_cs == 0;
- assign wr_rd_data2stop = state_c == WR_RD_DATA && end_cnt_bit ;
- assign stop2dile = state_c == STOP && spi_cs == 1;
-
- //计数器
- //cnt_sclk[5:00]
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_sclk <= 6'd0;
- end
- else if(add_cnt_sclk)begin
- if(end_cnt_sclk)begin
- cnt_sclk <= 6'd0;
- end
- else begin
- cnt_sclk <= cnt_sclk + 1'b1;
- end
- end
- else begin
- cnt_sclk <= 6'd0;
- end
- end
- assign add_cnt_sclk = state_c == WR_RD_DATA;
- assign end_cnt_sclk = add_cnt_sclk && cnt_sclk >= 9; //周期是10个时钟周期,当cnt_sclk加到5的时候翻转
- //cnt_bit
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_bit <= 4'd0;
- end
- else if(wr_rd_data2stop)begin
- cnt_bit <= 4'd0;
- end
- else if(add_cnt_bit)begin
- if(end_cnt_bit)begin
- cnt_bit <= 4'd0;
- end
- else begin
- cnt_bit <= cnt_bit + 1'b1;
- end
- end
- else begin
- cnt_bit <= cnt_bit;
- end
- end
-
- assign add_cnt_bit = state_c == WR_RD_DATA && end_cnt_sclk; //在计数器为0的时候加一,表示数据变化是在下降沿,采集是在上升沿
- assign end_cnt_bit = add_cnt_bit && cnt_bit >= 7;
- //第三段,定义状态机输出情况,可以时序逻辑,也可以组合逻辑
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- sclk <= 1'b1;
- spi_cs <= 1'b1;
- spi_mosi <= 1'b1;
- rx_data <= 8'd0;
- end
- else begin
- case(state_c)
- IDLE :begin//全高
- sclk <= 1'b1;
- spi_cs <= 1'b1;
- spi_mosi <= 1'b1;
- end
- START :begin
- spi_cs <= 1'b0;
- sclk <= 1'b1;
- spi_mosi <= 1'b1;
- end
- WR_RD_DATA :begin
- //发送
- spi_cs <= 1'b0;
- if(cnt_sclk < 5)begin
- sclk <= 1'b0;
- end
- else begin
- sclk <= 1'b1;
- end
- spi_mosi <= data_tmp[7-cnt_bit];
- //接收
-
- if(sclk == 1'b1)begin
- data_tmp_rx[7-cnt_bit] <= spi_miso;
- end
- end
- STOP :begin
- rx_data <= data_tmp_rx;
- sclk <= 1'b1;
- spi_cs <= 1'b1;
- spi_mosi <= 1'b1;
- end
- default:;
- endcase
-
- end
- end //always end
- assign send_done = stop2dile;
-
- endmodule

从机verilog代码:
- module spi_slave(
- input sclk , //
- input spi_cs , //
- input spi_mosi,//接收的数据
- output reg spi_miso, //输出的无数据
-
- output [7:0] rx_data,
- output data_vld//信号有效
- );
- //Parameter Declarations
- reg [7:0] rx_data_tmp;
-
- reg [2:0] cnt_bit;
- reg [7:0] tx_data = 8'd0;
- //Internal wire/reg declarations
- //Module instantiations , self-build module
-
- //Logic Description
- always @(posedge sclk)begin
- if(spi_cs == 1'b1)begin
- rx_data_tmp <= 8'd0;
- spi_miso <= 1'b1;
- cnt_bit <= 3'b111;
- end
- else begin
- cnt_bit = cnt_bit + 1;
- if(sclk)begin
- rx_data_tmp[7-cnt_bit] <= spi_mosi;
- end
- else ;
- if(cnt_bit == 1'b1)begin
- tx_data <= tx_data + 1;
- end
- else ;
- end
- end
-
- always @(*)begin
- if(spi_cs == 1'b1)begin
- spi_miso = 1'b1;
- end
- else begin
- if(sclk == 1'b0)begin
- spi_miso = tx_data[7 - cnt_bit];
- end
- else begin
- spi_miso = spi_miso;
- end
- end
- end
- assign rx_data = rx_data_tmp;
- assign data_vld = cnt_bit == 3'd7;
-
- endmodule

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。