13:HAL---SPI

 

目录

一:SPL通信

1:简历

2:硬件电路

3:移动数据图

4:SPI时序基本单元

A : 开/ 终条件

B:SPI时序基本单元

A:模式0

B:模式1

C:模式2

D:模式3

C:SPl时序

A:发送指令

B: 指定地址写

C:指定地址读

5:NSS(CS)

6:时钟

二: W25Q64

1:简历

2: 硬件电路

3:W25Q64框图

4: Flash操作注意事项

5:指令集

三:HAL

A:宏函数

B:轮询方式

C:中断

C:DMA

四:案例

A:轮询方式--W25Q64

B:单线双向模式--MAX7219(数码管)

C:中断方式--W25Q64

D:MDA----W25Q64 

主从通信

A:中断+主机身份不改变

B:中断+DMA

C:多主机通信--主从身份可以改变 


一:SPL通信

STM32F1c8T6 有2个SPI。

1:简历


          SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线

        四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)(NSS)

         同步(有时钟线),全双工 (传输线有2条,发送和接受线路)

        支持总线挂载多设备(一主多从)

        SPl没有应答机制

2:硬件电路

        所有SPI设备的SCK、MOSI、MISO分别连在一起

        主机另外引出多条SS(NSS)控制线,分别接到各从机的SS(NSS)引脚

        输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入

        SS(NSS)也叫CS片选信号 : 和全部的从机连接, 用于选择主机和那个从机进行通信, 低电平有效;   每个从机的SS(CS)都和主机的SSX相互连接,  SS对于主机来说的话,就是输出信号, 从机的话就是输入信号

        IO的配置 : 都是以STM32为主角进行的.  主机输出信号配置---推挽输出,  主机输入信号配置----浮空或上拉输入

        SCK : 时钟线,  时钟线完全由主机掌控,  所以对于主机来说,时钟线为输出;   对于所有从机来说,时钟线都为输入;  这样主机的同步时钟,就能送到各个从机了

        MOSI : 主机输出,从机输入

        MISO : 主机输入,从机输出

       关于CS和MISO主机输入,从机输出 : 当从机没有被选中的时候,也就是SS段电平为1; 从机的MISO主机输入,从机输出必须切换为高阻态 , 高阻态就相当于引脚断开,不输出任何电平;   这样就可以防止,一条线有多个输出,而导致的电平冲突的问题了;    在SS为低电平时,MISO才允许变为推挽输出----从机的操作-------一般情况下我们只需要写主机的程序,从机的程序不需要我们操心

3:移动数据图

交换数据, 高位先行

SPI的数据收发,都是基于字节交换,这个基本单元来进行的 (移位模型)

        首先,我们规定波特率发生器时钟的上升沿主机和从机都移出数据;  下将沿移入数据;

  

        数据为从左往右运动,所以是高为先行,  首先波特率发生器时钟产生上生沿, 主机把它的最高位的数据放在MOSI上面, 从机把它最高位置的数据放在MISO上面;          在由特率发生器产生的下降沿移入数据;  在MISO数据线上从机的最高位的数据放在主机的最低位置上面;  MOSI上面主机最高位的数据放在从机的最低位置

4:SPI时序基本单元

A : 开/ 终条件

        起始条件:SS从高电平切换到低电平

        终止条件:SS从低电平切换到高电平

B:SPI时序基本单元

在任何操作下, 我们只管主机(只写主机的代码) , 从机它自动操作(不用写从机的代码) 

我们经常使用的是模式0;主机和从机的模式必须一样,主机采用什么模式,取决于操作的芯片支持那种工作模式。

A:模式0

        交换一个字节(模式0)

        CPOL=0:空闲状态时,SCK为低电平

        CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据(第一个边沿采集)

        SCL上升沿主机和从机同步移入数据;  SCL下降沿主机和从机同步移出数据


/**
* @brief  SPL交换数据--使用的为模式0
	DI(MOSI)----SPI主机输出从机输入
	DO(MISO)-------SPI主机输入从机输出
	我们只操作主机:首先主机移出最高位,放在MOSI上面,---主机操作需要我们来
								齐次从机把数据放在MISO上面----从机的操作不需要我们管

* @param  ByteSend: 主机给从机发送的数据
	
  * @retval 主机读取的数据----即从机给主机发送的数据
  */
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{		
	
	MySPI_W_SCK(0);
	//一般来说&是用来清零的;
//一般来说|是用来值一的;
	uint8_t ByteReceive=0x00;
	for (uint8_t i=0;i<8;i++)
	{
		MySPI_W_MOSI(ByteSend & (0x80>>i)); //MOSI主机输出数据 1000 0000 
		/*
		我们只操作主机: 
		SCL上升沿主机和从机同步移入数据, 从机会自动把主机给它的最高为移动到了从机里面---从机不需要我们操作
		主机操作 : 主机需要把从机给它发送的数据移动到了主机里面---即读取MISO线上的数据
		*/
		MySPI_W_SCK(1);
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}//MySPI_R_MISO主机读取数据
		MySPI_W_SCK(0);
		//SCL下降沿主机和从机同步移出数据
		//|---置1
	
	}
	return ByteReceive;
}

在任何操作下, 我们只管主机(只写主机的代码) , 从机它自动操作(不用写从机的代码) 

B:模式1

        交换一个字节(模式1)

        CPOL=0:空闲状态时,SCK为低电平

        CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据(第二个边沿采集)

        SPl为了可以配置更多的时钟芯片, 给我们了2个可以自己调节的位, 分别为:CPOL (Clock Polarity)时钟极性和CPHA (Clock Phase)时钟相位配置这两个为,  就构成了4种模式

        模式1 : 波特率发生器时钟的上升沿主机和从机都移出数据;  下将沿移入数据;  模式1的数据移动方式和 3:移动数据图 一样 , 详情参考----3:移动数据图

C:模式2

        交换一个字节(模式2)

        CPOL=1:空闲状态时,SCK为高电平

        CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据 (第一个边沿采集。同样是在SCLK下降沿进行数据采样,上升沿进行数据传输。)

D:模式3

        交换一个字节(模式3)

        CPOL=1:空闲状态时,SCK为高电平

        CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据(第二个边沿采集。在SCLK上升沿进行数据采样,下降沿进行数据传输。)

C:SPl时序

A:发送指令

