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

STM32G474的SPI工作在从机模式

STM32G474的SPI工作在从机模式,我们令SPI1工作在主机模式,SPI2工作在从机模式中,实现数据数据互传。本测试只讲解SPI一主一从通讯,至于一主多从,程序中有说明。
SPI1外设用作主机,其接口:将SPI1_SCK映射到PA5,SPI1_MISO映射到PA6,SPI1_MOSI映射到PA7,SPI1_NSS映射到PA4;
SPI2外设用作从机,其接口:将SPI2_SCK映射到PB13,SPI2_MISO映射到PB14,SPI2_MOSI映射到PB15,SPI2_NSS映射到PB12;

1、连线:
1)、将SPI1_SCK(PA5)和SPI2_SCK(PB13)连接在一起;
2)、将SPI1_MISO(PA6)和SPI2_MISO(PB14)连接在一起,实现SPI2发送,SPI1接收;
3)、将SPI1_MOSI(PA7)和SPI2_MOSI(PB15)连接在一起,实现SPI1发送,SPI2接收;
4)、将SPI1_NSS(PA4)和SPI2_NSS(PB12)连接在一起,实现SPI1选择SPI2外设

为了保证数据传输正确,需要采用SPI中断方式发送和接收数据,否则就要使用DMA传输数据,才能保证数据传输的正确性。

2、SPI1.c主机程序如下

#include "SPI1.h"
#include "SPI_Std_Lib.h" //自定义USART标准库库
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "LED.h"
#include "delay.h"

//SPI1外设用作主机,其接口:将SPI1_SCK映射到PA5,SPI1_MISO映射到PA6,SPI1_MOSI映射到PA7,SPI1_NSS映射到PA4
//STM32G474RE使用SPI1中断发送和接收8位数据,其作用:可以提高系统的整体效率和响应速度。
uint8_t SPI1_RCV_buffer[SPI1_RCV_buffer_Size]; //用来存放硬件SPI1接收到的数据;
uint16_t SPI1_in;  //SPI1接收缓冲区的输入下标;
uint16_t SPI1_out; //SPI1接收缓冲区的输出下标;
SPI1_Receive_Data_t str_SPI1_RCV_Data;

uint8_t SPI1_TX_Buffer[SPI1_TX_Buffer_Size]; //SPI1发送缓冲区数组
uint8_t SPI1_TX_Buffer_Load_Index;          //SPI1_TX_Buffer[]的装载索引值
volatile uint8_t SPI1_TX_Buffer_Send_Index;          //发送下标值
uint16_t SPI1_Send_Cnt;

void SPI1_Init(void);
void Start_SPI1_Send_Data(void);
void Read_SPI1_RCV_buffer(uint16_t MAX_time);

void SPI1_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	SPI_HandleTypeDef hspi1;

	__HAL_RCC_SPI1_CLK_ENABLE();  //使能SPI1外设时钟
	__HAL_RCC_GPIOA_CLK_ENABLE(); //GPIOA时钟使能

	GPIO_InitStruct.Pin = GPIO_PIN_5;            //选择引脚编号为5
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
//	GPIO_InitStruct.Pull = GPIO_NOPULL;          //引脚上拉和下拉都没有被激活
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;   //PA5映射到SPI1_SCK
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOA的外设寄存器
	//配置“SPI1_SCK引脚”

	GPIO_InitStruct.Pin = GPIO_PIN_6;            //选择引脚编号为6
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
//	GPIO_InitStruct.Pull = GPIO_NOPULL;          //引脚上拉和下拉都没有被激活
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;   //PA6映射到SPI1_MISO
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOA的外设寄存器
	//配置“SPI1_MISO引脚”

	GPIO_InitStruct.Pin = GPIO_PIN_7;            //选择引脚编号为7
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
//	GPIO_InitStruct.Pull = GPIO_PULLDOWN;        //设置下拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;   //PA7映射到SPI1_MOSI
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOA的外设寄存器
	//配置“SPI1_MOSI引脚”

	GPIO_InitStruct.Pin = GPIO_PIN_4;            //选择引脚编号为4
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
//	GPIO_InitStruct.Pull = GPIO_PULLDOWN;        //设置下拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;   //PA4映射到SPI1_NSS
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOA的外设寄存器
	//配置“SPI1_NSS引脚”

  hspi1.Instance = SPI1;//选择SPI1
	hspi1.Init.Mode = SPI_MODE_MASTER;
	//SPIx_CR1寄存器bit2(MSTR),MSTR=1配置SPI外设为主机
	hspi1.Init.Direction = SPI_DIRECTION_2LINES;
	//SPIx_CR1寄存器bit15(BIDIMODE),BIDIMODE=0选择“双线单向数据模式” 
	hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
	//SPIx_CR1寄存器bit0(CPHA)
	//CPHA=0,表示从SPI_SCK的空闲位开始,第1个边沿用来采集第1个位
	//CPHA=1,表示从SPI_SCK的空闲位开始,第2个边沿用来采集第1个位
	hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
	//SPIx_CR1寄存器bit1(CPOL)
	//CPOL=0,表示SPI_SCK的空闲位为低电平
	//CPOL=1,表示SPI_SCK的空闲位为高电平

//	hspi1.Init.NSS = SPI_NSS_SOFT;
  //配置spi在master下,NSS作为普通IO,由用户自己写代码控制片选,可以1主多从
