本文主要描述SPI在FPGA上的应用实现,描述SPI时序在FPGA上的具体实现方式
SPI协议
SPI(Serial Peripheral Interface,串行外围设备接口),是Motorola公司提出的一种同步串行接口技术,三线或四线接口,收发独立。在使用中常用四线接口,SCLK决定了SPI协议的通信速率,一般在1~20M之间。SPI以主从模式进行通信,缺省数据应答,通常为一主一从或者一主多从的模式连接,连接示图如下:

SPI协议因空闲状态的SCLK电平极性(CPOL)和时钟采集相位(CPHA)的不同,分成了四种模式,四种模式的时序如下图所示:

模式0:CPOL = 0,CPHA = 0。SCLK串行时钟在空闲是为低电平,数据再SCLK的上升沿采样,下降沿切换。
模式1:CPOL = 0,CPHA = 1。SCLK串行时钟在空闲是为低电平,数据再SCLK的下降沿采样,上升沿切换。
模式2:CPOL = 1,CPHA = 0。SCLK串行时钟在空闲是为高电平,数据再SCLK的上升沿采样,下降沿切换。
模式3:CPOL = 1,CPHA = 1。SCLK串行时钟在空闲是为高电平,数据再SCLK的下降沿采样,上升沿切换。
FPGA实现
下面以模式0为例,进行SPI协议在FPGA一主一从的实现。
首先是SPI协议的发送模块,此模块中,输入为系统时钟、复位、发送信号使能位、发送的数据(8bit)、SPI的主接口端口,输出为SPI的片选、SPI时钟以及SPI的主发送端,模型如下:

此模块中,当send信号来临,表示需要发送当前tx_data中的数据,此时cs信号拉低,并且通过一个时钟节拍,抓取cs信号的下降沿和上升沿,代码如下:

 reg tmp_cs;always@(posedge pclk or negedge rst_n)beginif(!rst_n)spi_cs <= 1'b1;else if(send_en == 1'b1)spi_cs <= 1'b0;else if(send_cnt == 4'd8)spi_cs <= 1'b1;elsespi_cs <= spi_cs;endalways@(posedge pclk or negedge rst_n)beginif(!rst_n)tmp_cs <= 1'b1;elsetmp_cs <= spi_cs;endwire cs_rsing;wire cs_falling;assign cs_rsing = (spi_cs & (~tmp_cs));assign cs_falling = ((~spi_cs) & tmp_cs);

在完成cs的输出信号处理后,则需要根据实际需要,进行clk信号的发送,此模块的输入时钟需要高于SPI的发送时钟的4倍,便于能够稳定的输出clk时钟,此时计算出spi_clk与fclk的计数值,在根绝计数值,进行spi_clk的输出,代码实现如下:

 localparam CLK_HZ = 135_000_000;  //clk frequency;localparam SCK_HZ = 10_000_000;  //sck frequency;localparam DIV_NUMBER = CLK_HZ / SCK_HZ;localparam CNT_MAX = (DIV_NUMBER >>1) - 1'b1;/**************生成SPI时钟*************/ reg [4:0]spi_div_cnt;always@(posedge pclk or negedge rst_n)beginif(!rst_n)spi_div_cnt <= 4'd0;else if(spi_cs == 1'b1)spi_div_cnt <= CNT_MAX;else if(spi_div_cnt == CNT_MAX)spi_div_cnt <= 4'd0;elsespi_div_cnt <= spi_div_cnt + 1'b1;endreg tmp_sck;always@(posedge pclk or negedge rst_n)beginif(!rst_n)tmp_sck <= 1'b0;else if(spi_cs == 1'b1)tmp_sck <= 1'b0;else if(send_cnt == 4'd8)tmp_sck <= 1'b0;else if(spi_div_cnt == CNT_MAX)tmp_sck <= ~tmp_sck;elsetmp_sck <= tmp_sck;endalways@(posedge pclk or negedge rst_n)beginif(!rst_n) beginspi_clk <= 1'b0;endelse beginspi_clk <= tmp_sck;endend

在完成clk时钟的输出后,需要进行时钟边沿判断,以此来进行数据的采集及变换,时钟边沿提取如下:

 wire sck_rsing;wire sck_falling;assign sck_rsing = (tmp_sck & (~spi_clk));assign sck_falling = ((~tmp_sck) & spi_clk);

完成spi_clk的边沿判断之后,则进行数据的发送,按照标准的单字节数据发送为例,当发送8个spi_clk时钟之后,则完成当前时钟的发送,此时发送计数模块如下:

 always@(posedge pclk or negedge rst_n)beginif(!rst_n)send_cnt <= 4'd0;else if(spi_cs == 1'b1)send_cnt <= 4'd0;else if(send_cnt == 4'd8)send_cnt <= 4'd0;else if(sck_falling == 1'b1)send_cnt <= send_cnt + 1'b1;elsesend_cnt <= send_cnt;end

