FPGA - SPI总线介绍以及通用接口模块设计

一,SPI总线

1,SPI总线概述

        SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口串行外设接口总线(SPI),是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。

        SPI系统可直接与各个厂家生产的多种标准外围器件接口,它只需4条线:串行时钟线(SCK)、主机输入/从机输出数据线(MISO)、主机输出/从机输入数据线(MOSI)和低电平有效的从机选择线(NSS)。

(1)MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。

(2)MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。

(3)SCK:串口时钟,作为主设备的输出,从设备的输入。

(4)NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为片选引脚,让主设备可以单独地与特定从设备通信,避免数据线上的冲突。

SPI是一个环形总线结构,由NSS、SCK、MISO、MOSI构成,NSS引脚设置为输入,MOSI引脚相互连接,MISO引脚相互连接,数据在主和从之间串行地传输(MSB位在前)。

2,电路连接

下图表示基本的SPI设备连接示意图。片选信号NSS通常低电平有效。SPI数据传输原理是基于主从设备内部移位寄存器的数据交换。在主设备SCK的控制下,待传数据由各自设备的数据寄存器(Data Register)传输到移位寄存器(Shift Register),再通过MOSI和MISO信号线完成主从设备间的数据交换。

主从设备间数据交换逻辑示意图如下图所示:

3,硬件拓扑

(1)单主机单从机

(2)单主机多从机(片选方式)

        每个从设备都需要单独的片选信号,主设备每次只能选择其中一个从设备进行通信。因为所有从设备的SCK、MOSI、MISO都是连在一起的,未被选中从设备的MISO要表现为高阻状态(Hi-Z)以避免数据传输错误。由于每个设备都需要单独的片选信号,如果需要的片选信号过多,可以使用译码器产生所有的片选信号。

(3)菊花链方式

        数据信号经过主从设备所有的移位寄存器构成闭环。数据通过主设备发送绿色线)经过从设备返回蓝色线)到主设备。在这种方式下,片选和时钟同时接到所有从设备,通常用于移位寄存器和LED驱动器。注意,菊花链方式的主设备需要发送足够长的数据以确保数据送达到所有从设备。切记主设备所发送的第一个数据需(移位)到达菊花链中最后一个从设备。

        菊花链式连接常用于仅需主设备发送数据而不需要接收返回数据的场合,如LED驱动器。在这种应用下,主设备MISO可以不连。如果需要接收从设备的返回数据,则需要连接主设备的MISO形成闭环。同样地,切记要发送足够多的接收指令以确保数据(移位)送达主设备

4,SPI传输模式

通过设置控制寄存器SPICR1中的CPOL(时钟极性)CPHA(时钟相位,将SPI可以分成四种传输模式。

CPOL,即Clock Polarity,决定时钟空闲时的电平为高或低。对于SPI数据传输格式没有显著影响。
1 = 时钟低电平时有效,空闲时为高 
0 = 时钟高电平时有效,空闲时为低 

CPHA,即Clock Phase,定义SPI数据传输的两种基本模式。
1 = 数据采样发生在时钟(SCK)偶数(2,4,6,...,16)边沿(包括上下边沿)
0 = 数据采样发生在时钟(SCK)奇数(1,3,5,...,15)边沿(包括上下边沿)

四种模式如下图所示:

先看第一列两张图(CPHA = 0),采样发生在第一个时钟跳变沿,即数据采样发生在SCK奇数边沿

再看第二列CPHA =1),采样发生在第二个时钟跳变沿,即数据采样发生在SCK偶数边沿

第一行两张图(CPOL = 0),SCK空闲状态为低电平

第二行两张图(CPOL = 1),SCK空闲状态为高电平

主从设备进行SPI通讯时,要确保它们的传输模式设置相同。对于某些场合,可能需要调整CPOL/CPHA设置以满足设备特定要求。

5,SPI时序图 

CPHA = 0

  • 有些器件在片选后数据立即出现在MOSI/MISO管脚,数据锁存于第一个时钟边沿
  • 片选SS先于SCK半个时钟有效
  • 在SCK的第二个时钟边沿,上个时钟边沿锁存的数据写入移位寄存器(MSB或LSB)
  • 以此类推,数据在奇数边沿锁存,在偶数边沿写入移位寄存器
  • 经过16个时钟边沿后,串行传输的数据全部写入(并行的)移位寄存器,完成主从设备的数据交换

