SPI协议简介

SPI物理层特点

  

SPI协议层

QSPI协议简介

SPI框图

Flash写入与EEPROM有点相似,不同的是FLASH写入需要软件手动擦除,而EEPROM不用

SPI配置流程

1、初始化通讯使用的目标引脚及端口时钟

2、使能SPI外设的时钟

3、配置SPI外设的模式、地址、速率等参数并使能SPI外设

4、编写基本SPI按字节收发的函数

将片选信号拉低

查看数据发送完成标志,如果标志不存在

将数据写入SPI数据寄存器

5、编写对FLASH擦除及读写操作的函数

6、编写测试程序,对读写数据进行校验

HAL库读写串行FLASH(W125Q128)实验

flash 0xAB指令

flash 0x90指令

注意FLASH地址是24位的

这里直接放spi_flash的板级支持包,直接调用即可

可以直接将CubeMX生成的源文件spi.c和spi.h替换成下述文件即可

spi.c

/********************************************************************************* @file    spi.c* @brief   This file provides code for the configuration*          of the SPI instances.******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************//* Includes ------------------------------------------------------------------*/
#include "spi.h"/* USER CODE BEGIN 0 *//* USER CODE END 0 */SPI_HandleTypeDef SpiHandle;static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;   static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);/*** @brief SPI MSP Initialization *        This function configures the hardware resources used in this example: *           - Peripheral's clock enable*           - Peripheral's GPIO Configuration  * @param hspi: SPI handle pointer* @retval None*/
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{GPIO_InitTypeDef  GPIO_InitStruct;/*##-1- Enable peripherals and GPIO Clocks #################################*//* Enable GPIO TX/RX clock */SPIx_SCK_GPIO_CLK_ENABLE();SPIx_MISO_GPIO_CLK_ENABLE();SPIx_MOSI_GPIO_CLK_ENABLE();SPIx_CS_GPIO_CLK_ENABLE();/* Enable SPI clock */SPIx_CLK_ENABLE(); /*##-2- Configure peripheral GPIO ##########################################*/  /* SPI SCK GPIO pin configuration  */GPIO_InitStruct.Pin       = SPIx_SCK_PIN;GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull      = GPIO_PULLUP;GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;GPIO_InitStruct.Alternate = SPIx_SCK_AF;HAL_GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStruct);/* SPI MISO GPIO pin configuration  */GPIO_InitStruct.Pin = SPIx_MISO_PIN;GPIO_InitStruct.Alternate = SPIx_MISO_AF;HAL_GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStruct);/* SPI MOSI GPIO pin configuration  */GPIO_InitStruct.Pin = SPIx_MOSI_PIN;GPIO_InitStruct.Alternate = SPIx_MOSI_AF;  HAL_GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStruct);   GPIO_InitStruct.Pin = FLASH_CS_PIN ;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;HAL_GPIO_Init(FLASH_CS_GPIO_PORT, &GPIO_InitStruct);
}void SPI_FLASH_Init(void)
{/*##-1- Configure the SPI peripheral #######################################*//* Set the SPI parameters */SpiHandle.Instance               = SPIx;SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;SpiHandle.Init.Direction         = SPI_DIRECTION_2LINES;SpiHandle.Init.CLKPhase          = SPI_PHASE_2EDGE;SpiHandle.Init.CLKPolarity       = SPI_POLARITY_HIGH;SpiHandle.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;SpiHandle.Init.CRCPolynomial     = 7;SpiHandle.Init.DataSize          = SPI_DATASIZE_8BIT;SpiHandle.Init.FirstBit          = SPI_FIRSTBIT_MSB;SpiHandle.Init.NSS               = SPI_NSS_SOFT;SpiHandle.Init.TIMode            = SPI_TIMODE_DISABLE;SpiHandle.Init.Mode = SPI_MODE_MASTER;HAL_SPI_Init(&SpiHandle); __HAL_SPI_ENABLE(&SpiHandle);
}/*** @brief  擦除FLASH扇区* @param  SectorAddr:要擦除的扇区地址* @retval 无*/
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{/* 发送FLASH写使能命令 */SPI_FLASH_WriteEnable();SPI_FLASH_WaitForWriteEnd();/* 擦除扇区 *//* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 发送扇区擦除指令*/SPI_FLASH_SendByte(W25X_SectorErase);/*发送擦除扇区地址的高位*/SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);/* 发送擦除扇区地址的中位 */SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);/* 发送擦除扇区地址的低位 */SPI_FLASH_SendByte(SectorAddr & 0xFF);/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();/* 等待擦除完毕*/SPI_FLASH_WaitForWriteEnd();
}/*** @brief  擦除FLASH扇区,整片擦除* @param  无* @retval 无*/
void SPI_FLASH_BulkErase(void)
{/* 发送FLASH写使能命令 */SPI_FLASH_WriteEnable();/* 整块 Erase *//* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 发送整块擦除指令*/SPI_FLASH_SendByte(W25X_ChipErase);/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();/* 等待擦除完毕*/SPI_FLASH_WaitForWriteEnd();
}/*** @brief  对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区* @param   pBuffer,要写入数据的指针* @param WriteAddr,写入地址* @param  NumByteToWrite,写入数据长度,必须小于等于SPI_FLASH_PerWritePageSize* @retval 无*/
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{/* 发送FLASH写使能命令 */SPI_FLASH_WriteEnable();/* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 写页写指令*/SPI_FLASH_SendByte(W25X_PageProgram);/*发送写地址的高位*/SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);/*发送写地址的中位*/SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);/*发送写地址的低位*/SPI_FLASH_SendByte(WriteAddr & 0xFF);if(NumByteToWrite > SPI_FLASH_PerWritePageSize){NumByteToWrite = SPI_FLASH_PerWritePageSize;FLASH_ERROR("SPI_FLASH_PageWrite too large!");}/* 写入数据*/while (NumByteToWrite--){/* 发送当前要写入的字节数据 */SPI_FLASH_SendByte(*pBuffer);/* 指向下一字节数据 */pBuffer++;}/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();/* 等待写入完毕*/SPI_FLASH_WaitForWriteEnd();
}/*** @brief  对FLASH写入数据,调用本函数写入数据前需要先擦除扇区* @param pBuffer,要写入数据的指针* @param  WriteAddr,写入地址* @param  NumByteToWrite,写入数据长度* @retval 无*/
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;/*mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0*/Addr = WriteAddr % SPI_FLASH_PageSize;/*差count个数据值,刚好可以对齐到页地址*/count = SPI_FLASH_PageSize - Addr;    /*计算出要写多少整数页*/NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;/*mod运算求余,计算出剩余不满一页的字节数*/NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */if (Addr == 0) {/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0) {SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}else /* NumByteToWrite > SPI_FLASH_PageSize */{/*先把整数页都写了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr +=  SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不满一页的数据,把它写完*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}/* 若地址与 SPI_FLASH_PageSize 不对齐  */else {/* NumByteToWrite < SPI_FLASH_PageSize */if (NumOfPage == 0) {/*当前页剩余的count个位置比NumOfSingle小,写不完*/if (NumOfSingle > count) {temp = NumOfSingle - count;/*先写满当前页*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);WriteAddr +=  count;pBuffer += count;/*再写剩余的数据*/SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);}else /*当前页剩余的count个位置能写完NumOfSingle个数据*/{               SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}}else /* NumByteToWrite > SPI_FLASH_PageSize */{/*地址不对齐多出的count分开处理,不加入这个运算*/NumByteToWrite -= count;NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);WriteAddr +=  count;pBuffer += count;/*把整数页都写了*/while (NumOfPage--){SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);WriteAddr +=  SPI_FLASH_PageSize;pBuffer += SPI_FLASH_PageSize;}/*若有多余的不满一页的数据,把它写完*/if (NumOfSingle != 0){SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}}
}/*** @brief  读取FLASH数据* @param   pBuffer,存储读出数据的指针* @param   ReadAddr,读取地址* @param   NumByteToRead,读取数据长度* @retval 无*/
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{/* 选择FLASH: CS低电平 */SPI_FLASH_CS_LOW();/* 发送 读 指令 */SPI_FLASH_SendByte(W25X_ReadData);/* 发送 读 地址高位 */SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);/* 发送 读 地址中位 */SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);/* 发送 读 地址低位 */SPI_FLASH_SendByte(ReadAddr & 0xFF);/* 读取数据 */while (NumByteToRead--){/* 读取一个字节*/*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);/* 指向下一个字节缓冲区 */pBuffer++;}/* 停止信号 FLASH: CS 高电平 */SPI_FLASH_CS_HIGH();
}/*** @brief  读取FLASH ID* @param  无* @retval FLASH ID*/
uint32_t SPI_FLASH_ReadID(void)
{uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;/* 开始通讯:CS低电平 */SPI_FLASH_CS_LOW();/* 发送JEDEC指令,读取ID */SPI_FLASH_SendByte(W25X_JedecDeviceID);/* 读取一个字节数据 */Temp0 = SPI_FLASH_SendByte(Dummy_Byte);/* 读取一个字节数据 */Temp1 = SPI_FLASH_SendByte(Dummy_Byte);/* 读取一个字节数据 */Temp2 = SPI_FLASH_SendByte(Dummy_Byte);/* 停止通讯:CS高电平 */SPI_FLASH_CS_HIGH();/*把数据组合起来,作为函数的返回值*/Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;return Temp;
}/*** @brief  读取FLASH Device ID* @param   无* @retval FLASH Device ID*/
uint32_t SPI_FLASH_ReadDeviceID(void)
{uint32_t Temp = 0;/* Select the FLASH: Chip Select low */SPI_FLASH_CS_LOW();/* Send "RDID " instruction */SPI_FLASH_SendByte(W25X_DeviceID);SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp = SPI_FLASH_SendByte(Dummy_Byte);/* Deselect the FLASH: Chip Select high */SPI_FLASH_CS_HIGH();return Temp;
}
/*******************************************************************************
* Function Name  : SPI_FLASH_StartReadSequence
* Description    : Initiates a read data byte (READ) sequence from the Flash.
*                  This is done by driving the /CS line low to select the device,
*                  then the READ instruction is transmitted followed by 3 bytes
*                  address. This function exit and keep the /CS line low, so the
*                  Flash still being selected. With this technique the whole
*                  content of the Flash is read with a single READ instruction.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr)
{/* Select the FLASH: Chip Select low */SPI_FLASH_CS_LOW();/* Send "Read from Memory " instruction */SPI_FLASH_SendByte(W25X_ReadData);/* Send the 24-bit address of the address to read from -----------------------*//* Send ReadAddr high nibble address byte */SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);/* Send ReadAddr medium nibble address byte */SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);/* Send ReadAddr low nibble address byte */SPI_FLASH_SendByte(ReadAddr & 0xFF);
}/*** @brief  使用SPI读取一个字节的数据* @param  无* @retval 返回接收到的数据*/
uint8_t SPI_FLASH_ReadByte(void)
{return (SPI_FLASH_SendByte(Dummy_Byte));
}/*** @brief  使用SPI发送一个字节的数据* @param  byte:要发送的数据* @retval 返回接收到的数据*/
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待发送缓冲区为空,TXE事件 */while (__HAL_SPI_GET_FLAG( &SpiHandle, SPI_FLAG_TXE ) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);}/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */WRITE_REG(SpiHandle.Instance->DR, byte);SPITimeout = SPIT_FLAG_TIMEOUT;/* 等待接收缓冲区非空,RXNE事件 */while (__HAL_SPI_GET_FLAG( &SpiHandle, SPI_FLAG_RXNE ) == RESET){//发送缓冲区会在下一个周期将数据发送出去if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);}/* 读取数据寄存器,获取接收缓冲区数据 */return READ_REG(SpiHandle.Instance->DR);
//  static uint8_t Rx_Data[1];
//  static uint8_t Tx_Data[1];
//  Tx_Data[0]=byte;
//  if(HAL_SPI_TransmitReceive(&SpiHandle,Tx_Data,Rx_Data,1,1000)!=HAL_OK)
//  {
//      return 0;
//  }
//  return Rx_Data[0];
}/*******************************************************************************
* Function Name  : SPI_FLASH_SendHalfWord
* Description    : Sends a Half Word through the SPI interface and return the
*                  Half Word received from the SPI bus.
* Input          : Half Word : Half Word to send.
* Output         : None
* Return         : The value of the received Half Word.
*******************************************************************************/
uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord)
{SPITimeout = SPIT_FLAG_TIMEOUT;/* Loop while DR register in not emplty */while (__HAL_SPI_GET_FLAG( &SpiHandle,  SPI_FLAG_TXE ) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);}/* Send Half Word through the SPIx peripheral */WRITE_REG(SpiHandle.Instance->DR, HalfWord);SPITimeout = SPIT_FLAG_TIMEOUT;/* Wait to receive a Half Word */while (__HAL_SPI_GET_FLAG( &SpiHandle, SPI_FLAG_RXNE ) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);}/* Return the Half Word read from the SPI bus */return READ_REG(SpiHandle.Instance->DR);
}/*** @brief  向FLASH发送 写使能 命令* @param  none* @retval none*/
void SPI_FLASH_WriteEnable(void)
{/* 通讯开始:CS低 */SPI_FLASH_CS_LOW();/* 发送写使能命令*/SPI_FLASH_SendByte(W25X_WriteEnable);/*通讯结束:CS高 */SPI_FLASH_CS_HIGH();
}/*** @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕* @param  none* @retval none*/
void SPI_FLASH_WaitForWriteEnd(void)
{uint8_t FLASH_Status = 0;/* 选择 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 发送 读状态寄存器 命令 */SPI_FLASH_SendByte(W25X_ReadStatusReg);SPITimeout = SPIT_FLAG_TIMEOUT;/* 若FLASH忙碌,则等待 */do{/* 读取FLASH芯片的状态寄存器 */FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);  {if((SPITimeout--) == 0) {SPI_TIMEOUT_UserCallback(4);return;}} }while ((FLASH_Status & WIP_Flag) == SET); /* 正在写入标志 *//* 停止信号  FLASH: CS 高 */SPI_FLASH_CS_HIGH();
}//进入掉电模式
void SPI_Flash_PowerDown(void)
{ /* 选择 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 发送 掉电 命令 */SPI_FLASH_SendByte(W25X_PowerDown);/* 停止信号  FLASH: CS 高 */SPI_FLASH_CS_HIGH();
}   //唤醒
void SPI_Flash_WAKEUP(void)
{/*选择 FLASH: CS 低 */SPI_FLASH_CS_LOW();/* 发上 上电 命令 */SPI_FLASH_SendByte(W25X_ReleasePowerDown);/* 停止信号 FLASH: CS 高 */SPI_FLASH_CS_HIGH();                   //等待TRES1
}   /*** @brief  等待超时回调函数* @param  None.* @retval None.*/
static  uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{/* 等待超时后的处理,输出错误信息 */FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);return 0;
}/*********************************************END OF FILE**********************/

