spi协议--Verilog及仿真

摘要:
如果fpga作为主机,那么SCLK和CS必须由fpga产生。SPI串行外设接口,spi接口有四根信号线。在片选信号有效时,数据由高位到低位,在时钟的上升沿依次发送给主设备。spi传输有四种模式:由时钟极性和时钟相位来决定,其中CPOL决定SCLK在空闲状态是高电平还是低电平,CPHA决定数据是在SCLK的上升沿被采样还是下降沿被采样。

学习文章:https://www.cnblogs.com/liujinggang/p/9609739.html

1、协议原理:

spi协议采用的是主从模式控制,支持一个master和多个slave。如果fpga作为主机,那么SCLK和CS必须由fpga产生。

spi协议--Verilog及仿真第1张

SPI(Serial Peripheral Interface)串行外设接口,spi接口有四根信号线。

MOSI(SDO):主机数据输出,从机数据输入。在片选信号有效时,数据由高位到低位,在时钟的上升沿依次发送给从设备。

MISO(SDI):主机数据输入,从机数据输出。在片选信号有效时,数据由高位到低位,在时钟的上升沿依次发送给主设备。

SCLK:时钟信号,由主设备产生。

CS:从设备的片选线,由主设备控制。

spi协议--Verilog及仿真第2张

spi传输有四种模式:由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来决定,其中CPOL决定SCLK在空闲状态是高电平还是低电平,CPHA决定数据是在SCLK的上升沿被采样还是下降沿被采样。

spi协议--Verilog及仿真第3张

模式0:CPOL=0,CPHA=0。低电平、上升沿采样、下降沿跳变。

模式1:CPOL=0,CPHA=1。低电平、下降沿采样、上升沿跳变。

模式2:CPOL=1,CPHA=0。高电平、下降沿采样、上升沿跳变。

模式3:CPOL=1,CPHA=1。高电平、上升沿采样、下降沿跳变。

这里代码是按照模式0逻辑设计:

spi协议--Verilog及仿真第4张

这里通过设计一个有16个状态的状态机实现spi中模式0的时序图。


2、协议代码:spi实现数据发送和接收,所以模块用到输入的引脚要有发送的数据i_data_in和接收的MISO。对应要有输出的MOSI和数据输出o_data_out。

spi协议--Verilog及仿真第5张

综合代码:

modulespi(
inputsys_clk,
inputsys_rst_n,
inputi_tx_en,
inputi_rx_en,
input [7:0]i_data_in,
output reg[7:0]o_data_out,
output rego_tx_done,
output rego_rx_done,
//spi的四根信号线
inputMISO,
output regSCLK,
output regCS,
output regMOSI
);

reg [3:0]tx_state;
reg [3:0]rx_state;

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begintx_state<=4'd0;
rx_state<=4'd0;
o_data_out<=8'd0;
o_tx_done<=1'b0;
o_rx_done<=1'b0;
SCLK<=1'b0;
CS<=1'b0;
MOSI<=1'b0;
end
else if(i_tx_en)beginCS<=1'b0;//片选信号为0,表示开始发送
case(tx_state)
4'd1,4'd3,4'd5,4'd7,4'd9,4'd11,4'd13,4'd15:beginSCLK<=1'b1;
tx_state<=tx_state+1'b1;
o_tx_done<=1'b0;
end
4'd0:begin
SCLK<=1'b0;
MOSI<=i_data_in[7];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd2:begin
SCLK<=1'b0;
MOSI<=i_data_in[6];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd4:begin
SCLK<=1'b0;
MOSI<=i_data_in[5];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd6:begin
SCLK<=1'b0;
MOSI<=i_data_in[4];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd8:begin
SCLK<=1'b0;
MOSI<=i_data_in[3];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd10:begin
SCLK<=1'b0;
MOSI<=i_data_in[2];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd12:begin
SCLK<=1'b0;
MOSI<=i_data_in[1];
o_tx_done<=1'b0;
tx_state<=tx_state+1'b1;
end
4'd14:begin
SCLK<=1'b0;
MOSI<=i_data_in[0];
o_tx_done<=1'b1;
tx_state<=tx_state+1'b1;
end
default:tx_state<=4'd0;
endcase
end
else if(i_rx_en)beginCS<=1'b0;//片选信号为0,表示开始接收
case(rx_state)
4'd0,4'd2,4'd4,4'd6,4'd8,4'd10,4'd12,4'd14:beginSCLK<=1'b0;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd1:begin
SCLK<=1'b1;
o_data_out[7]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd3:begin
SCLK<=1'b1;
o_data_out[6]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd5:begin
SCLK<=1'b1;
o_data_out[5]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd7:begin
SCLK<=1'b1;
o_data_out[4]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd9:begin
SCLK<=1'b1;
o_data_out[3]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd11:begin
SCLK<=1'b1;
o_data_out[2]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd13:begin
SCLK<=1'b1;
o_data_out[1]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b0;
end
4'd15:begin
SCLK<=1'b1;
o_data_out[0]<=MISO;
rx_state<=rx_state+1'b1;
o_rx_done<=1'b1;
end
default:rx_state<=4'd0;
endcase
end
else begintx_state<=4'd0;
rx_state<=4'd0;
o_data_out<=8'd0;
o_tx_done<=1'b0;
o_rx_done<=1'b0;
SCLK<=1'b0;
CS<=1'b1;//片选信号为1,表示停止数据的接收
MOSI<=1'b0;
end
end
endmodule 

