零死角玩转stm32中级篇3-SPI总线

本篇博文目录:

      • 一.基础知识
        • 1.什么是SPI
        • 2.SPI和IIC有什么不同
        • 3.SPI的优缺点
        • 4.SPI是怎么实现通信的
        • 5.SPI 数据传输的步骤
        • 6.SPI菊花链
        • 7.通过SPI实现数据的读和写
      • 二.STM32F103C8T6芯片SPI协议案例代码

一.基础知识

1.什么是SPI

SPI(Serial Peripheral Interface,串行外设接口)是一种同步的串行通信协议,它被用于在微控制器、存储器芯片、传感器和其他外围设备之间传输数据。SPI通常由四个线组成:时钟线(SCK)、主设备输出/从设备输入(MOSI)、从设备输出/主设备输入(MISO)和片选线(SS)。SPI通信中,数据在时钟的边沿上进行传输,以实现高速、可靠的数据传输。SPI可以支持单主机和多从机的连接方式,并且具有简单、灵活和可扩展的特点。

(主从连接方式1:一个主机上有多个SS片选信号去连接从机,如下图)
在这里插入图片描述

(主从连接方式2:一个主机上只有一个片选信号可以通过daisy-chained(菊花链)进行连接,如下图)

在这里插入图片描述

备注:图片来源于https://www.circuitbasics.com/basics-of-the-spi-communication-protocol/。

2.SPI和IIC有什么不同

SPI和I2C(Inter-Integrated Circuit,即IIC)都是常见的串行通信协议,它们在一些方面有所不同:

  1. 总线结构:SPI是点对点的结构,每个设备占用一个片选线;而I2C是多主从结构,允许多个设备通过两根共享的线路进行通信。

  2. 传输速率:SPI的传输速率通常比I2C更快,SPI可以达到几百MHz的传输速率,而I2C通常只能达到几十kHz或几百kHz的传输速率。

  3. 电气特性:SPI时钟线和数据线的电平是由驱动器控制的,因此SPI的电气特性更容易控制和优化,而且SPI在长距离传输时噪声抗干扰能力更强;而I2C的时钟和数据线由开漏输出控制,需要加上外部上拉电阻,电气特性控制相对较难。

  4. 硬件资源:SPI传输需要占用多个GPIO,因此需要更多的硬件资源来实现;而I2C只需要两个GPIO,可以减少芯片上的硬件资源占用。

总之,SPI和I2C都有其适用的场景。SPI适用于高速、简单的点对点通信,而I2C适用于多设备的通信,因为I2C允许多个设备在同一个总线上进行通信。

3.SPI的优缺点

优点:

  • 没有启动和停止位,所以数据可以不间断地连续流
  • 没有像 I2C 这样复杂的从站寻址系统
  • 数据传输速率比 I2C 高(几乎是 I2C 的两倍)
  • 单独的MISO 和 MOSI 线路,这样数据可以同时发送和接收

缺点:

  • 使用四根线(I2C 和 UART 使用两根)
  • 没有确认数据已经成功接收(I2C 有这个)
  • 没有像 UART 中奇偶校验位那样的错误检查形式
  • 只允许一个master

4.SPI是怎么实现通信的

① 在SPI通信中,数据是通过一个主设备与一个或多个从设备进行的。通信的过程是主设备向从设备发送数据,并且同时接收从设备发回的数据。SPI总线由四个信号线构成,分别是:

  1. SCLK(Serial Clock):时钟线,用于同步主从设备之间的数据传输。

  2. MOSI(Master Out Slave In):主设备输出数据到从设备的信号线。

  3. MISO(Master In Slave Out):从设备输出数据到主设备的信号线。

  4. SS(Slave Select):从设备的选中信号线,用于让主设备控制从设备的选择。

② 简单来说,当主设备需要跟某个从设备通信时,它会先把该从设备的SS线拉低(低电平有效还是高电平有效要根据元件的数据手册来看),表示选中该从设备,然后主设备以时钟信号为基准,通过MOSI线发送数据,从设备则通过MISO线将响应数据发回主设备。通信结束后,主设备会将该从设备的SS线拉高,表示不再选中该从设备。
③ SPI通信的速度可以通过调整时钟频率来实现,而具体的通信协议和数据格式则需要根据具体的应用场景来确定。

