【STM32】SPI接口(非连续传输)

news2024/9/24 14:59:29

本篇博客重点在于标准库函数的理解与使用,搭建一个框架便于快速开发

目录

前言

SPI简介

IO口初始化 

SPI配置

时钟使能

SPI初始化 

SPI使能 

数据接收与发送 

硬件SPI代码

MySPI.h

MySPI.c


前言

【通信协议】SPI总线-CSDN博客

本篇博客学习使用STM32的SPI硬件收发电路生成SPI时序。SPI协议用模式0,一主多从,非连续传输。

SPI简介

STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担

可配置8位/16位数据帧、高位先行/低位先行

支持多主机模型、主或从操作

可精简为半双工/单工通信

支持DMA

STM32F103C8T6 硬件SPI资源:SPI1、SPI2

IO口初始化 

STM32F103C8T6模式
SCK复用推挽输出,由硬件SPI生成时钟
MOSI复用推挽输出,由硬件SPI生成数据帧
MISO上拉输入
SS推挽输出,不用硬件SPI的NSS,使用GPIO模拟通讯的起始与终止
GND与从机共地

GPIO的其它参数的理解可以阅读下方博客,这里不再赘述。

【STM32】GPIO和AFIO标准库使用框架_gpio afio-CSDN博客

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	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);//推挽输出默认输出低电平
	
	MySPI_W_SS(1);//不选择从机,空闲状态

    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);
	
	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配置

时钟使能

SPI1在APB2总线上,时钟频率最大72MHz

SPI2、SPI3在APB1总线上,时钟频率最大36MHz

由RCC时钟树,需要使能SPI外设对应的时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

SPI初始化 

SPI_Mode

SPI_Mode 描述
SPI_Mode_Master设置为主SPI
SPI_Mode_Slave设置为从 SPI

SPI_ Direction

SPI_ Direction描述
SPI_Direction_2Lines_FullDuplexSPI 设置为双线双向全双工
SPI_Direction_2Lines_RxOnlySPI 设置为双线单向接收
SPI_Direction_1Line_RxSPI 设置为单线双向接收
SPI_Direction_1Line_TxSPI 设置为单线双向发送

SPI_DataSize

SPI_DataSize 描述
SPI_DataSize_16bSPI 发送接收16 位帧结构
SPI_DataSize_8bSPI 发送接收 8 位帧结构

SPI_FirstBit

SPI_FirstBit 指定了数据传输从 MSB 位还是 LSB 位开始

SPI_FirstBit描述
SPI_FisrtBit_MSB数据传输从 MSB 位开始
SPI_FisrtBit_LSB数据传输从 LSB 位开始

SPI_BaudRatePrescale

通讯时钟频率(SCK): fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)

用来定义波特率预分频的值,这个值用以设置发送和接收的 SCK 时钟

注意:通讯时钟由主 SPI 的时钟分频而得,不需要设置从 SPI 的时钟

SPI_CPOL和SPI_CPHA

这两个参数有四种组合,对应四种模式。主机所选择SPI的模式,要与从机一致。

模式对应的参数见本博客前文提供的博客链接

SPI_NSS

本博客代码使用软件模拟来实现片选

SPI_NSS描述
SPI_NSS_HardNSS 由外部管脚管理
SPI_NSS_Soft内部 NSS 信号有 SSI 位控制

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使能 

初始化的最后调用即可

SPI_Cmd(SPI1, ENABLE);

数据接收与发送 

先判断发送缓存器中是否有数据,没有?数据就将放在发送缓存区。硬件将数据并行发送到移位寄存器,这时发送缓存器为空(非连续传输模式并没有在这时将下一个要发送的字节写入发送缓存器,而是等待接收到数据后再将数据放发送缓存区),移位寄存器在时钟同步下,将数据发送一位一位发送,对应SPI的模式0的SCL奇数上升沿,数据一位一位接收到移位寄存器,直到一个字节发送完成,也表示一个字节接收完成。移位寄存器的数据会移至接收缓存区,数据接收后及时取走,以免被覆盖。

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);// 在写入发送缓冲器之前,软件必须确认TXE标志为’1’,否则新的数据会覆盖已经在发送缓冲器中的数据
	SPI_I2S_SendData(SPI1, ByteSend);
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//发送完成一个字节的同时也接收一个字节,等待接收完成
	return SPI_I2S_ReceiveData(SPI1);
}

