news 2026/2/24 8:08:56

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/2/12 12:10:04

10、SSH 认证机制全解析:从密码到公钥的安全之旅

SSH 认证机制全解析:从密码到公钥的安全之旅 1. SSH 密码认证开启方法 在 Windows 平台上,开启 SSH Communications 的 SSH 服务器的密码认证十分简单。通过配置菜单(开始➪程序➪SSH Secure Shell Server➪配置),能找到用户认证部分的认证选项。在用户认证部分,有诸多…

作者头像 李华
网站建设 2026/2/17 7:03:39

25、SSH应用案例解析:安全访问与数据传输方案

SSH应用案例解析:安全访问与数据传输方案 在网络安全领域,SSH(Secure Shell)协议凭借强大的加密和身份验证功能,为远程访问和数据传输提供了安全保障。本文将通过三个具体案例,深入探讨SSH在不同场景下的配置与应用。 案例一:多用途SSH服务器配置 本案例涉及两个VShe…

作者头像 李华
网站建设 2026/2/23 7:58:25

1、深入了解 SSH:功能、优势与实施指南

深入了解 SSH:功能、优势与实施指南 1. SSH 概述 Secure Shell(SSH)是一种用途广泛的实用工具,它可以被描述为协议、加密工具、客户端/服务器应用程序或命令接口。SSH 凭借其多样的服务以及安全提供这些服务的能力,成为许多企业网络中的重要组成部分。 1.1 SSH1 与 SSH…

作者头像 李华
网站建设 2026/2/17 9:47:35

17、文本处理与输入输出操作指南

文本处理与输入输出操作指南 1. 常用文本处理命令 在文本处理中,有几个常用的命令可以帮助我们完成各种任务。 1.1 tr 命令 tr 命令可以用于字符替换和文件加密。例如,在显示 MyFile 文件时,tr 可以将单词 “Halloween” 改为 “Haloween”。同时,它还能使用 ROT13 方法…

作者头像 李华