5.SPI 数据传输的步骤

  • 主机(master)输出时钟信号

在这里插入图片描述

  • 主机(master)将对应从机(方式一)的片选信号切换到低电平,从而激活对应从机

在这里插入图片描述

  • 主机(master)通过MOSI线向从设备一次一位地向从机发送数据,从设备读取接收到的数据

在这里插入图片描述

  • 如果从设备有相应的回应,从设备通过MISO线向主设备一次一位地向主机发送数据,主设备读取接收到的数据

在这里插入图片描述

6.SPI菊花链

在SPI菊花链方式中,各个从机的MISO(Master In Slave Out)输入都连接到前一个从机的MOSI(Master Out Slave In)输出上,一直到链的最后一个从机(如下图所示)。主机通过片选信号来选择与其通讯的从机,只有被选中的从机的MISO输出的数据才会被主机的MOSI输入。

在这里插入图片描述
备注:图片来源于https://zhuanlan.zhihu.com/p/290620901

具体的连接图如下:

在这里插入图片描述

SPI菊花链方式通信的基本步骤:

  • 主机发送片选信号(CS)来选择要与之通讯的从机。

  • 在所选从机的MISO输入处放置数据,同时主机在MOSI输出口发送相应的数据。

  • 当所选从机选定数据并将其内容从MISO输出时,主机也会将其内容从它的MISO输入口中读取,完整的数据交换完成。

  • 当主机需要与另一个从机通讯时,它会将片选信号切换到下一个从机上,然后重复上述步骤。

  • 当通讯完成时,主机可以停止发送片选信号。

需要注意的是,在SPI菊花链方式中,所有从机的MISO都连接在同一条线上。在未选中的情况下,从机将忽略主机发出的数据。因此,在设计SPI系统时需要确保未选中的从机在通讯期间处于高阻状态,以避免因为信号冲突而产生干扰。

  • 上文说所的未选中

① SPI菊花链中,所有从机都与同一条MISO线相连,但是在不同时间内只会有一个从机处于被选中状态,其他从机都处于未选中状态。这是通过从机的片选信号(CS)来实现的。当主机选择与某个从机通信时,它会向该从机的CS引脚发送低电平信号,从而告诉该从机它正在被选中。其他未被选中的从机,它们未选中时CS引脚通常为高电平状态,并且未选中的从机的MISO输出需要设置为高阻状态。
② 因此,在SPI菊花链方式中,需要确保未被选中的从机在通讯期间处于高阻状态,以避免干扰。最好的做法是在主机与某个从机通信之前,先将所有其他未选中的从机的片选信号拉高,保证它们的MISO输出都处于高阻状态。这样可以减少通信期间出现干扰的可能性。

  • 什么是高阻态

① 高阻状态是指一个电路中的输入端或输出端等待输入或输出信号时,它处于一种电气状态,该状态被称作高阻态或三态,简称"Z"态。处于高阻态的信号线会表现出一种很高的电阻,阻止其他电路对其进行电流或电压的驱动,这样可以保证电路的安全和稳定性。
② 在数字电路中,高阻态被广泛应用于多路复用器、锁存器和开关等电路中。例如,在多路复用器中,当选择器控制线不代表选中任何一个输入端口时,所有的输入端口都处于高阻状态,以避免输入端口之间的干扰。在锁存器中,当时钟信号处于非稳定状态时,输入端口处于高阻态,以避免输出端脱离预期状态。在开关中,当输出端口未被激活时,它处于高阻状态,以防止从该端口流出意外的电流引起不必要的能量损耗。

在这里插入图片描述

7.通过SPI实现数据的读和写

通过SPI协议进行数据的读写操作和其他通信协议(UART,IIC)一样也会有相应的数据格式,下面给出是93C46存储器的SPI数据读写的格式(具体的读写的数据格式通过数据手册进行查询)。