//	hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
  //仅当配置spi在slave下,作为从机片选输入
	hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
	//配置spi在master下,NSS作为SPI专用IO,由MCU自动控制片选,只能1主1从
	//SPIx_CR1寄存器bit9(SSM),SSM=1,NSS引脚的输入将被替换为来自SSI位的值

	hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
	//SPIx_CR1寄存器bit5(BR[2:0])
	//BR[2:0]=000b,SCK的时钟频率为fPCLK/2
	//BR[2:0]=001b,SCK的时钟频率为fPCLK/4
	//BR[2:0]=010b,SCK的时钟频率为fPCLK/8
	//BR[2:0]=011b,SCK的时钟频率为fPCLK/16
	//BR[2:0]=100b,SCK的时钟频率为fPCLK/32
	//BR[2:0]=101b,SCK的时钟频率为fPCLK/64
	//BR[2:0]=110b,SCK的时钟频率为fPCLK/128
	//BR[2:0]=111b,SCK的时钟频率为fPCLK/256
	hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
	//SPIx_CR1寄存器bit7(LSBFIRST)
	//LSBFIRST=0,表示先传送最高位
	//LSBFIRST=1,表示先传送最低位

	hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	//SPIx_CR1寄存器bit13(CRCEN),CRCEN=0不使能CRC校验
	hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
	//SPIx_CR1寄存器bit11(CRCL)
	//CRCL=0表示CRC的长度为8位
	//CRCL=1表示CRC的长度为16位
	hspi1.Init.CRCPolynomial = 7;
	//SPIx_CRCPR,这个寄存器包含了CRC计算的多项式
	//CRC多项式(0x0007)是该寄存器的默认值。可以根据需要,配置自己的“CRC多项式”。 

#if (HalfWord == 0U)
	hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
	//SPIx_CR2寄存器bit11:8(DS[3:0])
	//DS[3:0]=0011b,表示SPI传输的一帧的数据长度为4位
	//DS[3:0]=0100b,表示SPI传输的一帧的数据长度为5位
	//DS[3:0]=0101b,表示SPI传输的一帧的数据长度为6位
	//DS[3:0]=0110b,表示SPI传输的一帧的数据长度为7位
	//DS[3:0]=0111b,表示SPI传输的一帧的数据长度为8位,这里读写8位数据
	//DS[3:0]=1000b,表示SPI传输的一帧的数据长度为9位
	//DS[3:0]=1001b,表示SPI传输的一帧的数据长度为10位
	//DS[3:0]=1010b,表示SPI传输的一帧的数据长度为11位
	//DS[3:0]=1011b,表示SPI传输的一帧的数据长度为12位
	//DS[3:0]=1100b,表示SPI传输的一帧的数据长度为13位
	//DS[3:0]=1101b,表示SPI传输的一帧的数据长度为14位
	//DS[3:0]=1110b,表示SPI传输的一帧的数据长度为15位
	//DS[3:0]=1111b,表示SPI传输的一帧的数据长度为16位
#else
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;//这里读写16位数据
#endif

	hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
	//SPIx_CR2寄存器bit4(FRF)
	//FRF=0,帧格式为SPI Motorola mode,这里使用“Motorola模式”
	//FRF=1,帧格式为SPI TI mode
//	hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
	//SPIx_CR2寄存器bit3(NSSP)
	//NSSP=0,在主机模式中,NSS引脚输出不使能
	hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
	//SPIx_CR2寄存器bit3(NSSP)
	//NSSP=1,在主机模式中,NSS引脚输出使能
	HAL_SPI_Init(&hspi1);

	_HAL_SPI_ENABLE(SPI1);
	//SPIx_CR1寄存器bit6(SPE),SPE=1令SPI外设使能

  _HAL_SPI_ENABLE_IT(SPI1,SPI_IT_ERR);
  _HAL_SPI_DISABLE_IT(SPI1,SPI_IT_TXE);
	_HAL_SPI_ENABLE_IT(SPI1,SPI_IT_RXNE);
	HAL_NVIC_SetPriority(SPI1_IRQn, 5, 0);
	//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
	//选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
	//这里设置SPI1的抢占优先级为5,响应优先级为0
	HAL_NVIC_EnableIRQ(SPI1_IRQn);

  SPI1_Send_Cnt=StartData1-1;
  SPI1_TX_Buffer_Send_Index = SPI1_TX_Buffer_Load_Index;
}

//函数功能:启动SPI1发送
void Start_SPI1_Send_Data(void)
{
	uint8_t k;

	if(SPI1_TX_Buffer_Send_Index >= SPI1_TX_Buffer_Load_Index)
	{
		SPI1_Send_Cnt++;
		if(SPI1_Send_Cnt>=EndData1) SPI1_Send_Cnt=StartData1;
		sprintf( (char*)SPI1_TX_Buffer,"0x%04X\r\n",SPI1_Send_Cnt );//装载发送数据
	  printf("SPI1 Send: %s",SPI1_TX_Buffer);     //打印待发送数据
	  k=strlen((char*)SPI1_TX_Buffer);            //计算待发送数据的字节总数
	  SPI1_TX_Buffer_Load_Index = k;
    SPI1_TX_Buffer_Send_Index=0;   //一定要先准备好数据,再启动SPI发送

启动发送/
//	  _HAL_SPI_ENABLE_IT(SPI1,SPI_IT_TXE);//使能SPI在TXE标志建立时产生SPI发送中断
		//为了同步,这条语句放到Start_SPI2_Send_Data()中执行
	}
}