规定 : SPL起始的第一个字节为指令集

发送指令

向SS指定的设备,发送指令(0x06)--0x06使能

B: 指定地址写

        指定地址写

        向SS指定的设备,发送写指令(0x02),---0x02写入的指令集    

        随后在指定地址(Address[23:0])下,写入指定数据(Data)   

        SPl没有应答机制, 交换一个字节后, 直接开始交换下一个字节

C:指定地址读

        指定地址读

        向SS指定的设备,发送读指令(0x03),---0x03发送指令的指令集   

        随后在指定地址(Address[23:0])下,读取从机数据(Data)

5:NSS(CS)

SPI_NSS_SOFT

应用:STM32在做主机,防止丢失主机身份。(SPI支持多主机通信,一个STM32可以在主机和从机身份中来回切换。)

SPI_NSS_HARD_INPUT:

主模式切换为从模式:

主控从主机切换为从机身份。NSS被拉意外丢失主模式。

应用:多少应用于多主机身份。

SPI_NSS_HARD_OUTPUT:


6:时钟

SPI1挂载在APB2总线上,APB2总线的最大时钟频率为=72MHZ

SPI2挂载在APB1总线上,APB2总线的最大时钟频率为=36MHZ

二: W25Q64

1:简历

        W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景。支持模式0和模式3。

        存储介质:Nor Flash(闪存)

        时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)

        存储容量(24位地址):

        W25Q40:      4Mbit / 512KByte     

        W25Q80:      8Mbit / 1MByte   

         W25Q16:      16Mbit / 2MByte     

        W25Q32:      32Mbit / 4MByte   

         W25Q64:      64Mbit / 8MByte   

         W25Q128:  128Mbit / 16MByte   

         W25Q256:  256Mbit / 32MByte

2: 硬件电路

3:W25Q64框图

4: Flash操作注意事项

非易失性存储器---掉电不丢失

写入操作时:

        写入操作前,必须先进行写使能------------是一种保护措施,防止你误操作的

        每个数据位只能由1改写为0,不能由0改写为1(写入只能写0,不能写1。)--------------Flash并没有像RAM那样的,  直接完全覆盖改写的能力. eg:在某一个直接的储存单元首先储存了0xaa 1010 1010 在储存0x55 0101 0101 因为Flash没有直接覆盖数据的能力,  在加上第二条规定的限制实际储存的数据为: 0000 0000 不是0x55, 使用在写入第二给数据前必须擦除之前的数据

        写入数据前必须先擦除,擦除后,所有数据位变为1--------------有专门的擦除电路把之前写的数据都值1(0xFF), 就可以弥补第二条规定的不足

        擦除必须按最小擦除单元进行------------不能指定某一个字节去擦除, 要擦,就得一大片一起擦, 在我们这个芯片里;  你可以选择,整个芯片擦除, 也可以选择,按块擦除,或者按扇区擦除;    最小的擦除单元,就是一个扇区, 个扇区,是4KB,就是4096个字节

        连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入--------一个写入时序,最多只能写一页的数据,也就是256字节;  一个页缓存区,它只有256字节;    Flash的写入,太慢了.  跟不上SPI的频率.  所以写入的数据,会先放在RAM里暂存.             必须得,从页起始位置开始,才能最大写入256字节,  如果从页中间的地址开始写, 那写到页尾时,这个地址就会跳回到页首, 这会导致地址错乱

        写入操作结束后,芯片进入忙状态,不响应新的读写操作--------要想知道芯片什么时候结束忙状态了,  我们可以使用读取状态寄存器的指令,  看一下状态寄存器的BUSY位是否为1,  BUSY位为0时,芯片就不忙了,我们再进行操作

        在发出擦除指令后,芯片也会进入忙状态, 我们也得等待忙状态结束后,才能进行后续操作

        扇区擦除也是写入所以需要使能

读取操作时:

        直接调用读取时序,无需使能,无需额外操作,没有页的限制,

        读取操作结束后不会进入忙状态,但不能在忙状态时读取,

5:指令集

INSTRUCTION NAME---指令的名字;     BYTE----字节X

 

Write Enable----写使能指令集

Write Disable --------写失能指令集

Read Status Register-1---------读状态寄存器1--作用: 判断寄存器在不在忙, 具体见 二: 4

Page Program----------页编程, 写数据,max为256个字节

Sector Erase (4KB)-------------按4KB的扇区擦除

JEDEC ID----------读取ID

Read Data-----读取数据

三:HAL

A:宏函数

B:轮询方式

HAL_SPl_Transmit:

HAL_SPl_Receive:

C:中断

C:DMA

四:案例

A:轮询方式--W25Q64

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "w25q64.h"

uint8_t wbuff[4096];
uint8_t rbuff[4096];
uint16_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi1_Init();

	W25q64_SectorErase(0);
	W25q64_ReadID();
	
//	
//	for(i=0;i<4096;i++)
//	{	
//		wbuff[i]=0x66;
//		
//	}
//	
//	for(i=0;i<16;i++)
//	{
//		//W25q64_WritePage(&wbuff[i*256],i);
//	}
//	
//	W25q64_ReadData(rbuff,0,4096);
//	
//	for(i=0;i<4096;i++)
//	{
//		printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
//	}
	
	 while (1)
	 {
		

			
	 }   
}

#include "stm32f1xx_hal.h"


SPI_HandleTypeDef SPI_Handle;

void Spi1_Init()
{
	SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
	SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2;  //分频
	SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHA
	SPI_Handle.Init.CLKPolarity=SPI_POLARITY_LOW;    //CPOL时钟极性
	SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
	SPI_Handle.Init.NSS=SPI_NSS_SOFT;    //NSS软件模式
	HAL_SPI_Init(&SPI_Handle);
}
	
	
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI1)
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_SPI1_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitType;
	
		
		GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;   //推挽输出  普通的IO口
		GPIO_InitType.Pin=GPIO_PIN_0;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 

		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_5;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		//MISO 主机输入,从机输出
		GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_6;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		
		//MOSI 主机输出,从机输入
		GPIO_InitType.Mode=GPIO_MODE_AF_PP;     //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_7;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
	}




}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
#define CS_ENABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
#define CS_DISABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);

/**
  * @brief  查看busy位是否忙碌.
  */
