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

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交换数据--使用的为模式0DI(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总线=72MHZSPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2;  //分频SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHASPI_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_InitType.Pin=GPIO_PIN_6;GPIO_InitType.Pull=GPIO_NOPULL;HAL_GPIO_Init(GPIOA,&GPIO_InitType); //MOSI 主机输出,从机输入GPIO_InitType.Mode=GPIO_MODE_AF_PP;     //复用推挽输出  复用为SPIGPIO_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;测试结束,正常显示:0Write_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总线=36MHZSPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16;  //分频  36/16=2SPI2_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHASPI2_Handle.Init.CLKPolarity=SPI_POLARITY_LOW;    //CPOL时钟极性   SPI_POLARITY_HIGHSPI2_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_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,普通模式:1Write_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;测试结束,正常显示:0Write_Max7219(1,leddata[0]);     //第一个数码管显示1Write_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总线=36MHZSPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16;  //分频  36/16=2SPI2_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHASPI2_Handle.Init.CLKPolarity=SPI_POLARITY_LOW;    //CPOL时钟极性   SPI_POLARITY_HIGHSPI2_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_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,普通模式:1Write_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总线=72MHZSPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2;  //分频SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHASPI_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_InitType.Pin=GPIO_PIN_6;GPIO_InitType.Pull=GPIO_NOPULL;HAL_GPIO_Init(GPIOA,&GPIO_InitType); //MOSI 主机输出,从机输入GPIO_InitType.Mode=GPIO_MODE_AF_PP;     //复用推挽输出  复用为SPIGPIO_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总线=72MHZSPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2;  //分频SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE;    //第一个边沿捕获;CPHASPI_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_InitType.Pin=GPIO_PIN_6;GPIO_InitType.Pull=GPIO_NOPULL;HAL_GPIO_Init(GPIOA,&GPIO_InitType); //MOSI 主机输出,从机输入GPIO_InitType.Mode=GPIO_MODE_AF_PP;     //复用推挽输出  复用为SPIGPIO_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总线=72MHZSPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_128;  //分频  72/256=0.60MHZSPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHASPI_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_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总线=36MHZSPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;  //分频  36/256=0.14SPI2_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHASPI2_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_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总线=36MHZSPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_128;  //分频  36/128=0.28MHZSPI2_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHASPI2_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_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总线=72MHZSPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;  //分频  72/256=0.28MHZSPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHASPI_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总线=72MHZSPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;  //分频  72/256=0.28MHZSPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE;    //第二个边沿捕获;CPHASPI_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;   //复用推挽输出  复用为SPIGPIO_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;   //复用输入  复用为SPIGPIO_InitType.Pin=GPIO_PIN_6;GPIO_InitType.Pull=GPIO_NOPULL;HAL_GPIO_Init(GPIOA,&GPIO_InitType); }else {//从机			GPIO_InitType.Mode=GPIO_MODE_AF_INPUT;   //复用输入  复用为SPIGPIO_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;   //复用推挽输出  复用为SPIGPIO_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);}}
}

 

相关文章:

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&#xff1a;NSS(CS) 6&#xff1a;时钟 二: W25Q64 1:简历 2…...

微服务---gateway网关

目录 gateway作用 gateway使用 添加依赖 配置yml文件 自定义过滤器 nacos上的gateway的配置文件 我们现在知道了通过nacos注册服务&#xff0c;通过feign实现服务间接口的调用&#xff0c;那对于不同权限的用户访问同一个接口&#xff0c;我们怎么知道他是否具有访问的权…...

HTML4(二)

文章目录 1 开发者文档2 基本标签2.1 排版标签2.2 语义化标签2.3 行内元素与块级元素2.4 文本标签2.5 常用标签补充 3 图片标签4 超链接标签4.1 跳转页面4.2 跳转文件4.3 跳转锚点4.4 唤起指定应用 5 列表5.1 有序列表5.2 无序列表5.3 自定义列表 6 表格6.1 基本结构6.2 表格标…...

SpringBoot 扩展篇:ConfigFileApplicationListener源码解析

SpringBoot 扩展篇&#xff1a;ConfigFileApplicationListener源码解析 1.概述2. ConfigFileApplicationListener定义3. ConfigFileApplicationListener回调链路3.1 SpringApplication#run3.2 SpringApplication#prepareEnvironment3.3 配置environment 4. 环境准备事件 Config…...

蓝桥杯省三爆改省二,省一到底做错了什么?