//函数功能:SPI在中断里发送和接收8位数据
void SPI1_IRQHandler(void)
{
//  HAL_SPI_IRQHandler(&hspi1);
  uint32_t itsource;
  uint32_t itflag;
	uint8_t RX_temp;

	(void)RX_temp;//防止RX_temp不使用而产生警告

	itsource = SPI1->CR2;//读SPIx_CR2寄存器
	itflag   = SPI1->SR; //读SPIx_SR寄存器
	if ( (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) == RESET) \
		&& (SPI_CHECK_FLAG(itflag, SPI_FLAG_RXNE) != RESET) \
   	&& (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_RXNE) != RESET) )
	{//SPI1接收中断
		RX_temp=*( uint8_t *)&SPI1->DR; //返回通过SPIx最近接收的数据,写读SPI1_DR
		SPI1_RCV_buffer[SPI1_in]=RX_temp;
		RX_temp=SPI1_in;          //将接收缓冲区的下标值暂存放在RX_temp中;
		SPI1_in=SPI1_in+1;        //修改SPI1_in的值,为下1次接收做准备;
		if(SPI1_in>=SPI1_RCV_buffer_Size-1) SPI1_in=0; //若接收缓冲区满,则将SPI1_in设置为0;
		if(SPI1_in==SPI1_out)  SPI1_in=RX_temp;       //若"输入下标值"同"输出下标值"相等,则将"输入下标值"改为原值;
	}

  if ( (SPI_CHECK_FLAG(itflag, SPI_FLAG_TXE) != RESET) \
		&& (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_TXE) != RESET) )
  {//SPI1发送中断
		if(SPI1_TX_Buffer_Send_Index >= SPI1_TX_Buffer_Load_Index)
		{
			_HAL_SPI_DISABLE_IT(SPI1,SPI_IT_TXE);
		}
		else //未发送完全部数据
		{
	    *( uint8_t *)&SPI1->DR=SPI1_TX_Buffer[SPI1_TX_Buffer_Send_Index]; //通过外设SPIx发送一个数据,写SPI1_DR
	    //发送一个字节
			SPI1_TX_Buffer_Send_Index++;
		}
  }

  if ( ((SPI_CHECK_FLAG(itflag, SPI_FLAG_MODF) != RESET) \
		|| (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) != RESET) \
	  || (SPI_CHECK_FLAG(itflag, SPI_FLAG_FRE) != RESET)) \
	  && (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_ERR) != RESET) )
	{//SPI1错误中断
    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) != RESET)
    {//SPI Overrun error interrupt occurred 
			_HAL_SPI_CLEAR_OVRFLAG(SPI1);//清除溢出错误
    }

    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_MODF) != RESET)
    {//SPI Mode Fault error interrupt occurred
      _HAL_SPI_CLEAR_MODFFLAG(SPI1);
    }

    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_FRE) != RESET)
    {//SPI Frame error interrupt occurred
      _HAL_SPI_CLEAR_FREFLAG(SPI1);
    }
	}
}

//函数功能:打印str_SPI1_RCV_Data.Rx_buf[]的数据;
void Print_str_SPI1_RCV_Data_Rx_buf(void)
{
	uint16_t i;
  uint8_t temp;
	
  if(str_SPI1_RCV_Data.valid=='Y')//读到新的数据;
	{
		printf("\r\nSPI1:");    //将"\r\nSPI1:"发送到调试串口,由PC显示;
		for(i=0;i<str_SPI1_RCV_Data.len;i++)
		{
			temp=0;
			if( ( (str_SPI1_RCV_Data.Rx_buf[i]==0x0D)||(str_SPI1_RCV_Data.Rx_buf[i]==0x0A) ) )
			{
				printf("%c",str_SPI1_RCV_Data.Rx_buf[i]);
				temp=1;
      }
			if(temp==0)
			{
				if( ( (str_SPI1_RCV_Data.Rx_buf[i]>=' ')&&(str_SPI1_RCV_Data.Rx_buf[i]<='~') ) ) printf("%c",str_SPI1_RCV_Data.Rx_buf[i]);
				else
				{
					printf(" ");
					printf("%02X",str_SPI1_RCV_Data.Rx_buf[i]);
					printf(" ");
        }
      }
    }
		printf("\r\n");//将"\r\n"发送到调试串口,由PC显示;
	}
}

void Read_SPI1_RCV_buffer(uint16_t MAX_time)
{
	uint16_t counter;  //声明双字节型变量counter,用作时间计数器;
	uint8_t SPI1_data;
	uint16_t i;      //声明16位整型变量i;
	uint8_t j,tmp;

	str_SPI1_RCV_Data.Rx_buf[0]='\0';
	str_SPI1_RCV_Data.len=0;    //假定没有接收到数据
	str_SPI1_RCV_Data.valid='N';//假定没有接收到数据
	counter=0;     //时间计数器counter清0;
	while(counter++ < MAX_time)
	{
		tmp=0;
		if(SPI1_RCV_Flag) tmp=1;
		if(tmp==0x01)//启动读SPI1_RCV_buffer[]中的数据
		{
			i=SPI1_RXD_buffer_Size-1;

			SPI1_data=SPI1_RCV_buffer[SPI1_out]; //从SPI1串口接收缓冲区读一个字节;
			SPI1_out=SPI1_out+1; //将SPI1串口接收缓冲区的输出下标值加1;
			if(SPI1_out>=SPI1_RCV_buffer_Size-1) SPI1_out=0; //若读完SPI1接收缓冲区,则将SPI1_out置为0;

			if( str_SPI1_RCV_Data.Rx_mode==0 )//无条件建立接收标志;
      {
        str_SPI1_RCV_Data.len=1;//数据长度为1
        str_SPI1_RCV_Data.Rx_buf[0]=SPI1_data;//保存接收到的第一个数据
				i=1;
      }
			if( (str_SPI1_RCV_Data.Rx_mode==1)&&(SPI1_data=='>') )//若读到'>',则退出接收数据;
      {
        str_SPI1_RCV_Data.len=1;//数据长度为1
        str_SPI1_RCV_Data.Rx_buf[0]=SPI1_data;
				str_SPI1_RCV_Data.valid='Y';
				counter=MAX_time;//退出while循环
				i=SPI1_RXD_buffer_Size-1;
      }
			
			for(;i<SPI1_RXD_buffer_Size-1;i++)
			{
				for(j=0;j<10;++j) //等待串口输入;
				{
					if(SPI1_RCV_Flag) j=50; //若SPI1的接收缓冲区有新的数据输入,令计数器j溢出,使程序退出for循环;
					else delay_ms(1); //若SPI1的接收缓冲区没有新的数据输入,则延时1ms;
				}
				if(SPI1_RCV_Flag) //若SPI1的接收缓冲区有新的数据输入,从SPI1接收缓冲区读一个数据;
				{
					SPI1_data=SPI1_RCV_buffer[SPI1_out]; //从SPI1串口接收缓冲区读一个字节;
					SPI1_out=SPI1_out+1; //将SPI1串口接收缓冲区的输出下标值加1;
					if(SPI1_out>=SPI1_RCV_buffer_Size-1) SPI1_out=0; //若读完SPI1接收缓冲区,则将SPI1_out置为0;
					str_SPI1_RCV_Data.Rx_buf[i]=SPI1_data;
					str_SPI1_RCV_Data.len=str_SPI1_RCV_Data.len+1;
        }
				else //若SPI1的接收缓冲区没有新的数据输入,则使程序退出for循环;
				{
					str_SPI1_RCV_Data.Rx_buf[i]='\0';
					str_SPI1_RCV_Data.valid='Y';
					counter=MAX_time;//退出while循环
					i=SPI1_RXD_buffer_Size; //退出for循环,停止读SPI1的接收缓冲区;
				}
      }
			Print_str_SPI1_RCV_Data_Rx_buf();//打印接收到数据
    }
		else delay_ms(10);//SPI1串口没有数据,则延时10ms
	}
}

