1. SPI外设简介

  • STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担
  • 可配置8位(常用)/16位数据帧、高位先行(常用)/低位先行
  • 时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)
  • 支持多主机模型、主或从操作
  • 可精简为半双工(去掉一根数据线,只在其中一根数据线上分时进行发送或接收)/单工(去掉接收数据线,在发送线进行只发的数据传输,只发模式;或者去掉发送数据线,在接收线进行只收的数据传输,只收模式)通信
  • 支持DMA
  • 兼容I2S协议(一种数字音频信号传输的专用协议)
  • STM32F103C8T6 硬件SPI资源:SPI1(APB2,PCLK为72MHz)、SPI2(APB1,PCLK为36MHz)

1.1 SPI框图

移位寄存器右边的数据低位,一位一位地从MOSI移出,MISO的数据一位一位地移入到移位寄存器左边的数据高位。显然移位寄存器是右移状态,所以框图中表示的是低位先行的配置。

LSBFIRST控制位(帧格式控制位)可以控制是低位先行还是高位先行。置0,先发送MSB(高位);置1,先发送LSB(低位)。

MOSI和MISO处的方框内,两者形成交叉。这是用来进行主从模式引脚变换的,此SPI外设既可以做主机,也可以做从机。

NSS引脚,SS为从机选择,低电平有效,所以前面加了个N。

SPI_CR1寄存器的BR2、BR1、BR0位用来控制分频系数,LSBFIRST位决定高位先行或低位先行,SPE位(SPI Enable)是SPI使能,即SPI_Cmd函数配置的位,MSTR位(Master)配置主从模式(1为主模式,0为从模式),CPOL和CPHA位用来选择SPI的4种模式。

SPI_SR寄存器的TXE位为发送寄存器空,RXNE位为接收寄存器非空。

SPI框图

1.2 SPI基本结构

TDR中的数据,整体转入移位寄存器的时刻,置TXE标志位;
移位寄存器中的数据,整体转入RDR的时刻,置RXNE标志位。

SPI基本结构

1.3 SPI时序流程 

主模式全双工连续传输效率高,但使用复杂。
非连续传输效率较低,但使用简便,因此大多对传输效率没有太高要求的情况下,均使用非连续传输。

主模式全双工连续传输
非连续传输

1.4 软件/硬件波形对比

硬件波形数据线的变化是紧贴SCK边沿的,而软件波形数据线的变化在SCK边沿后有一些延迟。

软件波形
硬件波形

2. 硬件SPI读写W25Q64

2.1 接线图

DO(从机输出)接到PA6、CLK(时钟)接到PA5、DI(从机输入)接到PA7

CS(片选)接到PA4,CS一般使用软件模拟的方式来实现,所以接在其他引脚也可以

2.2 代码

本节代码基于软件SPI读写W25Q64项目修改,用硬件控制替换MySPI.c中软件控制部分。 

SPI初始化流程:

  1. 开启SPI和GPIO的时钟
  2. 初始化GPIO口。其中,SCK和MOSI是由硬件外设控制的输出信号,所以配置为复用推挽输出;MISO是硬件外设的输入信号,可以配置为上拉输入;SS是软件控制的输出信号,所以配置为通用推挽输出
  3. 配置SPI外设

MySPI.c

#include "stm32f10x.h"                  // Device header

//  写SS/CS引脚
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;//	CS/SS(PA4),CLK/SCK(PA5),DI/MOSI(PA7)
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//	复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;//	CLK/SCK(PA5),DI/MOSI(PA7)
	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;//	DO/MISO(PA6)
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	SPI_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//	波特率预分频器,配置SCK时钟的频率。目前SCK的时钟频率为72M/128,大概是500多KHz
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//	时钟相位。第1个边沿开始采样
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//	时钟极性。选择SPI模式0,空闲默认低电平
	SPI_InitStructure.SPI_CRCPolynomial = 7;//	CRC校验多项式。填默认值7
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//	配置8位or16位数据帧
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//	配置SPI裁剪引脚。选择双线全双工
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//	配置高位先行or低位先行
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//	决定当前设备是SPI的主机or从机
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//	软件NSS
	SPI_Init(SPI1, &SPI_InitStructure);
	
	SPI_Cmd(SPI1, ENABLE);
	
	MySPI_W_SS(1);//	默认不选中从机
}

//  时序基本单元:起始条件
void MySPI_Start(void)
{
    MySPI_W_SS(0);
}

//  时序基本单元:终止条件
void MySPI_Stop(void)
{
    MySPI_W_SS(1);
}

//  时序基本单元:交换一个字节(模式0)
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
    //	1.等待TXE为1,发送寄存器为空
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);//	不需要手动清除标志位。当写入SPI_DR时,TXE标志被清除
		
	//	2.软件写入数据至SPI_DR
	SPI_I2S_SendData(SPI1, ByteSend);//	同时清除TXE标志位
		
	//	3.等RXNE为1,表示收到一个字节,同时也表示发送时序产生完成了
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//	不需要手动清除标志位。读SPI数据寄存器可以清除RXNE标志位
	
	//	4.读取DR
	return SPI_I2S_ReceiveData(SPI1);//	同时清除RXNE标志位
}

代码其他部分见(【江协STM32】11-2/3 W25Q64简介、软件SPI读写W25Q64,第2.2节)

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