spi.h

/********************************************************************************* @file    spi.h* @brief   This file contains all the function prototypes for*          the spi.c file******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __SPI_H__
#define __SPI_H__#ifdef __cplusplus
extern "C" {
#endif/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes *//* USER CODE END Includes */#define  sFLASH_ID                       0XEF4018     //W25Q128//#define SPI_FLASH_PageSize            4096
#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256/* Private define ------------------------------------------------------------*/
/*命令定义-开头*******************************/
#define W25X_WriteEnable              0x06
#define W25X_WriteDisable             0x04
#define W25X_ReadStatusReg          0x05
#define W25X_WriteStatusReg       0x01
#define W25X_ReadData                   0x03
#define W25X_FastReadData             0x0B
#define W25X_FastReadDual             0x3B
#define W25X_PageProgram              0x02
#define W25X_BlockErase               0xD8
#define W25X_SectorErase              0x20
#define W25X_ChipErase                0xC7
#define W25X_PowerDown                0xB9
#define W25X_ReleasePowerDown     0xAB
#define W25X_DeviceID                   0xAB
#define W25X_ManufactDeviceID       0x90
#define W25X_JedecDeviceID          0x9F #define WIP_Flag                  0x01  /* Write In Progress (WIP) flag */
#define Dummy_Byte                0xFF
/*命令定义-结尾*******************************///SPI号及时钟初始化函数
#define SPIx                             SPI1
#define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
#define SPIx_SCK_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPIx_MISO_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPIx_MOSI_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPIx_CS_GPIO_CLK_ENABLE()        __HAL_RCC_GPIOG_CLK_ENABLE() #define SPIx_FORCE_RESET()               __HAL_RCC_SPI1_FORCE_RESET()
#define SPIx_RELEASE_RESET()             __HAL_RCC_SPI1_RELEASE_RESET()//SCK引脚
#define SPIx_SCK_PIN                     GPIO_PIN_3
#define SPIx_SCK_GPIO_PORT               GPIOB
#define SPIx_SCK_AF                      GPIO_AF5_SPI1
//MISO引脚
#define SPIx_MISO_PIN                    GPIO_PIN_4
#define SPIx_MISO_GPIO_PORT              GPIOB
#define SPIx_MISO_AF                     GPIO_AF5_SPI1
//MOSI引脚
#define SPIx_MOSI_PIN                    GPIO_PIN_5
#define SPIx_MOSI_GPIO_PORT              GPIOB
#define SPIx_MOSI_AF                     GPIO_AF5_SPI1
//CS(NSS)引脚
#define FLASH_CS_PIN                     GPIO_PIN_6
#define FLASH_CS_GPIO_PORT               GPIOG
//设置为高电平
#define digitalHi(p,i)              {p->BSRR=i;}
//输出低电平
#define digitalLo(p,i)              {p->BSRR=(uint32_t)i << 16;}
#define SPI_FLASH_CS_LOW()      digitalLo(FLASH_CS_GPIO_PORT,FLASH_CS_PIN )
#define SPI_FLASH_CS_HIGH()     digitalHi(FLASH_CS_GPIO_PORT,FLASH_CS_PIN )
/*SPI接口定义-结尾****************************//*等待超时时间*/
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))/*信息输出*/
#define FLASH_DEBUG_ON         1#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{\if(FLASH_DEBUG_ON)\printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\}while(0)void SPI_FLASH_Init(void);
void SPI_FLASH_SectorErase(uint32_t SectorAddr);
void SPI_FLASH_BulkErase(void);
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
uint32_t SPI_FLASH_ReadID(void);
uint32_t SPI_FLASH_ReadDeviceID(void);
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr);
void SPI_Flash_PowerDown(void);
void SPI_Flash_WAKEUP(void);uint8_t SPI_FLASH_ReadByte(void);
uint8_t SPI_FLASH_SendByte(uint8_t byte);
uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord);
void SPI_FLASH_WriteEnable(void);
void SPI_FLASH_WaitForWriteEnd(void);#endif /* __SPI_FLASH_H */