3、SPI1.h程序如下

#ifndef __SPI1_H__
#define __SPI1_H__

#include "stm32g4xx_hal.h"
//使能int8_t,int16_t,int32_t,int64_t
//使能uint8_t,uint16_t,uint32_t,uint64_t
#include "stm32g4xx_hal_spi.h"

#define HalfWord  0  //SPI使用8位数据收发

#define StartData1  0x1000
#define EndData1    0x1100

#define SPI1_RCV_buffer_Size 1200
extern uint8_t SPI1_RCV_buffer[SPI1_RCV_buffer_Size]; //用来存放硬件SPI1接收到的数据;
extern uint16_t SPI1_in;  //SPI1接收缓冲区的输入下标;
extern uint16_t SPI1_out; //SPI1接收缓冲区的输出下标;
#define SPI1_RCV_Flag  (SPI1_in!=SPI1_out) //SPI1_RCV_Flag=1,表示SPI1的接收缓冲区有新的数据输入;

#define SPI1_RXD_buffer_Size  1200
typedef struct
{
	uint8_t  Rx_mode;
	uint8_t  valid;
  uint16_t len;
  uint8_t  Rx_buf[SPI1_RXD_buffer_Size];
}SPI1_Receive_Data_t;
extern SPI1_Receive_Data_t str_SPI1_RCV_Data;

#define SPI1_TX_Buffer_Size 		      125
extern uint8_t SPI1_TX_Buffer[SPI1_TX_Buffer_Size]; //SPI1发送缓冲区数组
extern uint8_t SPI1_TX_Buffer_Load_Index;          //SPI1_TX_Buffer[]的装载索引值

extern void SPI1_Init(void);
extern void Start_SPI1_Send_Data(void);
extern void Read_SPI1_RCV_buffer(uint16_t MAX_time);

#endif /*__ SPI1_H__ */

4、SPI2.c从机程序如下

#include "SPI2.h"
#include "SPI_Std_Lib.h" //自定义USART标准库库
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "LED.h"
#include "delay.h"

//SPI2外设用作从机,其接口:将SPI2_SCK映射到PB13,SPI2_MISO映射到PB14,SPI2_MOSI映射到PB15,SPI2_NSS映射到PB12

/*
STM32H474的SPI工作在从机模式,我们令SPI1工作在主机模式,SPI2工作在从机模式中,实现数据数据互传。
SPI1外设用作主机,其接口:将SPI1_SCK映射到PA5,SPI1_MISO映射到PA6,SPI1_MOSI映射到PA7,SPI1_NSS映射到PA4
SPI2外设用作从机,其接口:将SPI2_SCK映射到PB13,SPI2_MISO映射到PB14,SPI2_MOSI映射到PB15,SPI2_NSS映射到PB12
测试方法:
1)、将SPI1_SCK(PA5)和SPI2_SCK(PB13)连接在一起;
2)、将SPI1_MISO(PA6)和SPI2_MISO(PB14)连接在一起,实现SPI2发送,SPI1接收;
3)、将SPI1_MOSI(PA7)和SPI2_MOSI(PB15)连接在一起,实现SPI1发送,SPI2接收;
4)、将SPI1_NSS(PA4)和SPI2_NSS(PB12)连接在一起,实现SPI1选择SPI2外设
为了保证数据传输正确,需要采用SPI中断方式发送和接收数据,否则就要使用DMA传输数据,才能保证数据传输的正确性。
*/

//STM32G474RE使用SPI2中断发送和接收8位数据,其作用:可以提高系统的整体效率和响应速度。
uint8_t SPI2_RCV_buffer[SPI2_RCV_buffer_Size]; //用来存放硬件SPI2接收到的数据;
uint16_t SPI2_in;  //SPI2接收缓冲区的输入下标;
uint16_t SPI2_out; //SPI2接收缓冲区的输出下标;
SPI2_Receive_Data_t str_SPI2_RCV_Data;

uint8_t SPI2_TX_Buffer[SPI2_TX_Buffer_Size]; //SPI2发送缓冲区数组
uint8_t SPI2_TX_Buffer_Load_Index;          //SPI2_TX_Buffer[]的装载索引值
volatile uint8_t SPI2_TX_Buffer_Send_Index;          //发送下标值
uint16_t SPI2_Send_Cnt;



void SPI2_Init(void);
void Start_SPI2_Send_Data(void);
void Read_SPI2_RCV_buffer(uint16_t MAX_time);

void SPI2_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	SPI_HandleTypeDef hspi2;

	__HAL_RCC_SPI2_CLK_ENABLE();  //使能SPI2外设时钟
	__HAL_RCC_GPIOB_CLK_ENABLE(); //GPIOB时钟使能

	GPIO_InitStruct.Pin = GPIO_PIN_13;            //选择引脚编号为13
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
//	GPIO_InitStruct.Pull = GPIO_NOPULL;          //引脚上拉和下拉都没有被激活
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;   //PB13映射到SPI2_SCK
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOB的外设寄存器
	//配置“SPI2_SCK引脚”

	GPIO_InitStruct.Pin = GPIO_PIN_14;            //选择引脚编号为14
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
//	GPIO_InitStruct.Pull = GPIO_NOPULL;          //引脚上拉和下拉都没有被激活
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;   //PB14映射到SPI2_MISO
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOB的外设寄存器
	//配置“SPI2_MISO引脚”

	GPIO_InitStruct.Pin = GPIO_PIN_15;            //选择引脚编号为15
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
//	GPIO_InitStruct.Pull = GPIO_PULLDOWN;        //设置下拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;   //PB15映射到SPI2_MOSI
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOB的外设寄存器
	//配置“SPI2_MOSI引脚”

	GPIO_InitStruct.Pin = GPIO_PIN_12;            //选择引脚编号为12
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;      //复用推挽模式
	GPIO_InitStruct.Pull = GPIO_PULLUP;           //设置上拉