void W25q64_Busy(void)
{		
		uint8_t cmd[2],data[2];
	
		cmd[0]=0x05;
		cmd[1]=0xFF;
		
		do
		{
			CS_ENABLE;
			HAL_SPI_TransmitReceive(&SPI_Handle,cmd,data,2,1000);
			CS_DISABLE;
		}
		
		while((data[1] & 0x01)==0x01);  //busy为0不忙;
	
}
/**
  * @brief  写使能.
  */
void W25q64_WriteEnable(void)
{
	uint8_t cmd;
	cmd=0x06;
	
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,&cmd,1,1000);
	CS_DISABLE;
}

/**
  * @brief  擦除一个扇区的数据(4K;4*1024=4096)
  * @param  擦除第几页的数据 

  */
void W25q64_SectorErase(uint32_t Sectornum)
{
	uint8_t cmd[4];
	
	cmd[0]=0x20;
	cmd[1]=(Sectornum*4096)>>16;
	cmd[2]=(Sectornum*4096)>>8;
	cmd[3]=(Sectornum*4096)>>0;
	
	W25q64_WriteEnable();
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	CS_DISABLE;
}

/**
  * @brief  写入一页数据,一页256字节
  * @param  data: 写入的数据
  * @param  PageNum:给第几页写入数据

  */
void W25q64_WritePage(uint8_t * data,uint32_t PageNum)
{
	uint8_t cmd[4];
	cmd[0]=0x02;
	
	cmd[1]=(PageNum*256)>>16;
	cmd[2]=(PageNum*256)>>8;
	cmd[3]=(PageNum*256)>>0;
	
	W25q64_WriteEnable();
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	HAL_SPI_Transmit(&SPI_Handle,data,256,5000);  //sizeof(data)
	CS_DISABLE;
}

/**
  * @brief  读取数据
  * @param  *rdata: 读取数据存放的地址
	* @param  addr: 读取第几页
	* @param  len: 读取数据的长度(几个字节)
  * @retval 
  */
void W25q64_ReadData(uint8_t *rdata,uint32_t addr ,uint32_t len)
{
	
	uint8_t cmd[4];
	cmd[0]=0x03;
	
	cmd[1]=addr>>16;
	cmd[2]=addr>>8;
	cmd[3]=addr>>0;
	

	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	HAL_SPI_Receive(&SPI_Handle,rdata,len,50000);
	CS_DISABLE;

}

/**
  * @brief  读取ID.
  */
void W25q64_ReadID()
{
	
	uint8_t cmd[1],data_id[3];
	cmd[0]=0x9F;

	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,1,1000);
	HAL_SPI_Receive(&SPI_Handle,data_id,sizeof(data_id),50000);
	CS_DISABLE;
	printf("W25q64的ID为:制造商ID=%02X 设备ID=%02X 容量ID=%02X\r\n",   
           data_id[0], data_id[1], data_id[2]); 


}

重要参数:

HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)


typedef struct  
{  
  uint32_t Mode;                !< 指定SPI的工作模式。  
                                     这个参数可以是@ref SPI_Mode中的一个值 */  
  
  uint32_t Direction;           !< 指定SPI双向模式的状态。  
                                     这个参数可以是@ref SPI_Direction中的一个值 */  
  
  uint32_t DataSize;            !< 指定SPI的数据大小。  
                                     这个参数可以是@ref SPI_Data_Size中的一个值 */  
  
  uint32_t CLKPolarity;         !< 指定串行时钟的稳定状态。  
                                     这个参数可以是@ref SPI_Clock_Polarity中的一个值 */  
  
  uint32_t CLKPhase;            !< 指定用于位捕获的时钟活动边沿。  
                                     这个参数可以是@ref SPI_Clock_Phase中的一个值 */  
  
  uint32_t NSS;                 !< 指定NSS信号是由硬件(NSS引脚)管理还是由软件使用SSI位管理。  
                                     这个参数可以是@ref SPI_Slave_Select_management中的一个值 */  
  
  uint32_t BaudRatePrescaler;   !< 指定波特率预分频值,用于配置发送和接收SCK时钟。  
                                     这个参数可以是@ref SPI_BaudRate_Prescaler中的一个值  
                                     @note 通信时钟是从主时钟派生出来的。从机时钟不需要设置。 */  
  
  uint32_t FirstBit;            !< 指定数据传输是从MSB(最高有效位)还是LSB(最低有效位)开始。  
                                     这个参数可以是@ref SPI_MSB_LSB_transmission中的一个值 */  
  
  uint32_t TIMode;              !< 指定是否启用TI模式。  
                                     这个参数可以是@ref SPI_TI_mode中的一个值 */  
  
  uint32_t CRCCalculation;      !< 指定是否启用CRC(循环冗余校验)计算。  
                                     这个参数可以是@ref SPI_CRC_Calculation中的一个值 */  
  
  uint32_t CRCPolynomial;       !< 指定用于CRC计算的多项式。  
                                     这个参数必须是一个介于Min_Data = 1和Max_Data = 65535之间的奇数 */  
} SPI_InitTypeDef; 

B:单线双向模式--MAX7219(数码管)

频率:

模式:

CLK极性默认为低;上升沿采集数据说明第一个边沿采集数据。

高位先行:

译码的方法:

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"



int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi2_Init();
	

	 delay_ms(50);
	 Init_MAX7219();
	 delay_ms(1000);
	 Write_Max7219(0x0f, 0x00);       //显示测试:1;测试结束,正常显示:0
	 Write_Max7219(1,8);     //第一个数码管显示1
	 //Write_Max7219(1,0x88);   //D7位变为高电平,控制小数点
	 Write_Max7219(2,7);
	 Write_Max7219(3,6);
	 Write_Max7219(4,5); 
	 Write_Max7219(5,4);
	 Write_Max7219(6,3);
	 Write_Max7219(7,2);
	 Write_Max7219(8,1); 
	 while (1)
	 {
		 
	 }   
}

#include "stm32f1xx_hal.h"




SPI_HandleTypeDef SPI2_Handle;

