前置信息
全双工/半双工/单工通信协议
单工通信:数据流只能从发送端到接收端,为单向通道。如:键盘和显示屏之间的通信为单工通信,显示屏只能接收键盘的输入,无法向键盘反馈。
半双工通信:数据流可以在发送端和接收端双向流动,即:发送端既能发送数据,也能接收数据;接收端既能接收数据,也能发送数据。但是发送端发送数据和接收数据(接收端接收数据和发送数据)不能在同一时刻进行。如:对讲机通信为半双工通信,对讲机两端都可以说话,但是他们不能同时讲话,得一个一个讲。
全双工通信:数据流可以在发送端和接收端同时双向流动。发送端和接收端均具有独立的TX(发送数据)和RX(接收数据)通道。如:电话通信为全双工通信,电话两端可以同时讲话。
一、SPI协议
1.SPI传输原理介绍
SPI(Serial Peripheral Interface,串行外围设备接口),是一种全双工通信协议,同步通信。其基本通信模式为
SPI通信接口为:
SPI_CS_N:片选信号,一般低电平有效。由主机(master)产生,选择与之通信的从机(slaver),低电平表示从机被选中。
SPI_SCLK:时钟信号,由主机产生,用于控制数据传输速率和时机(即:数据采样时间)
SPI_MOSI:SPI接口的TX通道,主机发送给从机的数据(Master Output Slaver Input)
SPI_MISO:SPI接口的RX通道,从机发送给主机的数据(Master Input Slaver Output)
SPI传输特点
1.SPI通信为主从模式。支持一主多从的通信方式,主机通过片选CS信号选中从机。其中需要注意的是:时钟信号CLK只能由主机产生。
2.SPI为同步通信协议:SPI在传输数据的同时传输时钟信号。主机根据将要交换的数据产生时钟脉冲信号,时钟信号通过时钟极性(Clock Polarity)和时钟相位(Clock Phase)规定两个SPI设备在何时进行数据交换和数据采样,实现两个SPI设备的同步传输。
3.SPI为全双工通信协议:SPI同时拥有TX(MOSI)数据通道和RX(MISO)数据通道,为全双工通信。
SPI与I2C/UART的对比
1.硬件接口差异
IIC,拥有两根线,sda(双向端口)/scl(最大4Mbits/s).
SPI,拥有4根线,sclk/SDI(串行输入)/SDO(串行输出)/CS(片选:当接多个从设备时,需要用到该信号)
UART,拥有3根线,RX(接受数据)/TX(发送数据)/GND(地线)
2.协议差异
1.IIC和SPI均为先传输MSB,UART为LSB先传输
2.IIC的速度比SPI的速度更慢一些,协议更复杂一些,线也比标准的SPI少。
3.IIC通过地址选择从设备,SPI通过片选信号选择从设备
4.SPI和UART可以实现全双工通信,IIC为半双工通信(IIC只有一根数据线)
5.IIC需要上拉电阻,抗干扰的能力更弱。一般用于同一板卡上芯片之间的通信,较少用于远距离通信
6.UART需要固定的波特率,也就是说两位数据之间的间隔要相等。SPI无所谓,因为自己有时钟
7.UART为异步通信,一帧可以传送5/6/7/8位数据,SPI为同步通信可以一位一位的传送,IIC为同步通信传送8位连续数据
2.SPI传输模式
SPI协议规定SPI有四种传输模式,由CPOL(时钟极性:Clock Polarity)和CPHA(时钟相位:Clock Phase)决定
位 | 作用 |
---|---|
CPOL |
决定空闲时SCLK电平 CPOL=0:空闲时SCLK为低电平 CPOL=1:空闲时SCLK为高电平 |
CPHA |
决定SPI采样数据是第几个时钟沿 CPHA=0:第一个时钟沿采样 CPHA=1:第二个时钟沿采样 |
因此四种模式为:
Mode | CPOL/CPHA | 行为 |
---|---|---|
Mode 0 | CPOL=0 CPHA=0 |
SCLK空闲时为低电平,上升沿采样数据,下降沿切换数据 |
Mode 1 | CPOL=0 CPHA=1 |
SCLK空闲时为低电平,下降沿采样数据,上升沿切换数据 |
Mode 2 | CPOL=1 CPHA=0 |
SCLK空闲时为高电平,下降沿采样数据,上升沿切换数据 |
Mode 3 | CPOL=1 CPHA=1 |
SCLK空闲时为高电平,上升沿采样数据,下降沿切换数据 |
其中模式0和模式3比较常用
二、SPI Verilog实现
1.设计时序
Mode 0设计时序:
模式0:sclk空闲时为低电平,数据在上升沿采样
以传输8bit数据为例,数据传输可以分为0-15共16个状态;
发送方向——MOSI(Master Output Slaver Input):
主机发送数据,从机要在上升沿采集数据,为了保证从机进行采样时数据稳定,因此主机在时钟下降沿切换数据(sclk下降沿时,mosi切换),因此各个状态SCLK和MOSI的行为可总结为:
状态0:SCLK为0,MOSI保持不变
状态1:SCLK为1,MOSI进行数据切换
状态2:SCLK为0,MOSI保持不变
状态3:SCLK为1,MOSI进行数据切换
状态4:SCLK为0,MOSI保持不变
状态5:SCLK为1,MOSI进行数据切换
状态6:SCLK为0,MOSI保持不变
状态7:SCLK为1,MOSI进行数据切换
状态8:SCLK为0,MOSI保持不变
状态9:SCLK为1,MOSI进行数据切换
状态10:SCLK为0,MOSI保持不变
状态11:SCLK为1,MOSI进行数据切换
状态12:SCLK为0,MOSI保持不变
状态13:SCLK为1,MOSI进行数据切换
状态14:SCLK为0,MOSI保持不变
状态15:SCLK为1,MOSI进行数据切换,数据传输完毕
接收方向——MISO(Master Iutput Slaver Onput):
主机接收数据,为了保证主机进行采样时数据稳定,因此主机在时钟上降沿c采样(mode 0:数据在上升沿被采样),因此各个状态SCLK和MISO的行为可总结为:
状态0:SCLK为0,采样MISO
状态1:SCLK为1,不采样
状态2:SCLK为0,采样MISO
状态3:SCLK为1,不采样
状态4:SCLK为0,采样MISO
状态5:SCLK为1,不采样
状态6:SCLK为0,采样MISO
状态7:SCLK为1,不采样
状态8:SCLK为0,采样MISO
状态9:SCLK为1,不采样
状态10:SCLK为0,采样MISO
状态11:SCLK为1,不采样
状态12:SCLK为0,采样MISO
状态13:SCLK为1,不采样
状态14:SCLK为0,采样MISO
状态15:SCLK为1,不采样,数据传输完毕
2.设计框图
发送方向:
data_in[7:0]:主机需要向从机发送的8bit数据
data_vld:data_in有效指示
tx_en:主机向从机的发送使能
tx_done:主机发送数据完成标志
接收方向:
data_ou[7:0]:主机接受到的来自从机的8bit数据
rx_en:主机接收数据使能
rx_done:主机接收数据完成标志
SPI接口:
spi_cs_n:片选信号
spi_sclk:spi时钟信号
spi_mosi(master output slave input):主机输出数据,从机输入数据
spi_miso(master input slaver output):主机输入数据,从机输出数据
系统接口:
sys_clk:系统时钟
sys_rst_n:系统复位信号
3.verilog代码
1.设计过程中,使用shift_in/shift_ou指示发送和接收方向数据切换与采样时刻。
2.片选信号和时钟信号在状态机结束后立即处于默认值(片选拉高,时钟拉低)
`timescale 1ns/1ns
module spi_module #(parameter WORD_SIZE=8)(
input wire sclk,
input wire rst_n,
input wire tx_en,
input wire rx_en,
input wire [WORD_SIZE-1:0] data_in,
input wire data_vld,
output wire [WORD_SIZE-1:0] data_ou,
output reg tx_done,
output reg rx_done,
//spi interface
output reg spi_cs_n,
output reg spi_clk,
output wire spi_mosi,//MSI first send
input wire spi_miso
);
reg [3:0] tx_state;
reg [3:0] rx_state;
reg [WORD_SIZE-1:0] shift_in_reg;
reg [WORD_SIZE-1:0] shift_ou_reg;
reg shift_in;
reg shift_ou;
reg tx_done_r;
reg rx_done_r;
always @(posedge sclk or negedge rst_n)begin
if(rst_n==1'b0) begin
tx_done_r<=1'b0;
rx_done_r<=1'b0;
spi_cs_n<=1'b1;
spi_clk<=1'b0;//mode 0
//spi_mosi<=1'bz;
tx_state<=4'd0;
rx_state<=4'd0;
shift_in<=1'b0;
shift_ou<=1'b0;
end
else if(tx_en==1'b1)begin
spi_cs_n<=1'b0;
case(tx_state)
4'd1,4'd3,4'd5,4'd7,4'd9,4'd11,4'd13:begin
spi_clk<=1'b1;
tx_state<=tx_state+1'b1;
shift_in<=1'b1;
end
4'd0,4'd2,4'd4,4'd6,4'd8,4'd10,4'd12,4'd14:begin
spi_clk<=1'b0;
tx_state<=tx_state+1'b1;
shift_in<=1'b0;
end
4'd15:begin
spi_clk<=1'b1;
tx_state<=4'd0;
shift_in<=1'b1;
tx_done_r<=1'b1;
end
default:tx_state<=4'd0;
endcase
end
else if(rx_en==1'b1)begin
spi_cs_n<=1'b0;
case(rx_state)
4'd1,4'd3,4'd5,4'd7,4'd9,4'd11,4'd13:begin
spi_clk<=1'b1;
rx_state<=rx_state+1'b1;
shift_ou<=1'b0;
end
4'd0,4'd2,4'd4,4'd6,4'd8,4'd10,4'd12,4'd14:begin
spi_clk<=1'b0;
rx_state<=rx_state+1'b1;
shift_ou<=1'b1;
end
4'd15:begin
spi_clk<=1'b1;
rx_state<=rx_state+1'b1;
shift_ou<=1'b0;
rx_done_r<=1'b1;
end
default:rx_state<=4'd0;
endcase
end
else begin
tx_done_r<=1'b0;
rx_done_r<=1'b0;
spi_cs_n<=1'b1;
spi_clk<=1'b0;//mode 0
//spi_mosi<=1'bz;
tx_state<=4'd0;
rx_state<=4'd0;
shift_in<=1'b0;
shift_ou<=1'b0;
end
end
always @(posedge sclk or negedge rst_n)begin
if(rst_n==1'b0) shift_in_reg<='d0;
else if(data_vld==1'b1)
shift_in_reg<=data_in;
else if(shift_in==1'b1)
shift_in_reg<=shift_in_reg<<1;
end
assign spi_mosi=shift_in_reg[WORD_SIZE-1];
always @(posedge sclk or negedge rst_n)begin
if(rst_n==1'b0) shift_ou_reg<=4'd0;
else if(shift_ou==1'b1)
shift_ou_reg<={shift_ou_reg[WORD_SIZE-2:0],spi_miso};
end
assign data_ou=(rx_done==1'b1)? shift_ou_reg:{{WORD_SIZE{1'hz}}};
always @(posedge sclk or negedge rst_n)begin
if(rst_n==1'b0) begin
tx_done<=1'b0;
rx_done<=1'b0;
end
else begin
tx_done<=tx_done_r;
rx_done<=rx_done_r;
end
end
endmodule
testbench文件为:
注:仿真过程中只验证发送方向,未验证接收方向。
`timescale 1ns/1ns
module tb_spi_module #(parameter WORD_SIZE=8)();
reg sclk;
reg rst_n;
reg tx_en;
reg rx_en;
reg [WORD_SIZE-1:0] data_in;
reg data_vld;
wire [WORD_SIZE-1:0] data_ou;
wire tx_done;
wire rx_done;
wire spi_cs_n;
wire spi_clk;
wire spi_mosi;
reg spi_miso;
initial begin
sclk=1'b0;
forever #10 sclk=~sclk;
end
initial begin
rst_n=1'b0;
#100 rst_n=1'b1;
end
initial begin
rx_en=1'b0;
// tx_en=1'b1;//send data
spi_miso=1'b0;
end
initial begin
#100 data_in=8'haa;data_vld=1'b1;
#20 data_vld=1'b0;
#400 data_in=8'hfb;data_vld=1'b1;
#20 data_vld=1'b0;
#400 data_in=8'h99;data_vld=1'b1;
#20 data_vld=1'b0;
end
initial begin
tx_en=1'b0;
#150 tx_en=1'b1;
@(tx_done) tx_en=1'b0;
#150 tx_en=1'b1;
@(tx_done) tx_en=1'b0;
#150 tx_en=1'b1;
@(tx_done) tx_en=1'b0;
end
spi_module spi_module_inst(
. sclk ( sclk ) ,
. rst_n ( rst_n ) ,
. tx_en ( tx_en ) ,
. rx_en ( rx_en ) ,
. data_in ( data_in ) ,
. data_vld( data_vld) ,
. data_ou ( data_ou ) ,
. tx_done ( tx_done ) ,
. rx_done ( rx_done ) ,
. spi_cs_n( spi_cs_n) ,
. spi_clk ( spi_clk ) ,
. spi_mosi( spi_mosi) ,//MSI first send
. spi_miso( spi_miso)
);
endmodule
仿真结果为:
文章来源:https://www.toymoban.com/news/detail-428243.html
参考文献
SPI总线的原理与Verilog实现
SPI协议_Verilog实现
单工通信、半双工通信和全双工通信有何区别和联系?文章来源地址https://www.toymoban.com/news/detail-428243.html
到了这里,关于SPI详解——原理及Verilog实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!