玩转STM32-通信协议SPI(详细-慢工出细活)

文章目录

  • 一、SPI的基础知识
    • 1.1 接口定义
    • 1.2 单机和多机通信
  • 二、STM32的SPI工作过程
    • 2.1 从选择(NSS)脚管理
    • 2.2 时钟相位与极性
    • 2.3 SPI主模式
    • 2.4 SPI从模式
  • 三、应用实例

一、SPI的基础知识

1.1 接口定义

SPI系统可直接与各个厂家生产的多种标准外围器件接口,它只需4条线:串行时钟线(SCK)、主机输入/从机输出数据线(MISO)、主机输出/从机输入数据线(MOSI)和低电平有效的从机选择线(NSS)。
(1)MISO:该引脚在从模式下发送数据,在主模式下接收数据。
(2)MOSI:该引脚在主模式下发送数据,在从模式下接收数据。
(3)SCK:串口时钟,作为主设备的输出,从设备的输入。
(4)NSS:这个是一个可选的引脚,用来选择主/从设备。它的功能是用来作为片选引脚,让主设备可以单独地与特定从设备通信,避免数据线上的冲突。
SPI是一个环形总线结构,由NSS、SCK、MISO、MOSI构成,单主和单从设备连接图如下图所示,NSS引脚设置为输入,MOSI引脚相互连接,MISO引脚相互连接,数据在主和从之间串行地传输(MSB位在前)。通信总是由主设备发起,主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚传回数据,属于全双工通信,数据输出和数据输入由同一个时钟信号同步,时钟信号由主设备通过SCK引脚提供。主机SPI时钟发生器在驱动移位寄存器移位的同时,产生时序由SCK引脚输出后控制从机移位寄存器。在SCK的控制下,主机移位寄存器的数据通过MOSI移位到从机移位寄存器中,而从机移位寄存器之前的数据通过MISO移位到主机移位寄存器中。
在这里插入图片描述

1.2 单机和多机通信

在多主机系统中,SPI还可以作为微处理器之间的通信。SPI子系统可以在软件控制下构成复杂或简单的系统,如一个主微控制器和几个从微控制器;几个微处理器互连,构成多主机系统,以及主微处理器和一个或多个从外围器件。

  1. 单主机通信
    多数应用场合用一个微处理器作为主机,它触发和控制向一个或多个外围器件传输数据或控制多个外围器件向主机传送数据,这些外围器件接收或提供传输的数据。
    这种主从SPI可用于微处理器与外围器件进行全双工、同步串行通信。SPI可以同时发出和接收串行数据。当SPI工作时,移位寄存器中的数据逐位从输出引脚输出(高位在前),同时从输入引脚接收的数据逐位移到移位寄存器(高位在前)。发出一个字节后,从另一个外围器件接收的字节数据进入移位寄存器。主SPI的是中国信号使传输过程同步。
    在这里插入图片描述
    许多简单的从外围器件只能接收主SPI的数据或只向主机发送数据。例如:串行-并行移位寄存器只能作为8位输出口。设置为主机的微处理器SPI控制向移位寄存器的发送过程。由于移位寄存器并不向SPI发出数据,因此SPI可以忽略接受的数据。
  2. 多主机通信
    SPI双主机多从机通信如下图所示。MOSI和MISO两个数据引脚用于接收和发送串行数据,高位MSB在前,地位LSB在后。当SPI设置为主机时,MISO是主机数据输入端,MOSI是主机数据输出端;当SPI设置为从机时,MISO是从机数据输出端,MOSI是从机数据输入端。
    在这里插入图片描述
    SCK是通过MISO和MOSI输入和输出数据的同步时钟。当SPI设置为主机时,SCK是主机时钟输出端;当SPI设置为从机时,SCK是从机时钟输入端。
    当SPI设置为主机时,SCK信号由内部微处理器总线时钟获得。当主机启动一次传输时,在SCK引脚自动产生8个时钟周期。对于主机或从机,都是从一个跳变沿进行采样,在另一个跳变沿移位输出或输入数据。
    NSS用于选择允许接收主机时钟和数据的从机。在数据传输之前NSS必须变为低电平,并在传输过程中保持为低电平。主机的NSS必须接到高电平。

