SPI通信(STM32)

一、SPI通信

1、SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线

2、四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master I3、nput Slave Output)、SS(Slave Select) 同步,全双工 支持总线挂载多设备(一主多从)

二、硬件电路

1.所有SPI设备的SCK、MOSI、MISO分别连在一起

2.主机另外引出多条SS控制线,分别接到各从机的SS引脚

3.输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入

4.片选信号SS是低电平有效,哪个SS被置为低电平,总线上的设备就哪个起作用,有且至多    仅 有 一个从设备与主机通信

PS:

1、推挽输出均有很强的驱动能力,使得上升沿和下降沿的速度很快。

2、SS有高电平时(从机未被选中),此时从机的MISO切换成高阻态。

三、移位示意图

例如:主机的数据要发送到从机,从机的数据要发送主机(采用模式1)

1、时钟产生一个上升沿的电平,主机的高位移到MOSI(移位寄存器)线上,从机的高位移到MISO(移位寄存器)上。(SPI高位先行)

2、时钟产生一个下降沿时,主机会对MISO线上(移位寄存器)进行采样,从机会对MOSI线上(移位寄存器)进行采样

3、时钟继续产生一个上升沿的电平,主机的高位移到MOSI线上(移位寄存器),从机的高位移到MISO上(移位寄存器)。(SPI高位先行)

4、时钟继续产生一个下降沿时,主机会对MISO线上(移位寄存器)进行采样,从机会对MOSI线上(移位寄存器)进行采样

5、这样不断的循环直到主机把从机的数据置换过来

SPI通信最终的原理是主机与从机的字节交换,当主机没有数据给从机(主机只接收),而主机又需要从机的数据,主机就可以写一个垃圾数据(0XFF、0X00)给从机,就可以把主机想要的从机数据置换过来。当主机只发送的时候,从机也会随便写一个垃圾数据(0XFF、0XFF)给主机,将主机的数据置换出去

四、SPI时序

起始条件:SS从高电平切换到低电平

终止条件:SS从低电平切换到高电平

交换一个字节(模式0)

CPOL=0:空闲状态时,SCK为低电平

CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

由于SCK第一个边沿就要采样数据,那么从机和主机应该提前把数据移出来,所以在SS下降沿的时候主机和从机就移出数据了,如果一个字节交换结束之后还要继续交换数据,SCK的最后一个下降沿就会移出第二字节的最高位数据,如果主机和从机不继续交换数据,主机和从机的最高位也会冒一个头

交换一个字节(模式1)

CPOL=0:空闲状态时,SCK为低电平

CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

交换一个字节(模式2)

CPOL=1:空闲状态时,SCK为高电平

CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

交换一个字节(模式3)

CPOL=1:空闲状态时,SCK为高电平

CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据

五、硬件SPI代码