在这里插入图片描述

和异步不同的是,数据的发送会受到时钟线的控制,如下,就是当SS(片选线)为高电平时,进行数据的读写操作,从机采样数据由极性和相位决定,极性决定时钟SCK空闲时是高电平还是低电平,相位决定在第一个跳边沿还是第二个跳边沿进行数据采集,下图就是极性为低电平,相位为第一个跳边沿(上升沿:低->高)进行数据采集,采集MOSI上的数据,从机采集到了1 01 0000001 0000 1111的数据,然后进行解析,数据解析的结果表示将向000 0001地址写入数据0000 1111数据,从机解析完毕,执行解析的动作。

在这里插入图片描述

① SPI数据采样是由SPI总线信号的时钟极性和相位来决定的。通常情况下,SPI信号是由主设备(如微控制器)发出的,从设备(如传感器或存储器)则根据时钟信号进行响应。使用SPI时,必须确保主设备与从设备使用相同的时钟极性和相位,以确保数据采样的正确性。
② SPI采样方式有四种:mode0、mode1、mode2和mode3。下面分别介绍各种采样方式的时钟极性和相位:

  1. mode0:时钟极性为0,时钟相位为0。时钟极性为0表示空闲时时钟处于低电平,采样时时钟沿上升。时钟相位为0表示数据采样在时钟的上升沿进行,数据产生在下降沿。mode0是最常用的采样方式。

  2. mode1:时钟极性为0,时钟相位为1。时钟相位为1表示数据采样在时钟的下降沿进行,数据产生在上升沿。

  3. mode2:时钟极性为1,时钟相位为0。时钟极性为1表示空闲时时钟处于高电平,采样时时钟沿下降。时钟相位为0表示数据采样在时钟的下降沿进行,数据产生在上升沿。

  4. mode3:时钟极性为1,时钟相位为1。时钟相位为1表示数据采样在时钟的上升沿进行,数据产生在下降沿。

上面的解析可能不容易进行理解,大致意思就是SPI由极性和相位决定,极性和相位有二种取值即0和1,这样一共就由4种采集方式(00 01 10 11),其中当极性为0时表示时钟空闲状态为低电平(第一个跳变为上升沿),为1表示时钟空闲状态为高电平(第一个跳变为下降沿);而相位为0表示在第一个跳边沿进行数据采集,为1时表示在第二个跳边沿进行数据采集。下图中的图1的CPOL表示极性;图2CPHA表示相位;这二张图片来源于https://www.cnblogs.com/gmpy/p/12461461.html。

(图1)
在这里插入图片描述

(图2)
在这里插入图片描述

具体的四种采样方式如下图所示:

在这里插入图片描述

至于数据的读取,主机先通过MOSI向从机发送读取的操作指令(1 10 地址),从机收到该命令后,就会将数据放在MISO线上,主机就可以通过MISO,从从机的MISO上采集到对应的数据。

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

备注上图的图片和表格来源于:https://www.bilibili.com/video/BV1F54y1M7e7/

二.STM32F103C8T6芯片SPI协议案例代码

以下是一个基于STM32F103C8T6单片机的SPI协议案例代码,仅供参考(来源于ChartGPT):




#include "stm32f10x.h"

#define SPI_SCK_PIN  GPIO_Pin_5                //SPI时钟引脚
#define SPI_MISO_PIN GPIO_Pin_6                //SPI数据接收引脚
#define SPI_MOSI_PIN GPIO_Pin_7                //SPI数据发送引脚
#define SPI_CS_PIN   GPIO_Pin_4                //SPI片选引脚

void SPI_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);  //使能SPI1外设时钟

    //配置SPI1对应的GPIO口
    GPIO_InitStructure.GPIO_Pin   = SPI_SCK_PIN | SPI_MOSI_PIN | SPI_CS_PIN;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = SPI_MISO_PIN;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //配置SPI1参数
    SPI_InitStructure.SPI_Direction         = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL              = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA              = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial     = 7;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);  //使能SPI1外设
}

