STM32标准库学习笔记(十)SPI

前言


学习永无止境!本篇是嵌入式开发之片上外设SPI,了解基本硬件原理以及通信协议。
注:本文章为学习笔记,部分图片与文字来源于网络/江协科技课程/手册,如侵权请联系!谢谢!


一、SPI通信概述


1.1 SPI基本概念

        SPI(Serial Peripheral Interface)是由摩托罗拉公司提出的通信协议,是一种高速全双工、同步串行通信总线,主要应用在Flash、ADC、编码器等要求速率较高的场合。

1.2 SPI物理层

①信号线:

  • SCLK(Serial Clock):时钟线,用于数据同步,由主机产生,决定了通信速率,不同设备的之间的通信速率受限与低速设备;
  • MOSI(Master Output,Slave Input):主出从进,主设备发送数据,从设备接收数据线,数据方向由主机向从机;
  • MISO(Master Input,Slave Output):主进从出,主设备接收数据,从设备发送数据线,数据方向由从机到主机;
  • CS(Chip Select):片选线,也叫SS(Slave Select),用来寻址,指定要通信设备,一般有几个从设备,就要有几根CS。

②电平:

        TTL电平,时钟极性(CPOL)与时钟相位(CPHA)的的选择,决定数据是在时钟上升沿采样还是下降沿采样,共四种模式,下面具体介绍。

1.3 SPI协议层

①基本时序:

  • 起始信号:CS信号由高变低,表示选中从机,相应从机检测到起始信号,知道被主机选中;
  • 数据发送与接收:数据高位先行(MSB),双方数据在时钟上升沿或下降沿发送(接收);
  • 停止信号:CS信号由低变高,表示通信结束。

②SPI模式决定因素:

  • CPOL:时钟极性,SCLK时钟线在空闲状态是高电平还是低电平由此决定,当CPOL=0时,SCLK空闲低电平。当CPOL=1时,SCLK空闲状态高电平;
  • CPHA:时钟相位,数据的采样时刻,当CPHA=0时,MOSI或MISO数据线上的信号将会在时钟线的奇数边沿被采样。当CPHA=1时,MOSI或MISO数据线上的信号将会在时钟线的偶数边沿被采样。

③SPI四种通信模式:

  • CPHA=0,CPOL=0:数据在时钟奇数边沿采样,时钟默认空闲电平;
  • CPHA=0,CPOL=1:数据在时钟奇数边沿采样,时钟默认空闲电平;

  • CPHA=1,CPOL=0:数据在时钟偶数边沿采样,时钟默认空闲电平;
  • CPHA=1,CPOL=1:数据在时钟偶数边沿采样,时钟默认空闲电平;


二、STM32的SPI外设


2.1 基本简介

  • 主从机选择:可作为SPI通讯主机,也可作为从机;
  • 时钟频率:最大SCLK的频率为fpclk/2(f103的fpclk1为36MHZ,fpclk2为76MHZ);
  • 数据格式:可选MSB先行,也可选LSB先行;
  • 模式:除了支持正常双线全双工的四种模式,还有双向单线(同时向一个方向传输,速度提高一倍)以及单线模式(减少硬件接线);

2.2 硬件基本结构框图

  • ①外部引脚:四个通信口与GPIO口的对应关系,需要查表;
  • ②时钟控制:根据控制寄存器CR1BR[0:2],对fpclk进行分频;
  • ③数据控制:数据移位寄存器以发送缓冲区作为数据源,把数据一位一位发送,当从外部接收数据时,数据移位寄存器把数据线数据采样一位位存储到接收缓冲区。数据帧长度设定GFF位可为8或16位,配置LSBFIRST,可选择MSB先行或LSB先行;
  • ④主控:通过改变CR1/2的参数,可以配置SPI模式、波特率、LSB/MSB、主从模式、单双向模式,状态寄存器SR实时反馈SPI工作状态,除此之外还有中断、DMA请求等控制。

