news 2026/4/16 15:56:08

SPI通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SPI通信

1.SPI通信简介

SCK:串行时钟线

MOSI:主机输出、从机输入(主机向从机发送数据)

MISO:主机输入、从机输出(主机从从机接收数据)

SS:从机选择(从机寻址)

2.SPI硬件电路

SS线都是低电平有效的,同一时间只有一条SS线能被置低电平(即同一时间只能选中一个从机)

当从机被选中时(即SS线被置低电平时),MISO才会是推挽输出,否则会被设置成高阻态(被断开)

3.SPI时序基本单元

1.起始和发送

就是起始就是选择从机,结束从机选中状态

2.收发数据时序(模式功能都一样)

1.模式0

MISO初始时为高阻态,结束时要重新配置为高阻态

数据提前移出和移入(MOSI在第0个边沿移出,在第1个边沿移入)

原因:数据要先移出才能移入

2.模式1

MISO初始时为高阻态,结束时要重新配置为高阻态

3.模式2

MISO初始时为高阻态,结束时要重新配置为高阻态

4.模式3

MISO初始时为高阻态,结束时要重新配置为高阻态

3.收发SPI时序格式(以芯片W25Q64作为参考)

基本格式:起始+指令码(读写等功能)+数据将被写入的地址+数据

4.基本配置格式(手动实现时序)

//从机选择(写SS的引脚) void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); } //时钟线 void MySPI_W_SCK(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue); } //主机将数据写入从机(发送) void MySPI_W_MOSI(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue); } //主机接收从机数据(接收) uint8_t MySPI_R_MISO(void) { return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6); } //初始化 void MySPI_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //将SCK、MOSI、SS配置成推挽输出 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //将主机输入、从机输出的MISO配置成上拉输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //置默认电平 MySPI_W_SS(1); MySPI_W_SCK(0); } //起始时序 void MySPI_Start(void) { MySPI_W_SS(0); } //终止时序 void MySPI_Stop(void) { MySPI_W_SS(1); } //交换一个字节(模式0收发数据) //注意:从机的操作是从机自动进行的,软件代码只需管主机 //方法一:使用掩码,取出每一位数据进行操作 uint8_t MySPI_SwapByte(uint8_t ByteSend) { uint8_t i, ByteReceive = 0x00; for (i = 0; i < 8; i ++) { MySPI_W_MOSI(!!(ByteSend & (0x80 >> i))); MySPI_W_SCK(1); if (MySPI_R_MISO()){ByteReceive |= (0x80 >> i);} MySPI_W_SCK(0); } return ByteReceive; } //方法二:将数据本身进行移位,相当于将主机数据一位一位移出再一位一位移入从机数据 //uint8_t MySPI_SwapByte(uint8_t ByteSend) //{ // uint8_t i; // // for (i = 0; i < 8; i ++) // { // MySPI_W_MOSI(!!(ByteSend & 0x80); // ByteSend<<=1; // MySPI_W_SCK(1); // if (MySPI_R_MISO()){ByteSend |= 0x01;} // MySPI_W_SCK(0); // } // // return ByteSend; //}

4.硬件实现SPI通信(STM32内部的SPI外设)

1.SPI外设简介

注意:SPI1挂载在APB2,PCLK是72MHz;而SPI2挂载在APB1,PCLK是36MHz

2.STM32中SPI外设的内部结构图

LSBFIRST:帧格式,可以选择数据是低位先行还是高位先行(给0,先发送MSB即高位先行;给1,先发送LSB即低位先行)

TXE:发送寄存器空

RXNE:接受寄存器非空

NSS:从机选择(低电平有效) 当SSOE置1时,NSS配置成输出(即成为主机);当SSOE清0后,NSS变为输入(即成为从机)

3.SPI基本结构图

4.硬件SPI的操作流程

1.主模式全双工连续传输(效率高)

示例为模式三

发送数据解释:开始时TXE=1表示发送寄存器空,TXE为0时,数据进入发送寄存器;TXE再次为1时,数据由发送寄存器进入移位寄存器,同时下一个数据紧接着进入发送寄存器

接收数据解释:开始时RXNE=0表示接受寄存器空,RXNE为1时,数据由移位寄存器进入接受寄存器,完成后清除RXNE(即直接置0)