//MAX7219
void Spi2_Init()
{
	SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
	SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16;  //分频  36/16=2
	SPI2_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHA
	SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_LOW;    //CPOL时钟极性   SPI_POLARITY_HIGH
	SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI2_Handle.Init.Direction=SPI_DIRECTION_2LINES; //单线半双工
	SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI2_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
	SPI2_Handle.Init.NSS=SPI_NSS_SOFT;    //NSS软件模式
	HAL_SPI_Init(&SPI2_Handle);
}

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{


	if(hspi->Instance==SPI2)
	{
		__HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_SPI2_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitType;
	
		//片选
		GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;   //推挽输出  普通的IO口
		GPIO_InitType.Pin=GPIO_PIN_12;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 

		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_13;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
		
		//MISO 主机输入,从机输出
		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_15;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
	
	}


}#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include "UART.h"
#include "delay.h"
/* address define */
#define NO_OP              0x00
#define DIGIT0             0x01
#define DIGIT1             0x02
#define DIGIT2             0x03
#define DIGIT3             0x04
#define DIGIT4             0x05
#define DIGIT5             0x06
#define DIGIT6             0x07
#define DIGIT7             0x08
#define DECODE_MODE        0x09      //译码控制寄存器
#define INTENSITY          0x0A			 // 亮度控制寄存器
#define SCAN_LIMIT         0x0B      //扫描界限寄存器
#define SHUT_DOWN          0x0C      //关断模式寄存器
#define DISPLAY_TEST       0x0F       //显示测试寄存器
/* mode define */
#define NORMAL_MODE           0
#define NO_DECODE_D0_D7    0x00
#define DECODE_D0_ONLY     0x01
#define DECODE_D0_D3_ONLY  0x0F
#define DECODE_D0_D7       0xFF





#define CS_ENABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
#define CS_DISABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);                      
 

//功能:向MAX7219内部寄存器写入数据
//参数:addr、dat
void Write_Max7219(uint8_t addr,uint8_t dat)
{
	CS_ENABLE_MAX7291;	
	HAL_SPI_Transmit(&SPI2_Handle,&addr,1,1000);	//写入地址 
	delay_us(10);//必须,点阵不显示时可以延长此延时调试
	HAL_SPI_Transmit(&SPI2_Handle,&dat,1,1000);	//写入地址 
	delay_us(10);//必须,点阵不显示时可以延长此延时调试
	CS_DISABLE_MAX7291;
}

//MAX7291初始化                   
void Init_MAX7219(void)
{
	 Write_Max7219(0x09, 0xFF);       //译码方式:不译码
	 Write_Max7219(0x0a, 0x03);       //亮度
	 Write_Max7219(0x0b, 0x07);       //扫描界限;4个数码管显示
	 Write_Max7219(0x0c, 0x01);       //掉电模式:0,普通模式:1
	 Write_Max7219(0x0f, 0x01);       //显示测试:1;测试结束,正常显示:0
}

                        

不译码的方法:

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"



int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi2_Init();
	
	uint16_t leddata[]={
		0x7e,  //"0"
    0x30,  //"1"
    0x6D,  //"2"
    0x79,  //"3"
    0x33,  //"4"
    0x5B,  //"5"
    0x5F,  //"6"
    0x70,  //"7"
    0x7F,  //"8"
    0x7B,  //"9"
    0x77,  //"A"
    0x7F,  //"B"
    0x4E,  //"C"
    0x7e,  //"D"
    0x4F,  //"E"
    0x47,  //"F"
    0x37,  //"H"
    0x00  //熄灭
	};


	 delay_ms(50);
	 Init_MAX7219();
	 delay_ms(1000);
	 Write_Max7219(0x0f, 0x00);       //显示测试:1;测试结束,正常显示:0
	 Write_Max7219(1,leddata[0]);     //第一个数码管显示1

	 Write_Max7219(2,leddata[1]);
	 Write_Max7219(3,leddata[2]);
	 Write_Max7219(4,leddata[3]); 
	 Write_Max7219(5,leddata[4]);
	 Write_Max7219(6,leddata[5]);
	 Write_Max7219(7,leddata[6]);
	 Write_Max7219(8,leddata[7]); 
	 while (1)
	 {
		 for(uint16_t i=0;i<18;i++)
		 {
				Write_Max7219(1,leddata[i]);
			  delay_ms(500);
		 
		 }
	 }   
}

#include "stm32f1xx_hal.h"




SPI_HandleTypeDef SPI2_Handle;

//MAX7219
void Spi2_Init()
{
	SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
	SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16;  //分频  36/16=2
	SPI2_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHA
	SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_LOW;    //CPOL时钟极性   SPI_POLARITY_HIGH
	SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI2_Handle.Init.Direction=SPI_DIRECTION_1LINE; //单线半双工
	SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI2_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
	SPI2_Handle.Init.NSS=SPI_NSS_SOFT;    //NSS软件模式
	HAL_SPI_Init(&SPI2_Handle);
}

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{


	if(hspi->Instance==SPI2)
	{
		__HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_SPI2_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitType;
	
		//片选
		GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;   //推挽输出  普通的IO口
		GPIO_InitType.Pin=GPIO_PIN_12;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 

		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_13;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
		
		//MISO 主机输入,从机输出
		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_15;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
	
	}


}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include "UART.h"
#include "delay.h"
/* address define */
#define NO_OP              0x00
#define DIGIT0             0x01
#define DIGIT1             0x02
#define DIGIT2             0x03
#define DIGIT3             0x04
#define DIGIT4             0x05
#define DIGIT5             0x06
#define DIGIT6             0x07
#define DIGIT7             0x08
#define DECODE_MODE        0x09      //译码控制寄存器
#define INTENSITY          0x0A			 // 亮度控制寄存器
#define SCAN_LIMIT         0x0B      //扫描界限寄存器
#define SHUT_DOWN          0x0C      //关断模式寄存器
#define DISPLAY_TEST       0x0F       //显示测试寄存器
/* mode define */
#define NORMAL_MODE           0
#define NO_DECODE_D0_D7    0x00
#define DECODE_D0_ONLY     0x01
#define DECODE_D0_D3_ONLY  0x0F
#define DECODE_D0_D7       0xFF





#define CS_ENABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
#define CS_DISABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);                      
 

//功能:向MAX7219内部寄存器写入数据
//参数:addr、dat
void Write_Max7219(uint8_t addr,uint8_t dat)
{
	CS_ENABLE_MAX7291;	
	HAL_SPI_Transmit(&SPI2_Handle,&addr,1,1000);	//写入地址 
	delay_us(10);//必须,点阵不显示时可以延长此延时调试
	HAL_SPI_Transmit(&SPI2_Handle,&dat,1,1000);	//写入地址 
	delay_us(10);//必须,点阵不显示时可以延长此延时调试
	CS_DISABLE_MAX7291;
}