CPHA = 1

  • 有些设备要求数据输出在SCK第一个时钟边沿之后,数据锁存于第二个时钟边沿
  • 片选SS先于SCK半个时钟有效
  • 在SCK的第三个时钟边沿,上个时钟边沿锁存的数据写入移位寄存器(MSB或LSB)
  • 以此类推,数据在偶数边沿锁存,在奇数边沿写入移位寄存器
  • 经过16个时钟边沿后,串行传输的数据全部写入(并行的)移位寄存器,完成主从设备的数据交换

二,SPI 通用接口用户端模块-Verilog代码设计

1,SPI通用接口用户端设计框图

分析:
1,设计分频计数器(div_cnt)产生SCLK

2,设计比特计数器(bit_cnt)  sdo变化

3,设计字节计数器(byte_cnt) 

4,定义一个send_data  一直向右移位  把最低位send_data[0]给sdo

5,send_data向右移位,把最低位给sdo,在start信号为高的时候 ,把cmd赋值给send_data

6,CS在start信号为高的时候拉高 ,发完所有数据拉高

2,根据简单分析编写代码

// -----------------------------------------------------------------------------
// Author : RLG
// File   : spi_master.v
// Create : 2024-03-17 14:35:00
// Revise : 2024-03-17 16:20:40
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps
module spi_master #(parameter   SYS_CLK_FRWQ     = 50000000,parameter   SPI_CLK_FREQ     = 12500000,parameter   ADDR_WIDTH       = 24)(input                    		clk         ,input			      	 		reset       ,//SPI的物理接口      	output reg              		spi_sck     ,output reg              		spi_cs      ,output                   		spi_sdo     ,input                   		spi_sdi     ,//SPI的用户接口	input                    	    spi_start   ,input      [7:0]            	spi_cmd     ,input      [ADDR_WIDTH-1:0] 	spi_addr    ,input      [11:0]           	spi_length  ,output reg              	    spi_busy    ,output reg              	    spi_wr_req  ,input      [7:0]               	spi_wr_data ,output reg              	    spi_rd_vld  ,output reg [7:0]        	    spi_rd_data);localparam DIV_CNT_MAX      = SYS_CLK_FRWQ / SPI_CLK_FREQ - 1;  //只需计算一次 复位之前已经计算好了localparam DIV_CNT_MAX_HALE = DIV_CNT_MAX / 2;//定义3个计数器reg [$clog2(DIV_CNT_MAX) - 1 :0] div_cnt  ; //$clog2函数自动计算最小位宽reg [7:0]                        bit_cnt  ; reg [12:0]                       byte_cnt ;reg [11:0]                       spi_length_d0;reg [ADDR_WIDTH-1:0] 	         spi_addr_d0    ;reg [7:0]                        send_data;//锁存spi_lengthalways @(posedge clk ) if(spi_start) beginspi_length_d0 <= spi_length;end//分频计数器always @(posedge clk ) begin if(reset) div_cnt <= 0;else if(spi_cs)div_cnt <= 0;else if(div_cnt == DIV_CNT_MAX) div_cnt <= 0;else if(~spi_cs)div_cnt <= div_cnt + 1;end//比特计数器always @(posedge clk ) beginif(reset) bit_cnt <= 0;else if(spi_cs)bit_cnt <= 0;else if(bit_cnt == 7 &&div_cnt == DIV_CNT_MAX )bit_cnt <= 0;else if (div_cnt == DIV_CNT_MAX)bit_cnt <= bit_cnt + 1;end//字节计数器always @(posedge clk ) beginif(reset) byte_cnt <= 0;else if(spi_cs)byte_cnt <= 0;else if(byte_cnt == (spi_length_d0 + ADDR_WIDTH/8) && div_cnt == DIV_CNT_MAX && bit_cnt == 7)byte_cnt <= 0;else if (div_cnt == DIV_CNT_MAX && bit_cnt == 7)byte_cnt <= byte_cnt + 1;end//片选信号always @(posedge clk) if(reset) spi_cs <= 1'b1;else if(byte_cnt ==(spi_length_d0 + ADDR_WIDTH/8) && div_cnt == DIV_CNT_MAX && bit_cnt == 7)spi_cs <= 1'b1;else  if(spi_start)spi_cs <= 1'b0;//SCK时钟信号always @(posedge clk ) begin if(reset) spi_sck <= 0;else if(div_cnt == DIV_CNT_MAX)spi_sck <= 0;else if(div_cnt == DIV_CNT_MAX_HALE )spi_sck <= 1;end//spi_addr_d0always @(posedge clk) beginif (spi_start) beginspi_addr_d0	<= spi_addr;endelse if (byte_cnt <= ADDR_WIDTH/8 && div_cnt == DIV_CNT_MAX && bit_cnt == 7) beginspi_addr_d0 <= spi_addr_d0 >> 8;endelse beginspi_addr_d0 <= spi_addr_d0;endend//send_dataalways @(posedge clk) beginif (reset) beginsend_data <= 0;endelse if (spi_start) beginsend_data <= spi_cmd;endelse if(byte_cnt <= ADDR_WIDTH/8 -1 && div_cnt == DIV_CNT_MAX && bit_cnt == 7) beginsend_data <= spi_addr_d0[7:0];  //发送地址endelse if(byte_cnt >= ADDR_WIDTH/8 && div_cnt == DIV_CNT_MAX && bit_cnt == 7) beginsend_data <= spi_wr_data;endelse beginsend_data <= send_data >> 1;endendassign spi_sdo = send_data[0];always @(posedge clk ) beginif (reset) spi_wr_req <= 0;else if (~spi_cs && byte_cnt >= ADDR_WIDTH/8 && div_cnt == DIV_CNT_MAX - 2 && bit_cnt == 7) beginspi_wr_req <= 1;endelse beginspi_wr_req <= 0;endendalways @(posedge clk) beginif (reset) spi_rd_data <= 0;else if (byte_cnt >= ADDR_WIDTH/8 + 1 && div_cnt == DIV_CNT_MAX ) spi_rd_data <= {spi_sdi,spi_rd_data[7:1]};else spi_rd_data <= spi_rd_data;endalways @(posedge clk) beginif (byte_cnt >= ADDR_WIDTH/8 + 1 && div_cnt == DIV_CNT_MAX - 2 && bit_cnt == 7) beginspi_rd_vld <= 1;endelse beginspi_rd_vld <= 0;endendalways @(posedge clk ) beginspi_busy  <= ~spi_cs;endendmodule