//	GPIO_InitStruct.Pull = GPIO_PULLDOWN;        //设置下拉
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;   //PB12映射到SPI2_NSS
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	//根据GPIO_InitStruct结构变量指定的参数初始化GPIOB的外设寄存器
	//配置“SPI2_NSS引脚”

  hspi2.Instance = SPI2;//选择SPI2
//	hspi2.Init.Mode = SPI_MODE_MASTER;
  //SPIx_CR1寄存器bit2(MSTR),MSTR=1配置SPI外设为主机
	hspi2.Init.Mode = SPI_MODE_SLAVE;
	//SPIx_CR1寄存器bit2(MSTR),MSTR=0配置SPI外设为从机
	hspi2.Init.Direction = SPI_DIRECTION_2LINES;
	//SPIx_CR1寄存器bit15(BIDIMODE),BIDIMODE=0选择“双线单向数据模式” 
	hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
	//SPIx_CR1寄存器bit0(CPHA)
	//CPHA=0,表示从SPI_SCK的空闲位开始,第1个边沿用来采集第1个位
	//CPHA=1,表示从SPI_SCK的空闲位开始,第2个边沿用来采集第1个位
	hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
	//SPIx_CR1寄存器bit1(CPOL)
	//CPOL=0,表示SPI_SCK的空闲位为低电平
	//CPOL=1,表示SPI_SCK的空闲位为高电平

//	hspi2.Init.NSS = SPI_NSS_SOFT;
  //配置spi在master下,NSS作为普通IO,由用户自己写代码控制片选,可以1主多从
	hspi2.Init.NSS = SPI_NSS_HARD_INPUT;
  //仅当配置spi在slave下,作为从机片选输入
//	hspi2.Init.NSS = SPI_NSS_HARD_OUTPUT;
	//配置spi在master下,NSS作为SPI专用IO,由MCU自动控制片选,只能1主1从
	//SPIx_CR1寄存器bit9(SSM),SSM=1,NSS引脚的输入将被替换为来自SSI位的值

	hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
	//SPIx_CR1寄存器bit5(BR[2:0])
	//BR[2:0]=000b,SCK的时钟频率为fPCLK/2
	//BR[2:0]=001b,SCK的时钟频率为fPCLK/4
	//BR[2:0]=010b,SCK的时钟频率为fPCLK/8
	//BR[2:0]=011b,SCK的时钟频率为fPCLK/16
	//BR[2:0]=100b,SCK的时钟频率为fPCLK/32
	//BR[2:0]=101b,SCK的时钟频率为fPCLK/64
	//BR[2:0]=110b,SCK的时钟频率为fPCLK/128
	//BR[2:0]=111b,SCK的时钟频率为fPCLK/256
	hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
	//SPIx_CR1寄存器bit7(LSBFIRST)
	//LSBFIRST=0,表示先传送最高位
	//LSBFIRST=1,表示先传送最低位

	hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	//SPIx_CR1寄存器bit13(CRCEN),CRCEN=0不使能CRC校验
	hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
	//SPIx_CR1寄存器bit11(CRCL)
	//CRCL=0表示CRC的长度为8位
	//CRCL=1表示CRC的长度为16位
	hspi2.Init.CRCPolynomial = 7;
	//SPIx_CRCPR,这个寄存器包含了CRC计算的多项式
	//CRC多项式(0x0007)是该寄存器的默认值。可以根据需要,配置自己的“CRC多项式”。 

#if (SPI2_HalfWord == 0U)
	hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
	//SPIx_CR2寄存器bit11:8(DS[3:0])
	//DS[3:0]=0011b,表示SPI传输的一帧的数据长度为4位
	//DS[3:0]=0100b,表示SPI传输的一帧的数据长度为5位
	//DS[3:0]=0101b,表示SPI传输的一帧的数据长度为6位
	//DS[3:0]=0110b,表示SPI传输的一帧的数据长度为7位
	//DS[3:0]=0111b,表示SPI传输的一帧的数据长度为8位,这里读写8位数据
	//DS[3:0]=1000b,表示SPI传输的一帧的数据长度为9位
	//DS[3:0]=1001b,表示SPI传输的一帧的数据长度为10位
	//DS[3:0]=1010b,表示SPI传输的一帧的数据长度为11位
	//DS[3:0]=1011b,表示SPI传输的一帧的数据长度为12位
	//DS[3:0]=1100b,表示SPI传输的一帧的数据长度为13位
	//DS[3:0]=1101b,表示SPI传输的一帧的数据长度为14位
	//DS[3:0]=1110b,表示SPI传输的一帧的数据长度为15位
	//DS[3:0]=1111b,表示SPI传输的一帧的数据长度为16位
#else
  hspi2.Init.DataSize = SPI_DATASIZE_16BIT;//这里读写16位数据
#endif

	hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
	//SPIx_CR2寄存器bit4(FRF)
	//FRF=0,帧格式为SPI Motorola mode,这里使用“Motorola模式”
	//FRF=1,帧格式为SPI TI mode
	hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
	//SPIx_CR2寄存器bit3(NSSP)
	//NSSP=1,在主机模式中,NSS引脚输出使能
	HAL_SPI_Init(&hspi2);

	_HAL_SPI_ENABLE(SPI2);
	//SPIx_CR1寄存器bit6(SPE),SPE=1令SPI外设使能

  _HAL_SPI_ENABLE_IT(SPI2,SPI_IT_ERR);
  _HAL_SPI_DISABLE_IT(SPI2,SPI_IT_TXE);
	_HAL_SPI_ENABLE_IT(SPI2,SPI_IT_RXNE);
	HAL_NVIC_SetPriority(SPI2_IRQn, 5, 0);
	//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
	//选择中断优先级组4,即抢占优先级为4位,取值为0~15,响应优先级组为0位,取值为0
	//这里设置SPI2的抢占优先级为5,响应优先级为0
	HAL_NVIC_EnableIRQ(SPI2_IRQn);

  SPI2_Send_Cnt=StartData2;
	SPI2_TX_Buffer_Send_Index = SPI2_TX_Buffer_Load_Index;
}

