STM32存储左右互搏 QSPI总线读写FLASH W25QXX

STM32存储左右互搏 QSPI总线读写FLASH W25QXX

FLASH是常用的一种非易失存储单元,W25QXX系列Flash有不同容量的型号,如W25Q64的容量为64Mbit,也就是8MByte。这里介绍STM32CUBEIDE开发平台HAL库Qual SPI总线操作W25Q各型号FLASH的例程。

W25QXX介绍

W25QXX的SOIC封装如下所示,在采用QUAL SPI而不是SPI时,管脚定义为:
在这里插入图片描述
即由片选(/CS), 时钟(CLK), 双向4根输入输出线(IO0, IO1, IO2, IO3)组成6线QSPI信号接口。VCC和GND提供电源和接地连接。

例程采用STM32H750VBT6芯片, FLASH可以选择为8/16/32/64/128/256/512/1024 Mbit的W25Q型号。

STM32工程配置

首先建立基本工程并设置时钟:
在这里插入图片描述
在这里插入图片描述
注意QSPI时钟在单独的时钟树支上:
在这里插入图片描述
QSPI接口可以配置为两个Bank(并行协同存储扩展),也可以配置为单个Bank,并且可以由4线(数据线)模式降级为单线/双线(数据线)模式。这里对于单个Flash,选择一个bank配置:
在这里插入图片描述
在这里插入图片描述
注意QSPI的片选信号是硬件自动控制,和SPI可以采用软件代码控制不同,所以不必单独指定一个GPIO作为片选:
在这里插入图片描述
不用DMA
在这里插入图片描述

配置串口UART1作为命令输入和打印输出接口:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不用DMA:
在这里插入图片描述
保存并生成初始工程代码:
在这里插入图片描述

STM32工程代码

UART串口printf打印输出实现参考:STM32 UART串口printf函数应用及浮点打印代码空间节省 (HAL)

建立W25Q访问的库头文件W25QXX_QSPI.h:*

#ifndef INC_W25QXX_H_
#define INC_W25QXX_H_

#include "main.h"

//W25QXX serial chip list:
#define W25Q80_ID 	0XEF13
#define W25Q16_ID 	0XEF14
#define W25Q32_ID 	0XEF15
#define W25Q64_ID 	0XEF16
#define W25Q128_ID	0XEF17
#define W25Q256_ID 0XEF18
#define W25Q512_ID 0XEF19
#define W25Q1024_ID 0XEF20

extern uint16_t W25QXX_TYPE; //To indicate W25QXX type used in this procedure

//command table for W25QXX access
#define W25X_WriteEnable		0x06
#define W25X_WriteDisable		0x04
#define W25X_ReadStatusReg1		0x05
#define W25X_ReadStatusReg2		0x35
#define W25X_ReadStatusReg3		0x15
#define W25X_WriteStatusReg1    0x01
#define W25X_WriteStatusReg2    0x31
#define W25X_WriteStatusReg3    0x11
#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 W25X_Enable4ByteAddr    0xB7
#define W25X_Exit4ByteAddr      0xE9

#define W25X_QUAD_QuadInputPageProgram 0x32
#define W25X_QUAD_FastReadQuadOutput 0x6B
#define W25X_QUAD_ManufactDeviceID 0x94
#define W25X_QUAD_FastRead 0xEB
#define W25X_QUAD_SetBurstwithWrap 0x77


uint8_t W25QXX_Init(void);
uint16_t  W25QXX_ReadID(void);  	    		  //Read W25QXX ID
uint8_t W25QXX_ReadSR(uint8_t reg_num);           //Read from status register
void W25QXX_4ByteAddr_Enable(void);               //Enable 4-byte address mode
void W25QXX_Write_SR(uint8_t reg_num,uint8_t d);  //Write to status register
void W25QXX_Write_Enable(void);  		          //Write enable
void W25QXX_Write_Disable(void);		          //Write disable
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite); //Write operation w/o check
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);            //Read operation
void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);         //Write operation
void W25QXX_Erase_Chip(void);    	  	                                                //Erase whole chip
void W25QXX_Erase_Sector(uint32_t Sector_Num);	                                        //Erase sector in specific sector number
void W25QXX_Wait_Busy(void);           	       //Wait idle status before next operation
void W25QXX_PowerDown(void);        	       //Enter power-down mode
void W25QXX_WAKEUP(void);				       //Wake-up


#endif /* INC_W25QXX_H_ */

建立W25Q访问的库源文件W25QXX_QSPI.c:

#include "W25QXX_QSPI.h"

extern QSPI_HandleTypeDef hqspi;
extern void PY_Delay_us_t(uint32_t Delay);
extern HAL_StatusTypeDef QSPI_Send_CMD(uint8_t cmd,uint32_t addr,uint8_t mode,uint8_t dmcycle);
extern HAL_StatusTypeDef QSPI_Receive(uint8_t* buf,uint32_t datalen);
extern HAL_StatusTypeDef QSPI_Transmit(uint8_t* buf,uint32_t datalen);

