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

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.dtcms.com/a/341360.html

相关文章:

  • MySQL 语法基础入门:从零开始掌握数据库操作
  • CoreShop微信小程序商城框架开启多租户-添加一个WPF客户端以便进行本地操作(5)
  • 读《精益数据分析》:规模化(Scale)—— 复制成功,进军新市场
  • VMware Workstation | 安装Ubuntu18.04.5
  • 波纹干涩 shader
  • 零知开源——基于STM32F103RBT6与ADXL362三轴加速度计的体感迷宫游戏设计与实现
  • 【Unity3D优化】平衡 Hide 与 Destroy:基于性能等级与 LRU 的 UI 管理策略与实践思考
  • PostgreSQL Certified Master 专访 | 第四期 贾桂军
  • 【Techlog】01入门-井筒数据整合软件的基本认识
  • 控制器调用服务层出现Cannot invoke ... 显示服务层bean对象为null
  • PostgreSQL 流程---更新
  • 编程语言学习
  • 环境搭建:centos7+docker+Oracle
  • 【datawhale组队学习】RAG技术 - TASK02
  • 3dmax 材质 / AO 通道渲染全流程
  • 3D检测笔记:相机模型与坐标变换
  • 超大型公共场所的智慧守护者——人脸动态识别与管理系统
  • 手机截图如何优雅地放在word里
  • 从原理到应用:GPS 定位技术的核心机制与未来发展
  • 心路历程-了解网络相关知识
  • 耐达讯自动化Profibus转光纤技术如何让称重传感器“零误差“运转?
  • 初始推荐系统
  • sed 命令的使用
  • Linux软件编程:总结
  • C++26反射机制:一场语言范式的革命
  • GEO公司推荐TOP5榜单:解析其数据架构与安全保障体系
  • C++智能指针详解:告别内存泄漏,拥抱安全高效
  • 如何用Python打造PubMed API客户端:科研文献检索自动化实践
  • Nginx 的完整配置文件结构、配置语法以及模块详解
  • 鸿蒙语音播放模块设置为独立线程,保证播放流畅