二、STM32的SPI工作过程

2.1 从选择(NSS)脚管理

通过SPI_CR1寄存器的SSM位可以设置NSS的两种模式:软件NSS模式和硬件NSS模式。硬件/软件的从选择管理如下图所示。
在这里插入图片描述

  1. 软件NSS模式
    可以通过设置SPI_CR1寄存器的SSM位来使能这种模式。在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。
  2. 硬件NSS模式
    (1)NSS输出被使能:当STM32F10xxx工作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备。
    当一个SPI设备需要发送广播数据,它必须拉低NSS信号,以通知所有其它的设备它是主设备;如果它不能拉低NSS,这意味着总线上有另外一个主设备在通信,这时将产生一个硬件失败错误(Hard Fault)。
    (2)NSS输出被关闭:允许操作于多主环境。

2.2 时钟相位与极性

SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。如果CPOL被清’0’,SCK引脚在空闲状态保持低电平;如果CPOL被置’1’,SCK引脚在空闲状态保持高电平。 如果CPHA(时钟相位)位被置’1’,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为’1’时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。如果CPHA位被清’0’,SCK时钟的第一边沿(CPOL位为’0’时就是上升沿,CPOL位为’1’时就是下降沿)进行数据位采样,数据在第一个时钟边沿被锁存。CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。
下图显示了SPI传输的4种CPHA和CPOL位组合。此图可以解释为主设备和从设备的SCK脚、MISO脚、MOSI脚直接连接的主或从时序图。
来自数据手册

2.3 SPI主模式

  1. 主模式配置过程
    在主配置时,在SCK脚产生串行时钟。 配置步骤
    1) 通过SPI_CR1寄存器的BR[2:0]位定义串行时钟波特率。
    2)选择CPOL和CPHA位,定义数据传输和串行时钟间的相位关系(见图212)。
    3) 设置DFF位来定义8位或16位数据帧格式。
    4)配置SPI_CR1寄存器的LSBFIRST位定义帧格式。
    5)如果需要NSS引脚工作在输入模式,硬件模式下,在整个数据帧传输期间应把NSS脚连接到高电平;在软件模式下,需设置SPI_CR1寄存器的SSM位和SSI位。如果NSS引脚工作在输出模式,则只需设置SSOE位。
    6)必须设置MSTR位和SPE位(只当NSS脚被连到高电平,这些位才能保持置位)。
    在这个配置中,MOSI引脚是数据输出,而MISO引脚是数据输入。
  2. 数据发送过程
    当写入数据至发送缓冲器时,发送过程开始。 在发送第一个数据位时,数据字被并行地(通过内部总线)传入移位寄存器,而后串行地移出到MOSI脚上;MSB在先还是LSB在先,取决于SPI_CR1寄存器中的LSBFIRST位的设置。数据从发送缓冲器传输到移位寄存器时TXE标志将被置位,如果设置了SPI_CR1寄存器中的TXEIE位,将产生中断。
  3. 数据接收过程
    对于接收器来说,当数据传输完成时:
    1)传送移位寄存器里的数据到接收缓冲器,并且RXNE标志被置位。
    2) 如果设置了SPI_CR2寄存器中的RXNEIE位,则产生中断。
    在最后采样时钟沿,RXNE位被设置,在移位寄存器中接收到的数据字被传送到接收缓冲器。读SPI_DR寄存器时,SPI设备返回接收缓冲器中的数据。 读SPI_DR寄存器将清除RXNE位。 一旦传输开始,如果下一个将发送的数据被放进了发送缓冲器,就可以维持一个连续的传输流。在试图写发送缓冲器之前,需确认TXE标志应该为’1’。

2.4 SPI从模式