标志位由硬件清除

 

硬件SPI代码

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

MySPI.c

#include "stm32f10x.h"                  // Device header

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);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	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);
	
	MySPI_W_SS(1);
	
	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);
	
	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_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitStructure);
	
	SPI_Cmd(SPI1, ENABLE);
	
}

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);
	SPI_I2S_SendData(SPI1, ByteSend);
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
	return SPI_I2S_ReceiveData(SPI1);
}

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

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

相关文章

Expo 开发ReactNative 后切换 eas 账号

修改slug app.json中的sulg字段更新为新账号应用sulg 修改projectId app.json中的extra.eas.projectId字段更新为新账号应用projectId 退出账号: eas logout 重新登录: eas login

基于WEB的学生综合测评系统的设计与实现

第1章 绪 论 1.1 系统开发背景 随着计算机技术的不断发展,在现代化社会中,信息的处理越来越依赖计算机来完成。在没有测评系统之前,所有的信息记录都是由人工记录维护,不但不方便,还经常出错,有了测评系统就可以方便的对各种信息…

.net maui安卓开发中使用明文传输(一)

背景:最近在做一个pad上的项目,目的是执行每日点检功能(就是检查设备的各项保养指标);前期用HBuilder做了一个,但是现场的触摸屏选用的是TouchPie 安卓版本是6.0版本,上次开发的软件可以在安卓7.0上完美兼容,但由于触摸屏安卓版本太低不能兼容;询问厂商才知道这款触摸…

【Datawhale AI夏令营】从零上手CV竞赛Task1

【Datawhale AI夏令营】从零上手CV竞赛Task1 前言赛事官网学习任务TASK 1 前言 这个暑假我通过微信群关注到了【Datawhale AI夏令营】,并且记录我的代码学习以及调试的过程。 赛事官网 赛事官网:https://www.marsbigdata.com/competition/details?id…

RING总线(环形总线)Intel大小核的由来

想到Intel处理器,无一例外大家想到的就是现在的它具有大小核的结构,也就是性能核和效能核。这种结构的由来是由RING总线布局结构而诞生。ring总线(环形总线),这个名字经常出现在讨论普通消费级处理器的时候&#xff0c…

算法【Java】—— 二分查找

二分查找算法分析 二分查找算法其实也是对撞指针的另一种用法,左右两个指针分别指向数据的左右端点,然后双指针向中间移动。 朴素二分查找 上面这道题是朴素的二分查找算法,由于数据是有序的,我们可以从中间值入手 如果中间值大…

国货之光|暴雨机推出面向大模型训练的AI服务器

当前,“百模大战”带来了算力需求的爆发,尤其是以ChatGPT为代表的多模态AI大模型,‌其参数规模和训练数据量均达到了前所未有的规模。‌GPT-3.5、‌GPT-4等模型的参数规模突破了万亿级别,‌训练数据量达到了TB级别,‌这…

AI多模态模型架构之模态生成器:Modality Generator

〔探索AI的无限可能,微信关注“AIGCmagic”公众号,让AIGC科技点亮生活〕 本文作者:AIGCmagic社区 刘一手 前言 AI多模态大模型发展至今,每年都有非常优秀的工作产出,按照当前模型设计思路,多模态大模型的…

Ai+若依(定时任务):【01篇】

若依为定时任务功能提供方便友好的web界面,实现动态管理任务。 案例 每间隔5秒,控制台输出系统时间。 实现步骤: ①创建任务类 在服务 ==》ruoyi-quartz==》task==》MyTask package com.ruoyi.quartz.task;import org.springframework.stereotype.Component;@Component…

程序员修炼之道 10:并发