3,编写测试文件:

// -----------------------------------------------------------------------------
// Author : RLG
// File   : tb_spi_master.v
// Create : 2024-03-17 15:47:19
// Revise : 2024-03-17 15:57:44
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps
module tb_spi_master();parameter      SYS_CLK_FRWQ = 50000000;parameter      SPI_CLK_FREQ = 12500000;parameter        ADDR_WIDTH = 24;reg                   clk                      ;reg                   reset                    ;wire                   spi_sck                 ;wire                   spi_cs                  ;wire                   spi_sdo                 ;wire                   spi_sdi     = 1         ;reg                    spi_start               ;wire             [7:0] spi_cmd     = 8'h0a     ; wire  [ADDR_WIDTH-1:0] spi_addr    = 24'haabbcc;wire            [11:0] spi_length  = 5         ;wire                   spi_busy                ;wire                   spi_wr_req              ;wire            [7:0]  spi_wr_data = 8'haa     ;     wire                   spi_rd_vld              ;wire            [7:0]  spi_rd_data             ;spi_master #(.SYS_CLK_FRWQ(SYS_CLK_FRWQ),.SPI_CLK_FREQ(SPI_CLK_FREQ),.ADDR_WIDTH(ADDR_WIDTH)) inst_spi_master (.clk         (clk),.reset       (reset),.spi_sck     (spi_sck),.spi_cs      (spi_cs),.spi_sdo     (spi_sdo),.spi_sdi     (spi_sdi),.spi_start   (spi_start),.spi_cmd     (spi_cmd),.spi_addr    (spi_addr),.spi_length  (spi_length),.spi_busy    (spi_busy),.spi_wr_req  (spi_wr_req),.spi_wr_data (spi_wr_data),.spi_rd_vld  (spi_rd_vld),.spi_rd_data (spi_rd_data));// initial clk = 0;// always #10 clk = ~clk;initial beginclk = 0;forever #(10)clk = ~clk;endinitial beginreset = 1;#200;reset = 0;endinitial beginspi_start <= 0;#290;spi_start <= 1;#20;spi_start <= 0;#8000;$stop;end
endmodule

