三大通信协议(3)SPI——寄存器配置

2023-11-09

目录

一、SPI通信协议简介

二、SPI通信时序

1.主从通信

2.模式选择

三、实例

总结


一、SPI通信协议简介

     SPI是串行外设接口(Serial Peripheral Interface)的缩写,是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线,支持全双工通信、通信速率快(可以最高可达几十兆)。适用于主机与外设之间的近距离通信,经常应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间的通信。由于其通信速率快,支持全双工通信,并且占用的引脚数目较少(4根传输线),目前市面上很多芯片都集成了这种通信协议。当然,SPI通信协议也存在自身的缺点,没有指定的流控制,同时也没有相应的应答机制,因此在通信过程中缺乏一定的可靠性。

二、SPI通信时序

1.主从通信

       SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时),也是所有基于SPI的设备共有的,它们是SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选)。

2.模式选择

      SPI通信有4种不同的模式,不同的从设备可能在出厂是就是配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)即在没有数据传输时,时钟的空闲状态的电平和CPHA(时钟相位)即数据的采样时刻两个信号来控制我们主设备的通信模式,

具体如下:

   如果CPOL被清0,则SCLK在空闲状态保持低电平,反之被置1则保持高电平;如果CPHA位被清0,则在SCLK每个时钟周期的第1个边沿(奇数边沿)进行数据位采样,反之被置1则在SCLK每个时钟周期的第2个边沿(偶数边沿)采样

三、实例

          该例子是学习了V3学院——尤老师的腾讯课堂SPI接口实例。主机是FPGA,从机是DAC3283,通过FPGA进行写操作,将数据通过SPI协议发送给从机,完成主机与从机之间的单向通信(实质是FPGA通过该协议对DAC中的32个寄存器进行配置)。

    DAC3283内部共有32个寄存器,下图是每个寄存器的使用手则。

  每个寄存器最多可以写入的数据为5Byte,其中第1Byte用于命令传输,包括读写控制、传输字节个数、寄存器地址;剩余Byte用于数据传输,进行寄存器配置。第一个Byte的命令配置如下图所示:

      下图是发送第一个Byte和第二个Byte的时序图:

       设计过程:首先将要发送的命令信息和数据信息存在RAM中,RAM的深度是32(供32个寄存器),位宽是16(8bite的命令和8bite的数据),一次发送16bite信息,共发送32次。每次信息发送之间需要有一定的时间间隔,因此本设计采用状态机的形式进行设计。状态机共包括以下五个状态,上电复位后处于IDLE状态,Work_en拉高,进入到WAIT状态进行等待,等待8个SCLK时钟周期后进入READ_MEM状态,在该状态下将读RAM的地址累加,并将RAM中的数据读出存入到databuff中,等待一个系统周期 后进入WRITE_REG状态,在该状态下进行串并转换,将databuff中的数据输出给spi,同时输出SCLK时钟信号和sdenb使能信号,16个SCLK时钟周期进入WAIT状态,进行下次数据的发送,如果在WRITE_REG状态中检测到32组数据全部发送完毕,则进入STOP状态。在设计过程中应注意SCLK的上升沿对应每次数据的中心位置,保证充足的建立时间和保持时间。下图是对应的状态转移图和Verilog代码。

状态转移图:

时序图:

Verilog代码如下:

module  spi_ctrl(
          input              sys_clk,
	  input              sys_res,
	  input              work_en,
//	  input              spo,
	  output     reg     sclk,
	  output     reg     spi,
	  output     reg     sdenb
);
parameter       IDLE = 5'b00001;
parameter       WAIT = 5'b00010;
parameter       READ_MEM = 5'b00100;
parameter       WRITE_REG = 5'b01000;
parameter       STOP = 5'b10000;
reg     [4:0]state;
reg     [4:0]fre_count;
wire          sclk_n;
reg          sclk_p;
reg          sclk_flag;
reg     [3:0]wait_cnt;
reg     [3:0]shift_cnt;//数据移位计数器,前八位为命令位,后八位为数据位
reg     [4:0]r_addr;//读RAM中的地址计数器   
reg     [15:0]shift_buff;//存储从RAM中读出的数据
reg           data_end;