#define Use_Quad_Line 1

uint16_t W25QXX_TYPE=W25Q64_ID;

//W25QXX initialization
uint8_t W25QXX_Init(void)
{
    uint8_t temp;

	W25QXX_TYPE=W25QXX_ReadID();

	if((W25QXX_TYPE==W25Q256_ID)||(W25QXX_TYPE==W25Q512_ID)||(W25QXX_TYPE==W25Q1024_ID))
    {
        temp=W25QXX_ReadSR(3);              //read status register 3
        if((temp&0X01)==0)			        //judge address mode and configure to 4-byte address mode
		{
			QSPI_Send_CMD(W25X_Enable4ByteAddr, 0x00, (0<<6)|(0<<4)|(0<<2)|(1<<0), 0);
		}
    }

    if((W25QXX_TYPE==0x0000)||(W25QXX_TYPE==0xFFFF)) return 0;
    else return 1;
}

//Read status registers of W25QXX
//reg_num: register number from 1 to 3
//return: value of selected register

//SR1 (default 0x00):
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR: default 0, status register protection bit used with WP
//TB,BP2,BP1,BP0: FLASH region write protection configuration
//WEL: write enable lock
//BUSY: busy flag (1: busy; 0: idle)

//SR2:
//BIT7  6   5   4   3   2   1   0
//SUS   CMP LB3 LB2 LB1 (R) QE  SRP1

//SR3:
//BIT7      6    5    4   3   2   1   0
//HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
uint8_t W25QXX_ReadSR(uint8_t reg_num)
{
	uint8_t byte=0,command=0;
    switch(reg_num)
    {
        case 1:
            command=W25X_ReadStatusReg1;    //To read status register 1
            break;
        case 2:
            command=W25X_ReadStatusReg2;    //To read status register 2
            break;
        case 3:
            command=W25X_ReadStatusReg3;    //To read status register 3
            break;
        default:
            command=W25X_ReadStatusReg1;
            break;
    }

	QSPI_Send_CMD(command, 0x00, (1<<6)|(0<<4)|(0<<2)|(1<<0), 0);  //send command
	QSPI_Receive(&byte,1);                                         //read data
	return byte;
}

//Write status registers of W25QXX
//reg_num: register number from 1 to 3
//d: data for updating status register
void W25QXX_Write_SR(uint8_t reg_num,uint8_t d)
{
    uint8_t command=0;
    switch(reg_num)
    {
        case 1:
            command=W25X_WriteStatusReg1;    //To write status register 1
            break;
        case 2:
            command=W25X_WriteStatusReg2;    //To write status register 2
            break;
        case 3:
            command=W25X_WriteStatusReg3;    //To write status register 3
            break;
        default:
            command=W25X_WriteStatusReg1;
            break;
    }

	QSPI_Send_CMD(command, 0x00, (1<<6)|(0<<4)|(0<<2)|(1<<0), 0); //send command
	QSPI_Transmit(&d, 1);                                         //write data
}
//W25QXX write enable
void W25QXX_Write_Enable(void)
{
	QSPI_Send_CMD(W25X_WriteEnable, 0x00, (0<<6)|(0<<4)|(0<<2)|(1<<0), 0); //send command
}
//W25QXX write disable
void W25QXX_Write_Disable(void)
{
	QSPI_Send_CMD(W25X_WriteDisable, 0x00, (0<<6)|(0<<4)|(0<<2)|(1<<0), 0); //send command
}