4,仿真波形

spi_addr   = 24'haabbcc(给从机的地址) 移位传输 ,分别在

byte_cnt == 1时 传输8'hcc

byte_cnt == 2时 传输8'hbb

byte_cnt == 3时 传输8'hcc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/760787.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】为什么vector的地址与首元素地址不同?

文章目录 一、问题发现&#xff1a;二、结果分析三、问题解析 一、问题发现&#xff1a; &vector和&vector[0]得到的两个地址居然不相同&#xff0c;对数组array取变量名地址和取首元素地址的结果是相同的。这是为啥呢&#xff1f; 使用下面代码进行验证&#xff1a;…

html5cssjs代码 035 课程表

html5&css&js代码 035 课程表 一、代码二、解释基本结构示例代码常用属性样式和装饰响应式表格辅助技术 一个具有亮蓝色背景的网页&#xff0c;其中包含一个样式化的表格用于展示一周课程安排。表格设计了交替行颜色、鼠标悬停效果以及亮色表头&#xff0c;并对单元格设…

MoonBit 首场 MeetUp 活动火热报名中!更多活动惊喜等你来探索!

首场线下MeetUp来啦&#xff01; 在数字化浪潮中&#xff0c;基础软件构筑了信息产业发展的根基&#xff0c;不仅是技术进步的支柱&#xff0c;也是推动经济增长的重要力量。基础软件的发展不仅关乎硬件的完善与应用软件的创新&#xff0c;更是连接过去与未来的桥梁。 尽管中国…

Docker容器化技术(docker-compose安装部署案例)

docker-compose编排工具 安装docker-compose [rootservice ~]# systemctl stop firewalld [rootservice ~]# setenforce 0 [rootservice ~]# systemctl start docker[rootservice ~]# wget https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-…

anaconda迁移深度学习虚拟环境 在云服务器上配置

1 anaconda 虚拟环境操作 1、 查看虚拟环境 conda info -e2、 创建新的虚拟环境 conda create -n deeplearning_all pip python3.63、 激活新建的虚拟环境 Conda activate deeplearning_all2 环境中相关库的版本即安装说明&#xff08;这些库都是对应匹配的&#xff09; …

Lenze伦茨8400变频器E84A L-force Drives 操作使用说明

Lenze伦茨8400变频器E84A L-force Drives 操作使用说明

跟selenium并肩的自动化神器 Playwright 的 Web 自动化测试解决方案

1. 主流框架的认识 总结&#xff1a; 由于Selenium在3.x和4.x两个版本的迭代中并没有发生多大的变化&#xff0c;因此Selenium一统天下的地位可能因新框架的出现而变得不那么稳固。后续的Cypress、TestCafe、Puppeteer被誉为后Selenium时代Web UI自动化的三驾马车。但是由于这三…

JavaEE 初阶篇-深入了解操作系统中的进程与 PCB

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 关于计算机是如何进行工作的 “常识” 1.1 关于寄存器、缓存与内存是如何配合 CPU “工作” 2.0 操作系统概述 2.1 操作系统内核 2.2 进程 2.3 PCB 2.3.1 PCB 属性…

Centos7没有可用软件包 ifconfig问题解决

问题描述 在Centos7中查看ip没有ifconfig&#xff0c;使用yum安装ifconfig报错没有可用软件包 ifconfig问题解决 [rootlocalhost etc]# yum -y install ifconfig 已加载插件&#xff1a;fastestmirror base …

动手做简易版俄罗斯方块

