SPI简介及FPGA通用MOSI模块实现

简介

SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是Motorola公司提出的一种同步串行接口技术。是一种高速、全双工、同步通信总线。在芯片中只占用四根管脚用来控制及数据传输。

优缺点:

SPI通讯协议的优点是支持全双工通信,通讯方式较为简单,且相对数据传输速率较快;

缺点是没有指定的流控制,没有应答机制确认数据是否接收,与IIC总线通讯协议相比,在数据可靠性上有一定缺陷。

物理层

对于SPI协议的物理层,需要讲解的就是SPI通讯设备的连接方式和设备引脚的功能描述。

SPI通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分,根据从机设备的个数,SPI通讯设备之间的连接方式可分为一主一从和一主多从,具体见下图1、2。

SPIFla002

图 1 一主一从SPI通讯设备连接图

SPIFla003

图 2 一主多从SPI通讯设备连接图

SPI通讯协议包含1条时钟信号线、2条数据总线和1条片选信号线, 时钟信号线为SCK,2条数据总线分别为MOSI(主输出从输入)、MISO(主输入从输出),片选信号线为,它们的作用介绍如下:

  1. SCK (Serial Clock):时钟信号线,用于同步通讯数据。由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不同,两个设备之间通讯时,通讯速率受限于低速设备。

  2. MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,数据方向由主机到从机。

  3. MISO (Master Input,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据由这条信号线输出到主机,数据方向由从机到主机。

  4. (Chip Select):片选信号线,也称为CS_N,以下用CS_N表示。当有多个SPI从设备与SPI主机相连时,设备的其它信号线SCK、MOSI及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同使用这3条总线;而每个从设备都有独立的这一条CS_N信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。

I2C协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI协议中没有设备地址,它使用CS_N信号线来寻址,当主机要选择从设备时,把该从设备的CS_N信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以SPI通讯以CS_N线置低电平为开始信号,以CS_N线被拉高作为结束信号。

四种通讯模式

SPI通讯协议一共有四种通讯模式,模式0、模式1、模式2以及模式3,这4种模式分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义。

CPOL参数规定了空闲状态(CS_N为高电平,设备未被选中)时SCK时钟信号的电平状态;

CPHA规定了数据采样是在SCK时钟的奇数边沿还是偶数边沿。

极性和相位

SPI的极性Polarity和相位Phase,最常见的写法是CPOL和CPHA,不过也有一些其他写法,简单总结如下:
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位
(3) SCK=SCLK=SPI的时钟
(4) Edge=边沿,即时钟电平变化的时刻,即上升沿(rising edge)或者下降沿(falling edge)

模式判别

CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:
CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low;

CPHA=0,表示第一个边沿
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;

CPHA=1,表示第二个边沿
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;

SPI通讯协议的4种模式如下,通讯模式时序图,具体见下图 :

模式0:CPOL= 0,CPHA=0。空闲状态时SCK串行时钟为低电平;数据采样在SCK时钟的上升沿;数据更新在SCK时钟的下降沿。

模式1:CPOL= 0,CPHA=1。空闲状态时SCK串行时钟为低电平;数据采样在SCK时钟的下降沿;数据更新在SCK时钟的上升沿。

模式2:CPOL= 1,CPHA=0。空闲状态时SCK串行时钟为高电平;数据采样在SCK时钟的下降沿;数据更新在SCK时钟的上升沿。

模式3:CPOL= 1,CPHA=1。空闲状态时SCK串行时钟为高电平;数据采样在SCK时钟的上升沿;数据更新在SCK时钟的下降沿。

在这里插入图片描述

模式的判断:

如果起始的SCLK的电平是0,那么CPOL=0,如果是1,那么CPOL=1,

然后看数据采样时刻,对应到上面SCLK时钟的位置,对应着是第一个边沿或是第二个边沿,即CPHA是0或1

时序需求

tSLCH:cs_n拉低到sck高的时间

tCHSH:sck高到cs_n拉高的时间

SCK上升沿MOSI的建立时间保持时间需求

在这里插入图片描述

SPI通用模块

实现功能

用于将任意宽度向量型数据转换为SPI串行输出,模式0:CPOL= 0,CPHA=0;
默认串行数据mosi数据的建立时间和保持时间均为2个clk(0.5*DIV_FREQUENCY)周期,即sck上升沿的前后2个(0.5*DIV_FREQUENCY)clk数据稳定,串行时钟sck周期为4*clk(DIV_FREQUENCY)的周期;
默认tSLCH(cs_n拉低到sck高的时间为6*clk(1.5*DIV_FREQUENCY)周期),tCHSH(sck高到cs_n拉高的时间为2*clk(0.5*DIV_FREQUENCY)周期);
若时序满足此模块可以不做修改。若需要修改在外部例化时修改DIV_FREQUENCY(只能偶分频)和PERIOD_WIDTH_MAX即可。

使用方法

输入data的位宽和计数器位宽在外部进行例化时修改参数的值即可,不必修改SPI模块
DATA_WIDTH_MAX修改为输入数据的位宽,如[31:0]的数据则DATA_WIDTH_MAX=32
CNT_DATA_WIDTH_MAX修改为输入数据的位宽计数器需要的位宽上限,即DATA_WIDTH_MAX=32对应的二进制位宽32=6’b10_0000,所以CNT_DATA_WIDTH_MAX=6

输入输出端口说明:

//input
input 		wire 						clk 			, //系统时钟,spi串行时钟的分频基准 			
input 		wire						clr_n 			, //spi信号标志信号,允许发送时一直拉高,重新发送时拉低复位再拉高
input 	  	wire  [DATA_WIDTH_MAX-1:0] 	data 			, //需要data与clr_n一同进入			//output	                                            
output		reg							cs_n 			, //片选信号
output 	  	reg			 				sck 			, //串行时钟
output		reg							mosi 			, //主输出从输入数据
output		reg							flag 			  //spi发送完成标志位,完成则一直拉高;clr_n置0时拉低

例化模板:

spi #(
.DATA_WIDTH_MAX 	(8	 	),
.CNT_DATA_WIDTH_MAX	(4		) 
)
u_spi(.clk 	(clk			),.clr_n  (clr_n			),.data   (data_in		),.cs_n   (cs_n			),.sck    (sck 			),.mosi   (mosi			),.flag   (flag			)
); 