//Read chip ID
//return:
//0XEF13 for W25Q80
//0XEF14 for W25Q16
//0XEF15 for W25Q32
//0XEF16 for W25Q64
//0XEF17 for W25Q128
//0XEF18 for W25Q256
uint16_t W25QXX_ReadID(void)
{
	uint16_t Temp = 0;
	uint8_t st;
	uint8_t TD[8];

	if(Use_Quad_Line)
	{
		uint8_t extra_dummy = 4; //Adjust dummy here for I/O direction adjustment delay
		QSPI_Send_CMD(W25X_QUAD_ManufactDeviceID, 0x000000f0, (3<<6)|(3<<4)|(3<<2)|(1<<0), extra_dummy);   ///To read Manufacturer/Device ID in Quad mode
		st = QSPI_Receive(TD, 2);
		if(st==0)
		{
			  Temp = (TD[0]<<8)|TD[1];
		}
		else
		{
			  Temp = 0;
		}

		  return Temp;
	}
	else
	{
		  QSPI_Send_CMD(W25X_ManufactDeviceID, 0x00, (1<<6)|(0<<4)|(0<<2)|(1<<0), 0);  //To read Manufacturer/Device ID in single line mode
		  st = QSPI_Receive(TD, 5);
		  if(st==0)
		  {
			  Temp = (TD[3]<<8)|TD[4];
		  }
		  else
		  {
			  Temp = 0;
		  }

		  return Temp;

	}
}
//Read W25QXX from specific address for specific byte length
//pBuffer: data buffer
//ReadAddr: specific address
//NumByteToRead: specific byte length (max 65535)
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
	if(Use_Quad_Line)
	{
		  uint8_t extra_dummy = 8;    //Adjust dummy here for I/O direction adjustment delay
		  if((W25QXX_TYPE==W25Q256_ID)||(W25QXX_TYPE==W25Q512_ID)||(W25QXX_TYPE==W25Q1024_ID))
		  {
			  QSPI_Send_CMD(W25X_QUAD_FastReadQuadOutput, ReadAddr, (3<<6)|(3<<4)|(1<<2)|(1<<0), extra_dummy);
		  }
		  else
		  {
			  QSPI_Send_CMD(W25X_QUAD_FastReadQuadOutput, ReadAddr, (3<<6)|(2<<4)|(1<<2)|(1<<0), extra_dummy);
		  }
		  QSPI_Receive(pBuffer, NumByteToRead); //read data
	}
	else
	{
		  uint8_t extra_dummy = 8;    //Adjust dummy here for I/O direction adjustment delay
		  if((W25QXX_TYPE==W25Q256_ID)||(W25QXX_TYPE==W25Q512_ID)||(W25QXX_TYPE==W25Q1024_ID))
		  {
			  QSPI_Send_CMD(W25X_ReadData, ReadAddr, (1<<6)|(3<<4)|(1<<2)|(1<<0), extra_dummy);
		  }
		  else
		  {
			  QSPI_Send_CMD(W25X_ReadData, ReadAddr, (1<<6)|(2<<4)|(1<<2)|(1<<0), extra_dummy);
		  }
		  QSPI_Receive(pBuffer, NumByteToRead); //read data
	}

}

//Write W25QXX not more than 1 page (256 bytes)
//pBuffer: data buffer
//WriteAddr: specific address
//NumByteToWrite: specific byte length (max 256)
void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
    W25QXX_Write_Enable();                                        //write enable

    //send write command
	if(Use_Quad_Line)
	{
	    if((W25QXX_TYPE==W25Q256_ID)||(W25QXX_TYPE==W25Q512_ID)||(W25QXX_TYPE==W25Q1024_ID))
	    {
	        QSPI_Send_CMD(W25X_QUAD_QuadInputPageProgram, WriteAddr, (3<<6)|(3<<4)|(1<<2)|(1<<0), 0);
	    }
	    else
	    {
	    	QSPI_Send_CMD(W25X_QUAD_QuadInputPageProgram, WriteAddr, (3<<6)|(2<<4)|(1<<2)|(1<<0), 0);
	    }
	}
	else
	{
	    if((W25QXX_TYPE==W25Q256_ID)||(W25QXX_TYPE==W25Q512_ID)||(W25QXX_TYPE==W25Q1024_ID))
	    {
	        QSPI_Send_CMD(W25X_PageProgram, WriteAddr, (1<<6)|(3<<4)|(1<<2)|(1<<0), 0);
	    }
	    else
	    {
	    	QSPI_Send_CMD(W25X_PageProgram, WriteAddr, (1<<6)|(2<<4)|(1<<2)|(1<<0), 0);
	    }

	}

    QSPI_Transmit(pBuffer, NumByteToWrite); //write data
	W25QXX_Wait_Busy();
}

//Write W25QXX w/o erase check and w/o byte number restriction
//pBuffer: data buffer
//WriteAddr: specific address
//NumByteToWrite: specific byte length (max 65535)
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
	uint16_t remained_byte_num_in_page;
	remained_byte_num_in_page=256-WriteAddr%256;                                                       //remained byte number in page
	if( NumByteToWrite <= remained_byte_num_in_page ) remained_byte_num_in_page = NumByteToWrite;      //data can be written in single page
	while(1)
	{
		W25QXX_Write_Page(pBuffer,WriteAddr,remained_byte_num_in_page);
		if(NumByteToWrite==remained_byte_num_in_page)break;                                            //end write operation
	 	else                                                                                           //NumByteToWrite>remained_byte_num_in_page
		{
			pBuffer+=remained_byte_num_in_page;
			WriteAddr+=remained_byte_num_in_page;

			NumByteToWrite-=remained_byte_num_in_page;
			if(NumByteToWrite>256) remained_byte_num_in_page=256;                                       //for whole page write
			else remained_byte_num_in_page=NumByteToWrite; 	                                           //for non-whole page write
		}
	};
}