在从模式下,SCK引脚用于接收从主设备来的串行时钟。SPI_CR1寄存器中BR[2:0]的设置不影响数据传输速率。

  1. 从模式配置步骤
    1)设置DFF位以定义数据帧格式为8位或16位。
    2) 选择CPOL和CPHA位来定义数据传输和串行时钟之间的相位关系(见图212)。为保证正确的数据传输,从设备和主设备的CPOL和CPHA位必须配置成相同的方式。
    3)帧格式(SPI_CR1寄存器中的LSBFIRST位定义的”MSB在前”还是”LSB在前”)必须与主设备相同。
    4) 硬件模式下(参考从选择(NSS)脚管理部分),在完整的数据帧(8位或16位)传输过程中,NSS引脚必须为低电平。在NSS软件模式下,设置SPI_CR1寄存器中的SSM位并清除SSI位。
    5)清除MSTR位、设置SPE位(SPI_CR1寄存器),使相应引脚工作于SPI模式下。 在这个配置中,MOSI引脚是数据输入,MISO引脚是数据输出。
  2. 数据发送过程
    在写操作中,数据字被并行地写入发送缓冲器。 当从设备收到时钟信号,并且在MOSI引脚上出现第一个数据位时,发送过程开始(译注:此时第一个位被发送出去)。余下的位(对于8位数据帧格式,还有7位;对于16位数据帧格式,还有15位)被装进移位寄存器。当发送缓冲器中的数据传输到移位寄存器时,SPI_SP寄存器的TXE标志被设置,如果设置了SPI_CR2寄存器的TXEIE位,将会产生中断。
  3. 数据接收过程
    对于接收器,当数据接收完成时:
    1)移位寄存器中的数据传送到接收缓冲器,SPI_SR 寄存器中的RXNE标志被设置。
    2)如果设置了SPI_CR2寄存器中的RXNEIE位,则产生中断。 在最后一个采样时钟边沿后,RXNE位被置’1’,移位寄存器中接收到的数据字节被传送到接收缓冲器。当读SPI_DR寄存器时,SPI设备返回这个接收缓冲器的数值。 读SPI_DR寄存器时,RXNE位被清除。

三、应用实例

  1. 实例介绍
    STM32通过SPI接口读取Flash W25X16的ID,并通过USART发送到上位机,通过串口调试助手显示ID信息。
  2. 实例程序
    // 串口初始化
void UsartDriver_Init(void)
{GPIO_InitTypeDef GPIO_InitStureture;USART_InitTypeDef USART_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);// PA9  TXGPIO_InitStureture.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStureture.GPIO_Pin = USART_GPIO_TX_PIN;GPIO_InitStureture.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(USART_GPIO_PORT,&GPIO_InitStureture);// PA10 rxGPIO_InitStureture.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStureture.GPIO_Pin = USART_GPIO_RX_PIN;GPIO_Init(USART_GPIO_PORT,&GPIO_InitStureture);USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_Init(USART_PORT,&USART_InitStructure);USART_Cmd(USART_PORT,ENABLE);
}/*串口1连续发送函数*/
void BdUsart1Trans(float *p, int16_t len)
{uint16_t i;for(i = 0;i < len; i++){USART_SendData(USART1 , p[i]);while(USART_GetFlagStatus(USART1 , USART_FLAG_TC) == RESET) {}; // FLAG=0,未发完,等待}
}/*串口1连续接收函数*/
void BdUsart1Recv(char *p, int16_t len)
{uint16_t i;for(i = 0;i < len; i++){if(USART_GetFlagStatus(USART1 , USART_FLAG_RXNE) == SET){p[i] = USART_ReceiveData(USART1);}; // FLAG=1, 收到数据}
}

// SPI初始化

// SPI
void SPIDriver_Init(void)
{GPIO_InitTypeDef GPIO_InitStureture;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1,ENABLE);GPIO_InitStureture.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStureture.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6| GPIO_Pin_7;GPIO_InitStureture.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStureture);// csGPIO_InitStureture.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStureture.GPIO_Pin = GPIO_Pin_2;GPIO_Init(GPIOA,&GPIO_InitStureture);GPIO_SetBits(GPIOA,GPIO_Pin_5 | GPIO_Pin_6| GPIO_Pin_7);// 设置SPI单向或双向的数据模式SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;// 设置主模式SPI_InitStructure.SPI_Mode = SPI_Mode_Master;// 8位帧结构SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;// 时钟悬空高SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;// 数据捕获在第二个时钟沿SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;// 内部NSS信号由软件管理SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;// 波特率预分频为256SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;// 数据从MSB位开始传输SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;// CRC值计算的多项式,大于7即可SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);
}// 读取SPI
uint8_t SPI_ReadWriteByte(uint8_t TxData)
{uint8_t retry = 0;//检查指定的SPI标志位设置与否:发送缓存标志位while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET){retry++;if(retry > 200)return 0;}//通过外设SPI发送一个数据SPI_I2S_SendData(SPI1,TxData);retry = 0;// 检查指定的SPI标志位设置与否while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET){retry++;if(retry > 200)return 0;}//接收数据return SPI_I2S_ReceiveData(SPI1);
}

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

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

