当前位置: 首页 > backend >正文

STM32学习笔记16-SPI硬件控制

硬件SPI

SPI外设简介

  • STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
  • 可配置8位/16位数据帧、高位先行/低位先行
  • 时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)——PCLK外设时钟
  • 支持多主机模型、主或从操作
  • 可精简为半双工/单工通信
  • 支持DMA
  • 兼容I2S协议(数字音频协议)
  • STM32F103C8T6 硬件SPI资源:SPI1(挂载在APB2上,PCLK是72MHz)、SPI2(挂载在APB1上,PCLK是36MHz)

SPI框图

主设备

控制逻辑:

波特率发生器:产生SCK时钟,内部主要是一个分频器,根据PCLK来进行分频,BR0、BR1、BR2来控制分频系数

LSBFIRST:决定高位先行还是低位先行

SPE:是SPI使能。就是SPI_Cmd函数配置的位

BR:配置波特率,就是SCK时钟频率

MSTR:配置主从模式,1是主(常用),0是从

CPOL和CPHA:选择SPI的4种模式

SR状态寄存器:中的TXE发送寄存器空和RXNE接收寄存器非空

SPI基本结构

时序图

运行控制-如何产生时序,什么时候读写DR

主模式全双工连续传输

一旦TDR把数据放到移出寄存器,TEX就置1,会不断把数据放到TDR里,方便于快速传输

非连续传输-常用

会等待TDR把数据传向移位寄存器传完后使TXE置1

由于不会把数据提前放在TDR中,所以在数据传输中会有一定间隙,拖慢传输速度。

在SCK的频率越高越明显:

256分频示波图:

128分频示波图:

64分频示波图

2分频示波图


流程:

  1. 等待TXE为1
  2. 写入发送的数据至TDR
  3. 等待RXNE为1
  4. 读取RDR接收的数据
  5. 交换字节,重复

软件/硬件波形对比

区别与I2C一样:硬件的波形与边沿是贴合的,软件有点延迟

接线图:

引脚映射

SPI1的引脚映射:

SPI2的引脚映射:

重定义:

11-2 硬件SPI读写W25Q64——默认非连续

        修改底层MySPI.c文件

  1. 开启时钟,SPI和GPIO时钟
  2. 初始化GPIO口,其中SCK和MOSI,是由硬件外设控制的输出信号,所以配置为复用推挽输出;MISO是硬件外设的输入信号,配置为上拉输入;SS引脚是软件控制的输出信号,配置为通用推挽输出
  3. 配置SPI外设,使用结构体
  4. 开关控制
  5. 根据时序图来执行运行控制的代码,产生交换的时序:写DR、读DR和获取状态标志位等

时序步骤:

  1. 等待TXE为1,发送寄存器为空
  2. 软件写入数据至TDR,之后转移到移位寄存器,移位寄存器会自动生产波形(这个过程是自动完成):一位一位的移到MOSI上,产生波形;由于是非连续传输,所以在这个过程中死等就行了,但是接收与发送时同时进行的,也就是接收完成了发送也完成了。
  3. 所以接收完成了会收到一个标志位RXNE为1
  4. 读取RDR,把交换接收的数据读出来

注:不需要手动清除标志位


相关库函数:

void SPI_I2S_DeInit(SPI_TypeDef* SPIx);

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

void SPI_StructInit(SPI_InitTypeDef* SPI_I

void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);

void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);

void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);  //写DR数据寄存器,把数据发送到TDR

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);  //读DR数据寄存器,返回值就是接收数据寄存器RDR

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

MySPI.c
#include "stm32f10x.h"                  // Device header/*引脚配置层*/void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		//根据BitValue,设置SS引脚的电平
}
//初始化
void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;//SS引脚——通用推挽模式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);	//SCK和MOSI引脚——复用推挽模式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);		//MISO引脚——上拉输入模式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_CPHA=SPI_CPHA_1Edge;SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;SPI_InitStructure.SPI_CRCPolynomial=7;SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);MySPI_W_SS(1);  //默认不选择从机
}
//SPI的3个时序基本单元
//
//起始
void MySPI_Start(void)
{MySPI_W_SS(0);				//拉低SS,开始时序
}
//结束
void MySPI_Stop(void)
{MySPI_W_SS(1);				//拉高SS,终止时序
}//
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.xdnf.cn/news/18225.html

相关文章:

  • 力扣48:旋转矩阵
  • RAG拓展、变体、增强版(二)
  • redis执行lua脚本的原子性和数据库原子性的区别
  • C++STL-list 底层实现
  • GSPO:Towards scalable reinforcement learning for language models
  • Web 安全之延迟攻击(Delay Attack)详解
  • 从基础到本质:文件 IO 操作全解析
  • 【Linux仓库】进程等待【进程·捌】
  • vc++调试总结
  • E10 通过RPC实现账号批量锁定与解锁
  • 管理项目服务器连接数据库
  • 【语法】markdown非常用场景
  • 交叉编译.so到鸿蒙使用
  • ansible playbook 实战案例roles | 实现基于node_exporter的节点部署
  • AV1视频编码器2024-2025技术进展与行业应用分析
  • scikit-learn/sklearn学习|变量去中心化和标准化
  • 【iOS】NSRunLoop
  • PyCharm2025无法启动Powershell.exe的解决办法
  • Google Chrome 扩展不受信任 - 不受支持的清单版本 解决方案
  • 苹果XR芯片介绍
  • 【GPT入门】第51课 将hf模型转换为GGUF
  • MyBatis--缓存详解
  • ACE - 阿里开源的一站式AI图像生成和编辑模型
  • C++排序算法学习笔记
  • 【数据结构】用堆解决TOPK问题
  • Ansible 配置并行 - 项目管理笔记
  • Python入门第11课:Python网络请求入门,使用requests库轻松获取网页数据
  • Leetcode 深度优先搜索 (7)
  • Jenkins项目发布基础
  • UE5 使用RVT制作地形材质融合