//Write W25QXX w/ erase after check and w/o byte number restriction
//pBuffer: data buffer
//WriteAddr: specific address
//NumByteToWrite: specific byte length (max 65535)
uint8_t W25QXX_BUFFER[4096];
void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
	uint32_t secpos;
	uint16_t secoff;
	uint16_t secremain;
 	uint16_t i;
	uint8_t * W25QXX_BUF;
   	W25QXX_BUF=W25QXX_BUFFER;
 	secpos=WriteAddr/4096;                                        //sector number (16 pages for 1 sector) for destination address
	secoff=WriteAddr%4096;                                        //offset address in sector for destination address
	secremain=4096-secoff;                                        //remained space for sector
 	if(NumByteToWrite<=secremain)secremain=NumByteToWrite;        //data can be written in single sector
	while(1)
	{
		W25QXX_Read(W25QXX_BUF,secpos*4096,4096);                 //read sector data for ease necessity judgment
		for(i=0;i<secremain;i++)                                  //check sector data status
		{
			if(W25QXX_BUF[secoff+i]!=0XFF) break;                 //ease necessary
		}

		if(i<secremain)                                           //for ease
		{
			W25QXX_Erase_Sector(secpos);                          //ease sector
			for(i=0;i<secremain;i++)	                          //data copy
			{
				W25QXX_BUF[i+secoff]=pBuffer[i];
			}
			W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);     //write sector

		}
		else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);   //write data for sector unnecessary to erase

		if(NumByteToWrite==secremain)break;                        //for operation end
		else                                                       //for operation continuing
		{
			secpos++;                                              //sector number + 1
			secoff=0;                                              //offset address from 0

		   	pBuffer+=secremain;                                    //pointer adjustment
			WriteAddr+=secremain;                                  //write address adjustment
		   	NumByteToWrite-=secremain;				               //write number adjustment
			if(NumByteToWrite>4096) secremain=4096;	               //not last sector
			else secremain=NumByteToWrite;			               //last sector
		}
	};
}

//Erase whole chip, long waiting...
void W25QXX_Erase_Chip(void)
{
    W25QXX_Write_Enable();                  //write enable
    W25QXX_Wait_Busy();
    QSPI_Send_CMD(W25X_ChipErase, 0x00, (0<<6)|(0<<4)|(0<<2)|(1<<0), 0); //send erase command
	W25QXX_Wait_Busy();   				    //wait for erase complete
}

//Erase one sector
//Sector_Num: sector number
void W25QXX_Erase_Sector(uint32_t Sector_Num)
{
 	Sector_Num *= 4096;
    W25QXX_Write_Enable();                                     //write enable
    W25QXX_Wait_Busy();

    if((W25QXX_TYPE==W25Q256_ID)||(W25QXX_TYPE==W25Q512_ID)||(W25QXX_TYPE==W25Q1024_ID)) //send highest 8-bit address
    {
    	QSPI_Send_CMD(W25X_SectorErase, Sector_Num, (0<<6)|(3<<4)|(1<<2)|(1<<0), 0); //send erase command
    }
    else
    {
    	QSPI_Send_CMD(W25X_SectorErase, Sector_Num, (0<<6)|(2<<4)|(1<<2)|(1<<0), 0); //send erase command
    }
    W25QXX_Wait_Busy();   				                       //wait for erase complete
}

//Wait idle status before next operation
void W25QXX_Wait_Busy(void)
{
	while((W25QXX_ReadSR(1)&0x01)==0x01);    //wait for busy flag cleared
}

//Enter power-down mode
#define tDP_us 3
void W25QXX_PowerDown(void)
{
    QSPI_Send_CMD(W25X_PowerDown, 0, (0<<6)|(0<<4)|(0<<2)|(1<<0), 0);        //send power-down command
	PY_Delay_us_t(tDP_us);                   //tDP
}
//Wake-up
#define tRES1_us 3
void W25QXX_WAKEUP(void)
{
    QSPI_Send_CMD(W25X_ReleasePowerDown, 0, (0<<6)|(0<<4)|(0<<2)|(1<<0), 0); //send release power-down command
	PY_Delay_us_t(tRES1_us);                 //tRES1
}

main.c文件操作代码里包含几个QSPI操作函数定义如下:

#define page_byte_size 256
uint8_t sdbuffer[page_byte_size];

