首页 > 其他分享 >嵌入式通信协议-----SPI协议详解

嵌入式通信协议-----SPI协议详解

时间:2024-06-19 14:27:44浏览次数:28  
标签:通信协议 SPI Init ----- Dual hspi1 设备 时钟

目录

一、简介

1.概念

2.特点

3.优缺点

 4.应用场景

二、通信原理

三、通信特性

1.时钟频率

2.时钟极性(CPOL)

3.时钟相位(CPHA)

4.四种模式

5.多主机模式

(1)多片选

(2)菊花链

四、编程实现

五、三种SPI比较

1.Dual SPI

2.Queued SPI

3.三者的区别


一、简介

1.概念

      SPI(Serial Peripheral Interface)总线协 议是一种全双工同步串行总线通信协议,用于在 集成电路之间进行数据传输。SPI 总线通常由 一个主设备和多个从设备组成,,每个从设备都 有一个单独的片选信号,最高可以工作 在上百 MHz,一般 SPI 需要 4 根线,但是也可以使用三根线。

2.特点

  • 同步通信:SPI是同步通信协议,数据传输由主设备(Master)提供的时钟信号(SCLK)控制,保证数据同步传输。

  • 全双工通信:SPI支持全双工通信,主设备和从设备可以同时发送和接收数据。

  • 多从设备支持:一个主设备可以连接多个从设备,通过片选信号(Chip Select, CS)来选择具体的从设备进行通信。

  • 简单硬件连接:相比其他协议,如I²C,SPI的硬件连接相对简单,不需要复杂的握手信号。

3.优缺点

优点:

①高速传输:由于没有复杂的握手信号,SPI可以实现高速数据传输,几乎比IIC快两倍。

全双工通信:主从设备可以同时发送和接收数据,提高通信效率。

②灵活性高:可以轻松连接多个从设备,支持多种通信模式,数据传输更加灵活,可以传输任意大小的字。

③硬件结构简单:从站不需要唯一地址(与I2C不同)。从机使用主机时钟,不需要精密时钟振荡器/晶振(与UART不同)。不需要收发器(与CAN不同)。

缺点:

①引脚占用较多:每个从设备需要一个片选信号,如果有多个从设备,需要占用较多的GPIO引脚。

②无流控制和确认机制:SPI协议没有内置的流控制和数据确认机制,需要在应用层实现。

③传输距离短:与RS-232和CAN总线相比,只能支持非常短的距离。

④只允许一个主设备。

 4.应用场景

SPI协议广泛应用于需要高速数据传输和可靠性的场景,如:

①显示器驱动

②存储器(如SD卡、Flash存储器)

③传感器数据采集

④音频编解码器

⑤电机驱动

二、通信原理

SPI 一般应用由四个引脚组成(一主一从):

 SCLK(Serial Clock):串行时钟,由主机发出。

 MOSI(Master Output Slave Input):主机输出从机输入信号,由主机发出。

 MISO(Master Input Slave Output):主机输入从机输出信号,由从机发出。

 NSS(Slave Selected):选择信号,由主机发出,一般是低电位有效。

不同的芯片厂家命名不同除此之外还有:

 SCLK/SCK

 MOSI/SOMI/DIN/DI/SDI/SI/SIN

 MISO/SIMO/DOUT/SDO/SO/SOUT

 NSS/CE/CS/SSEL/PCS

SPI 的主从连接图如图1所示

图1 

其通信过程可分为以下几步:

        起始条件(Chip Select):主设备通过片 选信号(Chip Select)选择从设备,并发送 低电平信号,表示开始数据传输。

        传输方式:主设备和从设备之间使用 MOSI 和 MISO 两根线传输数据。主设备通过 MOSI 发送数据,从设备通过 MISO 接收数据,采集时机要根据模式选择来判定。

        时钟信号:主设备通过 SCLK 时钟信号控制 数据传输速率,同时从设备也按照同样的时钟 频率来接收数据。

        数据长度:SPI 总线没有固定数据长度的限 制,可以发送任意长度的数据块。

        结束条件(Chip Deselect):主设备通过 片选信号(Chip Deselect)取消对从设备的 选择,结束数据传输。

