硬件SPI解析-基于江科大的源码

news2024/12/24 8:15:19

一、SPI基本介绍

        SPI(Serial Peripheral Interface)通信协议是由摩托罗拉公司(现为NXP Semiconductors的一部分)在20世纪80年代中期开发的。SPI是一种同步串行通信接口,设计用于短距离通信,特别是嵌入式系统中的微控制器和外围设备之间的数据传输。

二、SPI通信原理

1、SPI通信方式

全双工通信:在同一时间,两台设备之间发送和接收可以同时进行。

2、SPI通信的两种情况

一主一从

一主多从

三、硬件SPI通信

1、GPIO初始化

这里选用了芯片的外设SPI,也就是常说的硬件SPI

在这里我们将

PA4端口SPI1_NSS配置为了推挽输出

NSS端口的作用

  • 设备选择:在多从机的SPI系统中,每个从设备都有一个独立的NSS信号线连接到主机。当主机想要与某个特定的从设备通信时,它会将该从设备的NSS线拉低(通常为激活状态),而其他从设备的NSS线保持高电平(非激活状态)。这样,只有被选中的从设备才会响应主机发送的数据和命令。
  • 同步开始/结束:NSS信号也可以用来标记一次SPI传输的开始和结束。当NSS从高变低时,意味着一次新的SPI传输即将开始;而当NSS从低变高时,则表示当前SPI传输已经结束。这种机制有助于从设备正确地识别数据流的边界。
  • 减少干扰:通过仅在需要时激活特定的从设备,NSS信号还有助于降低整个系统内的电气噪声水平,因为未被选中的设备不会对总线产生影响,从而提高了系统的稳定性和可靠性。
  • 灵活性:使用NSS可以实现更灵活的配置。例如,在某些应用场合下,可能希望同时向多个从设备广播相同的信息,这时可以通过将这些从设备的NSS线全部置低来实现这一点。

将PA5端口SPI1_SCK配置为复用推挽输出

SPI1_SCK的作用

  • 提供时钟信号:SCK提供了时钟脉冲,这些脉冲决定了数据传输的速度以及何时采样数据。每个时钟周期通常对应着一位数据的传送。
  • 数据同步:由于SPI是一种同步通信协议,因此SCK确保了发送方与接收方的数据传输保持同步。这意味着当主机发送数据时,它同时也在产生时钟信号,而从机则根据这个时钟信号来读取或写入数据位。
  • 极性和相位配置:SCK的极性(即空闲状态下的电平高低)和相位(数据是在时钟上升沿还是下降沿被采样)可以通过SPI模式来配置。常见的模式有0-3四种,它们定义了不同的时钟边沿组合用于数据捕获,从而适应不同类型的外围设备需求。
  • 控制传输速率:通过调整SCK的频率,可以改变SPI通信的速度。较快的时钟频率意味着更快的数据传输率,但同时也需要考虑设备之间的电气特性是否支持这样的高速率。

将PA6端口SPI1_MISO配置为上拉输入

SPI1_MISO的作用:英文解释一下主Input从Output

    • 主模式时候:主机输入,从机输出
    • 从模式时候:主机输出。从机输入

将PA7端口SPI1_MOSI配置为复用推挽输出

SPI1_MOSI的作用:英文解释一下主Output从Input

    • 主模式时候:从机输入,主机输出
    • 从模式时候:从机输出。主机输入
/*开启时钟*/
	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;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA4引脚初始化为推挽输出
	
	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);					//将PA5和PA7引脚初始化为复用推挽输出
	
	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);					//将PA6引脚初始化为上拉输入

2、SPI初始化

