三大低速总线之SPI

三大低速总线之SPI


文章目录

  • 三大低速总线之SPI
  • 前言
  • 一、基本概念
    • 1.1 物理层
    • 1.2 协议
      • 1.3 传输过程
  • 二、实战FLASH芯片
    • 2.1 SPI-Flash 全擦除实验
      • 2.1.1 程序设计
    • 2.2 SPI-Flash 扇区擦除实验
      • 2.2.1 整体设计
    • 2.3 SPI-Flash 页写实验
      • 2.3.1 操作时序
    • 2.4 SPI_Flash 读数据实验
    • 2.4.1 时序
  • 总结


前言

SPI(串行外设接口)以其高速度而著称,使其成为快速通信的首选。与 I2C 不同,SPI 使用四线工作:MISO(主输入从输出)、MOSI(主输出从输入)、SCK(串行时钟)和 SS(从选择),允许全双工通信(发送和同时接收)。尽管简单且速度快,但 SPI 比 I2C 需要更多的引脚,这可能是电路设计中需要考虑的一个因素。优点:

高速:SPI通信速度较快,适用于对速度要求较高的应用。
**全双工:**SPI支持全双工通信,可以同时进行数据发送和接收。
**简单:**SPI的通信协议相对简单,适用于快速开发和实现。

SPI的模式选择可以通过查看设备的数据手册或通过软件设置来实现。 SPI通信协议支持多种模式,这些模式由‌时钟极性(‌CPOL)和‌时钟相位(‌CPHA)的不同组合定义。具体选择哪种模式,取决于设备的具体需求和设计

缺点:

连线复杂:SPI需要多根线进行连接,可能会增加硬件设计的复杂性。
长距离传输受限:SPI的传输距离受到限制,过长的线路可能导致信号衰减和干扰。
主从模式限制:SPI通常采用主从模式,主设备数量受限,不适用于多主设备场景。
应用案例:SPI 非常适合需要快速可靠的数据传输的情况,例如 TFT 显示器SD 存储卡无线通信模块。然而,在具有许多从站的复杂系统中,其有效性会降低。

一、基本概念

1.1 物理层

在这里插入图片描述
SPI 通讯协议包含 1 条时钟信号线、2 条数据总线和 1 条片选信号线, 时钟信号线为SCK,2 条数据总线分别为 MOSI(主输出从输入)、MISO(主输入从输出),片选信号线为CS(低有效),

1.2 协议

时钟极性和相位就是什么时候空闲,什么时候采样。
模式 0、模式 1、模式 2 以及模式 3,这 4 种模式分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义。
有4中模式,具体使用哪种模式根据硬件需求来确定。
CPOL 比较好理解,就是表示设备未被选中的空闲状态时,串行时钟 SCK 的电平状态,CPOL = 0,空闲状态时 SCK 为低电平,CPOL = 1,空闲状态时SCK 为高电平;CPHA 的不同参数则规定了数据采样是在 SCK 时钟的奇数边沿还是偶数边沿,CPHA = 0,数据采样是在 SCK 时钟的奇数边沿,CPHA = 1,数据采样是在 SCK 时钟的偶数边沿,这里不使用上升沿或下降沿表示,是因为不同模式下,奇数边沿或偶数边沿

即CPOL = 0表示空闲时,SCK为低电平,CPHA 表示偶数或者奇数边沿采样。

在这里插入图片描述

1.3 传输过程

模式 0 为例:
在这里插入图片描述
此外,还需要确定一下几点:

  • 一次传输的bit
  • 先传高位还是低位(每次传输时不受限制的,一般是8/16bit)
  • 使用什么模式

二、实战FLASH芯片

2.1 SPI-Flash 全擦除实验

擦除方式有两种:

  • 通过 Quartus 软件的“programmer”窗口,将烧录到Flash 的*.jic 文件擦除
  • 编写全擦除程序
    实验目标:事先向 Flash 芯片中烧录流水灯程序,FPGA 上电执行流水灯程序,下载 Flash 芯片全擦除程序到 FPGA 内部 SRAM 并执行,擦除 Flash 芯片中烧录的流水灯程序,FPGA 重新上电后,无程序执行

此外,需要了解flash芯片需求的时序要求
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
片选信号自下降沿始到第一个有效数据写入时止,这一段等待时间定义为片选信号有效建立时间 tSLCH,由图 38-15 可知,这一时间段必须大于等于 5ns;片选信号自最后一个有效数据写入时始到片选信号上升沿止,这一段等待时间定义为片选信号有效保持时tCHSH,这一时间段必须大于等于 5ns;片选信号自上一个上升沿始到下一个下降沿止,这一段等待时间定义为片选信号高电平等待时间 tSHSL这一时间段必须大于等于 100ns。

