5、HAL库驱动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:y=Ax+B
****************************************************/
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(i=0;i<dataSize;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.h"typedef 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;/* Addr=0,则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 : FLASH's 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收发轮询函数进行读写开启外设
相关文章:
5、HAL库驱动W25Qxx
一、 SPI通信驱动W25Qxx 1、使用驱动文件快速配置工程代码驱动W25Qxx (此驱动文件只适合W25Qxx 16M及以下型号,因为访问地址位数不同) 注:本次使用SPI的方式进行访问W25Qxx Flash进行数据读写,关于W25Qxx芯片不会做…...
git rebase 洐合(变基)
洐合 把一个分支整合到另一个分支的办法有两种:merge(合并) 和 rebase(衍合) 为什么使用? 使提交记录更简洁 三种情况 第一种: 合并多条commit记录 git rebase -i HEAD~合并数量 HEAD~3&a…...
Kubernetes 1.18学习笔记
文章目录一、Kubernetes 概述和架构1、kubernetes 基本介绍2、Kubernetes 功能3、Kubernetes 架构组件4、Kubernetes 核心概念5、Kubernetes 工作原理二、Kubernetes 集群搭建1、系统环境准备1.1 安装要求1.2 系统初始化2、客户端工具kubeadm搭建2.1 安装步骤2.2 安装组件2.3 集…...
AJAX技术
AJAX技术 浏览器是多进程的,简单的说就是,浏览器每打开一个标签页,就相当于创建了一个独立的浏览器进程。但是js是基于单线程的,而这个线程就是浏览器的js引擎,浏览器无论在什么时候都只且只有一个线程在运行JavaScri…...
华为OD机试 - 最大排列(JS)
最大排列 题目 给定一组整数,重排序后输出一个最大的整数 输入 数字组合 输出 最大的整数 示例一 输入 10 9输出 910解题思路 我们可以读入一个字符串,将字符串中的单词按照每个单词的字典序长度,字典序从大到小的顺序排序&#x…...
Prometheus Docker安装及监控自身
前提环境: Docker环境 涉及参考文档: 安装Prometheus开始 Prometheusnode_exporter Agent组件 一、部署Prometheus 1、启动容器将文件拷贝出来 docker run -d prom/prometheus2、容器将文件拷贝出来 docker cp 容器ID:/usr/share/prometheus/conso…...
点云处理PCL常用函数与工具
点云处理PCL常用函数与工具 文章目录点云处理PCL常用函数与工具前言一、点云读取与保存数据读取数据保存自定义的点云保存格式二、点云显示点云显示-根据颜色点云显示-根据指定轴数值点云显示-根据指定信息显示多组点云显示三、点云滤波直通滤波统计滤波均匀下采样滤波VoxelGri…...
FyListen 在 MVP 架构中的内存优化表现
FyListen 在 MVP 中的内存优化表现 本文只是分享个人开源框架的内存优化测试,你可以直接跳到最后,参考内存泄漏的分析过程! 项目地址: https://github.com/StudyNoteOfTu/fylisten2-alpha1 由于使用到 AOP,所以直接…...
Qt代码单元测试以及报告生成
简介 单元测试是所有测试中最底层的一类测试,是第一个环节,也是最重要的一个环节,是唯一一次有保证能够代码覆盖率达到100%的测试,是整个软件测试过程的基础和前提,单元测试防止了开发的后期因bug过多而失控࿰…...
vscode构建Vue3.0项目(vite,vue-cli)
构建Vue3.0项目构建Vue3.0项目1.使用Vite构建vue项目的方法以及步骤1. 安装vite2. 运行vite vue 项目3.说明2.使用vue-cli构建vue项目的方法以及步骤1.安装全局vue cli —— 脚手架2、VSCode3.报错4.运行构建Vue3.0项目 1.使用Vite构建vue项目的方法以及步骤 1. 安装vite n…...
【2023】华为OD机试真题Java-题目0215-优雅数组
优雅数组 题目描述 如果一个数组中出现次数最多的元素出现大于等于 k k k 次,被称为k-优雅数组, k k k 也可以被称为优雅阈值。 例如,数组[1, 2, 3, 1, 2, 3, 1],它是一个3-优雅数组,因为元素1出现次数大于等于3次...
通过Prowork每日自动提醒待处理工作任务
对于中小团队来说,由于不需要繁琐的流程和高频的异地沟通,需要一款更适合中小团队的日程和项目管理工具。而Prowork就是这样一款敏捷高效的协同平台。Prowork与以往各种项目管理⼯具最⼤的不同在于,其弱化流程和弱化权限的特性,不…...
Linux自定义系统服务
文章目录一. Linux系统服务二. 自定义系统服务一. Linux系统服务 Linux 系统服务有时也称为守护程序,是在Linux启动时自动加载并在Linux退出时自动停止的系统任务,CentOS 7.x开始,CentOS开始使用 systemd服务来代替 daemon ,原来…...
mongodb lambda 查询插件
需求背景需要一个像mybatis plus 一样的基于lambda, 且面向对象的查询mongo数据的插件。在网上找了很久,没有发现有类似功能的插件。于是自己手写了一个,借助mongoTemplate屏蔽了底层查询语句的实现细节。在此基础上,实现了查询的统一封装。技…...
C++设计模式(16)——责任链模式
亦称: 职责链模式、命令链、CoR、Chain of Command、Chain of Responsibility 意图 责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理…...
springmvc+jsp电影院购票售票选座推荐网站java ssm
本电影购票推荐网站以SSM作为框架,B/S模式以及MySql作为后台运行的数据库。本系统主要包括以下功能模块:个人中心、用户管理、电影信息管理、电影类型管理、影院信息管理、系统管理、订单管理等模块,通过这些模块的实现能够基本满足日常电影购…...
ASEMI高压MOS管4N65SE,4N65SE参数,4N65SE特征
编辑-Z ASEMI高压MOS管4N65SE参数: 型号:4N65SE 漏极-源极电压(VDS):650V 栅源电压(VGS):30V 漏极电流(ID):4A 功耗(PD…...
第46章 自定义静态与数据库动态授权依赖注入的定义实现
1 数据库动态授权表授权原理 2 准备工作 2.1 重构Program.cs using Framework.Infrastructure.Extensions; var builder WebApplication.CreateBuilder(args); //如果启动项中不存在“appsettings.json”文件,则通过.Net(Core)的内置方法自动新建“appsettings.…...
Go语言面试题
请解释 Go 语言中的 goroutine 是什么。请解释 Go 语言中的 channel 是什么,并举例说明它的用途。请解释 Go 语言中的 interface 是什么,并举例说明它的用途。请解释 Go 语言中的 map 和 slice 是什么,并举例说明它们的用途。请解释 Go 语言中…...
Kubernetes入门级教程
Kubernetes入门级教程1. Introduction1.1 概述1.2 关键字介绍2. Cluster Install2.1 Big Data -- Postgres3. 基础知识3.1 Pod3.2 控制器3.3 通讯模式3.4 服务发现4. Command4.0 编辑文件4.1 在宿主机执行命令4.2 创建资源对象4.3 查询资源对象4.4 查询资源描述4.5 修改资源4.6…...
15个顶级思维模型
今天给大家分享几个思维模型,无论是数分、数开还是其他岗位,都会有所启发。 查理芒格认为,每个学科都是从一个独特的角度去切入了解这个世界,都是一个摸象的瞎子。 要超越普通人的认知决策,就必须掌握多个核心思维模…...
外贸谷歌优化,外贸google SEO优化费用是多少?
本文主要分享关于做外贸网站的谷歌seo成本到底需要投入多少这一件事。 本文由光算创作,有可能会被剽窃和修改,我们佛系对待这种行为吧。 那么外贸google SEO优化费用是多少? 答案是:2w~25w。 好,看到这个答案先别激…...
华为OD机试 - 统计匹配的二元组个数(Python) | 机试题算法思路
最近更新的博客 华为OD机试 - 招聘(Python) | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 五键键盘 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 热点网络统计 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 路灯照明 | 备考思路,刷题要点,答疑 【新解…...
Java 日志简介
目录1、Slf4j2、Log4j3、LogBack4、Logback 优点5、ELK1、Slf4j slf4j 的全称是 Simple Loging Facade For Java,即它仅仅是一个为 Java 程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如 JDBC 一样,只是一种规则…...
HTTPS协议原理---详解六个加密方案
目录 一、HTTPS 1.加密与解密 2.我们为什么要加密? 3.常见加密方式 ①对称加密 ②非对称加密 4.数据摘要 5.数字签名 二、HTTPS的加密方案 1.只是用对称加密 2.只使用非对称加密 3.双方都使用非对称加密 4.非对称加密+对称加密 中间人攻…...
纯css实现坤坤经典动作-“铁山靠”
背景 2023年2月16日,晴,今天没有工作,一直在掘金摸鱼,摸的我好累。 不行!我得找点有意义的事情做! 此时间,我发的一条沸点竟然有小黑子给我评论,\ 我看到之后气不打一处来ÿ…...
Linux 操作系统原理 — NUMA 体系结构
目录 文章目录 目录NUMA 体系结构NUMA 的基本概念查看 Host 的 NUMA TopologyBash 脚本DPDK 脚步NUMA 体系结构 NUMA(Non-Uniform Memory Access,非一致性存储器访问)的设计理念是将 CPU 和 Main Memory 进行分区自治(Local NUMA node),又可以跨区合作(Remote NUMA nod…...
cesium学习记录01
1,将右弦GISer的cesium实战系列的大部分功能(25-110) 都又跟着走了一遍(大部分是CTRL CCTRL V) 2,代码SVN地址(用户名:liu 密码:123) (如果我没有遗漏上传…...
Linux延时队列工作原理与实现
当进程要获取某些资源(例如从网卡读取数据)的时候,但资源并没有准备好(例如网卡还没接收到数据),这时候内核必须切换到其他进程运行,直到资源准备好再唤醒进程。 waitqueue (等待队列) 就是内核…...
【Python】scipy稀疏矩阵的奇异值分解svds
文章目录基本原理scipy实现测试基本原理 当AAA是方阵时,可以很容易地进行特征分解:AWΣW−1AW\Sigma W^{-1}AWΣW−1,其中Σ\SigmaΣ是AAA的特征值组成的对角矩阵。如果WWW由标准正交基组成,则W−1WTW^{-1}W^TW−1WT,…...
公众号链接的手机网站怎么做/百度seo快排软件
前段时间在哔哩哔哩上刷到了一个资源分享的博主,做一个前端学习资源总结,有兴趣学习或者前端查漏补缺的小伙伴去看看 哔哩哔哩的博主地址:技术胖的个人空间_哔哩哔哩_Bilibili前端知识总结:前端知识图谱B站视频整合: 前端知识图谱…...
网站排队队列怎么做/百度app下载官方
分享一个换肤解决方案参考文章: (1)分享一个换肤解决方案 (2)https://www.cnblogs.com/xie-zhonglai/p/skins.html 备忘一下。...
微信导购网站怎么做视频教学/磁力搜索
OSI model(open system interconnection)存在的原因: 网络模型建立是为了是网络的建造者可以建造出可以相互交流和一起工作的网络,并且描述了从一个电脑上通过网络传数据到另一个网络。 1.physical层 定义了对终端系统之间的连接的…...
营口软件开发/咖啡seo是什么意思
引言Java没有提供任何机制来安全地终止线程,但提供了中断机制,即thread.interrupt()方法。线程中断是一种协作式的机制,并不是说调用了中断方法之后目标线程一定会立即中断,而是发送了一个中断请求给目标线程,目标线程…...
怎么创建一个博客网站/营销型网站建设总结
作者 | 利开园责编 | Carol封图 | CSDN 下载自视觉中国很多开发者都遇到类似这样的经历:一个产品功能开发测试都正常,发布上线后也正常,但是过一段后,如果有个活动或流量一大程序就突然卡了,也有可能流量正常也没搞活动…...
成都市建设网站首页/网店推广的重要性
单线程模型指的是,JavaScript 只在一个线程上运行。也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。 注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。事实上&…...