这种数据传输模式是交叉的,并不是发送一个数据后马上接收此数据

2.非连续传输

基本逻辑:发送一个数据,然后等待此数据被接收,最后发送下一个字节。

注意:相较主模式全双工连续传输传输速度明显更慢

3.硬件SPI的实战代码

1.部分函数功能
//写DR数据寄存器 void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data); //读DR数据寄存器 uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
2.配置思路
1.RCC开启时钟(把SPI外设和对应的GPIO口的时钟打开)
2.配置GPIO(SCK、MOSI配置成复用推挽输出模式。MISO配置成上拉输入模式,SS配置成通用推挽输出)
3.配置SPI外设
4.开启SPI(使能)
3.基本配置格式(使用库函数实现时序)
//从机选择 void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); } //初始化 void MySPI_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //SPI1是APB2上的外设 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //开启SS从机选择时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //SCK和MOSI复用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //MISO上拉输入模式 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //SPI初始化 SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//SPI模式,决定SPI是主机还是从机 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//数据传输模式(此处为双线全双工) SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//8位数据帧 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//高位先行 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//128分频(SPI1外设72/128) //配置模式(模式0、1、2、3) SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//选择NSS模式(硬件NSS或软件NSS,此处为软件NSS) SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); MySPI_W_SS(1); } //起始时序 void MySPI_Start(void) { MySPI_W_SS(0); } //终止时序 void MySPI_Stop(void) { MySPI_W_SS(1); } //交换一个字节 uint8_t MySPI_SwapByte(uint8_t ByteSend) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);//等待TXE SPI_I2S_SendData(SPI1, ByteSend);//将数据写入DR while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//等待RNXE return SPI_I2S_ReceiveData(SPI1);//将DR中的数据读出 }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 10:58:49

19、应用的持续交付与部署策略

应用的持续交付与部署策略 在软件开发与运维的过程中,持续交付和不同的部署策略是保障软件稳定、高效发布的关键。下面将详细介绍如何搭建持续交付管道,以及规则发布、蓝绿部署和金丝雀部署等不同的部署策略。 持续交付管道搭建 在开始搭建持续交付管道之前,我们已经完成…

作者头像 李华
网站建设 2026/4/16 10:57:50

11、SSH 密钥使用与管理全攻略

SSH 密钥使用与管理全攻略 1. SSH 语法差异与基本操作 不同的 SSH 工具在语法上存在差异。例如,OpenSSH 使用“–i ”语法来指定私钥,而 SSH Communications 使用“–i identification”。在客户端创建识别文件的语法如下: echo “IdKey SSH2 - Shreya” >> ident…

作者头像 李华
网站建设 2026/4/16 10:58:04

【JavaSE】十五、线程同步wait | notify 单例模式 阻塞队列 线程池 定时器

文章目录Ⅰ. 线程同步一、wait && notify二、wait 与 sleep 的区别Ⅱ. 单例模式一、饿汉模式二、懒汉模式Ⅲ. 阻塞队列一、标准库中的阻塞队列 -- BlockingQueue二、自主实现阻塞队列&#xff08;理解原理、细节即可&#xff09;Ⅳ. 线程池一、Java 线程池总体架构为什…

作者头像 李华
网站建设 2026/4/16 0:41:43

15、用户与组管理全解析

用户与组管理全解析 1. 用户管理 1.1 修改用户 ID 修改用户 ID 时,该 ID 必须唯一,除非使用 -o 选项。操作前要确保用户未登录,且没有以该用户旧 ID 运行的进程。用户主目录中的文件 UID 会自动更改,但用户在其他位置的文件需手动修改,同时也要更改任何 crontab 或 a…

作者头像 李华
网站建设 2026/4/16 10:53:37

24、计算机技术术语与Linux认证全解析

计算机技术术语与Linux认证全解析 在计算机技术的领域中,有着众多专业术语和认证体系,了解这些内容对于深入学习和从事相关工作至关重要。下面将为大家详细介绍一些常见的计算机术语以及Linux专业认证的相关信息。 常见计算机术语解析 访问(access) :连接并使用设备,…

作者头像 李华