如下图2所示发送0x53为例:主机拉低NSS片选信号,启动通信,并且产生时钟信号,上升沿触发边沿信号,当主机在MOSI信号线一位一位开始发送0x53时(因为0101=0x5,0011=0x3所以根据低位在前先发送应为11001010),在MISO线路一位一位接收数据0X46(此处我也不懂为什么是01100010,有大佬解释一下就好了

图2

三、通信特性

        SPI 是一种非常灵活的通信协议,我们可以配置它的时钟极性、时钟相位等。

1.时钟频率

SPI的时钟频率是指用于同步数据传输的时钟信号的频率。它决定了数据传输的速度。时钟频率的配置需要考虑主设备和从设备的硬件限制以及通信距离等因素。

配置SPI时钟频率主要有以下步骤:

①确定主设备的最大时钟频率:查阅主设备的技术手册,找出SPI模块支持的最大时钟频率。

②确定从设备的最大时钟频率:查阅从设备的技术手册,找出从设备支持的最大时钟频率。

③选择合适的时钟频率:在主设备和从设备的最大支持频率之间选择一个合适的频率。这个频率不应超过任何一个设备的最大支持频率。

示例:以STM32微控制器为例,SPI时钟频率由APB时钟和波特率预分频器(Baud Rate Prescaler)决定。以下是一个配置示例

①假设APB2时钟频率为84MHz。

②波特率预分频器可以设置为2、4、8、16、32、64、128或256。假设我们希望配置SPI时钟频率为5.25MHz:

③SPI时钟频率 = APB时钟频率 / 波特率预分频器

5.25MHz = 84MHz / 16

④因此,波特率预分频器应设置为16。

2.时钟极性(CPOL)

        时钟极性用于设置时钟在空闲时的电平状态,

CPOL 为“1”则时钟空闲时为高电平。

CPOL 为“0”,则时钟空闲时为低电平。

如图 3 所示。这就直接导致了第一个信号沿是下降沿还是上升沿,通常 CPOL 和时钟相位(CPHA)配合使用。

图3


3.时钟相位(CPHA)

        时钟相位用于设置在第几个时钟沿发生对数据线就行采样,

当 CPHA=1 是表示第二个 时钟沿对数据线进行采样。

当 CPHA=0 时,在第一个时钟沿对数据进行采样。

如图 4 所 示:
 

图4

4.四种模式

上面我们知道了什么是相位和极性,为此我们可以根据他们之间的不同组合来将SPI分为四种模式主机与从机需要工作在相同的模式下才可 以正常通讯。实际中采用的较多的“模式 0”和“模式 3”。

SPI模式CPOLCPHA空闲时CLK时钟采样时刻
000低电平奇数边沿
101低电平偶数边沿
210高电平奇数边沿
311高电平偶数边沿

四种模式的具体解释:

Mode0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。

Mode1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

Mode2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

Mode3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

具体采样数据如图5所示:

5.多主机模式

前面说到SPI总线必须有一个主机,可以有多个从机,那么具体连接到SPI总线的方法有以下两种多片选和菊花链。

(1)多片选

①通常,每个从机都需要一条单独的CS线。

②如果要和特定的从机进行通讯,可以将相应的CS信号线拉低,并保持其他CS信号线的状态为高电平;如果同时将两个CS信号线拉低,则可能会出现乱码,因为从机可能都试图在同一条MISO线上传输数据,最终导致接收数据乱码。

(2)菊花链

菊花链(Daisy Chain)是一种常见的连接拓扑结构,用于串行连接多个设备,使得每个设备与前一个设备和下一个设备相连。菊花链连接在许多应用中都有广泛的使用,例如串行通信、计算机总线、音频设备和LED灯串等。

特点:

  • 串行连接:每个设备通过输入和输出端口与链上的下一个和上一个设备连接。
  • 单一通道:数据通过单一通道传输,依次通过每个设备。
  • 简化布线:相比星形拓扑结构,菊花链减少了布线的复杂度,因为不需要从每个设备到中心节点的独立连接。
  • 扩展方便:增加或移除设备相对简单,只需连接或断开相应的设备。

优点

  • 简化布线:减少了物理连线的复杂性。
  • 扩展方便:可以轻松增加或移除设备。

缺点

  • 延迟累积:每个设备增加了数据传输的延迟。
  • 可靠性问题:如果链中的一个设备出现故障,可能影响整个链的通信。

四、编程实现

​ 以国产芯片智芯半导体的Z20K118芯片为例,其例程为了实现使用SPI主机传输和接收数据其具体工程文件放在资源里:

硬件框图如图所示

软件代码如下

1.SPI结构体配置

const SPI_Config_t tSpiMasterCfgStruct = 
{
    SPI_MODE_MASTER,                     /* 模式选择 */
    32,                                  /* 数据帧的大小 */
    SPI_CLK_PHASE_FIRST,                 /* 时钟相位 */
    SPI_CLK_INACTIVE_LOW,                /* 始终极性 */
    SPI_TMOD_TR,                         /* 传输模式 */
    350,                                 /* 时钟分频 */
    2,                                   /* 发送FIFO阈值电平 */
    2                                    /* 接收FIFO阈值 */
};

2.系统初始化


    
    /* 选择osc时钟作为spi0的函数时钟 */
    CLK_ModuleSrc(CLK_SPI0, CLK_SRC_OSC40M);
    /* 设置spi0函数时钟的分频比 */
    CLK_SetClkDivider(CLK_SPI0, CLK_DIV_1);  
    /* 启用spi0的时钟 */
    SYSCTRL_EnableModule(SYSCTRL_SPI0);    
    /* 使能GPIO口引脚 时钟*/
    CLK_ModuleSrc(CLK_PORTB, CLK_SRC_OSC40M);   

    CLK_ModuleSrc(CLK_PORTE, CLK_SRC_OSC40M);

    SYSCTRL_EnableModule(SYSCTRL_PORTB);       

    SYSCTRL_EnableModule(SYSCTRL_PORTE);  
    
    /* 配置各IO口复用*/
    PORT_PinmuxConfig(PORT_B, GPIO_2, PTB2_SPI0_SCK);

    PORT_PinmuxConfig(PORT_E, GPIO_1, PTE1_SPI0_SIN);

    PORT_PinmuxConfig(PORT_B, GPIO_4, PTB4_SPI0_SOUT);

    PORT_PinmuxConfig(PORT_B, GPIO_5, PTB5_SPI0_PCS0); 

 3.使能SPI并下拉PCS选择从节点

    /* 初始化结构体配置 */
    SPI_Init(SPI0_ID, &tSpiMasterCfgStruct);
    /* 下拉PCS0选择从机 */
    SPI_SelectSlave(SPI0_ID, SPI_SS_PCS0);
    /* 使能SPI */
    SPI_Enable(SPI0_ID);

4.发送数据

 for(i=0; i<TRANS_LEN; i++)
    {
        localCnt = 0 ;
        /* 发送数据 */
        while (RESET == SPI_GetStatus(SPI0_ID, SPI_STATUS_TFNF));
        SPI_SendData(SPI0_ID, i);
        /* 等待状态寄存器的RFNE标志位被置位 */
        while(RESET == SPI_GetStatus(SPI0_ID, SPI_STATUS_RFNE))
        { 
            if(localCnt > 0xFFFF)
            {
                return RESET;
            }
            localCnt++;
        }
        /* 接收数据 */
        data[i] = SPI_ReceiveData(SPI0_ID);  
    }  

五、三种SPI比较

三种SPI通常是指标准SPI,Dual SPI,和Queued SPI 三种,标准SPI已经介绍过了不做过多介绍主要介绍后面两种SPI,以及三者的区别。

1.Dual SPI

        Dual SPI是一种增强的SPI通信模式,主要用于提高数据传输速率。它增加了数据传输通道,使得每个时钟周期可以传输更多的数据。

          Dual SPI只是针对SPI Flash而言,不是针对所有SPI外设。对于SPI Flash,全双工并不常用,因此扩展了mosi和miso的用法,让它们工作在半双工,用以加倍数据传输。也就是对于Dual SPI Flash,可以发送一个命令字节进入dual mode,这样MOSI变成SIO0(serial io 0),MISO变成SIO1(serial io 1),这样一个时钟周期内就能传输2个bit数据,加倍了数据传输。                 

特点

  • 双通道数据传输:使用两条数据线同时传输数据,通常是MOSI和MISO共同传输数据。
  • 更高的传输速率:在相同的时钟频率下,Dual SPI可以比标准SPI传输更多的数据。
  • 硬件要求高:需要支持Dual SPI模式的主设备和从设备。

2.Queued SPI

        Queued SPI是一种高级的SPI通信模式,通常用于需要高效管理多个SPI传输事务的场景。它在标准SPI的基础上增加了硬件队列(Queue)功能,可以预先配置多个SPI传输事务,主设备可以自动依次执行这些事务。

        在Dual SPI的基础上增加了两根I/O线(SIO2,SIO3),目的是一个时钟内传输4个bit,而QSPI就是Queued SPI的简写;

特点

  • 硬件队列:可以预先配置多个传输事务,并由硬件自动执行。
  • 高效管理:减少CPU干预,提高数据传输效率。
  • 适用于复杂场景:特别适用于需要频繁且复杂SPI传输的应用,如存储器访问。

Dual SPI和Queued SPI是针对flash的接口,根据flash工作的特性,将全双工改为半双工,提高通信的速率。 

3.三者的区别

(1)标准SPI是通用的,Dual SPI和QSPI只适用于flash;
(2)标准SPI:标准4线连接,全双工,同时收和发;
(3)Dual SPI:标准4线连接,半双工,2根数据线,并线发和收,双方向切换;
(4)QSPI:标准4线连接,半双工,4根数据线,并线发和收,双方向切换。

特性标准SPIDual SPIQueued SPI
数据通道单通道双通道单通道或者双通道
数据速率常规更高高效管理
硬件复杂度简单需要支持Dual SPI的设备需要支持QSPI的设备
传输速率普通更高最高
应用场景通用SPI通信高速数据传输复杂、频繁SPI传输

代码配置上的区别以stm32f4的HAl库为例:

①标准SPI代码配置

#include "stm32f4xx_hal.h"

SPI_HandleTypeDef hspi1;

void SPI1_Init(void)
{
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;
    HAL_SPI_Init(&hspi1);
}

②Dual SPI代码配置

配置Dual SPI时,需要额外设置数据线的双通道模式:

#include "stm32f4xx_hal.h"

SPI_HandleTypeDef hspi1;

void Dual_SPI1_Init(void)
{
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY; // 双通道接收
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;
    HAL_SPI_Init(&hspi1);

    // 额外的Dual SPI模式配置,根据具体芯片文档进行设置
}

③Queued SPI代码配置

QSPI配置较为复杂,需要配置队列管理和多通道模式

#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_qspi.h"

QSPI_HandleTypeDef hqspi;

void QSPI_Init(void)
{
    hqspi.Instance = QUADSPI;
    hqspi.Init.ClockPrescaler = 1;
    hqspi.Init.FifoThreshold = 4;
    hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
    hqspi.Init.FlashSize = 1;
    hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
    hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
    hqspi.Init.FlashID = QSPI_FLASH_ID_1;
    hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
    HAL_QSPI_Init(&hqspi);

    // 额外的队列和多通道配置,根据具体芯片文档进行设置
}

图片以及部分内容引自:SPI协议详解(图文并茂+超详细)-CSDN博客

标签:通信协议,SPI,Init,-----,Dual,hspi1,设备,时钟
From: https://blog.csdn.net/2201_75342985/article/details/139789971

相关文章

  • SpringData初步学习-连接MySQL数据库
    1.添加mysql驱动和spring-data-jpa依赖<dependencies><!--SpringDataJPA--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId><......
  • java面向对象-封装
    一封装(面向对象的三大特征--封装)1.1封装的概念将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问1.2封装的步骤私有化(private)是一个权限修饰符。可以修饰成员(成员变量和成员方法)被private修饰的成员......
  • 《产流模式的发现与发展》-芮孝芳-2013年1月发表于期刊<水利水电科技进展>
    摘要:回顾了产流理论的起源,指出Horton产流理论、Kohler与Linsley的5变量合轴相关图形式的降雨径流相关图,以及Dunne通过实验对Horton产流理论的拓展,奠定了产流理论和流域产流量计算方法的基础。总结了中国自20世纪50年代以来在这一领域的主要实践和理论探索,指......
  • HarmonyOS NEXT - 从TypeScript到ArkTS的适配指导
    一:ArkTS语法适配背景二:从TypeScript到ArkTS的适配规则三:适配指导案例ArkTS语法适配背景 ArkTS在保持TypeScript(简称TS)基本语法风格的基础上,进一步通过规范强化静态检查和分析,使得在程序开发期能检测更多错误,提升程序稳定性,并实现更好的运行性能。本文将进一步解释为什么......
  • HarmonyOS NEXT - ArkTS语言 - 模块、关键字、空安全
    模块程序可划分为多组编译单元或模块。每个模块都有其自己的作用域,即,在模块中创建的任何声明(变量、函数、类等)在该模块之外都不可见,除非它们被显式导出。与此相对,从另一个模块导出的变量、函数、类、接口等必须首先导入到模块中。导出可以使用关键字export导出顶层的声明......
  • java spring-data-jpa 使用方法
    SpringDataJPA是Spring生态系统的一部分,它提供了对JPA(JavaPersistenceAPI)的抽象,简化了数据访问层的开发。以下是使用SpringDataJPA的基本步骤和一些核心概念:1.添加依赖        在Maven项目的pom.xml文件中添加SpringDataJPA和相关数据库驱......
  • Oracle DataGuard异常处理【ORA-01119 ORA-17502 ORA-15041】→【ORA-01111 ORA-01110
    收到用户反馈DG上查不到最近一天的数据,怀疑同步有问题一、检查备库同步情况两个节点都未看到MRP0进程SQL>selectprocess,status,thread#,sequence#fromv$managed_standby;PROCESS STATUS THREAD# SEQUENCE#---------------------------------------......
  • 一行超长日志引发的 “血案” - Containerd 频繁 OOM 背后的真相
    案发现场:混沌初现2024年6月10日,本应是平静的一天。但从上午9点开始,Sealos公有云的运维监控告警就开始不停地响。北京可用区服务器节点突然出现大量“notready”告警,紧接着,系统自动触发004节点重启,让服务暂时恢复了正常。就在我以为这只是个小插曲的时候,7分钟后,广州可用......
  • 如何快速实现MODBUS TCP转Profinet---泗博网关EPN-330
    泗博网关EPN-330可作为PROFINET从站,支持与西门子S7-200SMART/300/400/1200/1500全系列PLC以及具有PROFINET主站的系统无缝对接,而ModbusTCP端,可以与ModbusTCP从站设备、主站PLC、DCS系统以及组态软件等进行数据交互。通过EPN-330,可以快速实现MODBUSTCP转Profinet,轻松地将多个M......
  • 汇编语言程序设计 - 显示EXE文件的头信息
    一、问题描述本程序旨在读取并显示一个DOS可执行文件(.EXE文件)的头信息。具体来说,该程序需要打开一个名为“xxx.exe”的文件,读取其头部信息并将其逐项打印出来,并在每个信息前面加上相应的标注,使输出的信息清晰易读。二、数据结构程序使用的主要数据结构包括:文件名字符串:存......