SPI模块

//========================================================================
// 	module_name.v	:spi.v
// 	Author			:YprgDay
// 	Description		:用于将任意宽度向量型数据转换为SPI串行输出,模式0:CPOL= 0,CPHA=0。
//========================================================================
module spi#(//=========================< Parameter >==============================parameter 				DATA_WIDTH_MAX		=	32				,//例化时修改为输入数据的位宽,如[31:0]的数据则DATA_WIDTH_MAX	=32parameter 				CNT_DATA_WIDTH_MAX	=	6				,//例化时修改为输入数据的位宽计数器需要的位宽上限,即DATA_WIDTH_MAX=32对应的二进制位宽32=6'b10_0000,所以CNT_DATA_WIDTH_MAX=6parameter 				DIV_FREQUENCY		=	4				,//分频数(只允许偶分频),串行时钟sck周期为DIV_FREQUENCY*clk的周期parameter 				PERIOD_WIDTH_MAX	=	2				,//(DIV_FREQUENCY-1)对应的二进制位宽即为PERIOD_WIDTH_MAXparameter 				CNT_PERIOD_MAX		=	DIV_FREQUENCY-1	,parameter 				CNT_HALF_PERIOD_MAX	=	CNT_PERIOD_MAX >> 1	//计数分频中值
) 
(//=========================< Port Name >==============================//inputinput 		wire 						clk 			, //系统时钟,spi串行时钟的分频基准 			input 		wire						clr_n 			, //spi信号标志信号,允许发送时一直拉高,重新发送时拉低复位再拉高		input 	  	wire  [DATA_WIDTH_MAX-1:0] 	data 			, //需要data与clr_n一同进入			//output	                                            output		reg							cs_n 			, //片选信号output 	  	reg			 				sck 			, //串行时钟output		reg							mosi 			, //主输出从输入数据output		reg							flag 			  //spi发送完成标志位,完成则一直拉高;clr_n置0时拉低);//=========================< Always block >===========================reg [CNT_DATA_WIDTH_MAX-1:0] 	cnt_data_width			;reg [PERIOD_WIDTH_MAX-1:0] 		cnt_spi_period			;reg [DATA_WIDTH_MAX-1:0] 		data_reg	  			;//输出的cs_n片选信号always @(posedge clk or negedge clr_n)beginif(clr_n == 1'b0)begincs_n <= 1'b1;endelse if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begincs_n <= 1'b1;endelse begincs_n <= 1'b0;endend	//输出的sck串行时钟信号,always @(posedge clk or negedge clr_n)beginif(clr_n == 1'b0)beginsck <= 0;endelse if(cnt_data_width > 0 && cnt_spi_period == CNT_PERIOD_MAX)beginsck <= 0;endelse if(cnt_data_width > 0 && cnt_spi_period == CNT_HALF_PERIOD_MAX)beginsck <= 1;endelse beginsck <= sck;endend//mosi的串行输出always @(posedge clk or negedge clr_n)beginif(clr_n == 1'b0)beginmosi <= 0;endelse if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)beginmosi <= 0;endelse if(cnt_spi_period == CNT_PERIOD_MAX)beginmosi <= data_reg[DATA_WIDTH_MAX-1-cnt_data_width];endelse beginmosi <= mosi;endend //输出的串行数据发送完成标志信号,发送完成即拉高always @(posedge clk or negedge clr_n)beginif(clr_n == 1'b0)beginflag <= 0;endelse if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)beginflag <= 1;endelse beginflag <= 0;endend//输入数据寄存,保证data在一串SPI数据发完之间不发生变化always @(posedge clk or negedge clr_n)beginif(clr_n == 1'b0)begindata_reg <= 0;endelse if(cnt_data_width == 0 && cnt_spi_period == 1)begindata_reg <= data;endelse begindata_reg <= data_reg;endend//时钟四分频计数always @(posedge clk or negedge clr_n)beginif(clr_n == 1'b0)begincnt_spi_period <= 0;endelse if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begincnt_spi_period <= cnt_spi_period;endelse if(cnt_spi_period == CNT_PERIOD_MAX)begincnt_spi_period <= 0;endelse if(cs_n == 0)begincnt_spi_period <= cnt_spi_period + 1'b1;endelse;end	//计数表示此时输出到[DATA_WIDTH_MAX:0] data的第几位位置always @(posedge clk or negedge clr_n)beginif(clr_n == 1'b0)begincnt_data_width <= 0;endelse if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begincnt_data_width <= cnt_data_width;endelse if(cnt_spi_period == CNT_PERIOD_MAX)begincnt_data_width <= cnt_data_width + 1'b1;endelse;endendmodule

仿真模块

`timescale 1ns / 1ps
//
// Module Name: tb_spi
// Dependencies: spi模块仿真
//
module tb_spi();//=========================< Parameter >==============================parameter 				SPI_CLK_PERIOD		=	2			    ;//设置spi时钟信号周期parameter 				HALF_SPI_CLK_PERIOD	=	SPI_CLK_PERIOD/2;//生成spi时钟信号半周期//=========================< Port Name >==============================//inputreg						clk								;reg    					clr_n							;reg 		[7:0]		data_in							;//outputwire					mosi							;wire					cs_n							;wire					sck								;wire					flag							;//==========================< Clock block >============================always 	  	#HALF_SPI_CLK_PERIOD		clk = ~clk	;//==========================< Reset block >============================initial beginclk 		= 	1'b1	;clr_n  		<= 	1'b0	;data_in     <=	0		;#HALF_SPI_CLK_PERIODclr_n  		<= 	1'b1	;data_in     <=	8'h83	;#30data_in     <=	8'h54	;#300clr_n  		<= 	1'b0	;data_in     <=	8'hc7	;#10clr_n  		<= 	1'b1	;end//==========================< Module Instance >============================spi #(.DATA_WIDTH_MAX 	(8	 	),.CNT_DATA_WIDTH_MAX	(4		)				)u_spi(.clk 	(clk			),.clr_n  (clr_n			),.data   (data_in		),.cs_n   (cs_n			),.sck    (sck 			),.mosi   (mosi			),.flag   (flag			)); endmodule

仿真时序图

以8位输入数据[7:0]data的SPI时序图为例

img

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

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

相关文章

yolov5 利用Labelimg对图片进行标注

首先打开yolov5-master&#xff0c;在data文件中新建一个文件夹来存放你需要跑的数据&#xff0c;例如我这次跑的是羽毛球&#xff0c;文件把文件取名为badminton。使用其他文件夹例如images也可以&#xff0c;就是跑多了以后不好整理&#xff0c;然后点击 选中刚刚你存放数据的…

iOS应用加固方案解析:ipa加固安全技术全面评测

​ 在移动应用开发领域&#xff0c;iOS应用的安全性一直备受关注。ipaguard作为一款专业的iOS应用加固方案&#xff0c;采用混淆加密技术&#xff0c;旨在保护应用免受破解、逆向和篡改等风险。本文将深入探讨ipaguard的产品功能、安全技术及其在iOS应用加固领域中的核心优势和…

从零开始搭建React+TypeScript+webpack开发环境-基于lerna的webpack项目工程化改造

项目背景 在实际项目中&#xff0c;我们的前端项目往往是一个大型的Webpack项目&#xff0c;结构较为复杂。项目根目录下包含了各种配置文件、源代码、以及静态资源&#xff0c;整体布局相对扁平。Webpack的配置文件分散在不同的部分&#xff0c;包括入口文件、输出目录、加载…

C#基于inpoutx64读写ECRAM硬件信息

inpoutx64.dll分享路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rOt0xtt9EcsrFQtf7S91ag 提取码&#xff1a;7om1 1.InpOutManager&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServi…

基于Docker容器DevOps应用方案

文章目录 基于docker容器DevOps应用方案环境基础配置1.所有主机永久关闭防火墙和selinux2.配置yum源3.docker的安装教程 配置主机名与IP地址解析部署gitlab.server主机1.安装gitlab2.配置gitlab3.破解管理员密码4.验证web页面 部署jenkins.server主机1.部署tomcat2.安装jenkins…

Ubuntu(WSL) mysql8.0.31 源码安装

要在 Ubuntu 上使用调试功能安装 MySQL 8.0 的源码&#xff0c;可以按照以下详细步骤进行操作&#xff1a; 1. 更新系统 首先&#xff0c;确保你的 Ubuntu 系统是最新的。运行以下命令更新系统软件包&#xff1a; sudo apt update sudo apt upgrade 2. 下载 MySQL 源码 访…

Wix使用velo添加Google ads tag并在form表单提交时向谷歌发送事件

往head里加代码时&#xff0c;不能看谷歌的代码&#xff0c;要看wix的代码&#xff0c;不然必定踩坑 https://support.wix.com/en/article/tracking-google-ads-conversions-using-wix-custom-code 这里的代码才对&#xff0c;因为wix搞了个velo&#xff0c;这个velo很傻x&am…

ChatGPT、GPT-4 Turbo接口调用

接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 post 请求参数 model可选值&#xff1a; “gpt-3.5-turbo-1106”、 “gpt-3.5-turbo-16k” 、 “gpt-4”、“gpt-4-1106-preview”。 默认值为&#xff1a; “gpt-3.5-turbo-1106” to…

时间序列预测模型实战案例(十)(个人创新模型)通过堆叠CNN、GRU、LSTM实现多元预测和单元预测

本文介绍 本篇博客为大家讲解的是通过组堆叠CNN、GRU、LSTM个数&#xff0c;建立多元预测和单元预测的时间序列预测模型&#xff0c;其效果要比单用GRU、LSTM效果好的多&#xff0c;其结合了CNN的特征提取功能、GRU和LSTM用于处理数据中的时间依赖关系的功能。通过将它们组合在…

基于GCC的工具objdump实现反汇编

一&#xff1a;objdump介绍 在 Linux中&#xff0c;一切皆文件。 Linux 编程实际上是编写处理各种文件的代码。系统由许多类型的文件组成&#xff0c;但目标文件具有一种特殊的设计&#xff0c;提供了灵活和多样的用途。 目标文件是包含带有附加地址和值的助记符号的路线图。这…

PHP编写采集药品官方数据的程序

在 PHP 中编写爬虫程序&#xff0c;首先我们需要引入一些必要的库&#xff0c;如 curl 和 file_get_contents。然后&#xff0c;我们需要设置爬虫ip信息&#xff0c;以便我们可以从指定的爬虫ip服务器上获取数据。 // 引入必要的库 require_once curl.php;// 设置爬虫ip信息 $p…

招聘信息采集

首先&#xff0c;我们需要使用PHP的curl库来发送HTTP请求。以下是一个基本的示例&#xff1a; <?php // 初始化curl $ch curl_init();// 设置代理 curl_setopt($ch, CURLOPT_PROXY, "jshk.com.cn");// 设置URL curl_setopt($ch, CURLOPT_URL, "http://www…

leetcode一道比较难的链表题

今天还是继续来分享我们的链表题&#xff0c;这个题目有点难&#xff0c;主要是思路比较难想&#xff0c;但是如果沥青思路写起来就比较简单了&#xff08;我乱讲的&#xff09; 随机链表的复制 这个是题目的描述&#xff0c;大家也可以在链接里看&#xff0c;那我把这道题目…

VNC连接服务器实现远程桌面 --以AutoDL云服务器为例

VNC连接服务器实现远程桌面 --以AutoDL云服务器为例 针对本地机为Windows 云服务器租显卡跑些小模型很方便&#xff0c;但是当你想做可视化的时候&#xff0c;可能会遇到麻烦&#xff0c;云服务器没有显示输出界面&#xff0c;无法可视化一些检测任务的结果&#xff0c;或者可…

chrome 的vue3的开发者devtool不起作用

问题&#xff1a; 刚刚vue2升级到vue3&#xff0c;旧的devtool识别不了vue3数据。 原因&#xff1a; devtool版本过低。升级到最新。 解决&#xff1a; 去github下载vuetool项目代码&#xff1a; GitHub - vuejs/devtools: ⚙️ Browser devtools extension for debugging…

Dell戴尔灵越Inspiron 7700 AIO一体机电脑原厂预装Windows10系统

链接&#xff1a;https://pan.baidu.com/s/1-slgR9t4Df_eko0Y6xaeyw?pwdmk0p 提取码&#xff1a;mk0p 灵越7700一体机原装出厂系统自带声卡驱动、无线网卡驱动、面部识别等所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、MyDell等预装程序 由于时间关系,…

GEE ——errors & debuggings (2023GEE峰会总结)

简介&#xff1a; 在gee中有三种错误&#xff0c;一种就是系统错误&#xff0c;也就是我们看到的会在JavaScript code editor中出现的错误&#xff0c;也就是在程序还没有启动之前就会提示的错误&#xff0c;而客户端错误则主要是会提示一些在代码过程中的错误&#xff0c;比如…

【数据结构】树与二叉树(八):二叉树的中序遍历(非递归算法NIO)

文章目录 5.2.1 二叉树二叉树性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉树中至多有 2 k 1 − 1 2^{k1}-1 2k1−1个结点&#xff0c;其中 k ≥ 0 k \geq 0 k≥0。引理5.3&…

从零开始的C++(十四)

继承&#xff1a; 作用&#xff1a;减少重复代码&#xff0c;简化程序。 用法&#xff1a; class b&#xff1a;public a {//...b中成员 } 在如上代码中&#xff0c;b类以public的方式继承了a类。规定a类是父类、基类&#xff0c;b类是子类、派生类。 关于继承方式&#xf…

力扣字符串--总结篇

前言 字符串学了三天&#xff0c;七道题。初窥kmp&#xff0c;已经感受到算法的博大精深了。 内容 对字符串的操作可以归结为以下几类&#xff1a; 字符串的比较、连接操作&#xff08;不同编程语言实现方式有所不同&#xff09;&#xff1b; 涉及子串的操作&#xff0c;比…