/*
QSPI TX in block mode for command byte
cmd: command to be sent to device
addr: address to be sent to device
mode: operation mode set as
	mode[1:0] for command transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[3:2] for address transmission mode ( 00: no address; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[5:4] for address length ( 00:8-bit address; 01: 16-bit address; 10: 24-bit address; 11: 32-bit address )
	mode[7:6] for data transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
dmcycle: dummy clock cycle
*/
HAL_StatusTypeDef QSPI_Send_CMD(uint8_t cmd,uint32_t addr,uint8_t mode,uint8_t dmcycle)
{
	QSPI_CommandTypeDef Cmdhandler;

	Cmdhandler.Instruction=cmd;		//set cmd
	Cmdhandler.Address=addr;		//set address
	Cmdhandler.DummyCycles=dmcycle; //set dummy circle number

	/*set cmd transmission mode*/
	if(((mode>>0)&0x03) == 0)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_NONE;
	else if(((mode>>0)&0x03) == 1)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_1_LINE;
	else if(((mode>>0)&0x03) == 2)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_2_LINES;
	else if(((mode>>0)&0x03) == 3)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_4_LINES;

	/*set address transmission mode*/
	if(((mode>>2)&0x03) == 0)
	Cmdhandler.AddressMode=QSPI_ADDRESS_NONE;
	else if(((mode>>2)&0x03) == 1)
	Cmdhandler.AddressMode=QSPI_ADDRESS_1_LINE;
	else if(((mode>>2)&0x03) == 2)
	Cmdhandler.AddressMode=QSPI_ADDRESS_2_LINES;
	else if(((mode>>2)&0x03) == 3)
	Cmdhandler.AddressMode=QSPI_ADDRESS_4_LINES;

	/*set address length*/
	if(((mode>>4)&0x03) == 0)
	Cmdhandler.AddressSize=QSPI_ADDRESS_8_BITS;
	else if(((mode>>4)&0x03) == 1)
	Cmdhandler.AddressSize=QSPI_ADDRESS_16_BITS;
	else if(((mode>>4)&0x03) == 2)
	Cmdhandler.AddressSize=QSPI_ADDRESS_24_BITS;
	else if(((mode>>4)&0x03) == 3)
	Cmdhandler.AddressSize=QSPI_ADDRESS_32_BITS;

	/*set data transmission mode*/
	if(((mode>>6)&0x03) == 0)
	Cmdhandler.DataMode=QSPI_DATA_NONE;
	else if(((mode>>6)&0x03) == 1)
	Cmdhandler.DataMode=QSPI_DATA_1_LINE;
	else if(((mode>>6)&0x03) == 2)
	Cmdhandler.DataMode=QSPI_DATA_2_LINES;
	else if(((mode>>6)&0x03) == 3)
	Cmdhandler.DataMode=QSPI_DATA_4_LINES;

	Cmdhandler.SIOOMode=QSPI_SIOO_INST_EVERY_CMD;				/*Send instruction on every transaction*/
	Cmdhandler.AlternateByteMode=QSPI_ALTERNATE_BYTES_NONE;		/*No alternate bytes*/
	Cmdhandler.DdrMode=QSPI_DDR_MODE_DISABLE;					/*Double data rate mode disabled*/
	Cmdhandler.DdrHoldHalfCycle=QSPI_DDR_HHC_ANALOG_DELAY;      /*Delay the data output using analog delay in DDR mode*/

	return HAL_QSPI_Command(&hqspi,&Cmdhandler,5000);
}

//QSPI RX in block mode for data
//buf : buffer address for RX data
//datalen : RX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Receive(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Receive(&hqspi,buf,5000);
}

//QSPI TX in block mode for data
//buf : buffer address for TX data
//datalen : TX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Transmit(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Transmit(&hqspi,buf,5000);
}

main.c文件操作代码里实现串口接收1个字节的指令,实现FLASH的ID读取,一页的写入,一页的读出三个功能。其它功能可以根据需要自行增加。完整的main.c文件如下

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
//Written by Pegasus Yu in 2023
//NOte: to access W25Qxx, send single line command at first to trigger consequent operation mode for address and data which could be 1-line or 4-line
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "usart.h"
#include "W25QXX_QSPI.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO float usDelayBase;
void PY_usDelayTest(void)
{
  __IO uint32_t firstms, secondms;
  __IO uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  usDelayBase = ((float)counter)/1000;
}

void PY_Delay_us_t(uint32_t Delay)
{
  __IO uint32_t delayReg;
  __IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}