//函数功能:启动SPI2发送
const char SPI2_Send_REG[]="123456\r\n";
void Start_SPI2_Send_Data(void)
{
	uint8_t k;

	if(SPI2_TX_Buffer_Send_Index >= SPI2_TX_Buffer_Load_Index)
	{
		SPI2_Send_Cnt--;
		if(SPI2_Send_Cnt<EndData2) SPI2_Send_Cnt=StartData2;
		sprintf( (char*)SPI2_TX_Buffer,"0x%04X\r\n",SPI2_Send_Cnt );//装载发送数据
	  printf("SPI2 Send: %s",SPI2_TX_Buffer);     //打印待发送数据
	  k=strlen((char*)SPI2_TX_Buffer);            //计算待发送数据的字节总数
	  SPI2_TX_Buffer_Load_Index = k;
    SPI2_TX_Buffer_Send_Index=0;   //一定要先准备好数据,再启动SPI发送

启动发送/
	  _HAL_SPI_ENABLE_IT(SPI2,SPI_IT_TXE);//使能SPI在TXE标志建立时产生SPI发送中断
		_HAL_SPI_ENABLE_IT(SPI1,SPI_IT_TXE);//使能SPI在TXE标志建立时产生SPI发送中断
	}
}

//函数功能:SPI在中断里发送和接收8位数据
void SPI2_IRQHandler(void)
{
//  HAL_SPI_IRQHandler(&hspi2);
  uint32_t itsource;
  uint32_t itflag;
	uint8_t RX_temp;

	(void)RX_temp;//防止RX_temp不使用而产生警告

	itsource = SPI2->CR2;//读SPIx_CR2寄存器
	itflag   = SPI2->SR; //读SPIx_SR寄存器
	if ( (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) == RESET) \
		&& (SPI_CHECK_FLAG(itflag, SPI_FLAG_RXNE) != RESET) \
   	&& (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_RXNE) != RESET) )
	{//SPI2接收中断
		RX_temp=*( uint8_t *)&SPI2->DR; //返回通过SPIx最近接收的数据,写读SPI2_DR
		SPI2_RCV_buffer[SPI2_in]=RX_temp;
		RX_temp=SPI2_in;          //将接收缓冲区的下标值暂存放在RX_temp中;
		SPI2_in=SPI2_in+1;        //修改SPI2_in的值,为下1次接收做准备;
		if(SPI2_in>=SPI2_RCV_buffer_Size-1) SPI2_in=0; //若接收缓冲区满,则将SPI2_in设置为0;
		if(SPI2_in==SPI2_out)  SPI2_in=RX_temp;       //若"输入下标值"同"输出下标值"相等,则将"输入下标值"改为原值;
	}

  if ( (SPI_CHECK_FLAG(itflag, SPI_FLAG_TXE) != RESET) \
		&& (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_TXE) != RESET) )
  {//SPI2发送中断
		if(SPI2_TX_Buffer_Send_Index >= SPI2_TX_Buffer_Load_Index)
		{
			_HAL_SPI_DISABLE_IT(SPI2,SPI_IT_TXE);
		}
		else //未发送完全部数据
		{
	    *( uint8_t *)&SPI2->DR=SPI2_TX_Buffer[SPI2_TX_Buffer_Send_Index]; //通过外设SPIx发送一个数据,写SPI2_DR
	    //发送一个字节
			SPI2_TX_Buffer_Send_Index++;
		}
  }

  if ( ((SPI_CHECK_FLAG(itflag, SPI_FLAG_MODF) != RESET) \
		|| (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) != RESET) \
	  || (SPI_CHECK_FLAG(itflag, SPI_FLAG_FRE) != RESET)) \
	  && (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_ERR) != RESET) )
	{//SPI2错误中断
    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) != RESET)
    {//SPI Overrun error interrupt occurred 
			_HAL_SPI_CLEAR_OVRFLAG(SPI2);//清除溢出错误
    }

    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_MODF) != RESET)
    {//SPI Mode Fault error interrupt occurred
      _HAL_SPI_CLEAR_MODFFLAG(SPI2);
    }

    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_FRE) != RESET)
    {//SPI Frame error interrupt occurred
      _HAL_SPI_CLEAR_FREFLAG(SPI2);
    }
	}
}

//函数功能:打印str_SPI2_RCV_Data.Rx_buf[]的数据;
void Print_str_SPI2_RCV_Data_Rx_buf(void)
{
	uint16_t i;
  uint8_t temp;
	
  if(str_SPI2_RCV_Data.valid=='Y')//读到新的数据;
	{
		printf("\r\nSPI2:");    //将"\r\nSPI2:"发送到调试串口,由PC显示;
		for(i=0;i<str_SPI2_RCV_Data.len;i++)
		{
			temp=0;
			if( ( (str_SPI2_RCV_Data.Rx_buf[i]==0x0D)||(str_SPI2_RCV_Data.Rx_buf[i]==0x0A) ) )
			{
				printf("%c",str_SPI2_RCV_Data.Rx_buf[i]);
				temp=1;
      }
			if(temp==0)
			{
				if( ( (str_SPI2_RCV_Data.Rx_buf[i]>=' ')&&(str_SPI2_RCV_Data.Rx_buf[i]<='~') ) ) printf("%c",str_SPI2_RCV_Data.Rx_buf[i]);
				else
				{
					printf(" ");
					printf("%02X",str_SPI2_RCV_Data.Rx_buf[i]);
					printf(" ");
        }
      }
    }
		printf("\r\n");//将"\r\n"发送到调试串口,由PC显示;
	}
}