即片选信号和有效数据写入之间的时序关系;和两个片选型号之间的时间间隔

2.1.1 程序设计

整体设计如下:
在这里插入图片描述
在这里插入图片描述
时序图如下:
在这里插入图片描述
设计要点:

  • flash芯片时钟频率选择为12.5MHz,即一个完整的bit指令输入为32个时钟周期
  • 为了节省计数器资源,统一规划片选和数据之间,片选和片选之间的等待时间也是32个时钟周期,因此只需要一个计数器,计数到32即可
`timescale  1ns/1ns

module  flash_be_ctrl
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号

    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010 ,   //写状态
            DELAY   =   4'b0100 ,   //等待状态
            BE      =   4'b1000 ;   //全擦除状态
parameter   WR_EN_INST  =   8'b0000_0110,   //写使能指令
            BE_INST     =   8'b1100_0111;   //全擦除指令

//reg   define
reg     [2:0]   cnt_byte;   //字节计数器
reg     [3:0]   state   ;   //状态机状态
reg     [4:0]   cnt_clk ;   //系统时钟计数器
reg     [1:0]   cnt_sck ;   //串行时钟计数器
reg     [2:0]   cnt_bit ;   //比特计数器

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk  <=  5'd0;
    else    if(state != IDLE)
        cnt_clk  <=  cnt_clk + 1'b1;

//cnt_byte:记录输出字节个数和等待时间
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte    <=  3'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == 3'd6))
        cnt_byte    <=  3'd0;
    else    if(cnt_clk == 31)
        cnt_byte    <=  cnt_byte + 1'b1;

//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if((state == WR_EN) && (cnt_byte == 1'b1))
        cnt_sck <=  cnt_sck + 1'b1;
    else    if((state == BE) && (cnt_byte == 3'd5))
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
      cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 3'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 3'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == 3'd6) && (cnt_clk == 5'd31) && (state == BE))
        cs_n    <=  1'b1;

//sck:输出串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <=  cnt_bit + 1'b1;

//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(key == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 3'd2) && (cnt_clk == 5'd31)) 
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 3'd3) && (cnt_clk == 5'd31))
                state   <=  BE;
        BE:     if((cnt_byte == 3'd6) && (cnt_clk == 5'd31))
                state   <=  IDLE;
        default:    state   <=  IDLE;
    endcase

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 3'd2))
        mosi    <=  1'b0;
    else    if((state == BE) && (cnt_byte == 3'd6))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 3'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];    //写使能指令
    else    if((state == BE) && (cnt_byte == 3'd5) && (cnt_sck == 5'd0))
        mosi    <=  BE_INST[7 - cnt_bit];       //全擦除指令

endmodule

2.2 SPI-Flash 扇区擦除实验

实验目标:
编写扇区擦除工程,擦除事先烧录到 Flash 中的流水灯程序所占的某个扇区,使流水灯程序不能正常工作。在此次实验工程,我们选择擦除第 0 个扇区,擦除地址为24’h00_04_25。

2.2.1 整体设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3 SPI-Flash 页写实验

使用页写指令,向 Flash 中写入 N 字节数据,N 为整数,且大于 0 小于等于 256。在本本 实 验 中 我 们 向 Flash 芯 片 中 写 入 0-99 , 共 100 字 节 数 据 , 数 据 初 始 地 址 为24’h00_04_25

2.3.1 操作时序

由数据手册中页写操作介绍部分可知,页写指令是根据写入数据将存储单元中的“1”置为“0”,实现数据的写入。在写入页写指令之前,需要先写入写使能(WREN)指令,将芯片设置为写使能锁存(WEL)状态;随后要拉低片选信号,写入页写指令、扇区地址、页地址、字节地址,紧跟地址写入要存储在 Flash 的字节数据,在指令、地址以及数据写入过程中,片选信号始终保持低电平,待指令、地址、数据被芯片锁存后,将片选信号拉高;片选信号拉高后,等待一个完整的页写周期(tPP),才能完成 Flash 芯片的页写操作。读者还要注意的是,Flash 芯片中一页最多可以存储 256 字节数据,这也表示页写操作一次最多向 Flash 芯片写入 256 字节数据。页写指令写入后,随即写入 3 字节数据写入首地址,首地址为扇区地址、页地址、字节地址组成,扇区地址与页地址是确定数据写入 Flash的特定扇区的特定页,字节地址位再该页数据写入的字节首地址。当数据写入的字节首地址为该页的首地址,及字节首地址为 8’b0000_0000,数据写入个数为 0-256 字节,数据可以被正确写入 Flash 芯片;当数据写入的字节首地址不是该页的首地址,及字节首地址不是 8’b0000_0000,数据写入个数为 0-256 字节,若数据写入个数少于字节首地址地址到末地址之间的存储单元个数,数据可以被正确写入 Flash 芯片;若数据写入个数多于字节首地址地址到末地址之间的存储单元个数,等于字节首地址地址到末地址之间的存储单元个数的数据可以被正确写入 Flash 芯片,超出的那部分数据,会以 8’b0000_0000 为字节首地址顺序写入本页,覆盖改地址之前存储的数据。

2.4 SPI_Flash 读数据实验

使用页写或连续写操作向 Flash 芯片写入数据,再使用数据读操作读取之前写入数
据,将读取的数据使用串口传回 PC 机,使用串口助手传回数据并与之前写入数据比较,
判断正误。
*注意:在向 Flash 芯片写入数据之前,先要对芯片执行全擦除操作。

2.4.1 时序

要执行数据读指令,首先拉低片选信号选中 Flash 芯片,随后写入数据读(READ)指令,紧跟指令写入 3 字节的数据读取首地址,指令和地址会在串行时钟上升沿被芯片锁存。随后存储地址对应存储单元中的数据在串行时钟下降沿通过串行数据总线输出。数据读取首地址可以为芯片中的任何一个有效地址,使用数据读(READ)指令可以对芯片内数据连续读取,当首地址数据读取完成,会自动对首地址的下一个地址进行数据读取。若最高位地址内数据读取完成,会自动跳转到芯片首地址继续进行数据读取,只有再次拉高片选信号,才能停止数据读操作,否者会对芯片执行无线循环读操作

总结

其实对于spi协议来说就只需要确定时什么时候采样什么时候空闲,然后空闲的时候变化数据,每次传输的bit数和先传高还是低和了解从设备的硬件特性。

对于flash芯片来说需要注意的时序就是cs和有效数据之间的时序,cs和cs之间的时序。

### SPI走线设计规范与最佳实践 #### 一、基本概念 SPI(Serial Peripheral Interface)是一种同步串行通信接口,广泛应用于微控制器与其他外围设备之间的短距离通信。尽管SPI是无地址的总线协议[^1],它依然具备严格的电气特性和物理布局要求。 #### 二、走线长度与时序控制 为了保持信号完整性并减少反射噪声的影响,在设计过程中应尽可能缩短MOSI (Master Out Slave In), MISO (Master In Slave Out),SCK (Serial Clock) 和 CS/SS (Chip Select / Slave Select) 的走线长度。较长的走线可能会引入额外的时间延迟,影响系统的正常工作频率。因此建议将主控芯片放置靠近从属器件的位置来优化布线路径[^4]。 #### 、阻抗匹配与端接电阻配置 当传输速率较高时,可能需要考虑终端匹配的问题。如果源端和负载两端之间存在显著差异,则会在接收端产生回波干扰。此时可以在每条差分对或单端线上加装适当小的串联终结电阻(通常为22Ω~75Ω),以达到良好的阻尼效果,从而提高信噪比(SNR)。 #### 四、电源管理与去耦电容布置 稳定的供电环境对于维持稳定的数据交换至关重要。应在靠近SPI模块处安装低ESR值的小容量陶瓷电容器作为局部滤波器,并确保GND平面具有足够的面积覆盖整个电路区域,以便提供快速有效的电流返回路径。此外,还应该注意避免不同电压域间的相互干扰。 #### 五、屏蔽措施与EMC防护 为了避免外部电磁场带来的不利因素,可以采取一些必要的隔离手段比如增加接地缝合孔阵列或者利用金属罩体包裹敏感部分等方式增强整体抗扰能力。同时也要遵循相关标准规定下的辐射发射限值要求,防止产品上市后因不符合法规而遭遇阻碍。 ```cpp // 示例代码展示如何设置SPI参数 void setup_SPI(){ // 设置为主模式 SPI.begin(); // 配置波特率因子 SPI.setClockDivider(SPI_CLOCK_DIV16); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值