个人网站免费建设,苏州艺术家网站建设,seo快速排名工具,如何给网站增加图标一、 SPI通信驱动W25Qxx
1、使用驱动文件快速配置工程代码驱动W25Qxx
#xff08;此驱动文件只适合W25Qxx 16M及以下型号#xff0c;因为访问地址位数不同#xff09;
注#xff1a;本次使用SPI的方式进行访问W25Qxx Flash进行数据读写#xff0c;关于W25Qxx芯片不会做…一、 SPI通信驱动W25Qxx
1、使用驱动文件快速配置工程代码驱动W25Qxx
此驱动文件只适合W25Qxx 16M及以下型号因为访问地址位数不同
注本次使用SPI的方式进行访问W25Qxx Flash进行数据读写关于W25Qxx芯片不会做介绍只在于如何配置代码使其能使用该芯片 关于SPI想使用CubeMx的方式配置代码解以参考我的文章– 4、HAL库SPI数据收发 编写好的驱动文件下载 链接https://pan.baidu.com/s/1r0JCrUNAHt6sGJ6D_tT0lg 提取码fxzn
W25Qxx驱动文件(SPI通信 SCK MOSI MISO CS) 使用方法 1、添加文件到工程Common_Driver.c、spi_Driver.c、W25Qxx_Driver.c 2、spi_Driver.h文件 修改外设SPI1相应挂载的外设片选引脚 3、spi_Driver.c文件 修改SPI1相应通信线(CLK、MOSI、MISO)的引脚宏 4、W25Qxx_Driver.h文件 修改W25Qxx通信时使用的SPI句柄和外设宏SPIx、SPIxHandle 修改W25Qxx型号对应的宏sFLASH_ID 5、W25Qxx_Driver.h文件 开启调试宏(芯片是否正常,会进行擦除数据写入读出对比数据),调试完毕后屏蔽:_W25Qxx_Debug 6、调用SPI初始化函数MX_SPI1_Init()(此时SPI进入空闲,CS引脚空闲) 7、调用W25Qxx_InitCheck()函数检查芯片(执行完后芯片进入省电模式) 8、完毕
时序说明全双工SPI 线束:SCK|MOSI|MISO CS SPI模式 CPOL CPHA 空闲时 SCK 时钟 采样时刻 0 0 0 低电平 奇数边沿(W25Qxx支持) 1 0 1 低电平 偶数边沿 2 1 0 高电平 奇数边沿 3 1 1 高电平 偶数边沿(W25Qxx支持)当前使用
数据长度8 高位在前 速度配置为PCLK2/2分频 42MHz
注意W25Qxx底层读写采用的直接操作寄存器,所以需要在MX_SPI1_Init()函数中的HAL_SPI_Init()后调用__HAL_SPI_ENABLE(hspi1); 以此使能外设SPI使用HAL库自带的收发函数不需要此使能 此驱动文件只适合W25Qxx 16M及以下型号因为访问地址位数不同 W25Qxx_Driver.c文件中的所有函数需要先初始化SPI后才可以调用 本代码W25Qxx驱动时序指令代码参考野火教程 驱动源码 Common_Driver.c
/**********************************************************************
*file:常用通用函数
*author:残梦
*date:2023.2.13
*note:
**********************************************************************/
#include Common_Driver.h
#include usart_Driver.h/****************************************************
function:串口重定义
param:void
return:void
date:2023.2.14
note:使用printf时需要此函数并在Keil魔术棒中勾选User MicroLIB库
****************************************************/
int fputc(int ch,FILE *f)
{uint8_t data ch;HAL_UART_Transmit(huart1,data,1,1);return(ch);
}/****************************************************
function:计算数据的拟合系数
param:*pA,*pB--系数x[],y[]--数据源dataSize--数据个数
return:void
date:2021.11.6
note:yAxB
****************************************************/
void LinearFitCoefficient(double *pA,double *pB,double x[],double y[],unsigned short int dataSize)
{unsigned short int i 0;double AverX 0.0f,AverY 0.0f,a1 0.0f,a2 0.0f;if(dataSize 0){*pA *pB 0.0;return;}else if(dataSize 1){*pA 0.0;*pB y[0];return;}while(i dataSize) {AverX x[i];AverY y[i];i;}AverX / (double)(dataSize);AverY / (double)(dataSize);a1 a2 0.0f;for(i0;idataSize;i){a1 (x[i] - AverX)*(y[i] - AverY);a2 (x[i] - AverX)*(x[i] - AverX);}*pA a1/a2;*pB AverY - (*pA)*AverX;
}/****************************************************
function:系统错误死循环显示信息
param:void
return:void
date:2023.2.14
note:使用printf时需要此函数并在Keil魔术棒中勾选User MicroLIB库
****************************************************/
void FunctionError(char *str)
{while(1){if(str ! NULL){printf(%s,str);}HAL_Delay(500);}
}/****************************************************
function:比较两个缓冲区中的数据是否相等
param:stringA、stringB--待比较的字符串指针Length--字符串待比较的长度不得大于stringA或stringB的长度
return:-1--不相等0--相等
date:2023.2.17
note:
****************************************************/
int StringCompare(uint8_t* stringA, uint8_t* stringB, uint16_t Length)
{while(Length--){if(*stringA ! *stringB){return -1;}stringA;stringB;}return 0;
}/****************************************************
function:计数延时
param:nCount--待延时的大小
return:void
date:2023.2.17
note:
****************************************************/
void EmptyDelay(uint32_t nCount)
{while(nCount--);
}
Common_Driver.h
#ifndef _Common_Driver_H_
#define _Common_Driver_H_
#include main.h
#include stdio.h
#include string.htypedef int32_t s32;
typedef int16_t s16;
typedef int8_t s8;typedef const int32_t sc32;
typedef const int16_t sc16;
typedef const int8_t sc8; typedef __IO int32_t vs32;
typedef __IO int16_t vs16;
typedef __IO int8_t vs8;typedef __I int32_t vsc32;
typedef __I int16_t vsc16;
typedef __I int8_t vsc8; typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;typedef const uint32_t uc32;
typedef const uint16_t uc16;
typedef const uint8_t uc8; typedef __IO uint32_t vu32;
typedef __IO uint16_t vu16;
typedef __IO uint8_t vu8;typedef __I uint32_t vuc32;
typedef __I uint16_t vuc16;
typedef __I uint8_t vuc8; #define INVALID 0
#define VALID 1
#define RETURN_NORMAL INVALID
#define RETURN_ABNORMAL -1
#define PI 3.14159265358979323846#define _BOOL(x) (x?VALID:INVALID)
#define _SET_PIN(GPIOx,Pin) GPIOx-BSRR Pin //pin set 1
#define _RESET_PIN(GPIOx,Pin) GPIOx-BSRR ((uint32_t)Pin 16u) //pin set 0typedef struct
{unsigned char byte1;unsigned char byte2;unsigned char byte3;unsigned char byte4;
}Byte4_MemoryParameterStructDef;typedef struct
{unsigned char byte1;unsigned char byte2;
}Byte2_MemoryParameterStructDef;typedef union
{short int Value;Byte2_MemoryParameterStructDef Memory;
}Convert_ShortIntParameter_UnionDef;typedef union
{unsigned short int Value;Byte2_MemoryParameterStructDef Memory;
}Convert_UnsignedShortIntParameter_UnionDef;typedef union
{unsigned long int Value;Byte4_MemoryParameterStructDef Memory;
}Convert_UnsignedLongIntParameter_UnionDef;typedef union
{float Value;Byte4_MemoryParameterStructDef Memory;
}Convert_FloatParameter_UnionDef;extern void LinearFitCoefficient(double *pA,double *pB,double x[],double y[],unsigned short int dataSize);
extern void FunctionError(char *str);
extern int StringCompare(uint8_t* stringA, uint8_t* stringB, uint16_t Length);
extern void EmptyDelay(uint32_t nCount);#endif
spi_Driver.c
/**********************************************************************
*file:spi驱动文件
*author:残梦
*date:2023.2.16
*note:
**********************************************************************/
#include spi_Driver.h#define SPI1_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPI1_CLK_GPIO_PORT GPIOB
#define SPI1_CLK_GPIO_PIN GPIO_PIN_3
#define SPI1_CLK_GPIO_AF GPIO_AF5_SPI1#define SPI1_MOSI_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPI1_MOSI_GPIO_PORT GPIOB
#define SPI1_MOSI_GPIO_PIN GPIO_PIN_5
#define SPI1_MOSI_GPIO_AF GPIO_AF5_SPI1#define SPI1_MISO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPI1_MISO_GPIO_PORT GPIOB
#define SPI1_MISO_GPIO_PIN GPIO_PIN_4
#define SPI1_MISO_GPIO_AF GPIO_AF5_SPI1SPI_HandleTypeDef hspi1;
/****************************************************
function:SPI1初始化
param:void
return:void
date:2023.2.16
note:时序说明全双工SPI 线束:SCK|MOSI|MISO CSSPI模式 CPOL CPHA 空闲时 SCK 时钟 采样时刻0 0 0 低电平 奇数边沿(W25Qxx支持)1 0 1 低电平 偶数边沿2 1 0 高电平 奇数边沿3 1 1 高电平 偶数边沿(W25Qxx支持)当前使用数据长度8高位在前速度配置为PCLK2/2分频 42MHz注意W25Qxx底层读写采用的直接操作寄存器,所以需要在MX_SPI1_Init()函数中的HAL_SPI_Init()后调用__HAL_SPI_ENABLE(hspi1);以此使能外设SPI使用HAL库自带的收发函数不需要此使能
****************************************************/
void MX_SPI1_Init(void)
{hspi1.Instance SPI1;hspi1.Init.Mode SPI_MODE_MASTER;hspi1.Init.Direction SPI_DIRECTION_2LINES;hspi1.Init.DataSize SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity SPI_POLARITY_HIGH;hspi1.Init.CLKPhase SPI_PHASE_2EDGE;hspi1.Init.NSS SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_2;hspi1.Init.FirstBit SPI_FIRSTBIT_MSB;hspi1.Init.TIMode SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE;hspi1.Init.CRCPolynomial 7;if(HAL_SPI_Init(hspi1) ! HAL_OK){Error_Handler();}__HAL_SPI_ENABLE(hspi1);//W25Qxx底层读写采用的直接操作寄存器,所以需要在MX_SPI1_Init()函数中的HAL_SPI_Init()后调用__HAL_SPI_ENABLE(hspi1),以此使能外设SPI使用HAL库自带的收发函数不需要此使能
}/****************************************************
function:SPI1 GPIO、NVIC、CLOCK初始化
param:void
return:void
date:2023.2.16
note:
****************************************************/
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{GPIO_InitTypeDef GPIO_InitStruct {0};if(spiHandle-Instance SPI1){__HAL_RCC_SPI1_CLK_ENABLE();SPI1_CLK_GPIO_CLK_ENABLE();SPI1_MOSI_GPIO_CLK_ENABLE();SPI1_MISO_GPIO_CLK_ENABLE();SPI1_W25Qxx_CS_GPIO_CLK_ENABLE();GPIO_InitStruct.Pin SPI1_CLK_GPIO_PIN;GPIO_InitStruct.Mode GPIO_MODE_AF_PP;GPIO_InitStruct.Pull GPIO_PULLUP;GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate SPI1_CLK_GPIO_AF;HAL_GPIO_Init(SPI1_CLK_GPIO_PORT,GPIO_InitStruct);GPIO_InitStruct.Pin SPI1_MOSI_GPIO_PIN;GPIO_InitStruct.Mode GPIO_MODE_AF_PP;GPIO_InitStruct.Pull GPIO_PULLUP;GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate SPI1_MOSI_GPIO_AF;HAL_GPIO_Init(SPI1_MOSI_GPIO_PORT,GPIO_InitStruct);GPIO_InitStruct.Pin SPI1_MISO_GPIO_PIN;GPIO_InitStruct.Mode GPIO_MODE_AF_PP;GPIO_InitStruct.Pull GPIO_PULLUP;GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate SPI1_MISO_GPIO_AF;HAL_GPIO_Init(SPI1_MISO_GPIO_PORT,GPIO_InitStruct);GPIO_InitStruct.Pin SPI1_W25Qxx_CS_GPIO_PIN;GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull GPIO_PULLUP;GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate 0;HAL_GPIO_Init(SPI1_W25Qxx_CS_GPIO_PORT,GPIO_InitStruct);}
}
spi_Driver.h
#ifndef _spi_Driver_H_
#define _spi_Driver_H_
#include main.h//外设SPI1相应挂载的外设片选引脚
#define SPI1_W25Qxx_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPI1_W25Qxx_CS_GPIO_PORT GPIOB
#define SPI1_W25Qxx_CS_GPIO_PIN GPIO_PIN_14extern SPI_HandleTypeDef hspi1;extern void MX_SPI1_Init(void);#endif
W25Qxx_Driver.c
/**********************************************************************
*file:W25Qxx驱动文件(SPI通信 SCK MOSI MISO CS)
*author:残梦
*date:2023.2.17
*note:使用方法1、添加文件到工程Common_Driver.c、spi_Driver.c、W25Qxx_Driver.c2、spi_Driver.h文件修改外设SPI1相应挂载的外设片选引脚3、spi_Driver.c文件修改SPI1相应通信线(CLK、MOSI、MISO)的引脚宏4、W25Qxx_Driver.h文件修改W25Qxx通信时使用的SPI句柄和外设宏SPIx、SPIxHandle修改W25Qxx型号对应的宏sFLASH_ID5、W25Qxx_Driver.h文件开启调试宏(芯片是否正常,会进行擦除数据写入读出对比数据),调试完毕后屏蔽:_W25Qxx_Debug6、调用SPI初始化函数MX_SPI1_Init()(此时SPI进入空闲,CS引脚空闲)7、调用W25Qxx_InitCheck()函数检查芯片(执行完后芯片进入省电模式)8、完毕时序说明全双工SPI 线束:SCK|MOSI|MISO CSSPI模式 CPOL CPHA 空闲时 SCK 时钟 采样时刻0 0 0 低电平 奇数边沿(W25Qxx支持)1 0 1 低电平 偶数边沿2 1 0 高电平 奇数边沿3 1 1 高电平 偶数边沿(W25Qxx支持)当前使用数据长度8高位在前速度配置为PCLK2/2分频 42MHz注意W25Qxx底层读写采用的直接操作寄存器,所以需要在MX_SPI1_Init()函数中的HAL_SPI_Init()后调用__HAL_SPI_ENABLE(hspi1);以此使能外设SPI使用HAL库自带的收发函数不需要此使能此驱动文件只适合W25Qxx 16M及以下型号因为访问地址位数不同W25Qxx_Driver.c文件中的所有函数需要先初始化SPI后才可以调用
**********************************************************************/
#include W25Qxx_Driver.h
#include spi_Driver.h#define W25Qxx_CS_LOW() _RESET_PIN(SPI1_W25Qxx_CS_GPIO_PORT,SPI1_W25Qxx_CS_GPIO_PIN)
#define W25Qxx_CS_HIGH() _SET_PIN(SPI1_W25Qxx_CS_GPIO_PORT,SPI1_W25Qxx_CS_GPIO_PIN)static __IO uint32_t SPITimeout SPIT_LONG_TIMEOUT;static uint8_t W25Qxx_ReadByte(void);
static uint8_t W25Qxx_SendByte(uint8_t byte);
static uint16_t W25Qxx_SendHalfWord(uint16_t HalfWord);
static void W25Qxx_WriteEnable(void);
static void W25Qxx_WaitForWriteEnd(void);
static uint16_t W25Qxx_SPI_TIMEOUT_UserCallback(uint8_t errorCode);/****************************************************
function:W25Q擦除FLASH扇区
param:SectorAddr要擦除的扇区地址
return:void
date:2023.2.17
note:
****************************************************/
void W25Qxx_SectorErase(uint32_t SectorAddr)
{/* 发送FLASH写使能命令 */W25Qxx_WriteEnable();W25Qxx_WaitForWriteEnd();/* 擦除扇区 *//* 选择FLASH: CS低电平 */W25Qxx_CS_LOW();/* 发送扇区擦除指令*/W25Qxx_SendByte(W25X_SectorErase);/*发送擦除扇区地址的高位*/W25Qxx_SendByte((SectorAddr 0xFF0000) 16);/* 发送擦除扇区地址的中位 */W25Qxx_SendByte((SectorAddr 0xFF00) 8);/* 发送擦除扇区地址的低位 */W25Qxx_SendByte(SectorAddr 0xFF);/* 停止信号 FLASH: CS 高电平 */W25Qxx_CS_HIGH();/* 等待擦除完毕*/W25Qxx_WaitForWriteEnd();
}/****************************************************
function:W25Q擦除FLASH扇区整片擦除
param:void
return:void
date:2023.2.17
note:
****************************************************/
void W25Qxx_BulkErase(void)
{/* 发送FLASH写使能命令 */W25Qxx_WriteEnable();/* 整块 Erase *//* 选择FLASH: CS低电平 */W25Qxx_CS_LOW();/* 发送整块擦除指令*/W25Qxx_SendByte(W25X_ChipErase);/* 停止信号 FLASH: CS 高电平 */W25Qxx_CS_HIGH();/* 等待擦除完毕*/W25Qxx_WaitForWriteEnd();
}/****************************************************
function:对FLASH按页写入数据调用本函数写入数据前需要先擦除扇区
param: pBuffer要写入数据的指针WriteAddr写入地址NumByteToWrite写入数据长度必须小于等于W25Qxx_PerWritePageSize
return:void
date:2023.2.17
note:
****************************************************/
void W25Qxx_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{/* 发送FLASH写使能命令 */W25Qxx_WriteEnable();/* 选择FLASH: CS低电平 */W25Qxx_CS_LOW();/* 写页写指令*/W25Qxx_SendByte(W25X_PageProgram);/*发送写地址的高位*/W25Qxx_SendByte((WriteAddr 0xFF0000) 16);/*发送写地址的中位*/W25Qxx_SendByte((WriteAddr 0xFF00) 8);/*发送写地址的低位*/W25Qxx_SendByte(WriteAddr 0xFF);if(NumByteToWrite W25Qxx_PerWritePageSize){NumByteToWrite W25Qxx_PerWritePageSize;FLASH_ERROR(W25Qxx_PageWrite too large!);}/* 写入数据*/while (NumByteToWrite--){/* 发送当前要写入的字节数据 */W25Qxx_SendByte(*pBuffer);/* 指向下一字节数据 */pBuffer;}/* 停止信号 FLASH: CS 高电平 */W25Qxx_CS_HIGH();/* 等待写入完毕*/W25Qxx_WaitForWriteEnd();
}/****************************************************
function:对FLASH写入数据调用本函数写入数据前需要先擦除扇区
param: pBuffer要写入数据的指针WriteAddr写入地址NumByteToWrite写入数据长度
return:void
date:2023.2.17
note:
****************************************************/
void W25Qxx_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint8_t NumOfPage 0, NumOfSingle 0, Addr 0, count 0, temp 0;/*mod运算求余若writeAddr是W25Qxx_PageSize整数倍运算结果Addr值为0*/Addr WriteAddr % W25Qxx_PageSize;/*差count个数据值刚好可以对齐到页地址*/count W25Qxx_PageSize - Addr; /*计算出要写多少整数页*/NumOfPage NumByteToWrite / W25Qxx_PageSize;/*mod运算求余计算出剩余不满一页的字节数*/NumOfSingle NumByteToWrite % W25Qxx_PageSize;/* Addr0,则WriteAddr 刚好按页对齐 aligned */if (Addr 0) {/* NumByteToWrite W25Qxx_PageSize */if (NumOfPage 0) {W25Qxx_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}else /* NumByteToWrite W25Qxx_PageSize */{/*先把整数页都写了*/while (NumOfPage--){W25Qxx_PageWrite(pBuffer, WriteAddr, W25Qxx_PageSize);WriteAddr W25Qxx_PageSize;pBuffer W25Qxx_PageSize;}/*若有多余的不满一页的数据把它写完*/W25Qxx_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}/* 若地址与 W25Qxx_PageSize 不对齐 */else {/* NumByteToWrite W25Qxx_PageSize */if (NumOfPage 0) {/*当前页剩余的count个位置比NumOfSingle小写不完*/if (NumOfSingle count) {temp NumOfSingle - count;/*先写满当前页*/W25Qxx_PageWrite(pBuffer, WriteAddr, count);WriteAddr count;pBuffer count;/*再写剩余的数据*/W25Qxx_PageWrite(pBuffer, WriteAddr, temp);}else /*当前页剩余的count个位置能写完NumOfSingle个数据*/{ W25Qxx_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}}else /* NumByteToWrite W25Qxx_PageSize */{/*地址不对齐多出的count分开处理不加入这个运算*/NumByteToWrite - count;NumOfPage NumByteToWrite / W25Qxx_PageSize;NumOfSingle NumByteToWrite % W25Qxx_PageSize;W25Qxx_PageWrite(pBuffer, WriteAddr, count);WriteAddr count;pBuffer count;/*把整数页都写了*/while (NumOfPage--){W25Qxx_PageWrite(pBuffer, WriteAddr, W25Qxx_PageSize);WriteAddr W25Qxx_PageSize;pBuffer W25Qxx_PageSize;}/*若有多余的不满一页的数据把它写完*/if (NumOfSingle ! 0){W25Qxx_PageWrite(pBuffer, WriteAddr, NumOfSingle);}}}
}/****************************************************
function:读取FLASH数据
param: pBuffer存储读出数据的指针ReadAddr读取地址NumByteToRead读取数据长度
return:void
date:2023.2.17
note:
****************************************************/
void W25Qxx_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{/* 选择FLASH: CS低电平 */W25Qxx_CS_LOW();/* 发送 读 指令 */W25Qxx_SendByte(W25X_ReadData);/* 发送 读 地址高位 */W25Qxx_SendByte((ReadAddr 0xFF0000) 16);/* 发送 读 地址中位 */W25Qxx_SendByte((ReadAddr 0xFF00) 8);/* 发送 读 地址低位 */W25Qxx_SendByte(ReadAddr 0xFF);/* 读取数据 */while (NumByteToRead--){/* 读取一个字节*/*pBuffer W25Qxx_SendByte(Dummy_Byte);/* 指向下一个字节缓冲区 */pBuffer;}/* 停止信号 FLASH: CS 高电平 */W25Qxx_CS_HIGH();
}/****************************************************
function:读取FLASH ID
param:void
return:FLASH ID
date:2023.2.17
note:
****************************************************/
uint32_t W25Qxx_ReadID(void)
{uint32_t Temp 0, Temp0 0, Temp1 0, Temp2 0;/* 开始通讯CS低电平 */W25Qxx_CS_LOW();/* 发送JEDEC指令读取ID */W25Qxx_SendByte(W25X_JedecDeviceID);/* 读取一个字节数据 */Temp0 W25Qxx_SendByte(Dummy_Byte);/* 读取一个字节数据 */Temp1 W25Qxx_SendByte(Dummy_Byte);/* 读取一个字节数据 */Temp2 W25Qxx_SendByte(Dummy_Byte);/* 停止通讯CS高电平 */W25Qxx_CS_HIGH();/*把数据组合起来作为函数的返回值*/Temp (Temp0 16) | (Temp1 8) | Temp2;return Temp;
}/****************************************************
function:读取FLASH Device ID
param:void
return:FLASH Device ID
date:2023.2.17
note:
****************************************************/
uint32_t W25Qxx_ReadDeviceID(void)
{uint32_t Temp 0;/* Select the FLASH: Chip Select low */W25Qxx_CS_LOW();/* Send RDID instruction */W25Qxx_SendByte(W25X_DeviceID);W25Qxx_SendByte(Dummy_Byte);W25Qxx_SendByte(Dummy_Byte);W25Qxx_SendByte(Dummy_Byte);/* Read a byte from the FLASH */Temp W25Qxx_SendByte(Dummy_Byte);/* Deselect the FLASH: Chip Select high */W25Qxx_CS_HIGH();return Temp;
}/****************************************************
function:W25Qxx_StartReadSequence
param:ReadAddr : FLASHs internal address to read from.
return:void
date:2023.2.17
note: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.
****************************************************/
void W25Qxx_StartReadSequence(uint32_t ReadAddr)
{/* Select the FLASH: Chip Select low */W25Qxx_CS_LOW();/* Send Read from Memory instruction */W25Qxx_SendByte(W25X_ReadData);/* Send the 24-bit address of the address to read from -----------------------*//* Send ReadAddr high nibble address byte */W25Qxx_SendByte((ReadAddr 0xFF0000) 16);/* Send ReadAddr medium nibble address byte */W25Qxx_SendByte((ReadAddr 0xFF00) 8);/* Send ReadAddr low nibble address byte */W25Qxx_SendByte(ReadAddr 0xFF);
}/****************************************************
function:使用SPI读取一个字节的数据
param:void
return:返回接收到的数据
date:2023.2.17
note:
****************************************************/
static uint8_t W25Qxx_ReadByte(void)
{return (W25Qxx_SendByte(Dummy_Byte));
}/****************************************************
function:使用SPI发送一个字节的数据
param:byte要发送的数据
return:返回接收到的数据
date:2023.2.17
note:
****************************************************/
static uint8_t W25Qxx_SendByte(uint8_t byte)
{SPITimeout SPIT_FLAG_TIMEOUT;/* 等待发送缓冲区为空TXE事件 */while (__HAL_SPI_GET_FLAG( SPIxHandle, SPI_FLAG_TXE ) RESET){if((SPITimeout--) 0) return W25Qxx_SPI_TIMEOUT_UserCallback(0);}/* 写入数据寄存器把要写入的数据写入发送缓冲区 */WRITE_REG(SPIxHandle.Instance-DR, byte);SPITimeout SPIT_FLAG_TIMEOUT;/* 等待接收缓冲区非空RXNE事件 */while (__HAL_SPI_GET_FLAG( SPIxHandle, SPI_FLAG_RXNE ) RESET){if((SPITimeout--) 0) return W25Qxx_SPI_TIMEOUT_UserCallback(1);}/* 读取数据寄存器获取接收缓冲区数据 */return READ_REG(SPIxHandle.Instance-DR);
}/****************************************************
function:W25Qxx_SendHalfWord
param:Half Word : Half Word to send.
return:The value of the received Half Word.
date:2023.2.17
note:Sends a Half Word through the SPI interface and return theHalf Word received from the SPI bus.
****************************************************/
static uint16_t W25Qxx_SendHalfWord(uint16_t HalfWord)
{SPITimeout SPIT_FLAG_TIMEOUT;/* Loop while DR register in not emplty */while (__HAL_SPI_GET_FLAG( SPIxHandle, SPI_FLAG_TXE ) RESET){if((SPITimeout--) 0) return W25Qxx_SPI_TIMEOUT_UserCallback(2);}/* Send Half Word through the SPIx peripheral */WRITE_REG(SPIxHandle.Instance-DR, HalfWord);SPITimeout SPIT_FLAG_TIMEOUT;/* Wait to receive a Half Word */while (__HAL_SPI_GET_FLAG( SPIxHandle, SPI_FLAG_RXNE ) RESET){if((SPITimeout--) 0) return W25Qxx_SPI_TIMEOUT_UserCallback(3);}/* Return the Half Word read from the SPI bus */return READ_REG(SPIxHandle.Instance-DR);
}/****************************************************
function:向FLASH发送 写使能 命令
param:void
return:void
date:2023.2.17
note:
****************************************************/
static void W25Qxx_WriteEnable(void)
{/* 通讯开始CS低 */W25Qxx_CS_LOW();/* 发送写使能命令*/W25Qxx_SendByte(W25X_WriteEnable);/*通讯结束CS高 */W25Qxx_CS_HIGH();
}/****************************************************
function:等待WIP(BUSY)标志被置0即等待到FLASH内部数据写入完毕
param:void
return:void
date:2023.2.17
note:
****************************************************/
static void W25Qxx_WaitForWriteEnd(void)
{uint8_t FLASH_Status 0;/* 选择 FLASH: CS 低 */W25Qxx_CS_LOW();/* 发送 读状态寄存器 命令 */W25Qxx_SendByte(W25X_ReadStatusReg);SPITimeout SPIT_FLAG_TIMEOUT;/* 若FLASH忙碌则等待 */do{/* 读取FLASH芯片的状态寄存器 */FLASH_Status W25Qxx_SendByte(Dummy_Byte); {if((SPITimeout--) 0) {W25Qxx_SPI_TIMEOUT_UserCallback(4);return;}} }while ((FLASH_Status WIP_Flag) SET); /* 正在写入标志 *//* 停止信号 FLASH: CS 高 */W25Qxx_CS_HIGH();
}/****************************************************
function:进入掉电模式
param:void
return:void
date:2023.2.17
note:
****************************************************/
void W25Qxx_PowerDown(void)
{ /* 选择 FLASH: CS 低 */W25Qxx_CS_LOW();/* 发送 掉电 命令 */W25Qxx_SendByte(W25X_PowerDown);/* 停止信号 FLASH: CS 高 */W25Qxx_CS_HIGH();
}/****************************************************
function:唤醒
param:void
return:void
date:2023.2.17
note:
****************************************************/
void W25Qxx_WAKEUP(void)
{/*选择 FLASH: CS 低 */W25Qxx_CS_LOW();/* 发上 上电 命令 */W25Qxx_SendByte(W25X_ReleasePowerDown);/* 停止信号 FLASH: CS 高 */W25Qxx_CS_HIGH(); //等待TRES1
} /****************************************************
function:等待超时回调函数
param:void
return:void
date:2023.2.17
note:
****************************************************/
static uint16_t W25Qxx_SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{/* 等待超时后的处理,输出错误信息 */FLASH_ERROR(SPI 等待超时!errorCode %d,errorCode);return 0;
}/****************************************************
function:计数延时
param:nCount--待延时的大小
return:void
date:2023.2.17
note:
****************************************************/
static void W25Qxx_EmptyDelay(uint32_t nCount)
{while(nCount--);
}/********************以下为测试代码*********************/
#ifdef _W25Qxx_Debug#define FLASH_WriteAddress 0x00000
#define FLASH_ReadAddress FLASH_WriteAddress
#define FLASH_SectorToErase FLASH_WriteAddress/* 获取缓冲区的长度 */
#define TxBufferSize1 (countof(TxBuffer1) - 1)
#define RxBufferSize1 (countof(TxBuffer1) - 1)
#define countof(a) (sizeof(a) / sizeof(*(a)))
#define BufferSize (countof(Tx_Buffer)-1)/****************************************************
function:比较两个缓冲区中的数据是否相等
param:stringA、stringB--待比较的字符串指针Length--字符串待比较的长度不得大于stringA或stringB的长度
return:-1--不相等0--相等
date:2023.2.17
note:
****************************************************/
static int W25Qxx_StringCompare(uint8_t* stringA, uint8_t* stringB, uint16_t Length)
{while(Length--){if(*stringA ! *stringB){return -1;}stringA;stringB;}return 0;
}#endif/****************************************************
function:W25Q初始化
param:void
return:-1--异常0--正常
date:2023.2.17
note:
****************************************************/
int W25Qxx_InitCheck(void)
{
//读取的ID存储位置uint32_t DeviceID 0,FlashID 0;DeviceID W25Qxx_ReadDeviceID();//获取 Flash Device IDW25Qxx_EmptyDelay(200);FlashID W25Qxx_ReadID();//获取 SPI Flash IDprintf(W25Qxx FlashID:0x%0X, Manufacturer Device ID:0x%0X\r\n, FlashID, DeviceID);if (FlashID sFLASH_ID)//检验SPI Flash ID{printf(检测到SPI FLASH W25Q128 !\r\n);#ifdef _W25Qxx_Debug/* 发送缓冲区初始化 */uint8_t Tx_Buffer[] 感谢您选用野火stm32开发板\r\nhttp://firestm32.taobao.com;uint8_t Rx_Buffer[BufferSize];W25Qxx_SectorErase(FLASH_SectorToErase);//擦除将要写入的 SPI FLASH 扇区FLASH写入前要先擦除W25Qxx_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);//将发送缓冲区的数据写到flash中printf(写入的数据为:\r\n%s, Tx_Buffer);W25Qxx_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);//将刚刚写入的数据读出来放到接收缓冲区中printf(读出的数据为:\r\n%s, Rx_Buffer);if(W25Qxx_StringCompare(Tx_Buffer, Rx_Buffer, BufferSize) 0)//检查写入的数据与读出的数据是否相等{printf(16M串行flash(W25Q128)测试失败!\n\r);return -1;}else{printf(16M串行flash(W25Q128)测试成功!\n\r);}#endif}else{printf(获取不到 W25Q128 ID!\n\r);return -1;}W25Qxx_PowerDown();//进入省电模式return 0;
}
W25Qxx_Driver.h
#ifndef _W25Qxx_Driver_H_
#define _W25Qxx_Driver_H_
#include Common_Driver.h
#define _W25Qxx_Debug //未定义--平时无需此代码,定义--芯片是否正常,会进行擦除数据写入读出对比数据#define SPIx SPI1 //使用的SPIx相应外设
#define SPIxHandle hspi1 //使用的spi相应句柄
//#define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET()
//#define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET()/* Private typedef ------选择实际使用的型号-------------------------------------*/
//#define sFLASH_ID 0xEF3015 //W25X16
//#define sFLASH_ID 0xEF4015 //W25Q16
//#define sFLASH_ID 0XEF4017 //W25Q64
#define sFLASH_ID 0XEF4018 //W25Q128//#define W25Qxx_PageSize 4096
#define W25Qxx_PageSize 256
#define W25Qxx_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
/*命令定义-结尾*******************************//*等待超时时间*/
#define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000)//使用HAL库SPI收发函数时超时时间需加大
#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)extern void W25Qxx_Init(void);
extern void W25Qxx_SectorErase(uint32_t SectorAddr);
extern void W25Qxx_BulkErase(void);
extern void W25Qxx_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
extern void W25Qxx_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
extern void W25Qxx_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
extern uint32_t W25Qxx_ReadID(void);
extern uint32_t W25Qxx_ReadDeviceID(void);
extern void W25Qxx_StartReadSequence(uint32_t ReadAddr);
extern void W25Qxx_PowerDown(void);
extern void W25Qxx_WAKEUP(void);
extern int W25Qxx_InitCheck(void);#endif
2、驱动代码笔记说明
1、HAL库和直接使用野火W25Qxx代码时进行SPI读写区别
__HAL_SPI_ENABLE(hspi1);这句是什么作用呢
/** brief Enable the SPI peripheral.* param __HANDLE__ specifies the SPI Handle.* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.* retval None*/
#define __HAL_SPI_ENABLE(__HANDLE__) SET_BIT((__HANDLE__)-Instance-CR1, SPI_CR1_SPE)由此明白了SPE 注意CubeMx配置生成的代码MX_SPI1_Init()函数并没有对SPI外设使能只有在调用读写函数时才进行使能后面会说到 W25Qxx底层读写采用的直接操作寄存器,所以需要在MX_SPI1_Init()函数中的HAL_SPI_Init()后调用__HAL_SPI_ENABLE(hspi1),以此使能外设SPI使用HAL库自带的收发函数不需要此使能语句 比如说这个使用SPI发送一个字节的数据函数
/****************************************************
function:使用SPI发送一个字节的数据
param:byte要发送的数据
return:返回接收到的数据
date:2023.2.17
note:
****************************************************/
static uint8_t W25Qxx_SendByte(uint8_t byte)
{SPITimeout SPIT_FLAG_TIMEOUT;/* 等待发送缓冲区为空TXE事件 */while (__HAL_SPI_GET_FLAG( SPIxHandle, SPI_FLAG_TXE ) RESET){if((SPITimeout--) 0) return W25Qxx_SPI_TIMEOUT_UserCallback(0);}/* 写入数据寄存器把要写入的数据写入发送缓冲区 */WRITE_REG(SPIxHandle.Instance-DR, byte);SPITimeout SPIT_FLAG_TIMEOUT;/* 等待接收缓冲区非空RXNE事件 */while (__HAL_SPI_GET_FLAG( SPIxHandle, SPI_FLAG_RXNE ) RESET){if((SPITimeout--) 0) return W25Qxx_SPI_TIMEOUT_UserCallback(1);}/* 读取数据寄存器获取接收缓冲区数据 */return READ_REG(SPIxHandle.Instance-DR);
}由此可以看出里面是寄存器直接读写但是读写前SPI外设没有进行使能 再来看看HAL自带的读写函数HAL_SPI_TransmitReceive()
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout)
{/* Set the transaction information */hspi-ErrorCode HAL_SPI_ERROR_NONE;hspi-pRxBuffPtr (uint8_t *)pRxData;hspi-RxXferCount Size;hspi-RxXferSize Size;hspi-pTxBuffPtr (uint8_t *)pTxData;hspi-TxXferCount Size;hspi-TxXferSize Size;/*Init field not used in handle to zero */hspi-RxISR NULL;hspi-TxISR NULL;/* Check if the SPI is already enabled */if ((hspi-Instance-CR1 SPI_CR1_SPE) ! SPI_CR1_SPE){/* Enable SPI peripheral */__HAL_SPI_ENABLE(hspi);}}此处可以可以看出该函数内部会自动使能CR1的SPE位注意这个函数使能此SPI外设后不会进行关闭如果不使用__HAL_SPI_ENABLE(hspi1)也可以直接随意调用HAL库的SPI收发轮询函数进行读写开启外设