0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于FPGA的SPI协议实现

FPGA设计论坛 来源:FPGA设计论坛 作者:FPGA设计论坛 2022-03-14 14:07 次阅读

一、SPI协议

1、SPI协议概括

SPI(Serial Peripheral Interface)——串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在EEPROM、FLASH、实时时钟AD转换器以及数字信号处理器和数字信号解码器之间。SPI是一种高速,全双工,同步的通信总线,在芯片上只占用四根线(CS、MOSI、MISO、SCK),极大的节约了芯片的引脚。

2、SPI物理层

SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或者多个从设备。图1是一个主设备一个从设备的物理连接示意图。图中SCK是由主设备发送给从的时钟,该时钟决定了主设备发送数据的速率;MOSI是主设备发送给从设备的数据;MISO是从设备发送给主设备的数据;CS是片选信号,即只有片选信号为预先规定的使能信号时(高电平或者低电平)对此芯片的操作才有效。

927212b0-a22a-11ec-952b-dac502259ad0.png

图1 点对点通信

927dfddc-a22a-11ec-952b-dac502259ad0.png

图2 一主多从通信

3、SPI协议层

SPI通信是四线串行通信,也就是说数据是一位一位传输的。这也即是SCK存在的意义,SCK提供通信所需的时钟脉冲,MOSI和MISO则基于此时钟进行数据传输。数据输出通过MOSI线,数据在时钟的上升沿或下降沿时改变,在紧接着的下降沿或者上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,至少在8次时钟信号的改变(上升沿和下降沿为一次),就可以实现8位数据的传输。

需要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少要有一个主控设备。这样传输的特点:此传输方式有一个优点,与普通串行通信不同,普通的串行通信一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通信的控制。SPI协议还可以实现数据的交换:因为SPI的数据输入和输出线独立所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要时改变和采集数据的时间不同,在时钟信号上升沿或下降沿采集有不同的定义。

SPI总线有四种工作方式(SPI0、SPI1、SPI2、SPI3),其中使用的最为广泛的是SPI0和SPI3方式。

SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升沿或下降沿)数据被采集;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升沿或下降沿)数据被采集。SPI主模块和与之通信的外设时钟相位和极性应该一致。

SPI时序图详解:SPI接口有四种不同的数据传输时序,取决于CPOL和CPHA的组合。图3中给出了这四种时序,时序与CPOL和CPHA的关系也可以从图中看出。

92974ba2-a22a-11ec-952b-dac502259ad0.png

图3 SPI四种时序

图3中可以看出,CPOL是用来决定SCK时钟信号空闲时的电平。CPOL=0,SCK空闲时为低电平;CPOL=1,SCK空闲时为高电平。CPHA是用来决定采样输入数据MISO时刻,CPHA = 0,在第一个SCK时钟沿进行数据采样;CPHA=1,在第二个SCK时钟沿进行数据采集。(工作模式的确定:由SLAVE的工作模式确定MASTER的工作模式)。

二、SPI协议使用举例

这里通过使用SPI3来实现主机发送数据。

92b499aa-a22a-11ec-952b-dac502259ad0.png

图4 SPI3 工作模式的主机发送数据

在SPI3模式下,CPOL = 1,CPHA = 1。SCK在空闲时为高电平,在SCK的第二个时钟沿从机进行数据的采集(只考虑主机发送情况),在SCK的第一个时钟沿发送数据MOSI。

三、使用verilog实现SPI3工作模式的时序

1、SPI3模式下工作过程如下图所示,

92cc8d8a-a22a-11ec-952b-dac502259ad0.png

图5 SPI发送数据过程

接下来分析图5所示SPI发送数据的过程,首先在复位信号到来时,进入s0状态,在s0状态计数器和分频器模块加载初始值,如果发送数据开始信号spi_start有效进入s1状态,s1状态加载待发送的数据,同时计数器计数计数,分频器开始工作,如果i=1,进入s2状态,s2状态主要用来发送数据,如果i为偶数,进入s3状态,该状态是用来采集数据,由于只考虑发送,因此此模块不进行数据采集工作,如果i=15,进入s4状态,否则如果i为奇数,则进入s2状态。;在s4状态,发送最后一位数据,如果i=16,进入s5状态,此时整个SPI时序模拟完成。