main.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>void SystemClock_Config(void);typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;/* 获取缓冲区的长度 */
#define TxBufferSize1   (countof(TxBuffer1) - 1)
#define RxBufferSize1   (countof(TxBuffer1) - 1)
#define countof(a)      (sizeof(a) / sizeof(*(a)))
#define  BufferSize (countof(Tx_Buffer)-1)
#define  FLASH_WriteAddress     0x00000
#define  FLASH_ReadAddress      FLASH_WriteAddress
#define  FLASH_SectorToErase    FLASH_WriteAddress/* 发送缓冲区初始化 */
uint8_t Tx_Buffer[] = "hello world!";
uint8_t Rx_Buffer[BufferSize];//读取的ID存储位置
__IO uint32_t DeviceID = 0;
__IO uint32_t FlashID = 0;
__IO TestStatus TransferStatus1 = FAILED;
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
/*** @brief  The application entry point.* @retval int*/
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();/* 16M串行flash W25Q128初始化 */SPI_FLASH_Init();MX_USART1_UART_Init();printf("\r\n这是一个16M串行flash(W25Q128)实验(QSPI驱动) \r\n");/* 16M串行flash W25Q128初始化 *//* 获取 Flash Device ID */DeviceID = SPI_FLASH_ReadDeviceID();HAL_Delay( 200 );/* 获取 SPI Flash ID */FlashID = SPI_FLASH_ReadID();printf("\r\nFlashID is 0x%X,  Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID);/* 检验 SPI Flash ID */if (FlashID == sFLASH_ID) { printf("\r\n检测到SPI FLASH W25Q128 !\r\n");/* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */SPI_FLASH_SectorErase(FLASH_SectorToErase);         /* 将发送缓冲区的数据写到flash中 */SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);printf("\r\n写入的数据为:\r\n%s", Tx_Buffer);/* 将刚刚写入的数据读出来放到接收缓冲区中 */SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);printf("\r\n读出的数据为:\r\n%s", Rx_Buffer);/* 检查写入的数据与读出的数据是否相等 */TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);if( PASSED == TransferStatus1 ){    LED_ALLON;printf("\r\n16M串行flash(W25Q128)测试成功!\n\r");}else{        LED2_ON;printf("\r\n16M串行flash(W25Q128)测试失败!\n\r");}}// if (FlashID == sFLASH_ID)else{    LED2_ON;printf("\r\n获取不到 W25Q128 ID!\n\r");}SPI_Flash_PowerDown();  while (1){}}TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{while(BufferLength--){if(*pBuffer1 != *pBuffer2){return FAILED;}pBuffer1++;pBuffer2++;}return PASSED;
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 25;RCC_OscInitStruct.PLL.PLLN = 336;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

LED和串口UART配置这里就不过多介绍了,感谢大家的阅读!^_^

STM32F4xx系列使用HAL库配置SPI-读写FLASH相关推荐

  1. STM32F1系列HAL库配置系统时钟

    STM32F1系列HAL库配置系统时钟 其实一开始对于时钟我也是知之甚少,在MSP432中我就一直忽视时钟配置,其实也是在STM32学习时落下的病根,现在趁有空补一下. 时钟简单讲解 对于时钟系统,在 ...

  2. STM32F4系列HAL库配置定时器实验——输入捕获

    STM32F4系列HAL库配置定时器实验--输入捕获 输入捕获简单讲解 输入捕获模式可以用来测量脉冲宽度或者测量频率.我们以测量周期和频率为例,用一个简图来说明输入捕获的原理 假定定时器工作在向上计数 ...

  3. 单片机:STM32F4x HAL库软硬SPI驱动ST7735s 1.8寸LCD屏幕

    单片机:STM32F4x HAL库软硬SPI驱动ST7735s 1.8寸LCD屏幕 说明:此篇为学习记录.可能存在错误或者不足.如有问题请指出. 硬件环境 主控芯片:STM32F411CEU6 主控开 ...

  4. STM32 HAL库 UART 串口读写功能笔记

    https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...

  5. 简单的记录一下使用HAL库的SPI外挂W25Q32

    简单的记录一下使用HAL库的SPI外挂W25Q32 抽筋了,想记录一下. cubeMX配置SPI CS脚 spi.h 里添加 #define FLASH_ID 0XEF14//指令表#define W ...

  6. STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片

    STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片 STM32基础工程生成 首先使用STM32CUBEMX生成STM32的基础工程,配置时钟到7 ...

  7. HAL库配置GPIO

    HAL库配置GPIO HAL库与标准库不同的特点: 使用`CubeMX`配置GPIO底层参数: 总结HAL库中GPIO的相关功能: GPIO的寄存器: 总结 HAL库与标准库不同的特点: 标准库中初始 ...

  8. HAL库配置FreeRTOS

    HAL库配置 配置时钟源 1:修改HAL库定时器时钟源. 由于HAL库内部会使用systick定时器用于系统延时功能,而FreeRTOS也需要一个定时器用于操作系统内核调度的使用,顾需修改HAL库的时 ...

  9. 《STM32从零开始学习历程》——SPI读写FLASH

    <STM32从零开始学习历程>@EnzoReventon SPI读写FLASH 相关链接: SPI物理层及FLASH芯片介绍 SPI协议层 SPI特性及架构 参考资料: [野火EmbedF ...

最新文章

  1. python找不到tushare_python tushare安装
  2. 【算法】《algorithm-note》算法笔记中文版正式发布!
  3. 《玩转微信6.0》一1.2 微信初体验
  4. EPOLL事件之EPOLLRDHUP
  5. leetcode1438. 绝对差不超过限制的最长连续子数组
  6. CodeForces 799B (B) T-shirt buying
  7. MySQL substring-index_mysql函数之SUBSTRING_INDEX(str,/,-1)
  8. 魅族回应“手机无法拨打120”一事:已安排负责人与客户沟通处理
  9. oracle数据库给用户解锁和修改密码和提升权限的命令
  10. [文摘20090622]HP大中华区总裁孙振耀退休十五天后九大感言
  11. 中兴java笔试题_中兴Java开发笔试题目及答案(7)
  12. vector的初始化
  13. PostgreSQL12.2-CN-v1.0中文手册.chm下载
  14. php如何取视频缩略图,视频提取缩略图
  15. Swiper参数说明(swiper参数配置)
  16. 通配符的匹配很全面, 但无法找到元素 ‘aop:aspectj-autoproxy‘ 的声明
  17. Data Modle:游戏对战模型
  18. 3、(三)外汇学习基础篇之银行间外汇即期交易
  19. splite()方法的使用
  20. [运动规划算法]Fast-tracker分析

热门文章

  1. Loan Repayment//二分//排位3
  2. 贪心算法实现最大收益
  3. 前端开发练习——包含了计时功能的动画时钟
  4. Matlab直方图显示y值
  5. [ Linux ] 零散文件系统安装各种命令和软件的方法
  6. Krita学习——Qt学习笔记(一)
  7. 对正在准备2022年哈尔滨工程大学计算机类考研复试的考生的一些建议
  8. 剖析SPDK读写NVMe盘过程--从hello_world开始
  9. JetBrains Account验证时JetBrains账户连接错误:连接拒绝(连接拒绝) 或 用于签署许可证的证书不是由JetBrains root certifi签署的。
  10. WebSocket connection to ‘wss://xxx.com’ failed: Error in NET::ERR_SSL_OBSOLETE_VERSION