/*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_Mode模式的选择

常用的就是上面两种模式,主要决定的就是MOSI和MISO两条线的数据传输方向,也就是上面GPIO配置中写到的

CPOL和CPHA的作用

时钟极性(CPOL)定义了时钟空闲状态电平:(决定了SCK的空闲状态)

CPOL=0,表示当SCLK=0时处于空闲态,所以有效状态就是SCLK处于高电平时

CPOL=1,表示当SCLK=1时处于空闲态,所以有效状态就是SCLK处于低电平时

时钟相位(CPHA)定义数据的采集时间:(决定了第几个跳变沿检测)

CPHA=0,在时钟的第一个跳变沿(上升沿或下降沿)进行数据采样。,在第2个边沿发送数据

CPHA=1,在时钟的第二个跳变沿(上升沿或下降沿)进行数据采样。,在第1个边沿发送数据

例如:

    • Mode0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。
    • Mode1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
    • Mode2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
    • Mode3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

SPI_NSS的选择

软件控制

  • 灵活性:通过软件控制NSS,可以在程序中根据需要动态地选择激活哪个从设备。这提供了更大的灵活性,尤其是在需要频繁切换被选中的从设备或者实现复杂的通信协议时。
  • 资源利用:软件控制通常使用通用I/O引脚来实现,这意味着它不占用专门的硬件资源,但可能会稍微增加处理器的工作负担,因为需要编写代码来管理这些引脚的状态变化。
  • 编程复杂度:相比硬件控制,软件控制可能需要更多的编程工作来确保正确设置和切换NSS线的状态。开发者必须确保在正确的时机拉低/拉高相应的NSS线以避免数据冲突。

硬件控制

  • 自动化:当使用硬件控制器内置的功能来管理NSS信号时,这个过程是自动化的。一旦配置完成,硬件会自动处理NSS信号的变化,减少了对处理器干预的需求。
  • 性能:由于不需要额外的软件干预,硬件控制下的SPI通信往往更加快速高效,特别是在进行连续的数据传输时。
  • 可靠性:硬件控制降低了因软件错误导致的问题风险,例如忘记切换NSS线或错误地设置了NSS线状态等。
  • 限制性:与软件控制相比,硬件控制提供的灵活性较低。如果硬件控制器支持有限数量的独立NSS线,则可能无法轻松扩展到更多的从设备上。

NSS端口的开始和停止信号

NSS在软件控制的时候,拉低则代表着数据传输开始,拉高代表结束

**
  * 函    数: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_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);								//读取接收到的数据并返回
}

三、源码分享

MySpi.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数: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引脚的电平
}

/**
  * 函    数: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;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA4引脚初始化为推挽输出
	
	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);					//将PA5和PA7引脚初始化为复用推挽输出
	
	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);					//将PA6引脚初始化为上拉输入
	
	/*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起始
  * 参    数:无
  * 返 回 值:无
  */
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_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);								//读取接收到的数据并返回
}

MySpi.h

#ifndef __MYSPI_H
#define __MYSPI_H

void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);

#endif

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

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

相关文章

图片怎么转换成pdf格式?这5种转换方法一看就会

在工作学习中,PDF格式因其跨平台兼容性和安全性成为了工作和学习中不可或缺的文件格式。然而,很多时候我们需要将图片转换为PDF,以便更好地整理、分享和保存。今天,就为大家介绍5种高效的图片转PDF方法,一起来学习下吧…

让机器来洞察他的内心!

