STM32F4xx系列使用HAL库配置SPI-读写FLASH

SPI协议简介

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第1张图片

SPI物理层特点

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第2张图片 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第3张图片 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第4张图片

SPI协议层

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第5张图片

 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第6张图片

 

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第7张图片

 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第8张图片

 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第9张图片

 QSPI协议简介

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第10张图片

 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第11张图片

 

 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第12张图片

 SPI框图

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第13张图片

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

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第14张图片

 SPI配置流程

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

2、使能SPI外设的时钟

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

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

将片选信号拉低

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

将数据写入SPI数据寄存器

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

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

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

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第15张图片

 STM32F4xx系列使用HAL库配置SPI-读写FLASH_第16张图片

 flash 0xAB指令

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第17张图片

 flash 0x90指令

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第18张图片

 注意FLASH地址是24位的

STM32F4xx系列使用HAL库配置SPI-读写FLASH_第19张图片

 这里直接放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
  *
  * 

© Copyright (c) 2022 STMicroelectronics. * All rights reserved.

* * 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
  *
  * 

© Copyright (c) 2022 STMicroelectronics. * All rights reserved.

* * 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
  *
  * 

© Copyright (c) 2022 STMicroelectronics. * All rights reserved.

* * 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 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配置这里就不过多介绍了,感谢大家的阅读!^_^

你可能感兴趣的:(stm32,单片机,arm)