2、数据路径

由图5可知,构成SPI发送时序的基本电路块包括计数器,移位寄存器和触发器模块。

92df65f4-a22a-11ec-952b-dac502259ad0.png

图6 数据路径

图6中,左移寄存器将8位的待发送的数据spi_data转换为串行的数据mosi一位一位的发送出去,计数器用来计数发送数据的个数,触发器用来产生分频后的sck时钟信号。

3、控制信号

92f25ec0-a22a-11ec-952b-dac502259ad0.png

图7 控制信号

图7中给出了各个状态哪些控制信号应该有效,参照图5图6图7可以理清spi整个发送数据的过程。

四、 verilog描述

接下来使用verilog来描述图6所示的电路,控制信号可根据图7进行描述。

spi发送模块(该模块主要描述控制信号):

module SPI_SEND(input clk_50m,

input rst_n,

input spi_start,

input[7:0] spi_data,

output reg spi_done,

output sck,

output reg cs,

output mosi

);

reg load_c;

reg en_c;

reg load_a;

reg en_a;

reg load_b;

reg en_b;

wire [4:0]i;

parameter [4:0] s0 = 'b000001;

parameter [4:0] s1 = 'b000010;

parameter [4:0] s2 = 'b000100;

parameter [4:0] s3 = 'b001000;

parameter [4:0] s4 = 'b010000;

parameter [4:0] s5 = 'b100000;

reg [5:0]current_state = 'd0;

reg [5:0]next_state = 'd0;

always @(posedge clk_50m or negedge rst_n)

if(!rst_n)

current_state <= s0;

else

current_state <= next_state;

always @(*)

case(current_state)

s0:begin

if(spi_start)

next_state = s1;

else

next_state = s0;

end

s1:begin/该状态加载待发送的数据