#include "stm32f10x.h"                  // Device header
#include "mySPI.h"//PA3 RTS
/*** 函    数:SPI写SS引脚电平,SS仍由软件模拟* 参    数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		//根据BitValue,设置SS引脚的电平
}
/*** 函    数:RST由软件模拟* 参    数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置RST为低电平,当BitValue为1时,需要置RST为高电平*/
void SPI_RST(uint8_t number)
{GPIO_WriteBit(GPIOA,GPIO_Pin_3,(BitAction)number);
}/*** 函    数:SPI初始化* 参    数:无* 返 回 值:无*/
void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);	//开启SPI1的时钟/*GPIO初始化*/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;		//SPI1 NSS/SDAGPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA4引脚初始化为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;	//SPI1 MOSI/--7GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//SPI1 SCK/--5GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA5和PA7引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//SPI1 MISO/GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA6引脚初始化为上拉输入//PA3是RC522的复位引脚GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;				 //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 PA3 RTSGPIO_Init(GPIOA, &GPIO_InitStructure);					 ///*SPI初始化*/SPI_InitTypeDef SPI_InitStructure;						//定义结构体变量SPI_InitStructure.SPI_Mode = SPI_Mode_Master;			//模式,选择为SPI主模式SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//方向,选择2线全双工SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//数据宽度,选择为8位SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;		//先行位,选择高位先行SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;	//波特率分频,选择128分频SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;				//SPI极性,选择低极性SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;			//SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//NSS,选择由软件控制SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC多项式,暂时用不到,给默认值7SPI_Init(SPI1, &SPI_InitStructure);						//将结构体变量交给SPI_Init,配置SPI1/*SPI使能*/SPI_Cmd(SPI1, ENABLE);									//使能SPI1,开始运行/*设置默认电平*/MySPI_W_SS(1);											//SS默认高电平SPI_RST(1);
}/*** 函    数:SPI起始* 参    数:无* 返 回 值:无*/
void MySPI_Start(void)
{MySPI_W_SS(0);				//拉低SS,开始时序
}/*** 函    数:SPI终止* 参    数:无* 返 回 值:无*/
void MySPI_Stop(void)
{MySPI_W_SS(1);				//拉高SS,终止时序
}/*** 函    数:SPI交换传输一个字节,使用SPI模式0* 参    数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
uint8_t MySPI_RW_Byte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);	//等待发送数据寄存器空SPI_I2S_SendData(SPI1, ByteSend);								//写入数据到发送数据寄存器,开始产生时序while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);	//等待接收数据寄存器非空return SPI_I2S_ReceiveData(SPI1);								//读取接收到的数据并返回
}

六、软件SPI代码

#include "stm32f10x.h"
#include "softspi.h"/***********************************该程序是软件SPI的测试 用SPI零模式来操作
*SS---PA4
*SCLK---PA5
*PA6--MISO
*PA7--MOSI*对于主机来说片选信号和时钟和主机输入都是输出引脚
*输出引脚配置为推挽输出(有独立的输入输出线,不需要考虑输入扫描时,输入线有输出的电平造成短路现象),输入引脚配置为浮空或上拉输入***********************************/
//片选信号写操作
void MySPI_W_SS(uint8_t value)
{GPIO_WriteBit(A, Pin_SS, (BitAction)value);
}
//主机输出从机输入写操作
void MySPI_W_MOSI(uint8_t value)
{GPIO_WriteBit(A, Pin_MOSI, (BitAction)value);
}
//时钟信号写操作
void MySPI_W_SCLK(uint8_t value)
{GPIO_WriteBit(A, Pin_SCLK, (BitAction)value);
}
//读取MISO操作
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(A,  Pin_MISO);
}//初始化SPI引脚
void MySoftSPI_Init(void)
{GPIO_InitTypeDef Init_GPIO; //初始化时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//初始化输出引脚Init_GPIO.GPIO_Pin = Pin_SS | Pin_SCLK | Pin_MOSI;		//设置引脚Init_GPIO.GPIO_Speed = GPIO_Speed_50MHz;	//设置输出的速度Init_GPIO.GPIO_Mode = GPIO_Mode_Out_PP;	//设置输出模式为推挽输出GPIO_Init(A, &Init_GPIO);//真正的初始化到引脚结构体中//初始化输入引脚Init_GPIO.GPIO_Pin = Pin_MISO;		//设置引脚Init_GPIO.GPIO_Speed = GPIO_Speed_50MHz;	//设置输出的速度Init_GPIO.GPIO_Mode = GPIO_Mode_IPU;	    //设置输入模式为上拉输入GPIO_Init(A, &Init_GPIO);//真正的初始化到引脚结构体中MySPI_W_SS(1);//片选拉高//SoftW25Q64_W_MOSI(Bit_SET);//没有明确规定 可以不需理会MySPI_W_SCLK(0);//0模式初始化是低电平
}//SPI起始信号
void MySPI_Start(void)
{MySPI_W_SS(0);//片选拉低
}//SPI停止信号
void MySPI_Stop(void)
{MySPI_W_SS(1);//片选拉高
}//SPI字节交换(传输数据),0模式
uint8_t MySPI_RW_Byte(uint8_t byte)
{uint8_t i,ReciveData = 0x00;for(i = 0;i < 8;i++){MySPI_W_MOSI(byte & (0x80 >>i));//SS拉低之后把byte的每一位依次传到MOSI上(MOSI引脚上输出对应的电平)MySPI_W_SCLK(1);//这时自动将MOSI数据进行采样if(MySPI_R_MISO() == 1){ReciveData |= (0x80>>i);}//MISO上的引脚进行读取电平MySPI_W_SCLK(0);//传出下一位到MOSI上}return ReciveData;
}

SPI有连续输出和非连续输出,一般采用非连续输出

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

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

相关文章

esp32-idf 开发踩坑记录

现象 直接使用原始命令编译idf.py build 但是提示idf 版本错误 卸载旧版本 编译出错build 问题 然后删除编译文件后&#xff0c;重新编译&#xff0c;还是出错 解决方法1 最后发现是因为项目所在文件夹有中文目录&#xff0c;把项目迁移到英文目录后&#xff0c;重新编译&a…

打破传统相亲模式,这几款靠谱的相亲软件助你脱单

相亲软件在当今社会已经变得越来越普遍&#xff0c;市面上有众多相亲软件可供选择&#xff0c;但哪些相亲软件好用呢&#xff1f;下面介绍几款备受好评的相亲软件&#xff0c;帮助你在茫茫人海中找到那个对的人&#xff01; 1、一伴婚恋 这个APP它最大的优点就是信息真实靠谱…

Filter和Interceptor

会话 cookie 服务器自动将cookie响应给浏览器 浏览器接收响应回来的数据 自动将cookie存储在本地&#xff0c; 后续请求当中浏览器将cookie携带到服务器 cookie不能跨域&#xff0c;存储在客户端 session 存储在服务器 token------>JWT 存储在客户端&#xff0c; 服务…

【C++ QT项目实战-03】---- C++ QT系统实现读取JSON文件数据的自动化模式

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a;C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &#…

Flink 窗口

窗口&#xff08;Window&#xff09; 窗口是处理无限流的核心。 窗口将流分割成有限大小的“桶”&#xff0c;我们可以计算窗口中的数据。 窗口程序一般有键控流&#xff08;keyed streams&#xff09;的窗口程序 和 非键控流&#xff08;non-keyed streams&#xff09;的窗口…

Rabbitmq 搭建使用案例 [附源码]

Rabbitmq 搭建使用案例 文章目录 RabbitMQ搭建docker 代码golang生产者消费者 可视化消费进度 RabbitMQ搭建 docker docker run -d --hostname rabbitmq --name rabbitmq -e RABBITMQ_DEFAULT_USERadmin -e RABBITMQ_DEFAULT_PASSadmin -e RABBITMQ_DEFAULT_VHOSTmy_vhost -e…

LeetCode/NowCoder-复杂度OJ练习

无论你觉得自己多么的不幸&#xff0c;永远有人比你更加不幸。&#x1f493;&#x1f493;&#x1f493; 目录 说在前面 题目一&#xff1a;消失的数字 题目二&#xff1a;轮转数组 SUMUP结尾 说在前面 dear朋友们大家好&#xff01;&#x1f496;&#x1f496;&#x1f496…

线性表(从数据结构的三要素出发)

文章目录 逻辑结构存储结构顺序存储链式存储单链表双链表循环单链表循环双链表静态链表 数据的操作顺序结构链式结构单链表双链表 逻辑结构 线性表是具有相同数据类型的 n ( n ≥ 0 ) n(n≥0) n(n≥0)个数据元素的有限序列&#xff0c;其中 n n n为表长&#xff0c;当 n 0 n0…

IT行业已经饱和?2024年报考计算机类专业还有出路吗?

&#x1f446;点击关注 获取更多编程干货&#x1f446; “高薪”光环加持&#xff0c;IT行业一直是不少人心仪的职业选择&#xff0c;计算机专业一度成为最热门的本科专业。 然而&#xff0c;正因报考计算机专业、想要入行IT行业的人越来越多&#xff0c;“行业饱和”、“人才…

MySQL(一) 库和表的基础操作

1. 数据库基础 1.1 什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题文件不利于数据查询和管理文件不利于存储海量数据文件在程序中控制不方便 数据库存储介质&#xff1a;磁盘内存 为了解…

增强ev代码签名证书2300

代码签名证书是软件开发者们确保软件完整性和安全性的重要工具之一。在各种类型的代码签名证书中&#xff0c;增强EV代码签名证书拥有许多独特的功能而受到企业开发者的欢迎&#xff0c;今天就随SSL盾小编了解增强EV代码签名证书的申请条件以及申请流程。 1.增强型EV代码签名证…

【小程序 按钮 表单 】

按钮 代码演示 xxx.wxml <view class"boss" hover-class"box"hover-start-time"2000"hover-stay-time"5000">测试文本<view hover-stop-propagation"true">子集</view><view>子集2</view>…

相关服务器介绍

服务器是一种高性能的计算机&#xff0c;它被设计用来为其他计算机或终端设备提供服务&#xff0c;如数据处理、文件存储、网络通信等。服务器通常具有强大的计算能力、大容量的存储空间和高效的网络连接能力。 常见的服务器种类及其特点 文件服务器 文件服务器主要负责中央存储…

Echarts图表使用

ECharts是一个用JS实现开源可视化库&#xff0c;它提供了丰富的图表类型和交互能力。使用户可以通过简单的配置生成各种各样的图表。 先安装ECharts图表直接下载echarts.min.js并用<script>标签引入也可以使用源代码版本echarts.js并用<script>标签引入&#xff0…

华为机考入门python3--(29)牛客29-字符串加解密

分类&#xff1a;字符变换 知识点&#xff1a; 字符是字母 char.isalpha() 字符是小写字母 char.islower() 字符是数字 char.isdigit() b变C chr((ord(b) - ord(a) 1) % 26 ord(A)) 题目来自【牛客】 # 加密 def encrypt_string(s):result ""for ch…

【算法】决策单调性优化DP

文章目录 决策单调性四边形不等式决策单调性 形式1法1 分治法2 二分队列例题 P3515Solution 形式2例题 P3195Solution 形式3例题 CF833BSolution 形式4例题Solution 后话 决策单调性 四边形不等式 定义: 对于二元函数 w ( x , y ) w(x,y) w(x,y)&#xff0c;若 ∀ a , b , …

强烈推荐 20.7k Star!企业级商城开源项目强烈推荐!基于DDD领域驱动设计模型,助您快速掌握技术奥秘,实现业务快速增长

更多资源请关注纽扣编程微信公众号 1 项目简介 商城是个从零到一的C端商城项目&#xff0c;包含商城核心业务和基础架构两大模块,推出用户、消息、商品、订单、优惠券、支付、网关、购物车等业务模块&#xff0c;通过商城系统中复杂场景&#xff0c;给出对应解决方案。使用 …

Linux网络编程:HTTP协议

前言&#xff1a; 我们知道OSI模型上层分为应用层、会话层和表示层&#xff0c;我们接下来要讲的是主流的应用层协议HTTP&#xff0c;为什么需要这个协议呢&#xff0c;因为在应用层由于操作系统的不同、开发人员使用的语言类型不同&#xff0c;当我们在传输结构化数据时&…

webpack编译过程

webpack编译过程 初始化 此阶段&#xff0c;webpack会将**CLI参数**、**配置文件**、**默认配置**进行融合&#xff0c;形成一个最终的配置对象。​ 对配置的处理过程是依托一个第三方库yargs完成的 ​ 此阶段相对比较简单&#xff0c;主要是为接下来的编译阶段做必要的准备 ​…