本文所涉及所有资源均在传知代码平台可获取。 目录 洞察你的内心:你真的这么认为吗? 一、研究背景 二、模型结构和代码 D. 不一致性学习网络 E. 多模态讽刺分类 三、数据集介绍 四、性能展示 五、实现过程 1. 下载预训练的 GloVe 词向量(Comm…

端口被占用问题的解决方案

一、问题描述 如图,启动服务失败,失败原因是8080端口被占用 二、解决方案 1.更换端口为其它,例如8002 9001等 2.关闭占用端口的进程,推荐这种解决方案 步骤一:在win命令行查询占用该端口号的进程 命令如下 netsta…

使用浏览器这么多年,你真的了解DevTools吗?

Devtools是Web测试时每天都要用的工具,它提供了很多调试功能,可以帮助我们更好的定位问题。而我们平时使用的功能只是它全部功能的子集,很多功能并没用到过。 作为高频使用的工具,还是有必要好好掌握的。测试时在日常工作中提BUG…

项目前置知识

目录 std::bind 定时器 timerfd 时间轮设计 C11正则库 日志打印宏 通用类型ANY std::bind std::bind是C11提供的一个接口,他的功能:传递一个原始函数对象,对其绑定某些参数,生成一个新的函数对象。 其实简单来说&#xff…

YOLO--前置基础词-学习总结(上)

RFBNet是什么意思 RFBNet 是一种用于目标检测的深度学习网络,它的名字来源于 "Receptive Field Block Network"(感受野块网络)。简单来说,RFBNet 是一种可以让计算机更好地“看”图像中不同大小的物体的方法。 在图像处…

混凝土裂缝检测分割系统源码&数据集分享

混凝土裂缝检测分割系统源码&数据集分享 [yolov8-seg-RCSOSA&yolov8-seg-C2f-REPVGGOREPA等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Glo…

时间序列+Transformer席卷而来,性能秒杀传统,创新性拉满,引爆顶会!

时间序列分析与Transformer模型的结合,已成为深度学习领域的一大趋势。这种结合能够高效捕捉序列中的长期依赖关系,提升时间序列分析和预测的准确性。 时间序列Transformer技术在股票价格预测、气候预测、交通流量预测、设备故障预测、自然语言处理等多…

43页PPT | 大健康行业BI解决方案

药企应用现状与挑战 大健康行业中的药企在数据分析方面仍面临诸多挑战。传统的数据分析模式依赖于纸质记录和线下手动更新,导致数据时效性低、人力成本高,难以快速发挥数据价值。随着数据量的激增,多源数据的归集和整合成为药企数据分析的难点…

Python画笔案例-080 绘制 颜色亮度测试

1、绘制 颜色亮度测试 通过 python 的turtle 库绘制 颜色亮度测试,如下图: 2、实现代码 绘制 颜色亮度测试,以下为实现代码: """颜色亮度测试.py本程序需要coloradd模块支持,请在cmd窗口,即命令提示符下输入pip install coloradd进行安装。本程序演示brig…

JavaSE——面向对象6.1:继承知识点补充(虚方法表等)

目录 一、子类到底能继承父类中的哪些内容? 二、继承内存图 三、继承中:成员变量和成员方法的访问特点 (一)成员变量的访问特点 (二)成员方法的访问特点 1.this与super访问成员方法的特点 2.方法重写 2.1方法重写的本质:子类覆盖了从…

社区交流礼仪 | 提问的艺术

唠唠闲话 2021 年通过 Julia 社区了解到开源,自此开始融入开源社区,学习和体验这种独特的协作模式与交流文化,受益良多。本篇文章为开源新手必读,文章中探讨的交流模式,不仅对参与开源项目的协作有所帮助,…

计组体系软考题2-计算机组成原理与计算机体系结构概论

一、CPU组成(运算器控制器) 1.运算器 题1-存放操作数/运算结果的ACC累加寄存器 1. 2. 题2-加法器(算术逻辑单元的部件) 题3-判断对错 程序计数器PC(运算器),只存放地址题4- 2. 控制器 题1-…

10.8每日作业

当用户点击取消按钮,弹出问题对话框,询问是否要确定退出登录,并提供两个按钮,yes|No,如果用户点击的Yes,则关闭对话框,如果用户点击的No,则继续登录 当用户点击的登录按钮&#xff…

热网无线监测系统 GetMenuItem 接口存在SQL注入漏洞

漏洞描述 热网无线监测系统 /DataSrvs/UCCGSrv.asmx/GetMenuItem 接口处存在SQL注入漏洞,未经身份验证的远程攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至…

Hydra 新手友好使用教程

1. Hydra 简介 Hydra是一款强大的网络登录暴力破解工具,支持多种协议。本教程将帮助新手快速上手,掌握常用指令和操作。 2. 基本语法 hydra [参数] 目标 3. 核心参数详解 3.1 用户名和密码设置 单个用户名: -l LOGIN 例:-l admin 用户名…

【LeetCode】动态规划—115. 不同的子序列(附完整Python/C++代码)

动态规划—115. 不同的子序列 前言题目描述基本思路1. 问题定义2. 理解问题和递推关系3. 解决方法3.1 动态规划方法3.2 空间优化的动态规划 4. 进一步优化5. 小总结 代码实现PythonPython3代码实现Python 代码解释 CC代码实现C 代码解释1. 变量定义:2. 初始化:3. 动态规划状态转…

高级算法设计与分析 学习笔记11 动态规划

要讲动态规划,当然少不了斐波拉及数列: 可以看到,动态规划效率高的秘诀就在于有记忆,不用做重复的事。 矩阵列乘法: 可以看到,只要找到了一个恰到好处的计算顺序(注意矩阵乘法只有结合律没有交换…

HT8513 内置自适应同步升压和防破音功能的6.5W D类及AB类音频功率放大器

1、特征 防削顶失真功能(防破音,Anti-Clipping Function, ACF) 免滤波器数字调制&#xff0c;直接驱动扬声器 输出功率 3W (VBAT3.3V, RL-4Ω, THDN<1%, 20-20kHz full band) 2.0W (VBAT3.3V, RL8Ω,THDN<1%, 20-20kHz full band) 6.5W (VBAT4.2V, RL2Ω, THDN10%,f1kHz…

SQL第14课挑战题

1. 将两个select语句结合起来&#xff0c;以便从OrderItems表中检索产品ID(prod_id)和quantity。其中&#xff0c;一个select语句过滤数量为100的行&#xff0c;另一个select语句过滤ID以BNBG开头的产品。按产品ID对结果进行排序。 2. 重新第一题&#xff0c;仅使用单个select语…