void SPI_SendByte(SPI_TypeDef* SPIx, uint8_t byte)
{
    while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET) ;  //等待发送缓冲区为空
    SPI_I2S_SendData(SPIx, byte);                                     //将数据写入发送缓冲区
    while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET) ; //等待接收缓冲区非空
    SPI_I2S_ReceiveData(SPIx);                                        //读取接收缓冲区数据,清除标志位
}

uint8_t SPI_ReceiveByte(SPI_TypeDef* SPIx)
{
    while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET) ;  //等待发送缓冲区为空
    SPI_I2S_SendData(SPIx, 0xFF);                                     //发送一个空数据,触发SPI通信
    while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET) ; //等待接收缓冲区非空
    return SPI_I2S_ReceiveData(SPIx);                                 //读取接收缓冲区数据
}

int main(void)
{
    uint8_t tx_data = 0x55;
    uint8_t rx_data;

    SPI_Init();  //初始化SPI1

    GPIO_ResetBits(GPIOA, SPI_CS_PIN);  //拉低SPI片选引脚,开始SPI通信
    SPI_SendByte(SPI1, tx_data);        //发送数据
    rx_data = SPI_ReceiveByte(SPI1);    //接收数据
    GPIO_SetBits(GPIOA, SPI_CS_PIN);    //拉高SPI片选引脚,结束SPI通信

    while (1)
    {
        //此处可添加其他代码
    }
}

以上代码实现了STM32F103C8T6单片机的SPI通信,并利用SPI1对应的GPIO口进行了初始化配置。在主函数中,通过拉低片选引脚、发送数据、接收数据、拉高片选引脚的方式实现了SPI通信。用户可以根据实际需求,在SPI_SendByte和SPI_ReceiveByte函数中修改数据的长度和数据位数等参数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flask开发之环境搭建

目录 1、安装flask 2、创建Flask工程 ​编辑 3、初始化效果 4、运行效果 5、设置Debug模式 6、设置Host 7、设置Port 8、在app.config中添加配置 1、安装flask 如果电脑上从没有安装过flask,则在命令行界面输入以下命令: pip install flask 如果电…

给大家介绍几个手机冷门但好用的小技巧

技巧一:拍照识别植物 手机的拍照识别植物功能是指在使用手机相机时,可以通过对植物进行拍照,并通过植物识别技术,获取植物的相关信息和资料。其主要优点如下: 方便实用:使用拍照识别植物功能,…

【Java笔试强训 18】

🎉🎉🎉点进来你就是我的人了博主主页:🙈🙈🙈戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔🤺🤺🤺 目录 一、选择题 二、编程题 🔥统计每…

基于springcloud微服务的java课程资源在线学习考试系统

在我国,由于计算机与网络技术的不断发展,信息化建设的不断深入,不管是企业、学校或个人都在结合计算机网络技术队现有的管理或生活中的一些环节进行开发研究,运用计算机进行一些必要的数据信息管理,分析及发布&#xf…

拷贝构造函数和赋值重载函数详解

1.拷贝构造函数 1.1拷贝构造函数的概念 拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。拷贝构造函数也是特殊的成员函数,其特征如下&#…

第三十一章 Unity骨骼动画

关于骨骼动画的原理,我们这里不再详细介绍,有不清楚的可以回去看DirectX课程和3dsMAX课程。接下来,我们来讲解一下Unity的骨骼动画系统。Unity 的动画系统基于动画剪辑(Animation Clip)的概念,它的本质就是…

LeetCode - 239 滑动窗口最大值

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 239. 滑动窗口最大值 - 力扣(LeetCode) 题目描述 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k…

springboot+vue前后端分离项目打包成jar包及运行

将 Spring Boot 和 Vue.js 项目打包成 jar 包需要按照以下步骤操作: 在项目的根目录中,使用命令行进入 Vue.js 项目的根目录,然后运行以下命令: npm run build这个命令将会构建 Vue.js 项目,并在项目的 dist 目录中生…

鸿蒙Hi3861学习八-Huawei LiteOS(事件标记)

一、简介 事件是一种实现任务间通信的机制,可用于实现任务间的同步。但事件通信只能是事件类型的通信,无数据传输。一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒…