到底怎么个事 这届蓝桥杯选的软件测试赛道&#xff0c;都说选择大于努力,软件测试一不卷二不难。省赛结束&#xff0c;自己就感觉稳啦&#xff0c;全部都稳啦。没想到一出结果&#xff0c;省三&#xff0c;g了。说落差&#xff0c;是真的有一点&#xff0c;就感觉和自己预期的…...

Unity EventSystem入门

概述 相信在学习Unity中&#xff0c;一定有被UI事件困扰的时候把&#xff0c;当添加UICanvas的时候&#xff0c;Unity会为我们自动添加EventSystem&#xff0c;这个是为什么呢&#xff0c;Unity的UI事件是如何处理的呢&#xff0c;在使用各个UI组件的时候&#xff0c;一定有不…...

第4章 Vim编辑器与Shell命令脚本

第4章 Vim编辑器与Shell命令脚本 1. Vim文本编辑器2. 编写Shell脚本2.2 接收用户的参数2.3 判断用户的参数 3. 流程控制语句3.1 if条件测试语句3.2 for条件循环语句3.3 while条件循环语句3.4 case条件测试语句 4. 计划任务服务程序复习题 1. Vim文本编辑器 Vim编辑器中设置了三…...

javaWeb快速部署到tomcat阿里云服务器

目录 准备 关闭防火墙 配置阿里云安全组 点击控制台 点击导航栏按钮 点击云服务器ECS 点击安全组 点击管理规则 点击手动添加 设置完成 配置web服务 使用yum安装heepd服务 启动httpd服务 查看信息 部署java通过Maven打包好的war包项目 Maven打包项目 上传项目 …...

[MQTT]Mosquitto的內網連接(intranet)和使用者/密碼權限設置

[MQTT | Raspberry Pi]Publish and Subscribe with RSSI Data of Esp32 on Intranet 延續[MQTT]Mosquitto的簡介、安裝與連接測試文章&#xff0c;接著將繼續測試在內網的兩台機器是否也可以完成發佈和訂閱作業。 同一網段的兩台電腦測試: 假設兩台電腦的配置如下: A電腦為發…...

某盾BLACKBOX逆向关键点

需要准备的东西&#xff1a; 1、原JS码 2、AST解混淆码 3、token(来源于JSON) 一、原JS码很好获取&#xff0c;每次页面刷新&#xff0c;混淆的代码都会变&#xff0c;这是正常&#xff0c;以下为部分代码 while (Qooo0) {switch (Qooo0) {case 110 14 - 55: {function O0…...

【2024全国青少年信息素养大赛初赛时间以及模拟题】

2024全国青少年信息素养大赛时间已经出来了 目录 全国青少年信息素养大赛智能算法挑战赛初中模拟卷 全国青少年信息素养大赛智能算法挑战赛初中模拟卷 1、比赛时间和考试内容&#xff1a; 算法创意实践挑战赛初中组于5月19日举行&#xff0c;检录时间为10:30-11:00&#xf…...

2024年软件测试最全jmeter做接口压力测试_jmeter接口性能测试_jmeter压测接口(3),【大牛疯狂教学

既有适合小白学习的零基础资料&#xff0c;也有适合3年以上经验的小伙伴深入学习提升的进阶课程&#xff0c;涵盖了95%以上软件测试知识点&#xff0c;真正体系化&#xff01; 由于文件比较多&#xff0c;这里只是将部分目录截图出来&#xff0c;全套包含大厂面经、学习笔记、…...

LLM——用于微调预训练大型语言模型(LLM)的GPU内存优化与微调

前言 GPT-4、Bloom 和 LLaMA 等大型语言模型&#xff08;LLM&#xff09;通过扩展至数十亿参数&#xff0c;实现了卓越的性能。然而&#xff0c;这些模型因其庞大的内存需求&#xff0c;在部署进行推理或微调时面临挑战。这里将探讨关于内存的优化技术&#xff0c;旨在估计并优…...

Telnet协议:远程控制的基石

目录 1. 概述 2. 工作机制 3. 网络虚拟终端 4. 选项协商 5. 操作方式 6. 用户接口命令 7. 验证的过程 1. 概述 Telnet&#xff08;Telecommunication Network&#xff09;是一种用于在互联网上远程登录到计算机系统的标准协议。它早期被广泛用于远程终端连接&#xff0…...

网络工程师必备:静态路由实验指南

