STM32 - SPI硬件外设

配合我的上一篇SPI ​​​​​​通信 协议-CSDN博客一起理解更佳,本文后看

SPI 是由摩托罗拉(Motorola)公司开发的全双工同步串行总线,是 MCU 和外围设备之间进行通信的同步串行端口。主要应用在EEPROM、Flash、RTC、ADC、网络控制器、MCU、DSP以及数字信号解码器之间。SPI 系统可直接与各个厂家生产的多种标准外围器件直接接口,一般使用4 条线:SCK、MISO、MOSI 、SS

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

  • 可配置8位/16位数据帧、高位先行/低位先行
  • 时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256) 支持多主机模型、主或从操作
  • 可精简为半双工/单工通信
  • 支持DMA 兼容I2S协议

STM32F103C8T6 硬件SPI资源:SPI1(APB2)、SPI2(APB1)

SPI基本图

NSS :从设备选择。这是一个可选的引脚,用来选择主 / 从设备。它的功能是用来作为"片选引脚",让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
LSBFIRST是低位先行 。如果是MSBFIRST,那么就是高位先行,
此时MISO和MOSI需要换,如图的另一种指法。

SPI基本结构

注意:该图是高位先行

主模式传输

主模式、全双工模式下

连续传输(BIDIMODE=0并且RXONLY=0)时

TXE/RXNE/BSY 的变化示意图

本质上来说:第一个字节数据发送,发送时下一个字节就已经到发送缓冲器等待,当第一个字节发送成功,立马发送第二个,第三个字节进入发送缓冲器等待 。

非连续传输发送(BIDIMODE=0并且RXONLY=0)时

TXE/BSY变化示意图

非连续传输发送来说:第一个字节数据发送,发送成功后,下一个字节才到发送缓冲器,再发送第二个,等发送成功后,第三个字节再进入发送缓冲器。

代码

cpp 复制代码
/**
  * 函    数: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起始
cpp 复制代码
void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);	
	//根据BitValue,设置SS引脚的电平
}

/**
  * 函    数:SPI起始
  * 参    数:无
  * 返 回 值:无
  */
void MySPI_Start(void)
{
	MySPI_W_SS(0);				//拉低SS,开始时序
}
SPI终止
cpp 复制代码
/**
  * 函    数:SPI终止
  * 参    数:无
  * 返 回 值:无
  */
void MySPI_Stop(void)
{
	MySPI_W_SS(1);				//拉高SS,终止时序
}
交换一个字节
cpp 复制代码
/**
  * 函    数: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);							
	//读取接收到的数据并返回
}
相关推荐
懒惰的bit4 小时前
STM32F103C8T6板子使用说明
stm32·单片机·嵌入式硬件
芯岭技术5 小时前
PY32系列单片机离线烧录器,可配置选项字节和上机台批量烧录
单片机·嵌入式硬件
阿川!7 小时前
嵌入式软件--stm32 DAY7 I2C通讯上
stm32·单片机·嵌入式硬件·mcu
O。o.尊都假都8 小时前
socket套接字的超时控制
单片机·嵌入式硬件·网络协议
欢乐熊嵌入式编程9 小时前
智能手表项目的《项目背景与目标说明书》样本文档
嵌入式硬件·目标跟踪·规格说明书·智能手表
海尔辛10 小时前
学习黑客BitLocker与TPM详解
stm32·单片机·学习
海染棠花10 小时前
vscode+platformIO开发STM32(八)
ide·vscode·stm32
上海合宙LuatOS11 小时前
全栈工程师实战手册:LuatOS日志系统开发指南!
java·开发语言·单片机·嵌入式硬件·物联网·php·硬件工程
公子无缘11 小时前
【嵌入式】记一次解决VScode+PlatformIO安装卡死的经历
vscode·stm32·单片机·mcu·platformio
昊昊昊昊昊明11 小时前
十天学会嵌入式技术之51单片机—day-9
单片机·嵌入式硬件·51单片机