学习STM32第十五天

SPI外设

一、简介

STM32F4XX内部集成硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU负担,可配置8位/16位数据帧,高位(最常用)/低位先行,三组SPI接口,支持DMA

由上图可知SPI是通过接收/发送缓冲区移位寄存器 进行通信,其中SPI1是在APB2总线,SPI2、SPI3在APB1总线。发送和接收共用一个SR,即SPI是同步通信 接口。SS引脚一般用GPIO口指定从机 ,硬件NSS引脚一般是用来配置多主机模式。

发送:数据先进入TDR,经SR通过MOSI向从机输出

接收:数据由MISO进入SR,然后经过RDR向地址数据总线输出

由此可对上面SPI框图进行简化,基本结构如下

这里给出SPI主模式全双工连续传输模式下的时序图,

上图选择的是模式3,SCK高电平为空闲状态,在SCK第一个边沿移出数据,第二个编译移入数据。上面时序图采用小端模式,低位先行,这里对进行分析

发送:

  • SS置低电平,开始时序,选中从机。此时TXE = 1,TDR为空;RXNE = 0,RDR为空。BSY = 1
  • 软件写入0xF1到SPI_DR,即要发送的第一个数据,此时TXE = 0,RXNE = 0,TDR非空
  • TDR中的0xF1会立刻转入到SR中,TDR清空,MOSI开始发送同时TXE = 1
  • 软件等待TXE = 1,然后写入0xF2到SPI_DR,即要发送的第二个数据,此时TXE = 0,RXNE = 0
  • TDR中的0xF2会随后自动进入SR,MOSI在发送完第一个数据会自动发送第二个数据
  • TDR发送完所有数据,TXE会自动置1,SR发送完所有数据后,BSY = 0

接收:

  • SS置低电平,开始时序,选中从机。此时TXE = 1,TDR为空;RXNE = 0,RDR为空。BSY = 1
  • MISO依次接收从机的数据,输出到SR
  • SR中的数据以小端模式进入到SPI_DR中
  • 软件等待RXNE = 1,然后数据总线读取RDR中的数据0xA1,同时RXNE = 0,RDR变为空
  • MISO接收第二个数据,输出到SR
  • SR中的第二个数据以小端模式进入到SPI_DR中
  • 软件等待RXNE = 1,然后数据总线读取RDR中的第二个数据,同时RXNE = 0,RDR变为空
  • RDR接收完所有数据,RXNE = 0

由上图可知,SPI全双工连续通信是交叉进行的 ,发送数据1,发送数据2,再接收数据1;发送数据3,再接收数据2;在时序上要求操作之间的间隙非常小。

非连续传输模式,只需要四行代码。上图是SPI模式3,SCK高电平为空闲状态,分析如下

  • SS置低电平,选中从机,开始时序此时TXE = 1,RXNE = 0,TDR为空
  • 软件写入0xF1到SPI_DR,此时TDR = 0xF1,TXE = 0
  • TDR中的0xF1立即进入SR中,MOSI开始发送0xF1,TDR清空,TXE = 1
  • 等待MOSI将第一个字节数据发送完毕,此时接收第一个字节数据的时序也完成,即RXNE = 1
  • 读取接收到的第一个字节数据,然后将第二个字节数据写入TDR,开始发送第二个数据
  • 等待MOSI发送完第二个字节数据,此时接收到了第二个字节数据
  • 读取完第二个字节数据,然后将第三个字节数据写入TDR,开始发送第三个数据

整体流程就是:等待TXE = 1,写入数据到TDR,等待RXNE = 1,读取RDR数据。这样实现发送数据1接收数据1,发送数据2接收数据2。但是字节之间存在一定的间隙,降低传输效率。

二、实验案例

进行STM32F4XX对板载W25Q16读写,代码如下

c 复制代码
#include "stm32f4xx.h"                  // Device header

//硬件SPI通信,采用非连续传输方案
/*PB0引脚模拟SS输出*/
void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)BitValue);//片选引脚输出
}

/*SS->PB0,MISO->PB4,MOSI->PB5,SCK->PB3,板载W25Q16支持SPI模式0和模式3*/
/*
*	SPI1是在APB2总线,SPI2、SPI3在APB1总线
*	PB3: SPI1_SCK、SPI3_SCK
*	PB4: SPI1_MISO、SPI3_MISO
*	PB5: SPI1_MOSI、SPI3_MOSI
*	PB0: 使用GPIO模拟SS
*/
void MySPI_Init()
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;	//PB3复用为SPI1_SCK,PB4复用为SPI1_MISO,PB5复用为SPI1_MOSI
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				//使用GPIO模拟片选信号SS
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);	//GPIO引脚复用
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);
	
	//SPI配置
	SPI_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC校验根据实际需求填写
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//数据帧大小
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//全双工
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;		//高位先行
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;			//这里选择主机
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//软件模拟片选SS
	SPI_Cmd(SPI1, ENABLE);

//	
	MySPI_W_SS(1);											//默认是终止条件
}

/*起始条件*/
void MySPI_Start()
{
	MySPI_W_SS(0);
}
/*终止条件*/
void MySPI_End()
{
	MySPI_W_SS(1);
}
/*交换一个字节,这里选择模式0*/
/*			SCK低电平为空闲状态
*	SS下降沿启动,主机移出高位数据到MOSI
*	SCK上升沿,主机移入高位数据MISO
*	SCK下降沿,主机移出高位数据MOSI
*			非连续传输需要四步
*/
uint8_t MySPI_SwapByte_Mode0(uint8_t ByteSend)
{
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);	//等待TXE = 1
 	SPI_I2S_SendData(SPI1, ByteSend);								//将数据写入到DR中
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);	//等待RXNE = 1
	return SPI_I2S_ReceiveData(SPI1);								//读取RDR的数据
}
相关推荐
Go-higher1 小时前
DriverTest 驾考知识卡片学习助手 —— 一款基于 Jetpack Compose 的现代 Android 学习APP
android·学习
CCPC不拿奖不改名1 小时前
Redis 工程化部署深度解析
linux·服务器·数据库·redis·深度学习·缓存·rag
星幻元宇VR1 小时前
公共安全主题展厅设备【防洪防汛安全科普系统】
科技·学习·安全
AI科技星1 小时前
32维超复数流形中意识信息场与物质耦合的拓扑动力学
人工智能·学习·算法·数据挖掘·回归·乖乖数学·全域数学
鱼很腾apoc2 小时前
【Linux】第7期 进程间通信 (IPC) 详解:管道 (匿名 / 命名) + System V
linux·服务器·c语言·学习·进程间通信·ipc
毒爪的小新2 小时前
踩坑实录 | RAG知识库完整搭建-Milvus2.4+BGE大中文AI模型嵌入
linux·人工智能·ai·milvus·rag
科技大视界2 小时前
大学生专业课笔记本用哪款?来酷Air14酷睿版14英寸轻薄笔记本电脑适合学习任务多的人
学习
2023自学中3 小时前
imx6ull 开发板, mame 模拟器,运行游戏 测试
linux·游戏·嵌入式·开发板
是个西兰花3 小时前
Linux:进程信号
linux·运维·服务器
weixin_423533993 小时前
c++类的继承学习-去中心化交易所(DEX)的“流动性池初始化与交易指令”设计
c++·学习·去中心化