不记录,等于没读。 这里是我阅读《程序员修炼之道》这本书的记录和思考。 并发和并行代码曾经是很新奇的玩意,但现在已经是标配。 并发 :Concurrency,指的是两个或更多个代码段在执行过程中表现得像是在同时运行一样。 并行 &am…

AD的3D模型格式是什么

AD通常指的是Altium Designer,这是一款用于电子设计自动化的软件,主要用于电路板的设计。在Altium Designer中,3D模型主要用于PCB(印制电路板)设计中的可视化,以便设计师能够在三维空间中查看组件和板的布局…

21:【stm32】定时器二:输入捕获

输出捕获 1、输出捕获的简介2、输入捕获原理2.1、输入滤波2.2、信号选择2.3、CCx事件和CCx中断 3、标准库编程 1、输出捕获的简介 定时器通过通道捕获输入电平的变化,而通道有CH1~CH4,这4个通道。 我们设定对通道CH3的输入电平进行捕获。假如CH3突然捕获…

Java排序算法详解

在Java编程中,排序算法是数据处理和算法设计中的基础且重要的部分。它们广泛应用于各种场景,如数据库管理、数据分析、图形界面排序等。掌握几种常见的排序算法对于提升程序效率和优化性能至关重要。本文将详细解析几种经典的Java排序算法,包…

OpenGL笔记二十之深度检测概念

OpenGL笔记二十之深度检测概念 —— 2024-08-25 晚上 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记二十之深度检测概念1.课程PPT截图2.运行3.代码 1.课程PPT截图 2.运行 3.代码 关键部分 main.cpp #include <iostream>#include "glfra…

【逐行注释】三维EKF的MATLAB代码|附下载链接

文章目录 程序概况源代码与注释运行结果代码块解析 程序概况 基于MATLAB的EKF&#xff08;扩展卡尔曼滤波&#xff09;代码解析。状态转移和观测都是非线性的&#xff0c;也就是说会涉及到雅克比矩阵的求解。我尽量将模型设计复杂一点&#xff0c;便于拿到手以后改成自己想要的…

C++类和对象(2)——拷贝构造函数

拷贝构造函数的语法 拷贝构造函数是构造函数的重载&#xff0c; 用于这种情况&#xff1a;用已经构造好的对象去给另一个对象初始化。 int main() {Date d1(2024, 8, 1);Date d2(d1);//用d1初始化d2return 0; } 我们以Date类为例子讲解一下。 class Date { public://全缺省…

.NET周刊【8月第3期 2024-08-18】

国内文章 Roslyn 简单实现代码智能提示补全功能 https://www.cnblogs.com/lindexi/p/18365261 相信有很多伙伴热衷于编写 IDE 应用&#xff0c;在 dotnet 系下&#xff0c;通过 Roslyn 友好的 API 和强大的能力&#xff0c;实现一个代码智能提示是非常简单的事情。本文将和大…

【互动直播】支付能力视角与年龄的调节作用—推文分享—2024-08-25

【今天的关键词是&#xff1a;“interaction” AND “live commerce”】 第一篇&#xff1a;通过信息技术的可供性&#xff08;IT affordance&#xff09;视角&#xff0c;直播电商的实用、社交和享乐三个维度的IT可供性如何影响购买意愿&#xff1b; 第二篇&#xff1a;研究…

凭借着“16连射”造就一个游戏角色的诞生

一、高桥名人的冒险岛起源 大约在 1986 年&#xff0c;Hudson 的所有人都很喜欢一款名为《神奇男孩》的街机游戏&#xff0c;公司内部有人建议将其改编成 Famicom 游戏。因此&#xff0c;副总裁和高桥名人去查看角色和游戏的开发情况&#xff0c;当他们查看正在构建的角色时&am…

C++类和对象的基础介绍(1)

类的定义 C类的关键字是class&#xff0c;学过c的朋友们可能会觉得和结构体struct有点像。 在C中struct也可以定义类&#xff0c;但和class定义的有所区别。 我们还是推荐使用class。 类里面会有成员变量、成员函数的定义和声明、构造函数、拷贝构造函数、析构函数、运算符…