2.3 主机发送与接收

  • ①CS片选:当要进行通信,首先选择从机地址,CS由高电平置为低电平;
  • ②SCLK:CS片选同时时钟进行工作;
  • ③数据发送:MOSI把发送缓冲区数据一位位传输出去,当发送完一帧数据,SR置TXE标志1(表示发送缓冲区已清空),若要再次发送数据,在TXE为1时,写入DR数据即可;
  • ④数据接收:MISO移位寄存器把数据线数据采样一位位存储到接收缓冲区,当接收完一帧数据,SR置RXNE标志1(表示接收缓冲区非空),即可读取DR数据,每次等待RXNE标志位1,即可读取接收数据。


三、应用


3.1 软件模拟SPI

①本次示例说明:使用软件模拟CS、SCLK、MOSI、MISO,利用I/O高低电平翻转实现SPI时序。

配置CS\SCLK\MOSI为推挽输出(PA4\5\7),MISO为上拉输入(PA6),选用模式1(SCLK初始为低电平,数据在奇数边沿采样)。

②配置步骤:

  • 开启RCC时钟:开启GPIOA外设时钟;
  • 配置GPIO:CS(GPIOA_Pin4)、SCLK(GPIOA_Pin5)、MOSI(GPIOA_Pin7)推挽输出,MISO(GPIOA_Pin6)上拉输出;
  • 起始信号;
  • 停止信号;
  • 发送字bit;
  • 接收bit;
  • 发送与接收字节:通过循环移位进行写入与读取,将数据从高位依次取出放在发送地址,每取一位,置SCLK位1,并读取MISO电平信号,读取完毕,置SCLK为0。

③代码实战:

MSPI.c:

#include "stm32f10x.h"                  // Device header/*设置CS电平*/
void MySPI_W_CS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		
}/*写SCLK*/
void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);		
}/*发送MOSI电平*/
void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);		
}/*接收MISO电平*/
uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);			
}/*初始化引脚配置*/
void MySPI_Init(void)
{/*开启GPIOA外设时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//将PA4、PA5和PA7引脚初始化为推挽输出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);					GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//将PA6引脚初始化为上拉输入GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					/*设置默认电平*/MySPI_W_CS(1);	//CS默认高电平										MySPI_W_SCK(0);	//SCLK默认低电平										
}/*协议层*//*起始信号*/
void MySPI_Start(void)
{MySPI_W_CS(0);				
}/*结束信号*/
void MySPI_Stop(void)
{MySPI_W_CS(1);				
}/*发送接收数据字节*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到for (i = 0; i < 8; i ++)						//循环8次,依次交换每一位数据{MySPI_W_MOSI(ByteSend & (0x80 >> i));		//使用掩码的方式取出ByteSend的指定一位数据并写入到MOSI线MySPI_W_SCK(1);								//拉高SCK,上升沿移出数据if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}	//读取MISO数据,并存储到Byte变量//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0MySPI_W_SCK(0);								//拉低SCLK,下降沿移入数据}return ByteReceive;								//返回接收到的一个字节数据
}

3.2 硬件SPI

①本次示例说明:使用STM32硬件SPI配置,选用SPI1外设,配置时钟极性为低电平,相位为上升沿采样。

②配置步骤:

  • 开启RCC时钟:开启GPIOA、SPI1外设时钟;
  • 配置GPIO:CS(GPIOA_Pin4)为推挽输出,SCLK(GPIOA_Pin5)、MOSI(GPIOA_Pin7)复用推挽输出,MISO(GPIOA_Pin6)上拉输出;
  • 起始信号;
  • 停止信号;
  • 发送与接收字节:等待TXE为空,写入字节,等待RXNE非空,读取字节。

③代码实战:

MSPI.c:

#include "stm32f10x.h"                  // Device header/*设置CS*/
void MySPI_W_CS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		
}/*初始化SPI*/
void MySPI_Init(void)
{/*开启GPIOA\SPI1的外设时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);	/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;//将PA4引脚初始化为推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;//将PA5和PA7引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//将PA6引脚初始化为上拉输入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_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);									/*设置默认电平*/MySPI_W_CS(1);											
}/*起始信号*/
void MySPI_Start(void)
{MySPI_W_CS(0);				
}/*结束信号*/
void MySPI_Stop(void)
{MySPI_W_CS(1);				
}/*** 函    数:SPI交换传输一个字节,使用SPI模式0* 参    数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
uint8_t MySPI_SwapByte(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);								//读取接收到的数据并返回
}

待续...

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

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

相关文章

从github上,下载的android项目,从0-1进行编译运行-踩坑精力,如何进行部署

因为国内的网络原因&#xff0c;一直在anroidstudio开发的问题上&#xff0c;是个每个开发者都会踩坑 一直以为是自己的原因&#xff0c;其实很多都是国内网络的原因&#xff0c;今天就从一个开发者的视角 把从github上一个陌生的项目&#xff0c;如何通过本地就行运行的 首先…

计算机网络 (40)域名系统DNS

前言 计算机网络域名系统DNS&#xff08;Domain Name System&#xff09;是互联网的基础技术之一&#xff0c;它负责将人类可读的域名转换为计算机用来通信的数字IP地址。 一、基本概念 DNS的主要目的是将域名解析或翻译为IP地址&#xff0c;使得用户可以通过简单易记的域名来访…

使用Dify创建个问卷调查的工作流

为啥要使用Dify创建工作流呢&#xff1f;一个基于流程的智能体的实现&#xff0c;特别是基于业务的实现&#xff0c;使用Dify去实现时&#xff0c;通常都是一个对话工作流&#xff0c;当设计到相对复杂一些的流程时&#xff0c;如果将所有逻辑都放在对话工作流中去实现&#xf…

toRef 和 toRefs 详解及应用

在 Vue 3 中&#xff0c;toRef 和 toRefs 是两个用于创建响应式引用的工具&#xff0c;主要用于组合式 API&#xff08;Composition API&#xff09;的场景中 1. toRef 定义 toRef 将某个对象的某个属性包装成一个响应式引用。这样可以直接对该引用进行操作&#xff0c;而不需…

八 rk3568 android11 AP6256 蓝牙调试

一 经典蓝牙 经典蓝牙默认可以工作, 验证可以连接 蓝牙鼠标,键盘, 连接手机等等, 在 系统设置里打开蓝牙 ,扫描设备,配对连接即可。 注: 连接 ANDROID 手机的坑 1 手机连接之后空闲状态会断开 ,变成 配对的设备不是已连接,是正常,使用时又会自动 连接 2 手机传…

解读若依微服务架构图:架构总览、核心模块解析、消息与任务处理、数据存储与缓存、监控与日志

文章目录 1. 引言2. 架构总览3. 核心模块解析3.1 服务注册与配置中心Nacos&#xff1a;微服务的中枢 3.2 网关层ruoyi-gateway&#xff1a;服务的统一入口 3.3 核心业务服务3.4 认证服务ruoyi-auth&#xff1a;认证与授权的守护者 3.5 异构服务整合Sidecar&#xff1a;连接异构…

【MySQL】基础架构分析

考察频率难度40%⭐⭐⭐⭐ 这道题在面试时的出现频率其实并不高&#xff0c;最起码对于笔者来说是没有遇到过。那为什么还是选择把这个问题作为 MySQL 八股文系列的第一个呢&#xff1f;其实原因也挺简单的&#xff0c;还是老规矩&#xff0c;先通过一个问题把整个知识框架来一…

【已解决】【记录】2AI大模型web UI使用tips 本地

docker desktop使用 互动 如果需要发送网页链接&#xff0c;就在链接上加上【#】号 如果要上传文件就点击这个➕号 中文回复 命令它只用中文回复&#xff0c;在右上角打开【对话高级设置】 输入提示词&#xff08;提示词使用英文会更好&#xff09; Must reply to the us…

热烈祝贺“钛然科技”选择使用订单日记

感谢珠海钛然科技有限公司选择使用订单日记&#xff01; 珠海钛然科技有限公司&#xff0c;成立于2020年&#xff0c;位于广东省珠海市高新区&#xff0c;是一家以从事研发和生产功能型纳米高分子涂层为主的企业。 在业务不断壮大的过程中&#xff0c;想使用一种既能提升运营…

Linux-----进程通讯(消息队列)

目录 相关API 1.相关数据类型 mqd_t struct mq_attr struct timespec 2.相关系统调用接口 mq_open() mq_timedsend() && mq_send() mq_timedreceive() && mq_receive() mq_unlink() clock_gettime() 父子进程使用消息队列通讯 平行进程使用消息队列…

【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解

分布式系统理论&#xff1a;CAP 与 BASE 详解 一、CAP 定理 背景与定义&#xff1a;1998 年由加州大学科学家埃里克布鲁尔提出&#xff0c;分布式系统存在一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&#xff08;Part…

数据结构与算法之二叉树: LeetCode 572. 另一棵树的子树 (Ts版)

另一棵树的子树 https://leetcode.cn/problems/subtree-of-another-tree/description/ 描述 给你两棵二叉树 root 和 subRoot检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false二叉树 tree …

NineData云原生智能数据管理平台新功能发布|2024年12月版

本月发布 7 项更新&#xff0c;其中重点发布 2 项、功能优化 5 项。 重点发布 数据库 Devops - Oracle 非表对象支持可视化创建与管理 Oracle 非表对象&#xff0c;包括视图&#xff08;View&#xff09;、包&#xff08;Package&#xff09;、存储过程&#xff08;Procedur…

[Unity]MacOS下开发Unity

需要的插件 我使用的是vscode&#xff0c;经过长时间的使用我发现一个问题就是很多插件都是动态的在变化的&#xff0c;不是一成不变的&#xff0c;可能是重构&#xff0c;可能直接换了其他的工具。 所以这个插件也会是更新的状态。 2025年01月08日更新 .NET Install Tool (…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(五)

文章目录 一、学生管理模块功能实现1、添加学生功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、学生管理功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询接口实现2.3.2 后端编辑接口实现2.3.3 后端删除接口实现2.4 效果展示二、代码…

实现一个VSCode插件(从创建到发布)

实现一个自己的VSCode 插件 本文将以 yo 为例&#xff0c; 实现一个 VS Code 插件 从创建到发布。 文章目录 实现一个自己的VSCode 插件1. 初始化项目2. 项目结构3. 实现插件功能4. 测试和运行插件5. 发布6. 下载自己发布的插件 1. 初始化项目 首先&#xff0c;我们需要安装 …

Unity TextMesh Pro入门

概述 TextMesh Pro是Unity提供的一组工具&#xff0c;用于创建2D和3D文本。与Unity的UI文本和Text Mesh系统相比&#xff0c;TextMesh Pro提供了更好的文本格式控制和布局管理功能。 本文介绍了TMP_Text组件和Tmp字体资产(如何创建字体资产和如何解决缺字问题),还有一些高级功…

【教程】数据可视化处理之2024年各省GDP排名预测!

过去的一年里&#xff0c;我国的综合实力显著提升&#xff0c;在新能源汽车、新一代战机、两栖攻击舰、航空航天、芯片电子、装备制造等领域位居全球前列。虽然全国各省市全年的经济数据公布还需要一段时间&#xff0c;但各地的工业发展数据&#xff0c;财政收入数据已大概揭晓…

后端:Spring(IOC、AOP)

文章目录 1. Spring2. IOC 控制反转2-1. 通过配置文件定义Bean2-1-1. 通过set方法来注入Bean2-1-2. 通过构造方法来注入Bean2-1-3. 自动装配2-1-4. 集合注入2-1-5. 数据源对象管理(第三方Bean)2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)2-1-7. 加载容器…

从CentOS到龙蜥:企业级Linux迁移实践记录(龙蜥开局)

引言&#xff1a; 在我们之前的文章中&#xff0c;我们详细探讨了从CentOS迁移到龙蜥操作系统的基本过程和考虑因素。今天&#xff0c;我们将继续这个系列&#xff0c;重点关注龙蜥系统的实际应用——特别是常用软件的安装和配置。 龙蜥操作系统&#xff08;OpenAnolis&#…