大家好&#xff0c;这里是G-LAB IT实验室。今天带大家学习一下华为静态路由实验配置 01、实验拓扑 02、实验需求 1.R1环回口11,1,1.1模拟PC1 2.R2建立2个环回口模拟Server server-1: 22,1,1.1 server-2: 44.1.1.1 3.要求使用静态路由实现全网互通 PC1去往server-1从R3走…...

springboot利用切面保存操作日志(支持Spring表达式语言(简称SpEL))

springboot利用切面保存操作日志&#xff08;支持Spring表达式语言&#xff08;简称SpEL&#xff09;&#xff09; 文章目录 springboot利用切面保存操作日志&#xff08;支持Spring表达式语言&#xff08;简称SpEL&#xff09;&#xff09;前言一、Spring EL是什么&#xff1f…...

遂宁专业知识付费系统报价,免费网课平台怎么开通?需要哪些条件?

其实&#xff0c;不少的大咖老师都不愿意在大平台上开课&#xff0c;因为学员的留存并不是自己的&#xff0c;所以&#xff0c;很多人也考虑自己开通网课平台&#xff0c;那免费的平台怎么开通?这就是我们今天要跟老师们分享的内容了。 需要哪些条件? 大家如果想要开通免费的…...

【linuxC语言】fcntl和ioctl函数

文章目录 前言一、功能介绍二、具体使用2.1 fcntl函数2.2 ioctl函数三、拓展:填写arg总结前言 在Linux系统编程中,经常会涉及到对文件描述符、套接字以及设备的控制操作。fcntl和ioctl函数就是用来进行这些控制操作的两个重要的系统调用。它们提供了对文件、设备和套接字进行…...

java——继承(一)

一&#xff1a;匿名对象 只能使用一次&#xff0c;每一次使用都会创建一个新的对象&#xff0c;默认值和数组的默认值的规则相同。所以适用于调用一次对象的情况&#xff1a; public class ClassAnonymous {String name;public void show(){System.out.println(name"真厉…...

【Linux】进程间通信方式之管道

&#x1f916;个人主页&#xff1a;晚风相伴-CSDN博客 &#x1f496;如果觉得内容对你有帮助的话&#xff0c;还请给博主一键三连&#xff08;点赞&#x1f49c;、收藏&#x1f9e1;、关注&#x1f49a;&#xff09;吧 &#x1f64f;如果内容有误的话&#xff0c;还望指出&…...

【Linux】yum与vim

文章目录 软件包管理器&#xff1a;yumLinux安装和卸载软件包Linux中的编辑器&#xff1a;vimvim下的底行模式vim下的正常模式vim下的替换模式vim下的视图模式vim下的多线程 软件包管理器&#xff1a;yum yum其实就是一个软件,也可以叫商店 和你手机上的应用商店或app store一…...

苍穹外卖Day06笔记

疯玩了一个月&#xff0c;效率好低&#xff0c;今天开始捡起来苍穹外卖~ 1. 为什么不需要单独引入HttpClient的dependency&#xff1f; 因为我们在sky-common的pom.xml中已经引入了aliyun-sdk-oss的依赖&#xff0c;而这个依赖低层就引入了httpclinet的依赖&#xff0c;根据依…...

Maximo 使用 REST API 创建 Cron Task

接前面几篇文章&#xff0c;我没有了 automation script 以后&#xff0c;有时候需要让其定期自动执行&#xff0c;这时候就可以通过 Cron Task 来实现了。 通过Maximo REST API 来创建 Cron Task request&#xff1a; POST {{base_url}}/api/os/mxapicrontaskdef?apikey{{…...

【镜像仿真篇】磁盘镜像仿真常见错误

【镜像仿真篇】磁盘镜像仿真常见错误 记系统镜像仿真常见错误集—【蘇小沐】 1、实验环境 2023AFS39.E01&#xff08;Windows11系统镜像&#xff09;Arsenal Image Mounter&#xff0c;[v3.10.262]‍Vmware Workstation 17 Pro&#xff0c;[v17.5.1]Windows 11 专业工作站版…...

代码随想录算法训练营DAY45|C++动态规划Part7|70.爬楼梯(进阶版)、322. 零钱兑换、279.完全平方数

文章目录 70.爬楼梯&#xff08;进阶版&#xff09;⭐️322. 零钱兑换思路CPP代码总结 279.完全平方数思路CPP代码 70.爬楼梯&#xff08;进阶版&#xff09; 卡码网&#xff1a;57. 爬楼梯 文章讲解&#xff1a;70.爬楼梯(进阶版) 本题就是典型了完全背包排列问题&#xff0c;…...