void Read_SPI2_RCV_buffer(uint16_t MAX_time)
{
	uint16_t counter;  //声明双字节型变量counter,用作时间计数器;
	uint8_t SPI2_data;
	uint16_t i;      //声明16位整型变量i;
	uint8_t j,tmp;

	str_SPI2_RCV_Data.Rx_buf[0]='\0';
	str_SPI2_RCV_Data.len=0;    //假定没有接收到数据
	str_SPI2_RCV_Data.valid='N';//假定没有接收到数据
	counter=0;     //时间计数器counter清0;
	while(counter++ < MAX_time)
	{
		tmp=0;
		if(SPI2_RCV_Flag) tmp=1;
		if(tmp==0x01)//启动读SPI2_RCV_buffer[]中的数据
		{
			i=SPI2_RXD_buffer_Size-1;

			SPI2_data=SPI2_RCV_buffer[SPI2_out]; //从SPI2串口接收缓冲区读一个字节;
			SPI2_out=SPI2_out+1; //将SPI2串口接收缓冲区的输出下标值加1;
			if(SPI2_out>=SPI2_RCV_buffer_Size-1) SPI2_out=0; //若读完SPI2接收缓冲区,则将SPI2_out置为0;

			if( str_SPI2_RCV_Data.Rx_mode==0 )//无条件建立接收标志;
      {
        str_SPI2_RCV_Data.len=1;//数据长度为1
        str_SPI2_RCV_Data.Rx_buf[0]=SPI2_data;//保存接收到的第一个数据
				i=1;
      }
			if( (str_SPI2_RCV_Data.Rx_mode==1)&&(SPI2_data=='>') )//若读到'>',则退出接收数据;
      {
        str_SPI2_RCV_Data.len=1;//数据长度为1
        str_SPI2_RCV_Data.Rx_buf[0]=SPI2_data;
				str_SPI2_RCV_Data.valid='Y';
				counter=MAX_time;//退出while循环
				i=SPI2_RXD_buffer_Size-1;
      }
			
			for(;i<SPI2_RXD_buffer_Size-1;i++)
			{
				for(j=0;j<10;++j) //等待串口输入;
				{
					if(SPI2_RCV_Flag) j=50; //若SPI2的接收缓冲区有新的数据输入,令计数器j溢出,使程序退出for循环;
					else delay_ms(1); //若SPI2的接收缓冲区没有新的数据输入,则延时1ms;
				}
				if(SPI2_RCV_Flag) //若SPI2的接收缓冲区有新的数据输入,从SPI2接收缓冲区读一个数据;
				{
					SPI2_data=SPI2_RCV_buffer[SPI2_out]; //从SPI2串口接收缓冲区读一个字节;
					SPI2_out=SPI2_out+1; //将SPI2串口接收缓冲区的输出下标值加1;
					if(SPI2_out>=SPI2_RCV_buffer_Size-1) SPI2_out=0; //若读完SPI2接收缓冲区,则将SPI2_out置为0;
					str_SPI2_RCV_Data.Rx_buf[i]=SPI2_data;
					str_SPI2_RCV_Data.len=str_SPI2_RCV_Data.len+1;
        }
				else //若SPI2的接收缓冲区没有新的数据输入,则使程序退出for循环;
				{
					str_SPI2_RCV_Data.Rx_buf[i]='\0';
					str_SPI2_RCV_Data.valid='Y';
					counter=MAX_time;//退出while循环
					i=SPI2_RXD_buffer_Size; //退出for循环,停止读SPI2的接收缓冲区;
				}
      }
			Print_str_SPI2_RCV_Data_Rx_buf();//打印接收到数据
    }
		else delay_ms(10);//SPI2串口没有数据,则延时10ms
	}
}

5、SPI2.h程序如下

#ifndef __SPI2_H__
#define __SPI2_H__

#include "stm32g4xx_hal.h"
//使能int8_t,int16_t,int32_t,int64_t
//使能uint8_t,uint16_t,uint32_t,uint64_t
#include "stm32g4xx_hal_spi.h"

#define SPI2_HalfWord  0  //SPI使用8位数据收发

#define StartData2  0x1100
#define EndData2    0x1000

#define SPI2_RCV_buffer_Size 1200
extern uint8_t SPI2_RCV_buffer[SPI2_RCV_buffer_Size]; //用来存放硬件SPI2接收到的数据;
extern uint16_t SPI2_in;  //SPI2接收缓冲区的输入下标;
extern uint16_t SPI2_out; //SPI2接收缓冲区的输出下标;
#define SPI2_RCV_Flag  (SPI2_in!=SPI2_out) //SPI2_RCV_Flag=1,表示SPI2的接收缓冲区有新的数据输入;

#define SPI2_RXD_buffer_Size  1200
typedef struct
{
	uint8_t  Rx_mode;
	uint8_t  valid;
  uint16_t len;
  uint8_t  Rx_buf[SPI2_RXD_buffer_Size];
}SPI2_Receive_Data_t;
extern SPI2_Receive_Data_t str_SPI2_RCV_Data;

#define SPI2_TX_Buffer_Size 		      125
extern uint8_t SPI2_TX_Buffer[SPI2_TX_Buffer_Size]; //SPI2发送缓冲区数组
extern uint8_t SPI2_TX_Buffer_Load_Index;          //SPI2_TX_Buffer[]的装载索引值

extern void SPI2_Init(void);
extern void Start_SPI2_Send_Data(void);
extern void Read_SPI2_RCV_buffer(uint16_t MAX_time);

#endif /*__ SPI2_H__ */

6、SPI_Std_Lib.h程序如下

这是一个自己定义的头文件

#ifndef __SPI_Std_Lib_H__
#define __SPI_Std_Lib_H__

#include "stm32g4xx_hal.h"
//使能int8_t,int16_t,int32_t,int64_t
//使能uint8_t,uint16_t,uint32_t,uint64_t
#include "stm32g4xx_hal_spi.h"