仿真代码:这个仿真只是对spi的发送数据的仿真,接收数据没有。

`timescale 1ns/1ns
modulespi_tb;
regsys_clk;
regsys_rst_n;
regi_tx_en;
regi_rx_en;
reg [7:0]i_data_in;
regMISO;
wire [7:0]o_data_out;
wireo_tx_done;
wireo_rx_done;
wireSCLK;
wireCS;
wireMOSI;
spi u_spi(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.i_tx_en (i_tx_en),
.i_rx_en (i_rx_en),
.i_data_in (i_data_in),
.o_data_out (o_data_out),
.o_tx_done (o_tx_done),
.o_rx_done (o_rx_done)
);
initial beginsys_clk=0;
sys_rst_n=0;
#10 sys_rst_n=1;
i_tx_en=1;
i_rx_en=0;
i_data_in=8'b0;
MISO=0;
end
always #10 sys_clk=~sys_clk;
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
i_data_in=8'b0000_0000;
else if(i_data_in==8'b1111_1111)begin
i_tx_en=0;
i_data_in=8'b0000_0000;
end
else if(o_tx_done)
i_data_in=i_data_in+1'b1;
end
endmodule

仿真结果:

spi协议--Verilog及仿真第6张

这里可以看到仿真时候i_data_in有8'b0000_0000、8'b0000_0001、8'b0000_0010等等,这里仿真时间是1us,所以未能把所有的情况都显示出波形。然后MOSI能够成功发送这三个数据,说明spi发送数据的功能成功实现!

spi协议--Verilog及仿真第7张

这里明明状态4'd1时,SCLK是1'b1的,还有状态4'd2时,SCLK是1'b0的,这里好像不一样。是因为SCLK的状态要等到sys_clk的上升沿,才能变化!

spi协议--Verilog及仿真第8张

所以这里能够解释,在发送8'b0000_0001,明明状态4'd14时,o_tx_done应该是1和MOSI应该是在发送最后位1,然后要推到下一个状态4'd15那里,因为其实那里算4'd14的,只是上升沿在那里所以跳变就慢一拍。

免责声明:文章转载自《spi协议--Verilog及仿真》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Mybatis笔记redis 数据类型详解 以及 redis适用场景场合下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Java SPI

一、什么是Java SPI?   SPI的全名为Service Provider Interface.大多数开发人员可能不熟悉,因为这个是针对厂商或者插件的。在java.util.ServiceLoader的文档里有比较详细的介绍。简单的总结下java spi机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块...

基于MCP2515的Linux CAN总线驱动程序设计(二)

基于MCP2515的Linux CAN总线驱动程序设计(二) 作者:李老师,华清远见嵌入式学院讲师。 1.前言 CAN(Controller Area Network)总线,即控制器局域网总线,是一种有效支持分布式控制或实时控制的串行通信网络。由于其高性能、高可靠性、及独特的设计和适宜的价格而广泛应用于工业现场控制、智能楼宇、医疗器械、交通工具以及传感器等...

STM32.SPI(25Q16)

1.首先认识下W25Q16DVSIG, SOP8 SPI FLASH 16MBIT 2MB(4096个字节) (里面能够放字库,图片,也能够程序掉电不丢失数据放里面) 例程解说: ① 1.用到SPI库;conf.h里打开头文件 2.2M串行FLASH W25Q16初始化 SPI_FLASH_Init(); RCC_APB2PeriphClockCmd...

ReactiveX 学习笔记(35)使用 RxDart + RxCommand 进行 GUI 编程

课题 程序界面由3个文本编辑框和1个文本标签组成。 要求文本标签实时显示3个文本编辑框所输入的数字之和。 文本编辑框输入的不是合法数字时,将其值视为0。 3个文本编辑框的初值分别为1,2,3。 创建工程 Flutter 安装完毕之后执行以下命令创建工程 flutter create rx_example 打开 Android Studio,File...

Spring SPI 机制总结

1、概念: SPI(Service Provider Interface)服务提供接口,简单来说就是用来解耦,实现插件的自由插拔,具体实现方案可参考JDK里的ServiceLoader(加载classpath下所有META-INF/services/目录下的对应给定接口包路径的文件,然后通过反射实例化配置的所有实现类,以此将接口定义和逻辑实现分离)Spri...

SystemParametersInfo API学习(128个中文参数解释,215个实际值)

uiAction:该参数指定要查询或设置的系统级参数。其取值如下;SPI_GETACCESSTIMEOUT:检索与可访问特性相关联的超时段的信息,PvParam参数必须指向某个ACCESSTIMEOUT结构以获得信息,并将该结构中的cbSjze成员和ulParam参数的值设为sizeof(ACCESSTIMEOUT)。SPI_GETACTIVEWINDOW...