最后,则是spi_mosi信号的输出,在spi协议中,mosi接口始终先发送tx_data的高位,再依次移位发送,在发送完成后,保持当前的电平,spi_mosi的发送模块如下:

 reg [7:0]tmp_tx_data;always@(posedge pclk or negedge rst_n)beginif(!rst_n)tmp_tx_data <= 8'b0;else if(cs_falling == 1'b1)tmp_tx_data <= spi_tx_data;else if(sck_rsing == 1'b1)tmp_tx_data <= {tmp_tx_data[6:0],1'b0};elsetmp_tx_data <= tmp_tx_data;endalways@(posedge pclk or negedge rst_n)beginif(!rst_n)spi_mosi <= 1'b1;else if(spi_cs == 1'b1)spi_mosi <= 1'b1;else if(cs_falling == 1'b1)spi_mosi <= spi_tx_data[7];else if(sck_falling == 1'b1)spi_mosi <= tmp_tx_data[7];elsespi_mosi <= spi_mosi;end

以上,则是完成了SPI的发送模块在FPGA上的实现。接下来则是SPI的接收模块在FPGA上的实现。
在SPI的接收模块中,输入为系统时钟、复位、SPI片选、SPI的主发送基于SPI时钟,输出为SPI接收数据、SPI接收完成信号以及SPI的从发送端,模块模型如下:

在此模块中,输入的系统时钟依旧需要是SPI时钟的4倍频以上,才能够正常的抓取到spi_clk和spi_cs的边沿信号。首先需要抓取cs的边沿信号,代码如下:

 reg tmp_spi_cs;always@(posedge pclk or negedge rst_n)beginif(!rst_n)tmp_spi_cs <= 1'b0;elsetmp_spi_cs <= spi_cs;endwire spi_cs_falling;wire spi_cs_rising;reg tmp_spi_cs_rising;assign spi_cs_rising = spi_cs & (~tmp_spi_cs);assign spi_cs_falling = (~spi_cs) & tmp_spi_cs;always@(posedge pclk or negedge rst_n)beginif(!rst_n)tmp_spi_cs_rising <= 1'b0;elsetmp_spi_cs_rising <= spi_cs_rising;   end

之后,则是进行spi_clk信号的抓取:

 reg tmp_spi_clk;wire spi_clk_rising;wire spi_clk_falling;always@(posedge pclk or negedge rst_n)beginif(!rst_n)tmp_spi_clk <= 1'b0;elsetmp_spi_clk <= spi_clk;endassign spi_clk_rising = spi_clk & (~tmp_spi_clk);assign spi_clk_falling = (~spi_clk) & tmp_spi_clk;

最后则是对spi_mosi信号的抓取,通过clk的边沿信号,进行rx_data的数据移位,代码如下:

 always@(posedge pclk or negedge rst_n)beginif(!rst_n)spi_rx_data <= 8'b0;else if(spi_cs ==1'b1)spi_rx_data <= spi_rx_data;else if(spi_clk_rising == 1'b1)spi_rx_data <= {spi_rx_data[6:0],spi_mosi};elsespi_rx_data <= spi_rx_data;end

在此模块中,over信号则是完成了一字节数据接收之后,cs的上升沿信号作为接收完成的信号,代码如下:
assign transfer_over = tmp_spi_cs_rising;
在此模块中,未进行spi_miso的从发送信号的处理,如果从接收端需要反馈数据,则在slave模块中添加需要反馈的tx_data数据,处理代码如下:

 always@(posedge pclk or negedge rst_n)begin    if(!rst_n)spi_tx_shift <= 8'b0;else if(spi_cs_falling == 1'b1)spi_tx_shift <= spi_tx_data;else if(spi_clk_falling == 1'b1)spi_tx_shift <= {spi_tx_shift[6:0],1'b0}; //发送最高位elsespi_tx_shift <= spi_tx_shift;endassign spi_miso = (spi_cs == 1'b0)?spi_tx_shift[7]:1'bz;    //

最后则是进行两个模块的通信仿真,仿真结果如下:

从上图可以看出,clk_135m表示内部倍频后的时钟,输入到spi的master和slave模块,作为模块的时钟,最后spi_rx_data表示为接受到的数据。可以看到,发送的数据为0x12,接受的数据也是0x12,在slave_transfor_over之后,可从模块中获取到spi接收到的数据。