//MAX7291初始化                   
void Init_MAX7219(void)
{
	 Write_Max7219(0x09, 0x00);       //译码方式:不译码
	 Write_Max7219(0x0a, 0x03);       //亮度
	 Write_Max7219(0x0b, 0x07);       //扫描界限;4个数码管显示
	 Write_Max7219(0x0c, 0x01);       //掉电模式:0,普通模式:1
	 Write_Max7219(0x0f, 0x01);       //显示测试:1;测试结束,正常显示:0
}

                        

C:中断方式--W25Q64

 调用回调的时候,说明它的收(发)已经完成。

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "w25q64.h"


uint16_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi1_Init();

	W25q64_SectorErase(0);
	W25q64_ReadID();
	
	
	for(i=0;i<4096;i++)
	{	
		wbuff[i]=0x66;
		
	}
	
	for(i=0;i<16;i++)
	{
		W25q64_WritePage(&wbuff[i*256],i);
	}
	
	W25q64_ReadData(rbuff,0,4096);
	
//	for(i=0;i<4096;i++)
//	{
//		printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
//	}
	
	 while (1)
	 {
		

			
	 }   
}

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "w25q64.h"
uint8_t wbuff[4096];
uint8_t rbuff[4096];
SPI_HandleTypeDef SPI_Handle;

void Spi1_Init()
{
	SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
	SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2;  //分频
	SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHA
	SPI_Handle.Init.CLKPolarity=SPI_POLARITY_LOW;    //CPOL时钟极性
	SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
	SPI_Handle.Init.NSS=SPI_NSS_SOFT;    //NSS软件模式
	HAL_SPI_Init(&SPI_Handle);
}
	
	
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI1)
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_SPI1_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitType;
	
		HAL_NVIC_SetPriority(SPI1_IRQn,3,2);
		HAL_NVIC_EnableIRQ(SPI1_IRQn);
		GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;   //推挽输出  普通的IO口
		GPIO_InitType.Pin=GPIO_PIN_0;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 

		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_5;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		//MISO 主机输入,从机输出
		GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_6;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		
		//MOSI 主机输出,从机输入
		GPIO_InitType.Mode=GPIO_MODE_AF_PP;     //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_7;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
	}


}

void SPI1_IRQHandler()
{
	HAL_SPI_IRQHandler(&SPI_Handle);

}
//IT的API函数执行完毕,开启中断接收函数,但是不代表接收操作的完成。
//调用回调函数的时候,表示接收完毕。
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI1)
	{
		for(uint16_t i=0;i<4096;i++)
		{
			CS_DISABLE;
			printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
		}
	}
}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
#define CS_ENABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
#define CS_DISABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);

/**
  * @brief  查看busy位是否忙碌.
  */
void W25q64_Busy(void)
{		
		uint8_t cmd[2],data[2];
	
		cmd[0]=0x05;
		cmd[1]=0xFF;
		
		do
		{
			CS_ENABLE;
			HAL_SPI_TransmitReceive(&SPI_Handle,cmd,data,2,1000);
			CS_DISABLE;
		}
		
		while((data[1] & 0x01)==0x01);  //busy为0不忙;
	
}
/**
  * @brief  写使能.
  */
void W25q64_WriteEnable(void)
{
	uint8_t cmd;
	cmd=0x06;
	
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,&cmd,1,1000);
	CS_DISABLE;
}

/**
  * @brief  擦除一个扇区的数据(4K;4*1024=4096)
  * @param  擦除第几页的数据 

  */
void W25q64_SectorErase(uint32_t Sectornum)
{
	uint8_t cmd[4];
	
	cmd[0]=0x20;
	cmd[1]=(Sectornum*4096)>>16;
	cmd[2]=(Sectornum*4096)>>8;
	cmd[3]=(Sectornum*4096)>>0;
	
	W25q64_WriteEnable();
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	CS_DISABLE;
}

/**
  * @brief  写入一页数据,一页256字节
  * @param  data: 写入的数据
  * @param  PageNum:给第几页写入数据

  */
void W25q64_WritePage(uint8_t * data,uint32_t PageNum)
{
	uint8_t cmd[4];
	cmd[0]=0x02;
	
	cmd[1]=(PageNum*256)>>16;
	cmd[2]=(PageNum*256)>>8;
	cmd[3]=(PageNum*256)>>0;
	
	W25q64_WriteEnable();
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	HAL_SPI_Transmit(&SPI_Handle,data,256,5000);  //sizeof(data)
	CS_DISABLE;
}

/**
  * @brief  读取数据
  * @param  *rdata: 读取数据存放的地址
	* @param  addr: 读取第几页
	* @param  len: 读取数据的长度(几个字节)
  * @retval 
  */
void W25q64_ReadData(uint8_t *rdata,uint32_t addr ,uint32_t len)
{
	
	uint8_t cmd[4];
	cmd[0]=0x03;
	
	cmd[1]=addr>>16;
	cmd[2]=addr>>8;
	cmd[3]=addr>>0;
	

	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	HAL_SPI_Receive_IT(&SPI_Handle,rdata,len);
	//CS_DISABLE;

}

/**
  * @brief  读取ID.
  */
void W25q64_ReadID()
{
	
	uint8_t cmd[1],data_id[3];
	cmd[0]=0x9F;

	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,1,1000);
	HAL_SPI_Receive(&SPI_Handle,data_id,sizeof(data_id),50000);
	CS_DISABLE;
	printf("W25q64的ID为:制造商ID=%02X 设备ID=%02X 容量ID=%02X\r\n",   
           data_id[0], data_id[1], data_id[2]); 


}

D:MDA----W25Q64 

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "w25q64.h"


uint16_t i=0;

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi1_Init();

	W25q64_SectorErase(0);
	W25q64_ReadID();
	
	
	for(i=0;i<4096;i++)
	{	
		wbuff[i]=0x44;
		
	}
	
	for(i=0;i<16;i++)
	{
		W25q64_WritePage(&wbuff[i*256],i);
	}
	
	W25q64_ReadData(rbuff,0,4096);
	

	
	 while (1)
	 {
		

			
	 }   
}

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "w25q64.h"
uint8_t wbuff[4096];
uint8_t rbuff[4096];
SPI_HandleTypeDef SPI_Handle;
DMA_HandleTypeDef DMA_HandleTX;
DMA_HandleTypeDef DMA_HandleRX;