wire         wren_sig;
wire   [15:0]r_data;
  

always@(posedge sys_clk or negedge sys_res)begin
     if(!sys_res)
	     fre_count <= 5'd0;
	 else if(fre_count == 5'd24)
	     fre_count <= 5'd0;
	 else 
	     fre_count <= fre_count + 1'd1;
end 

always@(posedge sys_clk or negedge sys_res)begin
     if(!sys_res)
         sclk_p <= 1'b0;
	 else if(fre_count == 5'd24)
	     sclk_p <= ~sclk_p;
	 else 
	     sclk_p <= sclk_p;
end 

assign sclk_n = ~sclk_p;

//sclk_flag 为分频后的时钟sclk_n一个周期的标志
always@(posedge sys_clk or negedge sys_res)begin
     if(!sys_res)
	    sclk_flag <= 1'b0;
	 else if(fre_count == 5'd24 && sclk_n == 1'b0)
	    sclk_flag <= 1'b1;
	 else 
	    sclk_flag <= 1'b0;
end 

always@(posedge sys_clk or negedge sys_res)begin
     if(!sys_res)
	wait_cnt <= 4'd0;
     else if(state == WAIT && sclk_flag)
        wait_cnt <= wait_cnt + 1'b1;
     else if(state != WAIT)
        wait_cnt <= 4'd0;
     else
        wait_cnt <= wait_cnt;
end 
always@(posedge sys_clk or negedge sys_res)begin
     if(!sys_res)
	shift_cnt <= 4'd0;
     else if(state == WRITE_REG && sclk_flag)
        shift_cnt <= shift_cnt + 1'b1;
     else if(state != WRITE_REG)
        shift_cnt <= 4'd0;
     else
        shift_cnt <= shift_cnt;
end 
always@(posedge sys_clk or negedge sys_res)begin
     if(!sys_res)
	    state <= IDLE;	
     else begin
        case(state)
		   IDLE:if(work_en)
		           state <= WAIT;
				else 
				   state <= state;
		   WAIT:if(wait_cnt[3])
		           state <= READ_MEM;	
				else
				   state <= state;
		   READ_MEM:state <= WRITE_REG;
		   WRITE_REG:if(shift_cnt == 4'd15 && sclk_flag && data_end)
						     state <= STOP;
					 else if(shift_cnt == 4'd15 && sclk_flag)
						     state <= WAIT;
					 else
					     state <= state;
		   STOP:state <= state;
           default:state <= IDLE; 		   
        endcase		
	 end 
end 

always@(posedge sys_clk or negedge sys_res)begin
     if(!sys_res)
	    r_addr <= 5'd0;
	 else if(state == READ_MEM)
	    r_addr <= r_addr + 1'b1;
	 else
	    r_addr <= r_addr; 
end  
always@(posedge sys_clk or negedge sys_res)
      if(!sys_res)
	     data_end <= 1'b0;
	  else if(state == READ_MEM && (&r_addr) == 1'b1)//等价于r_addr == 5‘d31
	     data_end <= 1'b1;
	  else
	     data_end <= data_end;


always@(posedge sys_clk or negedge sys_res)begin
   if(!sys_res)
	    shift_buff <= 16'd0;
	 else if(state == READ_MEM)
	    shift_buff <= r_data;
	 else if(state == WRITE_REG && sclk_flag)
	    shift_buff <= {shift_buff[14:0],1'b1};
	 else
	    shift_buff <= shift_buff;
end 

always@(posedge sys_clk or negedge sys_res)
     if(!sys_res)
	    spi <= 1'b0;
	 else if(state == WRITE_REG)
	    spi <= shift_buff[15];
	 else 
	    spi <= 1'b0;

always@(posedge sys_clk or negedge sys_res)
      if(!sys_res)
	   sdenb <= 1'b1;
        else if(state == WRITE_REG)
           sdenb <= 1'b0;
        else
           sdenb <= 1'b1;	  
	   
always@(posedge sys_clk or negedge sys_res)
      if(!sys_res)
	     sclk <= 1'b0;
	  else if(state == WRITE_REG)
	     sclk <= sclk_p;
	  else
	     sclk <= 1'b0;

assign wren_sig = 1'b0;
ram_16_32_r ram_16_32_r_inst (
    .address ( r_addr ),
    .clock ( sys_clk ),
    .data ( 16'd0 ),
    .wren ( wren_sig ),//写使能高有效,读使能低有效
    .q ( r_data )
    );

endmodule

仿真验证

tb测试代码:

`timescale  1ns/1ns
module tb_spi_ctrl;
reg     clk;
reg     res;
reg     work_en;
//wire    spo;
wire    sclk;
wire    spi;
wire    sdenb;
reg     [15:0]read_mem[31:0];
reg     [15:0]shift_buffer = 16'd0;
initial  begin
      clk <= 1'b0;
	  res <= 1'b0;
	  #200 res <= 1'b1;
end

initial  begin
     work_en <= 1'b0;
	 #250 work_en <= 1'b1;
end

initial begin
     $readmemb("ram_data.txt",read_mem);
end 
initial begin
    rec_spi();
end 


always #10 clk <= ~clk;

task  rec_spi();
   integer  i,j;
   begin

        for(i=0;i<32;i=i+1)begin
		    for(j=0;j<16;j=j+1)begin
			
			     @(posedge sclk)
				 shift_buffer = {shift_buffer[14:0],spi};
				 if(j == 15 && shift_buffer == read_mem[i])
				      $display("ok,data index is %d,rec_data is %d,send_data is %d",i,shift_buffer,read_mem[i]);
				 else if(j == 15)
				      $display("error");
			 	  
			end 
		end 

   end 

endtask


  spi_ctrl u_spi_ctrl(
         .sys_clk(clk),
	 .sys_res(res),
	 .work_en(work_en),
	// .spo(spo),
	 .sclk(sclk),
	 .spi(spi),
	 .sdenb(sdenb)
);
  
  
  
endmodule 

读取的数据:

0000000000011111
0000001010011001
0000000000011000
0000001011110110
0001010101000111
0000001001001010
0000001100010001
0010000101111111
0001010101011100
0000011100011100
0000000000000000
0000001100010001
0001011011110011
0000000001010110
0000000000000000
0000001100010001
0010001010101011
0000000000000100
0000001100010001
0001011011100001
1101100111101001
0000000000111000
0000000001010111
0000001101101011
0001111010101110
0000001000111111
0000000111001010
0000000000000000
0000000001010111
0000000001010110
1101010100111011
0000000111001000

仿真结果(注意由于调用了ramip核,因此,在仿真的过程中需要添加ramip核生成的.v文件以及库文件altera_mf.v文件):



总结

        SPI通信协议应用范围十分广泛,接下来将继续学习一下该通信协议的一些例子,比如在FPGA内部对外部Flash进行全擦除或扇区擦除以及FPGA对外部Flash进行读、写操作的实验。

        初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

三大通信协议(3)SPI——寄存器配置 的相关文章

  • FPGA面试真题解析(3)

    9 寄存器的Tsu 建立时间 是如何定义的 硬件逻辑实习岗 A 在时钟沿到来之后数据保持稳定的时间 B 在时钟沿带来前后数据都需要保持稳定的时间 C 在整个时钟周期数据保持稳定的时间 D 在时钟沿到来之前数据保持稳定的时间 解析 考察数字电
  • 基于FPGA的频率计设计

    文章目录 写在前面 1 什么是频率计 2 测量方法与基本原理 3 待测信号如何输入FPGA 一 实验程序 1 RTL图 2 Verilog 参考设计 3 指派引脚 二 调试验证 输入不同频率的方波 写在前面 1 什么是频率计 频率计是一种专
  • [FPGA系列] 扩展知识 --- 时钟小结

    一 基本概念 时钟域 由同一个时钟信号控制的区域 时钟抖动 Jitter 相对于理想时钟信号 实际时钟信号存在时而超前 时而之后的偏移 时钟偏斜 Skew 时钟信号到达数字电路各个部分所用时间的差异 时钟漂移 Wander 工程上解释 抖动
  • 硬件设计---了解电源篇

    1 概述 在高速电路设计中一块单板上常存在多种电源 3 3V 1 8V 1 2V 1 0V 0 9V 0 75V等 有时光是对FPGA供电就需要五六种电源 为了便于使用往往用户只需要提供一种或几种电源 然后经过板上电源模块转换到各个目标电源
  • 2022芯原芯片设计 笔试题分析和讨论

    2022芯原设计笔试题分析和讨论 以下仅为个人理解和分析 不保证正确 欢迎大家发表自己的想法 讨论出正确答案 企业知识题 1 1 D 芯原的主要经营模式为芯片设计平台即服务 Silicon Platform as a Service SiP
  • Xilinx 7系列芯片选型手册的资源量怎么看

    推荐阅读AMD官方文档 该文档介绍了各种资源的具体含义 链接 7 Series FPGAs Configurable Logic Block User Guide UG474 以XC7A35T为例 Logic Cells 逻辑单元 对于7系
  • Lattice Diamond安装

    1 下载 到Lattice官网 http www latticesemi com 注册一个lattice的账号后就可以去下载Diamond 登陆后如下图 根据自己系统情况选择对应的版本 我用的是32位win8 Diamond软件安装包和La
  • libero-soc许可证申请和环境配置

    环境 64位机 在哪台电脑上安装libero soc 就用哪台电脑申请许可证 1 注册 https www microsemi co 在官网注册 之后申请的许可证会发到注册时填写的邮箱 2 申请许可证 https www microsemi
  • FPGA学习笔记(一)__电平知识

    常见电平标准 文章目录 1 TTL电平标准 2 LVTTL电平标准 1 LVTTL3V3 2 LVTTL2V5 3 CMOS电平标准 4 LVCOMS电平标准 1 LVCOMS3V3 2 LVCOMS2V5 3 LVCOMS1V8 4 LV
  • FPGA功耗估计(二)

    针对于Altera的Cyclone III 做出了静态功耗 对于Altera 其提供了一个功耗早期估计工具 可以在官网上下到 首先需要将宏设置为安全 在excel选型中选择文件 之后便可看到 根据相应的选择 红框部分 可以查看静态功耗 对于
  • Spartan-3E 上的随机数生成

    我需要在 Spartan 3E FPGA 上为我的遗传算法生成伪随机数 并且我想在 verilog 中实现它 您能给我任何关于此的指示吗 当然 Adam 的随机生成器是不可合成的 您必须显式创建一个LFSR 以下示例可能会有所帮助 它是一个
  • 基于FPGA的简易BPSK和QPSK

    1 框图 2 顶层 3 m generator M序列的生成 输出速率为500Kbps 4 S2P是串并转换模块 将1bit的m序列转换到50M时钟下的2bit M序列数据 就有4个象限 5 my pll是生成256M的时钟作为载波 因为s
  • 修改后的 baugh-wooley 算法乘法 verilog 代码不能正确乘法

    以下 verilog 源代码和 或测试平台可以很好地工作商业模拟器 iverilog https www edaplayground com x 3TuQ也形式化验证工具 yosys smtbmc https gist github com
  • if 语句导致 Verilog 中的锁存推断?

    我正在编写用于合成算法的 Verilog 代码 我对哪些情况可能导致推断锁存器有点困惑 下面是这样的一段代码 虽然它在模拟中工作得很好 但我担心它可能会导致硬件问题 always b1 or b2 b1 map b2 map m1 map
  • 如何在Altera Quartus中生成.rbf文件?

    什么是 rbf 文件以及如何在 Windows 上从 Quartus 输出文件 sof 生成它们 An RBF is a 原始二进制文件例如 它代表原始数据 这些数据将被加载到闪存中 以便在上电时初始化 FPGA A SOF is an S
  • FPGA 有哪些实际应用?

    我对我的程序为一个小型七段显示器提供动力感到非常兴奋 但是当我向不在现场的人展示它时 他们总是说 那么你能用它做什么 我永远无法给他们一个简洁的答案 谁能帮我吗 第一 它们不需要具有易失性存储器 事实上 大厂商 Xilinx Altera
  • FPGA大输入数据

    我正在尝试向 FPGA 发送 4 KB 字符串 最简单的方法是什么 是我正在使用的fpga的链接 我正在使用 Verilog 和 Quartus 您的问题的答案在很大程度上取决于将数据输入 FPGA 的内容 即使没有您需要遵守的特定协议 S
  • 如何从 Spartan 6 写入 Nexys 3 FPGA 板上的 Micron 外部蜂窝 RAM?

    我到处都查过了 数据表 Xilinx 网站 digilent 等等 但什么也没找到 我能够使用 Adept 工具来验证我的蜂窝 RAM 是否正常运行 但我找不到任何库存 VHDL 代码作为控制器来写入数据和从中读取数据 帮助 找到了此链接
  • 学习 Verilog 的资源 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我是 Verilog 新手 有人可以推荐学习资源 书籍 视频 博客或任何他们有良好个人经验并帮助他们更
  • 可以购买哪些 FPGA(现场可编程门阵列)在家中进行实验? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 什么是 FPGA 在哪里可以买到 它们要花多少钱 您需要什么样的系统来试验它们 如何对它们进行编程 如果这是正确的术语 您能否使用普通 M

随机推荐

  • 如何把Application项目转为Library项目

    如何把Application项目转为Library项目 本篇文章针对项目里含有ButterKnife框架的情况 Module里的Build gradle apply plugin com android application 改成appl
  • 台达b3伺服参数设置方法_技术分享

    西门子博途的全套视频教程下载 西门子S7 1500内部培训资料下载 台达 ASDA 伺服定位演示系统 控制要求 1 由台达 PLC 和台达伺服组成一个简单的定位控制演示系统 通过 PLC 发送脉冲控制伺服 实现原点回归 相对定位和绝对定位功
  • Pytorch实战——气温预测(基础)附完整代码

    一 数据集 http 链接 https pan baidu com s 1mVAIWZquUyaCpgj1K51o5A 提取码 abcd 该数据集记录了一年内的气温信息 我们将针对于此进行分析 以上是部分数据情况 请查看 二 气温预测实战
  • 【Nginx】Nginx发布社区最新稳定版1.22.1版本

    Nginx官方于2022年10月19日发布了1 22稳定版的第一个分支版本 1 22 1 根据官方对于此版本的修改说明 可以发现该版本修复了一个安全漏洞 风险等级 中等 在使用ngx http mp4 module模块来处理一个特别构造的m
  • 在Java中使用HttpURLConnection发送http客户端请求、服务器端返回信息、接受服务器端的信息

    在最近的项目中要用到后台发送http请求 post 方式 获取服务器端返回哦信息 下面是自己做的一个简单的演示示例 首先创建一个web工程 简单的一个web工程就好 里面创建两个jsp和一个servlet 其中jsp作为客户端 而servl
  • Ubuntu个人终端常用命令

    Ubuntu终端提供了丰富的命令行工具和功能 现将一些基本的Ubuntu终端命令记录下来 方便查看 1 文件和目录管理命令 ls 列出当前目录下的文件和文件夹 cd 切换到指定目录 pwd 显示当前工作目录的路径 mkdir 创建新目录 r
  • Android Studio安装和使用教程(全文图解)

    目录 JDK安装与配置 一 下载JDK 二 JDK安装 三 JDK的环境配置 四 JDK的配置验证 Android studio安装 Android studio连接手机真机调试 以华为鸿蒙为例 一 新建一个android项目 二 进入项目
  • LeetCode刷题笔记:669.修剪二叉搜索树

    1 问题描述 给你二叉搜索树的根节点 root 同时给定最小边界low 和最大边界 high 通过修剪二叉搜索树 使得所有节点的值在 low high 中 修剪树 不应该 改变保留在树中的元素的相对结构 即 如果没有被移除 原有的父代子代关
  • Chrome浏览器的跨域设置

    做前后端分离的开发的时候 出于一些原因往往需要将浏览器设置成支持跨域的模式 而且chrome浏览器支持可跨域的设置 但是新版本的chrome浏览器提高了跨域设置的门槛 原来的方法不再适用了 其实网上也有很多大神总结的chrome跨域设置教程
  • 去耦电容的选择举例

    在高速时钟电路中 尤其要注意元件的RF去耦问题 究其原因 主要是因为元件会把一部分能量耦合到电源 地系统之中 这些能量以共模或差模RF的形式传播到其他部件中 陶瓷片电容需要比时钟电路要求的自激频率更大的频率 这样可选择一个自激频率在10 3
  • JavaWeb酒店管理系统

    酒店管理系统 一 项目介绍 1 项目用到的技术栈 开发工具 idea 语言 java js html ajax 数据库 MySQL 服务器 Tomcat 框架 mybatis jQuery 2 项目实现功能 管理员和用户登录和退出功能以及用
  • ParameterizedType应用,java反射,获取参数化类型的class实例

    ParameterizedType是一个接口 这个类可以用来检验泛型是否被参数化 比如 class Dao
  • python https RecursionError详解

    RecursionError maximum recursion depth exceeded while calling a Python object File root miniconda3 envs devops lib pytho
  • Ubuntu下安装Pytorch

    安装CPU版本 https www cnblogs com wangqinze p 13407610 html 完成后检验是否有误 python import torch import torchvision 若没有报错则完成 安装GPU版
  • CMake I 设置语言标准

    目录 一
  • docker基础篇-docker的安装、配置、卸载,自定义镜像和搭建公有和私有镜像仓库

    本篇主要是docker的安装 配置 卸载 自定义镜像和搭建公有和私有镜像仓库 以及常规软件安装示例 mysql主从 redis主从搭建 Dockerfile解析及docker compose编排等见另一篇文章 docker高级篇 mysql
  • python之正则表达式:匹配ip地址

    首先分析ip地址的特征 255 255 255 255 1位 0 9 0 9 d 2位 10 99 1 9 d 2位以内 1 9 d 3位 100 199 1 d 2 3位 200 249 2 0 4 d 3位 250 255 25 0 5
  • 如何让opencv成为MATLAB中的一个工具箱

    如何让opencv成为MATLAB中的一个工具箱 前言 谈到图像处理的编程工具 MATLAB和opencv是最经常提到的 两者各有优缺点 比如 MATLAB的数据可视化做得很好 而且还有其它可以方便调用的工具箱 但是在图像的处理上面 不如o
  • IDEA快速生成方法

    快捷键 Alt Insert 按下快捷键之后会弹出如下界面 依次是Constructor方法 Getter方法 Setter方法 Getter 和Setter方法 equa 和 hashCode 方法 toString方法 Override
  • 三大通信协议(3)SPI——寄存器配置

    目录 一 SPI通信协议简介 二 SPI通信时序 1 主从通信 2 模式选择 三 实例 总结 一 SPI通信协议简介 SPI是串行外设接口 Serial Peripheral Interface 的缩写 是 Motorola 公司推出的一种