我是靠谱客的博主 失眠哑铃,最近开发中收集的这篇文章主要介绍SPI详解——原理及Verilog实现前置信息一、SPI协议二、SPI Verilog实现参考文献,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 前置信息
    • 全双工/半双工/单工通信协议
  • 一、SPI协议
    • 1.SPI传输原理介绍
      • SPI传输特点
      • SPI与I2C/UART的对比
    • 2.SPI传输模式
  • 二、SPI Verilog实现
    • 1.设计时序
    • 2.设计框图
    • 3.verilog代码
  • 参考文献

前置信息

全双工/半双工/单工通信协议

单工通信:数据流只能从发送端到接收端,为单向通道。如:键盘和显示屏之间的通信为单工通信,显示屏只能接收键盘的输入,无法向键盘反馈。
单工通信
半双工通信:数据流可以在发送端和接收端双向流动,即:发送端既能发送数据,也能接收数据;接收端既能接收数据,也能发送数据。但是发送端发送数据和接收数据(接收端接收数据和发送数据)不能在同一时刻进行。如:对讲机通信为半双工通信,对讲机两端都可以说话,但是他们不能同时讲话,得一个一个讲。
半双工通信
全双工通信:数据流可以在发送端和接收端同时双向流动。发送端和接收端均具有独立的TX(发送数据)和RX(接收数据)通道。如:电话通信为全双工通信,电话两端可以同时讲话。

全双工通信

一、SPI协议

1.SPI传输原理介绍

SPI(Serial Peripheral Interface,串行外围设备接口),是一种全双工通信协议,同步通信。其基本通信模式为
SPI一对一通信

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只能由主机产生
SPI一主多从通信
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:第二个时钟沿采样

SPI四种传输模式
因此四种模式为:

ModeCPOL/CPHA行为
Mode 0CPOL=0
CPHA=0
SCLK空闲时为低电平,上升沿采样数据,下降沿切换数据
Mode 1CPOL=0
CPHA=1
SCLK空闲时为低电平,下降沿采样数据,上升沿切换数据
Mode 2CPOL=1
CPHA=0
SCLK空闲时为高电平,下降沿采样数据,上升沿切换数据
Mode 3CPOL=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,不采样,数据传输完毕
SPI Mode0设计时序

2.设计框图

SPI_Controller设计框图

发送方向:
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

仿真结果为:
SPI Master发送数据访问结果

参考文献

SPI总线的原理与Verilog实现
SPI协议_Verilog实现
单工通信、半双工通信和全双工通信有何区别和联系?

最后

以上就是失眠哑铃为你收集整理的SPI详解——原理及Verilog实现前置信息一、SPI协议二、SPI Verilog实现参考文献的全部内容,希望文章能够帮你解决SPI详解——原理及Verilog实现前置信息一、SPI协议二、SPI Verilog实现参考文献所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部