void Spi1_Init()
{
	SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
	SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2;  //分频
	SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHA
	SPI_Handle.Init.CLKPolarity=SPI_POLARITY_LOW;    //CPOL时钟极性
	SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
	SPI_Handle.Init.NSS=SPI_NSS_SOFT;    //NSS软件模式
	HAL_SPI_Init(&SPI_Handle);
}
	
	
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI1)
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_SPI1_CLK_ENABLE();
		__HAL_RCC_DMA1_CLK_ENABLE();
		GPIO_InitTypeDef GPIO_InitType;

//		HAL_NVIC_SetPriority(SPI1_IRQn,3,2);
//		HAL_NVIC_EnableIRQ(SPI1_IRQn);
		
					
			//MDA通道6配置--发送数据
		DMA_HandleTX.Instance=DMA1_Channel3;
		//传输方向:内存(数组)--->外设(SPI的DR寄存器)
		DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
		DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
		DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区(数组)地址自增
		DMA_HandleTX.Init.Mode=DMA_NORMAL;
		DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
		DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增(SPI的DR寄存器)
		DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
		__HAL_LINKDMA(&SPI_Handle,hdmatx,DMA_HandleTX);   //双向链接
		//__HAL_LINKDMA(hspi,hdmatx,DMA_HandleTX);   //双向链接
		HAL_DMA_Init(&DMA_HandleTX);
		
		HAL_NVIC_SetPriority(DMA1_Channel3_IRQn,1,2);
		HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
		
		//MDA通道7配置---接收数据
		DMA_HandleRX.Instance=DMA1_Channel2;
		//传输方向:外设(SPI的DR寄存器)  ---> 内存(数组)
		DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
		DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
		DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
		DMA_HandleRX.Init.Mode=DMA_NORMAL;
		DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
		DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
		DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
		__HAL_LINKDMA(&SPI_Handle,hdmarx,DMA_HandleRX);   //双向链接
		//__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
		HAL_DMA_Init(&DMA_HandleRX);
		
		HAL_NVIC_SetPriority(DMA1_Channel2_IRQn,1,2);
		HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);

		
		GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;   //推挽输出  普通的IO口
		GPIO_InitType.Pin=GPIO_PIN_0;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 

		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_5;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		//MISO 主机输入,从机输出
		GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_6;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		
		//MOSI 主机输出,从机输入
		GPIO_InitType.Mode=GPIO_MODE_AF_PP;     //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_7;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		
		
		
		
	}


}

//void SPI1_IRQHandler()
//{
//	HAL_SPI_IRQHandler(&SPI_Handle);

//}

void DMA1_Channel2_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX);

}

void DMA1_Channel3_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX);

}
//DMA的API函数执行完毕,开启中断接收函数,但是不代表接收操作的完成。
//调用回调函数的时候,表示接收完毕。
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI1)
	{
		for(uint16_t i=0;i<4096;i++)
		{
			CS_DISABLE;
			printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
		}
	}
}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
#define CS_ENABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
#define CS_DISABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);

/**
  * @brief  查看busy位是否忙碌.
  */
void W25q64_Busy(void)
{		
		uint8_t cmd[2],data[2];
	
		cmd[0]=0x05;
		cmd[1]=0xFF;
		
		do
		{
			CS_ENABLE;
			HAL_SPI_TransmitReceive(&SPI_Handle,cmd,data,2,1000);
			CS_DISABLE;
		}
		
		while((data[1] & 0x01)==0x01);  //busy为0不忙;
	
}
/**
  * @brief  写使能.
  */
void W25q64_WriteEnable(void)
{
	uint8_t cmd;
	cmd=0x06;
	
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,&cmd,1,1000);
	CS_DISABLE;
}

/**
  * @brief  擦除一个扇区的数据(4K;4*1024=4096)
  * @param  擦除第几页的数据 

  */
void W25q64_SectorErase(uint32_t Sectornum)
{
	uint8_t cmd[4];
	
	cmd[0]=0x20;
	cmd[1]=(Sectornum*4096)>>16;
	cmd[2]=(Sectornum*4096)>>8;
	cmd[3]=(Sectornum*4096)>>0;
	
	W25q64_WriteEnable();
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	CS_DISABLE;
}

/**
  * @brief  写入一页数据,一页256字节
  * @param  data: 写入的数据
  * @param  PageNum:给第几页写入数据

  */
void W25q64_WritePage(uint8_t * data,uint32_t PageNum)
{
	uint8_t cmd[4];
	cmd[0]=0x02;
	
	cmd[1]=(PageNum*256)>>16;
	cmd[2]=(PageNum*256)>>8;
	cmd[3]=(PageNum*256)>>0;
	
	W25q64_WriteEnable();
	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	HAL_SPI_Transmit(&SPI_Handle,data,256,5000);  //sizeof(data)
	CS_DISABLE;
}

/**
  * @brief  读取数据
  * @param  *rdata: 读取数据存放的地址
	* @param  addr: 读取第几页
	* @param  len: 读取数据的长度(几个字节)
  * @retval 
  */
void W25q64_ReadData(uint8_t *rdata,uint32_t addr ,uint32_t len)
{
	
	uint8_t cmd[4];
	cmd[0]=0x03;
	
	cmd[1]=addr>>16;
	cmd[2]=addr>>8;
	cmd[3]=addr>>0;
	

	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
	HAL_SPI_Receive_DMA(&SPI_Handle,rdata,len);
	//CS_DISABLE;

}

/**
  * @brief  读取ID.
  */
void W25q64_ReadID()
{
	
	uint8_t cmd[1],data_id[3];
	cmd[0]=0x9F;

	W25q64_Busy();
	CS_ENABLE;
	HAL_SPI_Transmit(&SPI_Handle,cmd,1,1000);
	HAL_SPI_Receive(&SPI_Handle,data_id,sizeof(data_id),50000);
	CS_DISABLE;
	printf("W25q64的ID为:制造商ID=%02X 设备ID=%02X 容量ID=%02X\r\n",   
           data_id[0], data_id[1], data_id[2]); 


}

主从通信

A:中断+主机身份不改变

