SPI通信(STM32)

news2025/3/18 20:23:26

一、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/SDA
	GPIO_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/--7
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//SPI1 SCK/--5
	GPIO_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 RTS
	GPIO_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模式0
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//NSS,选择由软件控制
	SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC多项式,暂时用不到,给默认值7
	SPI_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.coloradmin.cn/o/1694593.html

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

相关文章

5.18 TCP机械臂模拟

#include <netinet/tcp.h>//包含TCP选项的头文件 #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/input.h>//读取输入事件 #include <sys/types.h> #include <sys/stat.h&…

截图工具PixPin(比Snipaste更强大)

PixPin官网链接&#xff1a;https://pixpinapp.com/ 最近新出的一款截图工具PixPin&#xff0c;比Snipaste功能多一些。在Snipaste功能基础上&#xff0c;还支持长截图&#xff0c;截动图&#xff0c;文本识别。

第一篇【传奇开心果系列】Python的跨平台开发工具beeware技术点案例示例:使用beeware实现跨平台开发,从hello world开始

传奇开心果博文系列 系列博文目录Python的跨平台开发工具beeware技术点案例示例系列 博文目录前言一、BeeWare套件主要功能介绍二、Toga相对于其他Python UI库具有的优势介绍三、使用toga开发安卓手机应用hello world步骤和示例代码四、使用toga写一个iOS 苹果手机应用hello wo…

探索 Vue 3 的动态布局解决方案:Grid Layout Plus

探索 Vue 3 的动态布局解决方案&#xff1a;Grid Layout Plus 文章目录 探索 Vue 3 的动态布局解决方案&#xff1a;Grid Layout PlusGrid Layout Plus 概览0、元信息1、核心特性可拖拽部件可缩放部件静态部件边界检查避免重建栅格可序列化和还原的布局自动化 RTL 支持响应式设…

插件:NGUI

一、版本 安装完毕后重启一下即可&#xff0c;否则可能创建的UI元素不生效 二、使用 Label文字 1、创建Canvs 2、只有根节点的这些脚本全部展开才能鼠标右键创建UI元素 3、选择字体 Sprite图片 1、选择图集 2、选择图集中的精灵 Panel容器 用来装UI的容器&#xff0c;一般UI…

【c语言】了解指针,爱上指针(5)

了解指针&#xff0c;爱上指针&#xff08;5&#xff09; 回调函数qsort函数冒泡排序模拟实现qsort函数 回调函数 回调函数&#xff1a;就是一个通过函数指针调用的函数。 把函数的指针作为参数传给另一个函数&#xff0c;当这个指针被用来调用指向的函数时&#xff0c;此时被…

K8s的常用命令以及yaml文件的创建

目录 一、声明式管理方法&#xff1a;YAML文件 1、yaml文件简介 2、yaml和json的主要区别&#xff1a; 3、YAML的语法格式 4、yaml文件组成部分 ①控制器定义 5、查看api资源版本标签 6、编写nginx-deployment.yaml资源配置清单 6.1创建资源对象 6.2查看创建的pod资源…

vue3 使用css实现一个弧形选中角标样式

文章目录 1. 实现效果2. 实现demo 在前端开发中&#xff0c;ui同学经常会设计这样的样式&#xff0c;用于区分选中的状态 下面抽空简单些了一下&#xff0c;记录下&#xff0c;后面直接复制用 1. 实现效果 实现一个菜单切换&#xff0c;右下角有个角标的样式 2. 实现demo 主要…

通过Kafka-Logger插件收集流量进行漏洞扫描

通过Kafka-Logger插件收集流量进行漏洞扫描 方案 可以通过APISIX kafka-logger 插件将日志作为 JSON 对象推送到 Apache Kafka 集群中&#xff0c;消费Kafka里的数据格式化后添加到MySQL。 方案详情 1、登录APISIX&#xff0c;启用内置的kafka-logger 插件&#xff1a; 2…

数据可视化第9天(利用wordcloud和jieba分析蝙蝠侠评论的关键字)

数据可以在这里下载 https://github.com/harkbox/DataAnalyseStudy WordCloud wordcloud可以很方便的生成词云图&#xff0c;方便的提供可视化可以直接使用pip install wordcloud进行安装如果使用的是Anaconda,可以使用conda install进行安装 下面看一个简单的例子 txt &qu…

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

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

【CTF Web】CTFShow web3 Writeup(SQL注入+PHP+UNION注入)

web3 1 管理员被狠狠的教育了&#xff0c;所以决定好好修复一番。这次没问题了。 解法 注意到&#xff1a; <!-- flag in id 1000 -->但是拦截很多种字符。 if(preg_match("/or|\-|\\|\*|\<|\>|\!|x|hex|\/i",$id)){die("id error"); }使用…

vite-plugin-vue-devtools插件

vite-plugin-vue-devtools插件旨在帮助开发者更快地理解并调试Vue应用。它通过提供全面的功能和直观的界面&#xff0c;以图形化的方式展示应用程序状态&#xff0c;使开发者能够更方便地查看和管理Vue应用的各个方面。此外&#xff0c;该插件还支持Vue3.0版本&#xff0c;并且…

【简单介绍下链表基础知识】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

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

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

k8s命令式对象管理和配置

kubectl补全: # dnf install -y bash-completion # echo "source <(kubectl completion bash)" >> ~/.bashrc # kubectl completion bash > /etc/bash_completion.d/kubectl 命令式对象管理 kubectl命令 # 查看所有pod kubectl get pod # 查看某个po…

swust oj 1075: 求最小生成树(Prim算法)

#include <iostream> using namespace std;typedef struct {int n;int e;char data[500];int edge[500][500]; }Graph;typedef struct {int index;int cost; }mincost;typedef struct {int x;//起点int y;//终点int weight;//权重 }EDGE;typedef struct {int index;int …

Arduino下载与安装(Windows 10)

Arduino下载与安装(Windows 10) 官网 下载安装 打开官网&#xff0c;点击SOFTWARE&#xff0c;进入到软件下载界面&#xff0c;选择Windows 选择JUST DOWNLOAD 在弹出的界面中&#xff0c;填入电子邮件地址&#xff0c;勾选Privacy Policy&#xff0c;点击JUST DOWNLOAD即可 …

C语言:表达式运算的类型转换

相关阅读 C语言https://blog.csdn.net/weixin_45791458/category_12423166.html?spm1001.2014.3001.5482 1、前言 C语言作为一种强类型语言&#xff0c;要求在进行操作时变量类型必须匹配。然而&#xff0c;为了提高灵活性和便利性&#xff0c;C语言提供了一套规则&#xff0…

【C语言】C语言-学生成绩管理系统(源码+数据文件+课程论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…