void PY_usDelayOptimize(void)
{
  __IO uint32_t firstms, secondms;
  __IO float coe = 1.0;

  firstms = HAL_GetTick();
  PY_Delay_us_t(1000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  usDelayBase = coe*usDelayBase;
}


void PY_Delay_us(uint32_t Delay)
{
  __IO uint32_t delayReg;

  __IO uint32_t msNum = Delay/1000;
  __IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

QSPI_HandleTypeDef hqspi;

UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */
uint8_t uart1_rx[16];
uint8_t cmd;
uint32_t Flash_Access_Addr = 0;

uint8_t st;
uint16_t FID;
uint8_t TD[256];
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_QUADSPI_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define page_byte_size 256
uint8_t sdbuffer[page_byte_size];

/*
QSPI TX in block mode for command byte
cmd: command to be sent to device
addr: address to be sent to device
mode: operation mode set as
	mode[1:0] for command transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[3:2] for address transmission mode ( 00: no address; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
	mode[5:4] for address length ( 00:8-bit address; 01: 16-bit address; 10: 24-bit address; 11: 32-bit address )
	mode[7:6] for data transmission mode ( 00: no command; 01: single-line transmission; 10: dual-line transmission; 11: four-line transmission )
dmcycle: dummy clock cycle
*/
HAL_StatusTypeDef QSPI_Send_CMD(uint8_t cmd,uint32_t addr,uint8_t mode,uint8_t dmcycle)
{
	QSPI_CommandTypeDef Cmdhandler;

	Cmdhandler.Instruction=cmd;		//set cmd
	Cmdhandler.Address=addr;		//set address
	Cmdhandler.DummyCycles=dmcycle; //set dummy circle number

	/*set cmd transmission mode*/
	if(((mode>>0)&0x03) == 0)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_NONE;
	else if(((mode>>0)&0x03) == 1)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_1_LINE;
	else if(((mode>>0)&0x03) == 2)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_2_LINES;
	else if(((mode>>0)&0x03) == 3)
	Cmdhandler.InstructionMode=QSPI_INSTRUCTION_4_LINES;

	/*set address transmission mode*/
	if(((mode>>2)&0x03) == 0)
	Cmdhandler.AddressMode=QSPI_ADDRESS_NONE;
	else if(((mode>>2)&0x03) == 1)
	Cmdhandler.AddressMode=QSPI_ADDRESS_1_LINE;
	else if(((mode>>2)&0x03) == 2)
	Cmdhandler.AddressMode=QSPI_ADDRESS_2_LINES;
	else if(((mode>>2)&0x03) == 3)
	Cmdhandler.AddressMode=QSPI_ADDRESS_4_LINES;

	/*set address length*/
	if(((mode>>4)&0x03) == 0)
	Cmdhandler.AddressSize=QSPI_ADDRESS_8_BITS;
	else if(((mode>>4)&0x03) == 1)
	Cmdhandler.AddressSize=QSPI_ADDRESS_16_BITS;
	else if(((mode>>4)&0x03) == 2)
	Cmdhandler.AddressSize=QSPI_ADDRESS_24_BITS;
	else if(((mode>>4)&0x03) == 3)
	Cmdhandler.AddressSize=QSPI_ADDRESS_32_BITS;

	/*set data transmission mode*/
	if(((mode>>6)&0x03) == 0)
	Cmdhandler.DataMode=QSPI_DATA_NONE;
	else if(((mode>>6)&0x03) == 1)
	Cmdhandler.DataMode=QSPI_DATA_1_LINE;
	else if(((mode>>6)&0x03) == 2)
	Cmdhandler.DataMode=QSPI_DATA_2_LINES;
	else if(((mode>>6)&0x03) == 3)
	Cmdhandler.DataMode=QSPI_DATA_4_LINES;

	Cmdhandler.SIOOMode=QSPI_SIOO_INST_EVERY_CMD;				/*Send instruction on every transaction*/
	Cmdhandler.AlternateByteMode=QSPI_ALTERNATE_BYTES_NONE;		/*No alternate bytes*/
	Cmdhandler.DdrMode=QSPI_DDR_MODE_DISABLE;					/*Double data rate mode disabled*/
	Cmdhandler.DdrHoldHalfCycle=QSPI_DDR_HHC_ANALOG_DELAY;      /*Delay the data output using analog delay in DDR mode*/

	return HAL_QSPI_Command(&hqspi,&Cmdhandler,5000);
}

//QSPI RX in block mode for data
//buf : buffer address for RX data
//datalen : RX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Receive(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Receive(&hqspi,buf,5000);
}

//QSPI TX in block mode for data
//buf : buffer address for TX data
//datalen : TX data length
//return : 0, OK
//         others, error code
HAL_StatusTypeDef QSPI_Transmit(uint8_t* buf,uint32_t datalen)
{
    hqspi.Instance->DLR=datalen-1;
    return HAL_QSPI_Transmit(&hqspi,buf,5000);
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_QUADSPI_Init();
  /* USER CODE BEGIN 2 */
  PY_usDelayTest();
  PY_usDelayOptimize();

  HAL_UART_Receive_IT(&huart1, uart1_rx, 1);

  W25QXX_Init();

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		 if(cmd==0x01) //Read ID
		 {
			  cmd = 0;

			  FID = W25QXX_ReadID();
			  printf("Flash ID = 0x%x\r\n\r\n", FID);
		      printf("W25Q80_ID: 0XEF13\r\n");
		      printf("W25Q16_ID: 0XEF14\r\n");
		      printf("W25Q32_ID: 0XEF15\r\n");
		      printf("W25Q64_ID: 0XEF16\r\n");
		      printf("W25Q128_ID: 0XEF17\r\n");
		      printf("W25Q256_ID: 0XEF18\r\n");
		      printf("W25Q512_ID: 0XEF18\r\n");
		      printf("W25Q1024_ID: 0XEF20\r\n");
		  }

	     if(cmd==2) //Write one page
	     {
	    	 cmd = 0;

	       	   for(uint32_t i=0;i<page_byte_size;i++)
	       	   {
	       		  sdbuffer[i]=i;
	       	   }

	       	  Flash_Access_Addr = 0;
	       	  W25QXX_Write(sdbuffer, Flash_Access_Addr, page_byte_size);
	       	  printf("Write to W25QXX done!\r\n");
	     }

	     if(cmd==3)//Read one page
	     {
	    	 cmd = 0;

	    	 memset(sdbuffer, 0, page_byte_size);
	    	 printf("Start to read W25QXX......\r\n");

	    	 Flash_Access_Addr = 0;
	    	 W25QXX_Read(sdbuffer, Flash_Access_Addr, page_byte_size);

	    	 for(uint32_t i=0; i<page_byte_size; i++)
	    	 {
                printf("%d ", sdbuffer[i]);

	    	 }
	    	 printf("\r\n");

	     }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 60;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  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_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief QUADSPI Initialization Function
  * @param None
  * @retval None
  */
static void MX_QUADSPI_Init(void)
{

  /* USER CODE BEGIN QUADSPI_Init 0 */

  /* USER CODE END QUADSPI_Init 0 */

  /* USER CODE BEGIN QUADSPI_Init 1 */

  /* USER CODE END QUADSPI_Init 1 */
  /* QUADSPI parameter configuration*/
  hqspi.Instance = QUADSPI;
  hqspi.Init.ClockPrescaler = 9;
  hqspi.Init.FifoThreshold = 4;
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 25;
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_5_CYCLE;
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
  hqspi.Init.FlashID = QSPI_FLASH_ID_1;
  hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
  if (HAL_QSPI_Init(&hqspi) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN QUADSPI_Init 2 */

  /* USER CODE END QUADSPI_Init 2 */

}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart==&huart1)
	{
		cmd = uart1_rx[0];
		HAL_UART_Receive_IT(&huart1, uart1_rx, 1);
	}

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

STM32例程测试

串口指令0x01测试效果如下:
在这里插入图片描述
串口指令0x02测试效果如下:
在这里插入图片描述

串口指令0x03测试效果如下:
在这里插入图片描述

STM32例程下载

STM32H750VBT6 QSPI总线读写FLASH W25QXX HAL库例程下载

–End–

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/362676.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

游泳耳机要怎么选购?一篇文章告诉你如何选购游泳耳机

在进行运动时享受音乐的乐趣是许多人的喜好&#xff0c;对于在地面展开的一般运动&#xff0c;选择耳机相对简单&#xff0c;但若是涉及水中游泳&#xff0c;我们就需要一款具备防水性能的专业游泳耳机。市面上已有数款针对游泳设计的防水耳机&#xff0c;本文将为您详细介绍如…

【解刊】审稿人极其友好!中科院2区SCI,3个月录用,论文质量要求宽松!

计算机类 • 高分快刊 今天带来Springer旗下计算机领域高分快刊&#xff0c;有投稿经验作者表示期刊审稿人非常友好&#xff0c;具体情况一起来看看下文解析。如有投稿意向可重点关注&#xff1a; 01 期刊简介 Complex & Intelligent Systems ✅出版社&#xff1a;Sprin…

光杆司令如何部署大模型?

1、背景 今天这种方式非常贴合低配置笔记本电脑的小伙伴们, 又没有GPU资源, 可以考虑使用api方式,让模型服务厂商提供计算资源 有了开放的api,让你没有显卡的电脑也能感受一下大模型管理知识库,进行垂直领域知识的检索和问答.算是自己初步玩一下AI agent 之前有写过一篇《平民…

Java二维码图片识别

前言 后端识别二维码图片 代码 引入依赖 <dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.2.1</version></dependency><dependency><groupId>com.google.zxing<…

软件压力测试:探究其目的与重要性

随着软件应用在各行各业中的广泛应用&#xff0c;确保软件在高负载和极端条件下的稳定性变得至关重要。软件压力测试是一种验证系统在不同负载条件下的性能和稳定性的方法。本文将介绍软件压力测试的目的以及为什么它对软件开发和部署过程至关重要。 验证系统性能的极限&#x…

二、人工智能之提示工程(Prompt Engineering)

黑8说 岁月如流水匆匆过&#xff0c;哭一哭笑一笑不用说。 黑8自那次和主任谈话后&#xff0c;对这个“妖怪”继续研究&#xff0c;开始学习OpenAI API&#xff01;关注到了提示工程(Prompt Engineering)的重要性&#xff0c;它包括明确的角色定义、自然语言理解&#xff08;…

10个关键字让你的谷歌竞价排名瞬间飙升-华媒舍

在现代社会中&#xff0c;搜索引擎已经成为获取信息的主要途径之一。在这其中&#xff0c;谷歌搜索引擎以其强大的搜索算法和智能化的用户体验而闻名。对于企业主来说&#xff0c;如何提高在谷歌搜索结果中的排名&#xff0c;对于他们的品牌推广和获取潜在客户非常重要。 1. 关…

springboot137欢迪迈手机商城设计与开发

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

Shell脚本——免交互

目录 一、Here Document免交互 1、免交互概述 2、语法格式 2.1示例&#xff1a;免交互方式实现对行数的统计&#xff0c;将要统计的内容置于标记EOF之间&#xff0c;直接将内容传给wc-l来统计 3、变量设定 ①变量图换成实际值 ②整行内容作为变量并输出结果 ③使输出内…

二、图像色彩空间转换

一、色彩空间头文件 在项目的头文件中&#xff0c;右击添加&#xff0c;新建项 例如我的是testopencv.h 自定义一个头文件&#xff0c;用于图片色彩空间的转换和保存操作 定义个Colors类 里面有一个函数声明void colorspaces(Mat& image);&#xff0c;用于实现图片的色…

第九节HarmonyOS 常用基础组件22-Marquee

1、描述 跑马灯组件&#xff0c;用于滚动展示一段单行文本&#xff0c;仅当文本内容宽度超过跑马灯组件宽度时滚动。 2、接口 Marquee(value:{start:boolean, step?:number, loop?:number, fromStart?: boolean ,src:string}) 3、参数 参数名 参数类型 必填 描述 st…

SparkStreaming---入门

文章目录 1.SparkStreaming简介1.1 流处理和批处理1.2 实时和离线1.3 SparkStreaming是什么1.4 SparkStreaming架构图 2.背压机制3.DStream案例实操 1.SparkStreaming简介 1.1 流处理和批处理 流处理和批处理是两种不同的数据处理方式&#xff0c;它们在处理数据的方式和特点…

【Midjourney】AI绘画案例(1)龙年吉祥神兽

说明&#xff1a; 1、文中图片版权均为Midjourney所有&#xff0c;请勿用作商业用途。 2、文中图片均经过 Upscale x 4 处理。 3、由于模型原因&#xff0c;某些图片存在暇玼。 1、吉祥神兽——天马&#xff08;独角兽&#xff09; 天马消灾星。 提示词 Prompt: Sky Unicor…

Vue.js 学习14 集成H265web.js播放器实现webpack自动化构建

Vue.js 学习14 集成H265web.js播放器实现webpack自动化构建 一、项目说明1. H265web.js 简介2. 准备环境 二、项目配置1. 下载 H265web.js2. 在vue项目里引入 H265web3. 设置 vue.config.js 三、代码引用1. 参照官方demo &#xff0c; 创建 executor.js2. 在 vue 页面里引用htm…

你的MiniFilter安全吗?

简介 筛选器管理器 (FltMgr.sys)是Windows系统提供的内核模式驱动程序, 用于实现和公开文件系统筛选器驱动程序中通常所需的功能; 第三方文件系统筛选器开发人员可以使用FltMgr的功能可以更加简单的编写文件过滤驱动, 这种驱动我们通常称为MiniFilter, 下面是MiniFilter的基本…

【Vue】vue项目中使用tinymce富文本组件(@tinymce/tinymce-vue)

【Vue】vue项目中使用tinymce富文本组件&#xff08;tinymce/tinymce-vue&#xff09; 一、安装二、前期准备工作1、去[官网](https://www.tiny.cloud/get-tiny/language-packages/)下载语言包&#xff1b;2、将下载的语言包复制到项目中的assets&#xff08;存放路径您随意&am…

TensorFlow2实战-系列教程5:猫狗识别2------数据增强

&#x1f9e1;&#x1f49b;&#x1f49a;TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 猫狗识别1 数据增强 猫狗识别2------数据增强 猫狗识别3------迁移学习 1、猫狗识别任…

通过ETLCloud CDC构建高效数据管道解决方案

随着企业数据规模的快速增长和多样化的数据&#xff0c;如何高效地捕获、同步和处理数据成为了业务发展的关键。本文将介绍如何利用ETLCloud CDC技术&#xff0c;构建一套高效的CDC数据管道&#xff0c;实现实时数据同步和分析&#xff0c;助力企业实现数据驱动的业务发展。 一…

基于Java SSM框架实现影院购票系统项目【项目源码+论文说明】

基于java的SSM框架实现影院购票系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#…

Redis -- 单线程模型

失败是成功之母 ——法国作家巴尔扎克 目录 单线程模型 Redis为什么这么快 单线程模型 redis只使用一个线程&#xff0c;处理所有的命令请求&#xff0c;不是说redis服务器进场内部真的就只有一个线程&#xff0c;其实也有多个线程&#xff0c;那就是处理网络和io的线程。 R…