发现问题:2个板子的SPI通信正常,主机可以正常给从机发送数据,但是从机给主机发送数据的数据顺序会乱。

解决:换线,一定要共电源和共地!!!!!!!!!!!!!!!!!!(下面所有的主从通信必须遵守这个)

主机代码:


#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"




int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi1_Init();


	 while (1)
	 {
		if(KEY_Scan())
		{
				printf("PA0按下,主机发送数据\r\n");
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
				HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
		}
		
	 }   
}

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"

SPI_HandleTypeDef SPI_Handle;

uint8_t wbuff[10]={10,11,12,13,14,15,16,17,18,19};
uint8_t rbuff[10];
void Spi1_Init()
{
	//主机
	SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
	SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_128;  //分频  72/256=0.60MHZ
	SPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHA
	SPI_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH;    //CPOL时钟极性
	SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
	SPI_Handle.Init.NSS=SPI_NSS_SOFT;    //NSS软件模式
	HAL_SPI_Init(&SPI_Handle);
}
	



void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI1)
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_SPI1_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitType;
	
		HAL_NVIC_SetPriority(SPI1_IRQn,3,1);
		HAL_NVIC_EnableIRQ(SPI1_IRQn);
		
		GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;   //推挽输出  普通的IO口
		GPIO_InitType.Pin=GPIO_PIN_4;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_5|GPIO_PIN_7;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
	
		GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_6;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		
 
	}


}

void SPI1_IRQHandler()
{
	HAL_SPI_IRQHandler(&SPI_Handle);

}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI1)
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
			for(uint8_t i=0;i<10;i++)
			{
					printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
			}
		}
}

从机代码:

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"

int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi2_Init();
	HAL_SPI_TransmitReceive_IT(&SPI2_Handle,w_buff,r_buff,10);
	

	 while (1)
	 {
	
		
	 }   
}

#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"


SPI_HandleTypeDef SPI2_Handle;

uint8_t w_buff[10]={0,1,2,3,4,5,6,7,8,9};
uint8_t r_buff[10];

void Spi2_Init()
{
	//从机
	SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
	SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;  //分频  36/256=0.14
	SPI2_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHA
	SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH;    //CPOL时钟极性
	SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI2_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI2_Handle.Init.Mode=SPI_MODE_SLAVE; //从机模式
	SPI2_Handle.Init.NSS=SPI_NSS_HARD_INPUT;    //NSS硬件模式
	HAL_SPI_Init(&SPI2_Handle);


}

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI2)
	{
		__HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_SPI2_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitType;
		
		HAL_NVIC_SetPriority(SPI2_IRQn,3,1);
		HAL_NVIC_EnableIRQ(SPI2_IRQn);
		
//		//片选
//		GPIO_InitType.Mode=GPIO_MODE_INPUT;   //输入  普通的IO口
//		GPIO_InitType.Pin=GPIO_PIN_12;
//		GPIO_InitType.Pull=GPIO_NOPULL;
//		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 

//			配置为	SPI2_Handle.Init.NSS=SPI_NSS_HARD_INPUT;    //NSS硬件模式
//			从机片选不用IO口的配置
		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_14;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
		
		//MISO 主机输入,从机输出
		GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_13|GPIO_PIN_15;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
	
	}


}

void SPI2_IRQHandler()
{
	HAL_SPI_IRQHandler(&SPI2_Handle);

}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI2)
		{
			for(uint8_t i=0;i<10;i++)
			{
					printf("rbuff[%d]=%d\r\n",i,r_buff[i]);
			}
			HAL_SPI_TransmitReceive_IT(&SPI2_Handle,w_buff,r_buff,10);
		}
}

B:中断+DMA

主机代码和上面的A:中断+主机身份不改变一样;只是把从机改为DMA模式

从机代码:

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi2_Init();
	HAL_SPI_TransmitReceive_DMA(&SPI2_Handle,wbuff,rbuff,10);
	printf("从机串口正常\r\n");

	 while (1)
	 {
	
		
	 }   
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"


SPI_HandleTypeDef SPI2_Handle;
DMA_HandleTypeDef DMA_HandleTX;
DMA_HandleTypeDef DMA_HandleRX;
uint8_t wbuff[10]={10,11,12,13,14,15,16,17,18,19};
uint8_t rbuff[10];

void Spi2_Init()
{
	//从机
	SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
	SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_128;  //分频  36/128=0.28MHZ
	SPI2_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHA
	SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH;    //CPOL时钟极性
	SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI2_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI2_Handle.Init.Mode=SPI_MODE_SLAVE; //从机模式
	SPI2_Handle.Init.NSS=SPI_NSS_HARD_INPUT;    //NSS输入
	HAL_SPI_Init(&SPI2_Handle);
}

void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI2)
	{
		__HAL_RCC_GPIOB_CLK_ENABLE();
		__HAL_RCC_SPI2_CLK_ENABLE();
		__HAL_RCC_DMA1_CLK_ENABLE();
		GPIO_InitTypeDef GPIO_InitType;
		
	
		
		//MDA通道5配置--发送数据
		DMA_HandleTX.Instance=DMA1_Channel5;
		//传输方向:内存(数组)--->外设(SPI的DR寄存器)
		DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
		DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
		DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE;    //存储区(数组)地址自增
		DMA_HandleTX.Init.Mode=DMA_NORMAL;
		DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
		DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增(SPI的DR寄存器)
		DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
		__HAL_LINKDMA(&SPI2_Handle,hdmatx,DMA_HandleTX);   //双向链接
		//__HAL_LINKDMA(hspi,hdmatx,DMA_HandleTX);   //双向链接
		HAL_DMA_Init(&DMA_HandleTX);
		
		HAL_NVIC_SetPriority(DMA1_Channel5_IRQn,1,2);
		HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
		
		//MDA通道4配置---接收数据
		DMA_HandleRX.Instance=DMA1_Channel4;
		//传输方向:外设(SPI的DR寄存器)  ---> 内存(数组)
		DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
		DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
		DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE;    //存储区地址自增
		DMA_HandleRX.Init.Mode=DMA_NORMAL;
		DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
		DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
		DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM;   //优先级
		__HAL_LINKDMA(&SPI2_Handle,hdmarx,DMA_HandleRX);   //双向链接
		//__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX);   //双向链接
		HAL_DMA_Init(&DMA_HandleRX);
		
		HAL_NVIC_SetPriority(DMA1_Channel4_IRQn,1,2);
		HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);

			
		GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_14;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
		
		//MISO 主机输入,从机输出
		GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
		GPIO_InitType.Pin=GPIO_PIN_13|GPIO_PIN_15;
		GPIO_InitType.Pull=GPIO_NOPULL;
		HAL_GPIO_Init(GPIOB,&GPIO_InitType); 
	
	}


}