FPGA基础之SPI通信相关推荐

  1. 基于FPGA的 SPI通信 设计(1)

    引言 低速通信目前搞过 UART串口通信.IIC通信.其实 SPI 也算是中低速(有时也可以用作高速通信)串行通信的范畴,但是一直还没真正实现过,所以此系列就 SPI的协议以及FPGA设计作几篇博客记 ...

  2. SPI通信实验---verilog(FPGA作为从机,使用可读可写)

    文章原地址: http://www.cnblogs.com/fhyfhy/p/4429302.html SPI通信实验---verilog(FPGA作为从机,使用可读可写) 本实验讲究实用性,故设计思 ...

  3. FPGA基础入门【8】开发板外部存储器SPI flash访问

    前两篇教程利用数码管project介绍了chipscope和各种烧写开发板的方式,这篇开始继续探索开发板,这次关注外置存储器的控制,外置指的是芯片外部,不是开发板外部.板子上的外置存储器有DDR2和S ...

  4. 初学者必须要知道的FPGA基础知识

    初学者必须要知道的FPGA基础知识 一.FPGA是什么? 在<FPGA至简设计原理与应用>一书里是这样描述的:『FPGA的全称为Field-Programmable Gate Array, ...

  5. 基于labview的温湿度数据采集_【零偏原创】基于FPGA的多路SPI接口并行数据采集系统...

    摘 要:本文简述了SPI协议,建立了基于FPGA的SPI接口电路模型,并说明其输入输出端口和数据发送和接收过程,仿真验证了在主状态机控制下10个SPI接口并行采集数据,并在FPGA开发板上进行验证. ...

  6. 如何让FPGA中的SPI与其他模块互动起来

    在上一篇文章<FPGA的SPI从机模块实现>中,已经实现了SPI的从机模块,如何通过SPI总线与FPGA内部其他模块进行通信,是本文的主要讨论内容. 一. 新建FPGA内部DAC控制模块 ...

  7. FPGA基础入门篇(四) 边沿检测电路

    FPGA基础入门篇(四)--边沿检测电路 一.边沿检测 边沿检测,就是检测输入信号,或者FPGA内部逻辑信号的跳变,即上升沿或者下降沿的检测.在检测到所需要的边沿后产生一个高电平的脉冲.这在FPGA电 ...

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

    写在前面 SPI协议系列文章: FPGA实现的SPI协议(一)----SPI驱动 FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用 1.什么是SPI协议 SPI( ...

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

    写在前面 SPI协议系列文章: FPGA实现的SPI协议(一)----SPI驱动 FPGA实现的SPI协议(二)----基于SPI接口的FLASH芯片M25P16的使用 在上篇文章,简要介绍了SPI协 ...

最新文章

  1. Python 技术篇-不使用os模块判断指定路径是文件还是文件夹,使用pathlib库判断文件和文件夹
  2. 俄罗斯小方块游戏html,通过h5的canvas手写一个俄罗斯方块小游戏
  3. 有些数的素因子只有3,5,7.请设计一个算法,找出其中第k个数
  4. 每天一道LeetCode-----计算一个直方图空隙的容量(如果装水能装多少)
  5. stepinto stepout stepover的区别
  6. 使用Jexus服务器运行Asp.Net Core2.0程序
  7. TOP命令监视系统任务及掩码umask的作用
  8. 下载OneDrive共享的数据集
  9. linux下插入的mysql数据乱码问题及第三方工具显示乱码问题
  10. 2019牛客多校第六场H Pair(数位DP 多个数相关)题解
  11. 设备管理系统未来发展的四大趋势
  12. 2022-2028年中国电子政务行业投资策略探讨及市场规模预测报告
  13. visio2016上下标
  14. 关于电脑软件的一些实用推荐(二)
  15. 计算机科学与技术职员考试,在职计算机科学与技术考试科目
  16. shell数组 IFS
  17. 搜狗语音云开发入门--移动端轻松添加高大上的语音识别
  18. Python游戏开发-02-生成日志写入文件
  19. 第四章 安装centos与多重引导
  20. 揭秘京东城市时空数据引擎—JUST如何助力交通流量预测

热门文章

  1. Pr 入门教程如何创建颜色溶解过渡?
  2. 红米k30pro参数配置红米k30pro怎么样
  3. 最大流算法之ISAP
  4. Bootstrap CSS入门初步学习---导航栏设计、代码展示
  5. 安全狗| 一家云原生安全厂商的成长独白
  6. 度度熊与邪恶大魔王(2017百度之星程序设计大赛 - 资格赛)
  7. 中录国际 php,第四届公共卫生与预防医学国际学术会议(PHPM 2021)
  8. i5 10600k和i5 9600k的区别
  9. 函数TrackPopupMenu()
  10. 数学一年级应用题_小学一年级数学应用题大全(50题)