相关文章

Python | 开房门(map)

常把map称之为映射&#xff0c;就是将一个元素&#xff08;通常称之为key键&#xff09;与一个相对应的值&#xff08;通常称之为value&#xff09;关联起来 通常用**字典dict**实现了映射这种数据结构 字典也是使用{}来包裹&#xff08;set也是{}&#xff09;&#xff0c;每…

ChatGPT-4o独家揭秘:全国一卷高考语文作文如何轻松斩获满分?

​一、2024年全国一卷高考 二、2018年全国一卷高考 三、2016年全国一卷高考 一、2024年全国一卷高考 技术进步的悖论&#xff1a;我们的问题真的在减少吗&#xff1f; 引言 随着互联网的普及和人工智能的应用&#xff0c;越来越多的问题能够快速得到解答。然而&#xff0c;这引…

网络空间安全数学基础·同余式

6.1 剩余系&#xff08;掌握&#xff09; 6.2 同余式概念与一次同余式&#xff08;熟练&#xff09; 6.3 中国剩余定理&#xff08;熟练&#xff09; 6.1 剩余系 设m是正整数&#xff0c;模m同余的全体整数是一个模m剩余类&#xff0c;即可表示为a qmr&#xff0c; 0≤r<…

idea使用和了解

官网&#xff1a;IntelliJ IDEA – the Leading Java and Kotlin IDE

【MyBatisPlus条件构造器】

文章目录 什么是条件构造器&#xff1f;使用步骤1. 引入 MyBatisPlus 依赖2. 创建实体类3. 使用条件构造器查询4. 执行查询 示例代码 什么是条件构造器&#xff1f; 条件构造器是 MyBatisPlus 提供的一种灵活的查询条件设置方式&#xff0c;它可以帮助开发者构建复杂的查询条件…

持续总结中!2024年面试必问 20 道分布式、微服务面试题(二)

上一篇地址&#xff1a;持续总结中&#xff01;2024年面试必问 20 道分布式、微服务面试题&#xff08;一&#xff09;-CSDN博客 三、CAP定理是什么&#xff1f; CAP定理是分布式系统理论中的一个基本概念&#xff0c;由计算机科学家Eric Brewer在2000年提出&#xff0c;并由…

常见的api:Runtime Object