void DMA1_Channel4_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleRX);

}

void DMA1_Channel5_IRQHandler()
{
	HAL_DMA_IRQHandler(&DMA_HandleTX);

}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI2)
		{
			for(uint8_t i=0;i<10;i++)
			{
					printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
			}
			HAL_SPI_TransmitReceive_DMA(&SPI2_Handle,wbuff,rbuff,10);
		}
}

C:多主机通信--主从身份可以改变 

PA3控制对方的PA4;PA4(CS片选)

主机和从机可以使用这一个代码,只需要把里面发送的数据改了就ok了,其他代码部分都不需要改。

把对方拉为从机: 走的流程

 while (1)
	 {
		if(KEY_Scan())
		{
				printf("PA0按下,把对方拉为从机,发送数据\r\n");
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
				HAL_Delay(20);
				HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
		}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI1)
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);		
		}

}

被对面拉低为从机:被拉低产生MODE错误,进入

//主机配拉低,被拉低产生MODE错误,进入这个中断回调函数
//在这个错误中断回调函数中把它变为从机
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI1)
		{
			if(hspi->ErrorCode==HAL_SPI_ERROR_MODF)
			{
					printf("被拉成从机\r\n");
					HAL_SPI_DeInit(&SPI_Handle);  //清除以前的配置
					Spi1_SInit();   //重新配置为从机
					HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
			}
		}
}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI1)
		{
			
			for(uint8_t i=0;i<10;i++)
			{
				
					printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
			
			}
			Spi1_MInit();  //被对方拉低接收数据,接收完毕在变为主模式,以便下一次使用
			
		}

}
	__HAL_SPI_ENABLE(&SPI_Handle);
	__HAL_SPI_ENABLE_IT(&SPI_Handle,SPI_IT_ERR);   手动打开错误中断,
            被拉为从机的时候发送这个错误,调用错误中断回调函数

__HAL_SPI_ENABLE(&SPI_Handle):这个是在接收和发送函数内部的HAL帮助我们调用的。我们现在都是主机不能调用收发函数,(在一主一从的时候才可以正常通信,两个都为主机时没有办法通信。)我们需要自己手动打开SPI的接口。

代码:

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"


int main(void)
{

  
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
  LED_Init();                        /* LED初始化 */
	Uart_Init(115200);
	LED_Exit_Init();
	KEY_Init();
	Spi1_MInit();


	 while (1)
	 {
		if(KEY_Scan())
		{
				printf("PA0按下,把对方拉为从机,发送数据\r\n");
				HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
				HAL_Delay(20);
				HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
		}
		
	 }   
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"




SPI_HandleTypeDef SPI_Handle;



uint8_t wbuff[10]={1,2,3,4,5,6,7,8,9,10};

uint8_t rbuff[10];
void Spi1_MInit()
{
	//主机
	SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
	SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;  //分频  72/256=0.28MHZ
	SPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHA
	SPI_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH;    //CPOL时钟极性
	SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
	SPI_Handle.Init.NSS=SPI_NSS_HARD_INPUT;    //NSS硬件输入
	HAL_SPI_Init(&SPI_Handle);
	
	__HAL_SPI_ENABLE(&SPI_Handle);
	__HAL_SPI_ENABLE_IT(&SPI_Handle,SPI_IT_ERR);   //手动打开错误中断
}
	
void Spi1_SInit()
{
	//从机
	SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
	SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;  //分频  72/256=0.28MHZ
	SPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHA
	SPI_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH;    //CPOL时钟极性
	SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;  //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
	SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
	SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
	SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
	SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE;   //不支持TI模式
	SPI_Handle.Init.Mode=SPI_MODE_SLAVE; //从机模式
	SPI_Handle.Init.NSS=SPI_NSS_HARD_INPUT;    //硬件输入
	HAL_SPI_Init(&SPI_Handle);

}


void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{

	if(hspi->Instance==SPI1)
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_SPI1_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitType;
	
		HAL_NVIC_SetPriority(SPI1_IRQn,3,1);
		HAL_NVIC_EnableIRQ(SPI1_IRQn);
		
		GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP;   //推挽输出  普通的IO口
		GPIO_InitType.Pin=GPIO_PIN_3;
		GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
		HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
		
		if(hspi->Init.Mode==SPI_MODE_MASTER)
		{	
			//主机
			GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
			GPIO_InitType.Pin=GPIO_PIN_5|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
			HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
			
			GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
			GPIO_InitType.Pin=GPIO_PIN_6;
			GPIO_InitType.Pull=GPIO_NOPULL;
			HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
		}
		else 
		{
			//从机			
			GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPI
			GPIO_InitType.Pin=GPIO_PIN_5|GPIO_PIN_7;
			GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
			HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
			
			
			GPIO_InitType.Mode=GPIO_MODE_AF_PP;   //复用推挽输出  复用为SPI
			GPIO_InitType.Pin=GPIO_PIN_6;
			GPIO_InitType.Pull=GPIO_NOPULL;
			HAL_GPIO_Init(GPIOA,&GPIO_InitType); 
			
		}
		
	
		
 
	}


}

void SPI1_IRQHandler()
{
	HAL_SPI_IRQHandler(&SPI_Handle);

}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI1)
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
			
			for(uint8_t i=0;i<10;i++)
			{
				
					printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
			
			}
			Spi1_MInit();  //被对方拉低接收数据,接收完毕在变为主模式,以便下一次使用
			
		}

}

//主机配拉低,被拉低产生MODE错误,进入这个中断回调函数
//在这个错误中断回调函数中把它变为从机
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
		if(hspi->Instance==SPI1)
		{
			if(hspi->ErrorCode==HAL_SPI_ERROR_MODF)
			{
					printf("被拉成从机\r\n");
					HAL_SPI_DeInit(&SPI_Handle);  //清除以前的配置
					Spi1_SInit();   //重新配置为从机
					HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
			}
		}
}

 

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值