Linux(openEuler、CentOS8)企业内网DHCP服务器搭建(固定Mac获取指定IP)

----本实验环境为openEuler系统<以server方式安装>&#xff08;CentOS8基本一致&#xff0c;可参考本文&#xff09;---- 目录 一、知识点二、实验&#xff08;一&#xff09;为服务器配置网卡和IP&#xff08;二&#xff09;为服务器安装DHCP服务软件&#xff08;三&a…...

c#读取hex文件方法,相对来说比较清楚

Hex文件解读_c#读取hex文件-CSDN博客 https://wenku.csdn.net/answer/d67f30cf834c435ca37c3d1ef5e78a62?ops_request_misc%257B%2522request%255Fid%2522%253A%2522171498156816800227423661%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&…...

【ytb数据采集器】按关键词批量爬取视频数据,界面软件更适合文科生!

一、背景介绍 1.1 爬取目标 用Python独立开发的爬虫工具&#xff0c;作用是&#xff1a;通过搜索关键词采集油管的搜索结果&#xff0c;包含14个关键字段&#xff1a;关键词,页码,视频标题,视频id,视频链接,发布时间,视频时长,频道名称,频道id,频道链接,播放数,点赞数,评论数…...

三条命令快速配置Hugging Face

大家好啊&#xff0c;我是董董灿。 本文给出一个配置Hugging Face的方法&#xff0c;让你在国内可快速从Hugging Face上下在模型和各种文件。 1. 什么是 Hugging Face Hugging Face 本身是一家科技公司&#xff0c;专注于自然语言处理&#xff08;NLP&#xff09;和机器学习…...

Python网络编程 03 实验:FTP详解

文章目录 一、小实验FTP程序需求二、项目文件架构三、服务端1、conf/settings.py2、conf/accounts.cgf3、conf/STATUS_CODE.py4、启动文件 bin/ftp_server.py5、core/main.py6、core/server.py 四、客户端1、conf/STATUS_CODE.py2、bin/ftp_client.py 五、在终端操作示例 一、小…...

郑州哪里做网站最好/百度科技有限公司

一、介绍 CSerialIO实现一个对串口读写的类&#xff0c;有一个线程负责处理对串口数据的读写&#xff0c;只需要简单调用CSerialIO提供的API就可以侦听串口的数据。 二、背景 目前网上有同步和异步的串口读写控件&#xff0c;需要用户去定时的或者启动一个线程去读取数据&#…...

嘉定企业网站制作/12345浏览器网址大全

为什么80%的码农都做不了架构师&#xff1f;>>> <script type"text/javascript">var whenReady (function () { //这个函数返回whenReady&#xff08;&#xff09;函数var funcs []; //当获得事件时&#xff0c;要运行的函数var…...

wordpress商业用途/全搜网

首先&#xff0c;我开始遇到的时候&#xff0c;认为是高分辨屏幕的问题&#xff0c;所以修改了屏幕的分辨率&#xff0c;结果并没有改变。 然后&#xff0c;认为是对话窗口的显示&#xff0c;修改字体大小&#xff0c;也没有显示完整。 最后&#xff0c;是修改了ArcGIS的注册表…...

做网站 融资/今天最新的新闻

一&#xff0e;概述 suite套件&#xff0c;就是多个测试的集合&#xff0c;可以同时测试多个测试类。 二&#xff0e;TestSuite的两种用法 在写用法之前&#xff0c;先做点准备工作。 demo.php <?phpclass Demo{public function add($a, $b){return $a $b; } }业务类&a…...

网站建设维护php/查收录网站

目前/ boot partition /文件夹没有足够的空间,无法执行软件更新.问题&#xff1a;我应该如何正确释放该目录中的一些空间&#xff1f;这是列表&#xff1a;rootmindaugas-ubuntu-14:/boot# ls -latotal 156607drwxr-xr-x 4 root root 3072 Kov 12 09:37 .drwxr-xr-x 24 root ro…...

网上北京网站制作公司/百度推广seo是什么意思

想成为一名架构师,但是架构师对应的技能&#xff0c;我应该掌握哪些啊&#xff1f;以及掌握的程度是什么样的&#xff1f;如何成为一名真正的实战性架构师&#xff1f; 我简要分为以下7点来谈谈&#xff0c;从技能的角度抛砖引玉&#xff0c;希望你对你架构师之路有一定的参考。…...