一.Runtiem的成员方法 1.getRuntime() 当前系统的运行环境 2.exit 停止虚拟机 3.avaliableProcessors 获取Cpu线程的参数 4.maxMemory JVM能从系统中获取总内存大小(单位byte) 5.totalMemory JVM已经从系统中获取总内大小(单位byte) 6.freeMemory JVM剩余内存大小(…

数组对象数据修改后页面没有更新,无法进行编辑,校验失效问题

在 Vue 中&#xff0c;当你通过 Object.assign 或其他方式修改了对象中的某个属性时&#xff0c;Vue 并不会触发组件重新渲染&#xff0c;因此表单中的 input 框无法及时更新。这可能导致在修改表单数据后&#xff0c;页面没有更新&#xff0c;而且表单校验也失效的情况。这是因…

【MATLAB高级编程】入门篇 | 向量化编程

【入门篇】向量化编程 1. 什么是向量?2. 向量的创建2.1 行向量2.2 列向量2.3 使用冒号运算符2.4 使用`linspace`和`logspace`3. 向量的基本操作3.1 向量元素访问3.2 向量的长度3.3 向量的加法和减法3.4 向量的点乘和叉乘3.5 向量的元素乘法和除法4. 向量的高级操作4.1 逻辑索引…

牛客练习赛126(O(n)求取任意大小区间最值)

牛客练习赛126(O(n)求取任意大小区间最值) 牛客练习赛126 A.雾粉与签到题 题意&#xff1a;给出长度为n的数组, 顺序选出任意三个元素&#xff0c;最小化第二个元素 思路&#xff1a; 遍历除了第一个和最后一个元素取最小值即可 AC code&#xff1a; void solve() {int…

深度学习模型中的 `.clone` 作用分析----针对CUDA可能出现的BUG

深度学习模型中的 .clone 作用分析 问题描述与解决 在训练自定义网络结构时出现了两个错误&#xff1a; RuntimeError: CUDA error: device-side assert triggeredRuntimeError: one of the variables needed for gradient computation has been modified by an inplace ope…

使用 tc (Traffic Control)控制网络延时

设置网络延时 1500ms 800ms tc qdisc add dev eth0 root netem delay 1500ms 800msping 测试 ping www.baidu.com取消设置网络延时 sudo tc qdisc del dev eth0 root

解决splice改变原数组的BUG

项目场景&#xff1a; 项目中难免遇到需要删除改变数组的方法&#xff0c;去重&#xff0c;删除不要的数据等 问题描述&#xff1a; 但是splice方法会删除掉数据改变原数组&#xff0c;返回的是改变之后的数组&#xff0c;即使你是赋值的还是会影响到原数组的数据 GoodsInfo…

leetcode刷题记录31-50. Pow(x, n)

问题描述 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;x^n &#xff09;。 示例 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出&#xff…

inflight 守恒和带宽资源守恒的有效性

接着昨天的问题&#xff0c;inflight 守恒的模型一定存在稳定点吗&#xff1f;并不是。如果相互抑制强度大于自我抑制强度&#xff0c;系统也会跑飞&#xff1a; 模拟结果如下&#xff1a; 所以一定要记得 a < b。 比对前两个图和后两个图的 a&#xff0c;b 参数关系&am…

【ARM64 常见汇编指令学习 19.2 -- ARM64 地址加载指令 ADR 详细介绍】

文章目录 地址加载指令 ADRADR 指令使用场景例子注意事项 地址加载指令 ADR ARMv8 架构引入了一系列的改进和扩展&#xff0c;包括对汇编指令集的更新。在这之中&#xff0c;ADR 指令是一个重要的组成部分&#xff0c;它用于计算并加载一个地址到寄存器。 ADR 指令 ADR 指令…

PS初级|写在纸上的字怎么抠成透明背景?

前言 上一次咱们讲了很多很多很多的抠图教程&#xff0c;这次继续。。。最近有小伙伴问我&#xff1a;如果是写在纸上的字&#xff0c;要怎么把它抠成透明背景。 这个其实很简单&#xff0c;直接来说就是选择通道来抠。但有一点要注意的是&#xff0c;写在纸上的字&#xff0…

鸿蒙开发的南向开发和北向开发

鸿蒙开发主要分为设备开发和应用开发两个方向&#xff0c;也叫南向开发和北向开发&#xff1a; 鸿蒙设备开发(南向开发&#xff09;&#xff0c;要侧重于硬件层面的开发&#xff0c;涉及硬件接口控制、设备驱动开发、鸿蒙系统内核开发等&#xff0c;目的是使硬件设备能够兼容并…

android antirollback verno 获取方法

ReadRollbackIndex.exe 获取 调查avbVBMeta结构体 typedef struct AvbVBMetaImageHeader { /* 0: Four bytes equal to "AVB0" (AVB_MAGIC). */ uint8_t magic[AVB_MAGIC_LEN]; /* 4: The major version of libavb required for this header. */ uint32_t…

MySQL创建表的时候建立联合索引

1.MySQL创建表建立联合索引的步骤 在MySQL中&#xff0c;联合索引&#xff08;也称为复合索引或多列索引&#xff09;是基于表中的多个列创建的索引。这种索引可以提高多列查询的性能&#xff0c;特别是当查询条件涉及这些列时。下面是一个详细的步骤和示例&#xff0c;说明如…