if(i == 'd1)

next_state = s2;

else

next_state = s1;

end

s2:begin1,3,5,7,9,11,13,15

if(i[0] == 1'b0)//

next_state = s3;

else

next_state = s2;

end

s3:begin2,4,6,8,10,12,14,16

if(i == 'd15)

next_state = s4;

else if(i[0] == 'd1)

next_state = s2;

else

next_state = s3;

end

s4:begin

if(i == 'd16)

next_state = s5;

else

next_state = s4;

end

s5:begin

if(i == 'd0)

next_state = s0;

else

next_state = s5;

end

default:next_state = s0;

endcase

always @(*)

case(current_state)

s0:begin///空闲状态

load_c = 'd1;

en_c = 'd0;

load_a = 'd0;

en_a = 'd0;

load_b = 'd1;

en_b = 'd0;

spi_done = 'd0;

cs = 'd1;

end

s1:begin加载待发送数据状态

load_c = 'd0;

en_c = 'd1;

load_a = 'd1;

en_a = 'd0;

load_b = 'd0;

en_b = 'd1;

spi_done = 'd0;

cs = 'd0;

end

s2:begin第一个时钟沿发送数据

load_c = 'd0;

en_c = 'd1;

load_a = 'd0;

en_a = 'd1;

load_b = 'd0;

en_b = 'd1;

spi_done = 'd0;

cs = 'd0;

end

s3:begin第二个时钟沿采样数据

load_c = 'd0;

en_c = 'd1;

load_a = 'd0;

en_a = 'd0;

load_b = 'd0;

en_b = 'd1;

spi_done = 'd0;

cs = 'd0;

end

s4:begin数据发送完毕

load_c = 'd0;

en_c = 'd1;

load_a = 'd0;

en_a = 'd0;

load_b = 'd0;

en_b = 'd0;

spi_done = 'd0;

cs = 'd0;

end

s5:begin

load_c = 'd0;

en_c = 'd0;

load_a = 'd0;

en_a = 'd0;

load_b = 'd0;

en_b = 'd0;

spi_done = 'd1;

cs = 'd1;

end

default:begin

load_c = 'd1;

en_c = 'd0;

load_a = 'd0;

en_a = 'd0;

load_b = 'd1;

en_b = 'd0;

spi_done = 'd0;

cs = 'd1;

end

endcase

// Instantiate the module

count_num count_num (

.clk_50m(clk_50m),

.load_c(load_c),

.en_c(en_c),

.count(i)

);

// Instantiate the module

left_shifter left_shifter (

.clk_50m(clk_50m),

.load_a(load_a),

.en_a(en_a),

.spi_data_in(spi_data),

.mosi(mosi)

);

// Instantiate the module

sck_generate sck_generate (

.clk_50m(clk_50m),

.load_b(load_b),

.en_b(en_b),

.sck(sck)

);

endmodule

计数器电路描述:

module count_num(input clk_50m,

input load_c,

input en_c,

output reg[4:0]count

);

always @(posedge clk_50m)

if(load_c)

count <= 'd0; 

else if(en_c)begin

if(count == 'd16)

count <= 'd0;

else

count <= count + 'd1;

end

else

count <= count;

endmodule

移位寄存器电路描述:

module left_shifter(input clk_50m,

input load_a,

input en_a,

input [7:0]spi_data_in,

output mosi

);

reg [7:0]data_reg;

always @(posedge clk_50m)

if(load_a)

data_reg <= spi_data_in;

else if(en_a)

data_reg <= {data_reg[6:0],1'b0};

else

data_reg <= data_reg;

assign mosi = data_reg[7];

endmodule

触发器电路描述:

//SPI3模式下工作,SCK空闲时为高电平

//

module sck_generate(input clk_50m,

input load_b,

input en_b,

output reg sck

);

always @(posedge clk_50m)

if(load_b)

sck <= 'd1;

else if(en_b)

sck <= ~sck;

else

sck <= 'd1;

endmodule

仿真激励文件:

module test;

// Inputs

reg clk_50m;

reg rst_n;

reg spi_start;

reg [7:0]spi_data;

// Outputs

wire spi_done;

wire sck;

wire cs;

wire mosi;

// Instantiate the Unit Under Test (UUT)

SPI_SEND uut (

.clk_50m(clk_50m),

.rst_n(rst_n),

.spi_start(spi_start),

.spi_done(spi_done),

.sck(sck),

.cs(cs),

.spi_data(spi_data),

.mosi(mosi)

);

initial begin

// Initialize Inputs

clk_50m = 0;

rst_n = 0;

spi_start = 0;

spi_data = 'd0;

// Wait 100 ns for global reset to finish

#100;

// Add stimulus here

end

always #5 clk_50m = ~clk_50m;

reg [4:0] count = 'd0;

always @(posedge clk_50m)

if(count == 'd20)

count <= 'd20;

else

count <= count + 'd1;

always @(posedge clk_50m)

if(count <= 'd10)

rst_n <= 'd0;

else

rst_n <= 'd1;

reg [9:0]cnt = 'd0;

always @(posedge clk_50m)

if(spi_done)

cnt <= 'd0;

else if(cnt == 'd500)

cnt <= 'd500;

else

cnt <= cnt + 'd1;

always @(posedge clk_50m)

if(cnt=='d499)begin

spi_start <= 'd1;

spi_data <= 'b10101010;

end

elsebegin

spi_start <= 'd0;

spi_data <= spi_data;

end

endmodule

使用ISIM仿真结果:

9305d31a-a22a-11ec-952b-dac502259ad0.png

图8 仿真结果

图8中待发送的数据spi_data[7:0]=10101010,由于使用的是SPI3模式(CPOL=1,CPHA=1),此模式下SCK空闲时为1,在SCK第一个时钟沿进行数据发送(即图中SCK下降沿进行数据发送),从图中波形可以看出 ,在cs为低时,mosi被一位一位的送出(高位先输出)。

审核编辑 :李倩

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1628

    文章

    21722

    浏览量

    602879
  • SPI
    SPI
    +关注

    关注

    17

    文章

    1706

    浏览量

    91483

原文标题:FPGA学习-基于FPGA的SPI协议实现

文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    fpgaspi通信协议

    fpga通过spi通信协议在与外围设备实现通信的过程中程序代码该怎么写?新手勿喷
    发表于 04-09 12:44

    DSP和FPGASPI通信不能实现怎么办

    用的贵公司的TL138F-EVM A2开发板。想利用SPI协议实现FPGA和DSP通信。看了核心板引脚说明 没有看到FPGA这边
    发表于 04-24 06:46

    基于FPGA来介绍并设计标准的SPI总线协议

    不懂的有疑惑的也可以加微信咨询,欢迎大家前来投稿,谢谢!引言介绍在电子通信领域里采用的通信协议有IIC,SPI,UART,FSMC等协议。本文将基于FPGA来介绍并设计标准的
    发表于 11-10 09:37

    SPI协议是怎么实现

    、硬件的 SPI四、模拟的 SPI五、对 W25Q64的实际应用一、SPI协议实现 SPI功能
    发表于 02-17 07:29

    FPGA实现SPI协议

    写在前面SPI协议系列文章:FPGA实现SPI协议(一)----
    发表于 02-17 06:03

    SPI-4.2接口的FPGA实现

    去偏移和包重组是在FPGA实现SPI-4.2接口的核心难点,在分析偏移和包重组原理的基础上,给出基于FPGASPI-4.2接口的设计与
    发表于 04-10 09:43 32次下载

    如何在FPGA实现SPI4.2接口

    偏移和包重组是在FPGA实现SPI一4.2接口的核心难点,在分析偏移和包重组原理的基础E,给出基于FPGASPI一4.2接口的设计与
    发表于 01-25 14:51 13次下载
    如何在<b class='flag-5'>FPGA</b>中<b class='flag-5'>实现</b><b class='flag-5'>SPI</b>4.2接口

    基于SPI协议的SD卡读写机制与实现方法

    基于SPI协议的SD卡读写机制与实现方法。
    发表于 03-25 11:21 27次下载
    基于<b class='flag-5'>SPI</b><b class='flag-5'>协议</b>的SD卡读写机制与<b class='flag-5'>实现</b>方法

    基于FPGA的TCP/IP协议实现

    基于FPGA的TCP/IP协议实现说明。
    发表于 04-28 11:19 53次下载

    基于FPGA与MCU通信的SPI协议设计

    typora-copy-images-to: typora_picture基于FPGA与MCU通信的SPI协议设计1. SPI总线协议介绍及
    发表于 11-05 15:35 16次下载
    基于<b class='flag-5'>FPGA</b>与MCU通信的<b class='flag-5'>SPI</b><b class='flag-5'>协议</b>设计

    基于FPGASPI协议及设计实现

    基于FPGASPI协议及设计实现博主微信:flm13724054952,不懂的有疑惑的也可以加微信咨询,欢迎大家前来投稿,谢谢!引言介绍在电子通信领域里采用的通信
    发表于 11-05 19:05 24次下载
    基于<b class='flag-5'>FPGA</b>的<b class='flag-5'>SPI</b><b class='flag-5'>协议</b>及设计<b class='flag-5'>实现</b>

    FPGA实现SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用

    写在前面SPI协议系列文章:FPGA实现SPI协议(一)----
    发表于 12-22 19:25 19次下载
    <b class='flag-5'>FPGA</b><b class='flag-5'>实现</b>的<b class='flag-5'>SPI</b><b class='flag-5'>协议</b>(二)----基于<b class='flag-5'>SPI</b>接口的FLASH芯片M25P16的使用

    FPGA实现SPI协议(一)----SPI驱动

    1、什么是SPI协议SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是 Motorola 公司提出的一种同步串行接口技术,是一种高速、全双工
    发表于 12-22 19:29 19次下载
    <b class='flag-5'>FPGA</b><b class='flag-5'>实现</b>的<b class='flag-5'>SPI</b><b class='flag-5'>协议</b>(一)----<b class='flag-5'>SPI</b>驱动

    一文看懂SPI协议

    作者:王超首发:电子电路开发学习都有哪些内容?SPI协议简介4线还是3线?4种工作模式多种传输速率SPI协议的时序SPI
    发表于 01-25 18:35 38次下载
    一文看懂<b class='flag-5'>SPI</b><b class='flag-5'>协议</b>

    FPGA实现SPI

    FPGA实现SPI协议
    发表于 03-20 10:35 2次下载