/** @brief  Check whether the specified SPI flag is set or not.
  * @param  __SPIx__ specifies the SPI peripheral.
  *         This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
  * @param  __FLAG__ specifies the flag to check.
  *         This parameter can be one of the following values:
  *            @arg SPI_FLAG_RXNE: Receive buffer not empty flag
  *            @arg SPI_FLAG_TXE: Transmit buffer empty flag
  *            @arg SPI_FLAG_CRCERR: CRC error flag
  *            @arg SPI_FLAG_MODF: Mode fault flag
  *            @arg SPI_FLAG_OVR: Overrun flag
  *            @arg SPI_FLAG_BSY: Busy flag
  *            @arg SPI_FLAG_FRE: Frame format error flag
  *            @arg SPI_FLAG_FTLVL: SPI fifo transmission level
  *            @arg SPI_FLAG_FRLVL: SPI fifo reception level
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define _HAL_SPI_GET_FLAG(__SPIx__, __FLAG__) (( ( (__SPIx__)->SR ) & (__FLAG__) ) == (__FLAG__))

#define _HAL_SPI_ENABLE(__SPIx__)  SET_BIT((__SPIx__)->CR1, SPI_CR1_SPE)
//SPIx_CR1寄存器bit6(SPE),SPE=1令SPI外设使能

#define _HAL_SPI_DISABLE(__SPIx__) CLEAR_BIT((__SPIx__)->CR1, SPI_CR1_SPE)
//SPIx_CR1寄存器bit6(SPE),SPE=0令SPI外设不使能

/** @brief  Enable the specified SPI interrupts.
  * @param  __SPIx__ specifies the SPI Handle.
  *         This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
  * @param  __INTERRUPT__ specifies the interrupt source to enable.
  *         This parameter can be one of the following values:
  *            @arg SPI_IT_TXE: Tx buffer empty interrupt enable
  *            @arg SPI_IT_RXNE: RX buffer not empty interrupt enable
  *            @arg SPI_IT_ERR: Error interrupt enable
  * @retval None
  */
#define _HAL_SPI_ENABLE_IT(__SPIx__, __INTERRUPT__)   SET_BIT((__SPIx__)->CR2, (__INTERRUPT__))
//__INTERRUPT__ = SPI_IT_TXE,设置SPIx_CR2寄存器bit7(TXEIE位),TXEIE=1表示使能SPI在TXE标志建立时产生SPI发送中断
//__INTERRUPT__ = SPI_IT_RXNE,设置SPIx_CR2寄存器bit6(RXNEIE位),RXNEIE=1表示使能SPI在RXNE标志建立时产生SPI接收中断
//__INTERRUPT__ = SPI_IT_ERR,设置SPIx_CR2寄存器bit5(ERRIE位),ERRIE=1表示使能SPI在CRCERR,OVR和MODF标志建立时产生SPI错误中断

/** @brief  Disable the specified SPI interrupts.
  * @param  __SPIx__ specifies the SPI handle.
  *         This parameter can be SPIx where x: 1, 2, or 3 to select the SPI peripheral.
  * @param  __INTERRUPT__ specifies the interrupt source to disable.
  *         This parameter can be one of the following values:
  *            @arg SPI_IT_TXE: Tx buffer empty interrupt enable
  *            @arg SPI_IT_RXNE: RX buffer not empty interrupt enable
  *            @arg SPI_IT_ERR: Error interrupt enable
  * @retval None
  */
#define _HAL_SPI_DISABLE_IT(__SPIx__, __INTERRUPT__)  CLEAR_BIT((__SPIx__)->CR2, (__INTERRUPT__))

/** @brief  Clear the SPI OVR pending flag.
  * @param  __SPIx__ specifies the SPI Handle.
  *         This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
  * @retval None
  */
#define _HAL_SPI_CLEAR_OVRFLAG(__SPIx__)        \
  do{                                              \
    __IO uint32_t tmpreg_ovr = 0x00U;              \
    tmpreg_ovr = (__SPIx__)->DR;       \
    tmpreg_ovr = (__SPIx__)->SR;       \
    UNUSED(tmpreg_ovr);                            \
  } while(0U)

/** @brief  Clear the SPI MODF pending flag.
  * @param  __SPIx__ specifies the SPI Handle.
  *         This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
  * @retval None
  */
#define _HAL_SPI_CLEAR_MODFFLAG(__SPIx__)             \
  do{                                                    \
    __IO uint32_t tmpreg_modf = 0x00U;                   \
    tmpreg_modf = (__SPIx__)->SR;            \
    CLEAR_BIT((__SPIx__)->CR1, SPI_CR1_SPE); \
    UNUSED(tmpreg_modf);                                 \
  } while(0U)

	/** @brief  Clear the SPI FRE pending flag.
  * @param  __SPIx__ specifies the SPI Handle.
  *         This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
  * @retval None
  */
#define _HAL_SPI_CLEAR_FREFLAG(__SPIx__)        \
  do{                                              \
    __IO uint32_t tmpreg_fre = 0x00U;              \
    tmpreg_fre = (__SPIx__)->SR;       \
    UNUSED(tmpreg_fre);                            \
  }while(0U)

#endif /* __SPI_Std_Lib_H__ */

7、测试结果:


http://www.kler.cn/a/316414.html

相关文章:

  • 目标检测,语义分割标注工具--labelimg labelme
  • java相关学习文档或网站整理
  • win11永久修改pdf默认打开方式
  • uni-app开发-识图小程序-识图功能
  • Nmon(Nigel‘s Performance Monitor)
  • 【Python系列】Python 连接 PostgreSQL 数据库并查询数据
  • SpringCloud解读
  • 回归模型拟合效果的统计指标: 决定系数 R2 r2 r2_score
  • Jupyter的使用
  • 【RabbitMQ 项目】服务端:数据管理模块之虚拟机模块
  • Maven进阶-二、依赖
  • 面试金典题2.4
  • Rust 文件与 IO
  • 【网络】TCP/IP 五层网络模型:数据链路层
  • 苍穹外卖学习笔记(五)
  • 高级java每日一道面试题-2024年9月19日-框架篇[Mybatis篇]-说一下mybatis的一级缓存和二级缓存?
  • Android对象池的深入理解和使用
  • 7.搭建个人金融数据库之快速获取股票列表和基本信息!
  • PowerShell install 一键部署Oracle12c
  • Guava中Preconditions校验
  • 数据安全治理
  • JVM 字节码与 JIT 编译详解
  • 云原生信息安全:筑牢数字化时代的安全防线
  • Vue3:mitt实现组件通信
  • Three.js 3D人物漫游项目(下)
  • 统信服务器操作系统a版e版【dde桌面限制登录次数】介绍