导读&#xff1a;让我们了解如何处理形状的旋转、行的消除以及游戏结束条件等控制因素。 目录 准备工作 游戏设计概述 构建游戏窗口 游戏方块设计 游戏板面设计 游戏控制与逻辑 行消除和计分 判断游戏结束 界面美化和增强体验 看看游戏效果 准备工作 在开始编码之前…

火灾自动报警及消防联动控制系统主机的九个主要组成部分

关于火灾报警联动系统的主机组成&#xff0c;一般有两种不同的概括&#xff0c;下面分别讨论。 一&#xff1a; 火灾报警主机的组成部分较多&#xff0c;主要包括以下消防设备&#xff1a;主电源、联动电源、打印机、驱动器、直接控制板、总线控制板、消防广播、消防电话主机…

粒子群算法 - 目标函数最优解计算

粒子群算法概念 粒子群算法 (particle swarm optimization&#xff0c;PSO) 由 Kennedy 和 Eberhart 在 1995 年提出&#xff0c;该算法模拟鸟群觅食的方法进行寻找最优解。基本思想&#xff1a;人们发现&#xff0c;鸟群觅食的方向由两个因素决定。第一个是自己当初飞过离食物…

FPGA工程正确的设计流程

1 正确的设计流程 分析项目的具体需求来设计系统的结构&#xff0c;划分系统的层次&#xff0c;确定各个子模块的结构关系和信号之间的相互关系&#xff0c;然后确定模块的端口信号等根据每隔模块的功能和自己的理解&#xff0c;结合芯片手册接口的时序&#xff0c;使用visio画…

基于QGraphicsView的图像显示控件,支持放大、缩小、鼠标拖动

原链接 前言 这是一个Qt平台的基于QGraphicsView类的图像显示控件&#xff0c;支持输入QPixmap、QImage、opencv的从cv::Mat类。 实现平台&#xff1a;Windows 10 x64 Qt 6.2.3 MSVC 2019 opencv 4.5 先来看演示视频 控件类实现 ImageViewer.h文件 #ifndef IMAGEVIEWER…

Docker 笔记(八)--Dockerfile

目录 1. 背景2. 参考3. 原文3.1 Dockerfile 支持的指令3.2 Dockerfile格式3.3 Parser指令syntaxescape 3.4 环境变量替换3.5 docker构建忽略文件3.6 Shell 和 exec 格式Exec 格式Shell 格式使用不同的 shell 3.7 FROM指令了解ARG和FROM如何交互 3.8 RUN指令RUN指令缓存失效RUN …

4 CUDA 环境搭建

4.1 简介 本章面向从未接触过CUDA的初学者。我们将依次介绍如何在不同操作系统上安装CUDA、有哪些可用的CUDA 工具以及CUDA如何编译代码&#xff0c;最后介绍应用程序接口提供的错误处理手段&#xff0c;并帮助读者识别CUDA代码和开发过程中必然碰到的应用程序接口报错。Windo…

java框架 2 springboot 过滤器 拦截器 异常处理 事务管理 AOP

Filter 过滤器 对所有请求都可以过滤。 实现Filter接口&#xff0c;重写几个方法&#xff0c;加上WebFilter注解&#xff0c;表示拦截哪些路由&#xff0c;如上是所有请求都会拦截。 然后还需要在入口处加上SvlterComponentScan注解&#xff0c;因为Filter是javaweb三大组件之…

Leetcode刷题【每日n题】(8)

题目一 思路分析 1.循环遍历直到这个数小于102.获取每个位数之合3.将合赋值给目标数&#xff0c;直到小于10 代码实现 class MyTest{public int addDigits(int num) {//直到目标数小于10while(num>10){//定义各个位数合int sum0;//num不能为0while(num>0){//获取每个位上…

java-ssm-jsp基于javaweb的宠物猫狗商业系统

java-ssm-jsp基于javaweb的宠物猫狗商业系统 获取源码——》公主号&#xff1a;计算机专业毕设大全 获取源码——》公主号&#xff1a;计算机专业毕设大全

leetcode106从中序与后序遍历序列构造二叉树

目录 1.解题关键2.思路3.变量名缩写与英文单词对应关系4.算法思路图解5.代码 本文针对原链接题解的比较晦涩的地方重新进行说明解释 原题解链接&#xff1a;https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/solutions/50561/tu-jie-…