华为网络设备+WinRadius 实现用户统一管理设备

一、直接贴配置 ###配置VTY用户界面所支持的协议、验证方式 user-interface vty 0 4 protocol inbound telnet authentication-mode aaa quit ###配置RADIUS认证 ###(1)配置RADIUS服务器模板,指定服务器的IP地址与端口号、共享密钥 radius-s…

Unity - Render Doc - 解决 Waiting For Debugger 导致连接不了 APP 的问题

环境 Unity : 2020.3.37f1 Pipeline : BRP RDC : 1.26 问题 平常有一些公司内的游戏发布在移动端运行会有各种异常,但是 unity editor (android opengl es / dx) 下正常 如果没有真机抓帧分析,是搞不定的 然后 RenderDoc 在抓发布出来的调试包也抓不…

漫画 | Linux之父:财务自由以后,我失眠了!

前言:今年是Linux诞生的30周年! 1991年的8月, Linus在新闻组中公布了他正在开发的一个免费的操作系统,这也是以后风靡世界的Linux操作系统的雏形。 今天翻到这篇漫画,看到Linux的诞生过程,很是感慨&#x…

SuperMap GIS基础产品云GIS FAQ集锦(2)

SuperMap GIS基础产品云GIS FAQ集锦(2) 【iManager】云套件ispeco-dashboard-api的日志等级只有到info,如何设置才能查看到debug级别的日志? 【解决方案】可以在ispeco-dashboard-api的deployment中添加以下环境变量,…

vue框架快速入门

vue 1、第一个Vue程序1.1、什么是Vue程序1.2、为什么要使用MVVM1.3、Vue1.4、第一个vue程序 2、基础语法2.1、v-bind2.2、v-if, v-else2.3、v-for2.4、v-on 3、Vue表单双绑、组件3.1、什么是双向数据绑定3.2、在表单中使用双向数据绑定3.3、什么是组件 4、Axios异步…

PyQt5 基础篇(一)-- 安装与环境配置

1 PyQt5 图形界面开发工具 Qt 库是跨平台的 C 库的集合,是最强大的 GUI 库之一,可以实现高级 API 来访问桌面和移动系统的各种服务。PyQt5 是一套 Python 绑定 Digia QT5 应用的框架。PyQt5 实现了一个 Python模块集,有 620 个类,…

从0学会Spring框架

文章目录 1. 对Spring的理解2. Spring IoC3. DI4. 如何创建一个Spring项目4.1 创建一个Maven项目4.2 添加Spring框架支持4.3 添加启动类 5. 存储Bean对象5.1 添加配置文件5.2 创建Bean对象5.3 注册Bean 6. 获取并使用Bean对象7. 更简单存储Bean对象的方式7.1 前置工作7.2 添加存…

基于javaweb的学生就业管理系统

一、简介 学生基业管理系统有三个角色:管理员、企业、学生 对学生信息管理、企业信息管理、求职信息管理 后端架构:spring springmvc mybatis 前端架构:jsp layui 系统环境:jdk1.8 | maven | mysql 二、主要功能 1. 登录…

一个集团企业,如何从0到1构建信息化系统?

当今时代,信息技术已经成为企业发展不可或缺的一部分,特别是对于一个大型集团公司来说,如何构建一个高效的信息化系统对于其业务发展至关重要。 我们想要构建一个优质高效的信息化系统,首先需要了解现在大的趋势是怎样的。 目前…

看我如何通过帮助服务台轻松黑掉数百家公司

导语:几个月前,我发现黑客可以利用一个漏洞访问目标公司的内部通信。 这个漏洞只需要点击几下,就可以访问企业内部网络、 Twitter等社交媒体账户,以及最常见的Yammer和Slack团队。 更新: The Next Web 写了一篇我发现的这个漏洞的…

【Java笔试强训 28】

🎉🎉🎉点进来你就是我的人了博主主页:🙈🙈🙈戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔🤺🤺🤺 目录 一、选择题 二、编程题 🔥猴子分桃…