当前位置: 首页 > news >正文

STM32单片机初学8-SPI flash(W25Q128)数据读写

        当使用单片机进行项目开发,涉及大量数据需要储存时(例如使用了屏幕作为显示设备,常常需要存储图片、动画等数据),单靠单片机内部的Flash往往是不够用的。

        如STM32F103系列,内部Flash最多只能达到512KByte,假设要储存240*240分辨率、64K彩色图片,只够存储4张左右。如果使用外置储存器,将图片等其他数据放置在外置储存器,内部Flash只储存程序,就能减小内部Flash的需求,降低成本。     f89a1e2ee7ce46f29538d0aae0a794ad.png         Flash种类有很多,按其制程和制作工艺的不同可分为NOR Flash、NAND Flash。NAND的串行结构使得其容量很容易做的很大(SD卡、U盘、硬盘大都采用该类Flash),但是其读取速度却比不上并行结构的NOR Flash,且可靠性要差些,一旦出现数据块坏点,是不可逆、无法修复的。由于其数据存储原理,Flash在写入新的数据之前,都需要将数据地址所在的块擦除,NOR Flash的擦除速度比NAND慢很多。

        Flash按其数据传输方式的不同可分为并口传输与串口传输。STM32的并口传输需使用FSMC接口,虽然其读写速度很快,但对于100PIN脚以下的封装是不带FSMC功能的。

         所以使用更多的是串口传输方式。串口方式一般采用的是SPI通讯。

          W25Q系列Flash是Winbond(台湾华邦科技)生产的SPI Flash系列,是单片机开发中比较常用的外置Flash。其支持标准四线SPI、Dual SPI、Quad SPI、QPI,其时钟频率分别可达到104MHz、208MHz、416MHz。对于STM32F103系列,其主频最高72MHz(SPI通信速率最高18Mbps),所以标准SPI就已经是足够F103系列单片机使用了。这里我使用W25Q128FV来讲解Flash的使用。

b29518b923934ed3bb13dcceccd20530.png

        先来了解其引脚定义,上面展示的是SOP8封装,还有SOP16封装的,功能都是差不多的。

ac7b949edaa94de1a6d71992a34c5903.png        /CS: Flash的片选引脚。当/CS高时,Flash的串行数据输出(DO或IO0、IO1、IO2、IO3)引脚处于高阻抗,此时设备功耗将处于待机水平(除非正在进行内部擦除、程序或写入状态寄存器周期)。当/CS为低电平,Flash将被选中,功耗将增加到活动水平,并且可以向该设备写入指令和从该设备读取数据。启动后,/CS必须从高电平转换到低电平,才能接受新的指令。

        DO(IO1):DO指数据输出口(Data Out),一般连接到单片机SPI接口的数据输入端,即MISO。IO1是其复用功能,当启用SPI四位传输模式时,该引脚功能为IO1.

        /WP:写保护(WP)引脚。可以用来防止状态寄存器被写入。与状态寄存器的块保护(CMP, SEC, TB, BP2, BP1和BPO)位和状态寄存器保护(SRP)位一起使用,小到4KB扇区或整个内存阵列都可以被硬件保护。/WP引脚低电平有效。当状态寄存器2的QE位设置为Quad I/O时,/WP引脚功能不可用,因为该引脚用于IO2。如果不想使用该功能,可以直接将该引脚接VCC。

        GND:Flash的供电GND

        DI:DI指数据输入口(Data In),一般连接到单片机SPI接口的数据输出端,即MOSI。IO0是其复用功能,当启用SPI四位传输模式时,该引脚为IO0

        CLK:SPI时钟线。连接至单片机的SPI时钟接口。

        /HOLD,/RESET:/HOLD能让设备主动暂停数据传输。当/HOLD低时,而/CS低时,DO引脚将处于高阻抗,Dl和CLK引脚上的信号将被忽略。当/HOLD调高时,设备可以恢复运行。当多个设备共享相同的SPl信号时,/HOLD就能发挥作用。 /RESET引脚用于设备复位。注意,如果在写入数据时复位,可能会造成数据丢失。所以如果不需要给Flash复位,该引脚常常直接与VCC相接。

        VCC:Flash供电电源3.3V。

         不管何种存储器,在进行数据读写时都需要知道数据的地址。数据存储在寄存器中,所以数据的地址即寄存器地址。我们来看看W25Q128的内部原理图。

aa61ec1836eb43cc8abd2057bcae6def.jpeg

        其内部是由数据存储单元和各种控制器组成。

        存储单元的最小单位为一个寄存器,每个寄存器可存储1个字节的数据。

        每256个寄存器组成一页(Page),也就是一页能存储256Byte数据,

        每16页组成一个扇区(Sector),一个扇区能储存16x256=4096Byte数据(近似4KB)。比如扇区0的数据地址范围为000000 h-0000FF h。

        每16个扇区又组成一个块(Block),一个块能储存4096x16=65536Byte数据(近似64K)。例如块0的数据地址范围为000000 h-000FFF h 。

         整个存储单元共256个块,所以其总存储容量为256x65536=16777216Byte数据,近似为16MByte。数据地址范围为000000 h-FFFFFF h。

        不管何种外设,都是通过发送命令与数据来控制的。Flash也不例外,所以需要知道如何使用Flash,只需在其技术手册上找到其命令表即可。

         可用的命令有很多,但常用的就一部分。

        现在我们来讲解程序里如何实现STM32F103读写SPI Flash的数据。

        这里我使用的是SPI2,硬件连接如下。

        再来看程序部分:

1.SPI2初始化。为了将读出来的数据显示出来,这里我使用串口将数据传输到电脑上。所以对usart1也初始化。

void SPI2_UserInit(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//使能GPIOB的时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;    //PA13为SCK时钟,PA15为MISOGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHzGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			//复用推挽输出GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;					//PA14为MISOGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			    //速度50MHzGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			//浮空输入GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;					//PA12为片选GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			    //速度50MHzGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;				//复用推挽输出GPIO_Init(GPIOB, &GPIO_InitStructure);SPI_InitTypeDef SPI_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);		     //使能SPI时钟SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//设置双向双线全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master;						//设置为SPI主站SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;					//设置为8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;					//串行时钟的稳态为时钟高SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;		//位捕获的时钟活动沿为第1个时钟沿SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;					//指定NSS信号由软件控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;    //波特率预分频值SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;					//数据位从MSB开始SPI_InitStructure.SPI_CRCPolynomial = 7;								//CRC检验SPI_Init(SPI2, &SPI_InitStructure);							//按以上设置初始化SPI2SPI_Cmd(SPI2, ENABLE);							//使能SPI2GPIO_SetBits(GPIOB,GPIO_Pin_12);	            	//CS置高
}void USART1_Userinit(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;    //PA9为USART1_TX将这个GPIO初始化GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHzGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				//复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;	//PA10为USART_RX,将这个GPIO初始化GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHzGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitTypeDef USART_InitStructure;						//定义USART配置结构体RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);		//打开USART1时钟USART_InitStructure.USART_BaudRate = 115200; 					//波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//数据帧位数 USART_InitStructure.USART_StopBits = USART_StopBits_1; 			//停止位数目USART_InitStructure.USART_Parity = USART_Parity_No; 			//奇偶模式(USART_Parity_No 无,USART_Parity_Even 偶SART_Parity_Odd奇)USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 	//硬件流控制模式USART_InitStructure.USART_Mode = USART_Mode_Tx| USART_Mode_Rx; 									//发送、接收使能USART_Init(USART1, &USART_InitStructure);											//初始化USART_Cmd(USART1,ENABLE);																			//使能USART1串口	USART_ITConfig(USART1,USART_IT_RXNE, ENABLE);		//使能USART1接收中断NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;				//中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;	//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;				//子优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能中断NVIC_Init(&NVIC_InitStructure);									//初始化中断}

2.发送命令或者读写数据都是通过最基本的发送数据、接受数据函数来实现。为了不出现数据丢失,每次发送数据前都需要判断上次发送的数据是否已经发送玩,这可以通过相关标志位来判断;同样,为了不出现数据重复,每次接收数据前都要判断接收缓存区是否为空。

void Flash_WriteData8(u8 Data)	//写8位数据(1个字节)
{u8 Wait=0;	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET&&Wait<20) //检查指定的SPI标志位设置与否:发送缓存空标志位,RESET表示正在发送数据{Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待}SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE);    //清除发送完成标志位SPI_I2S_SendData(SPI2,Data);				//发送Data}u8 Flash_ReadData( )														//读一个字节
{		u8 Wait=0;SPI_I2S_SendData(SPI2,0xff);									//发送0x00,产生时钟信号,用来接收数据,也可以发送其他无响应的命令while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET&&Wait<200) //检查指定的SPI标志位设置与否:接受缓存非空标志位{Wait++;	}	SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_RXNE);	//清除标志位return SPI_I2S_ReceiveData(SPI2);						 //返回通过SPI2最近接收的数据			
}

        需要注意:在接受数据的函数中,之所以在SPI_I2S_ReceiveData(SPI2)函数之前要使用SPI_I2S_SendData(SPI2,0xff)函数,是为了产生时钟信号。

        SPI采用的是主从通信结构,时钟信号只能由主设备产生,主设备发送数据的过程中会产生时钟信号,但是从设备发送数据时并不能自己产生时钟信号,所以就无法将数据一位一位发送出去(同步通信必须依靠时钟信号保持时序一致),那就只能依靠主设备产生时钟信号。主设备发送的0xFF,对从设备来说,是无效的数据,不会对该数据做出响应,但是主设备发送0xFF这个数据的时候,产生了时钟信号,所以从设备就依靠这段时钟信号,将数据发送给了主设备,主设备接受会暂存在接收缓存寄存器中,等接受到新的数据自动更新缓存器。

         具体的通信时序可查阅W25Q128的技术手册(W25Q128FV_PDF_数据手册_Datasheet),这里就不一一列举了。

3.把W25Q128常用的命令封装成函数,只要调用对应的函数,就能实现命令的发送与数据的读写

#define Flash_CS_H() GPIO_SetBits(GPIOB,GPIO_Pin_12)		//Flash 片选信号
#define Flash_CS_L() GPIO_ResetBits(GPIOB,GPIO_Pin_12)      //低电平选中,高电平取消选中/*****W25Q128常用命令定义*****/
#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_BlockErase32		0x52 		//32K块擦除
#define W25X_BlockErase64		0xD8 		//64K块擦除
#define W25X_SectorErase		0x20 		//4k扇区擦除
#define W25X_ChipErase			0xC7 		//整片擦除
#define W25X_PowerDown			0xB9 		//
#define W25X_ReleasePowerDown	0xAB 	
#define W25X_DeviceID			0xAB 		//读设备ID
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F		//读取JEDECIDu8 Flash_ReadSR(void)   									//读状态寄存器
{  u8 Byte=0;Flash_CS_L();        //CS选中Flash_WriteData8(W25X_ReadStatusReg); 	//发送读取状态寄存器命令    Byte=Flash_ReadData(); //读取一个字节 Flash_CS_H();        //CS取消选中return Byte;   
} void Flash_Write_SR(u8 Sr)   
{                               Flash_WriteData8(W25X_WriteStatusReg);	//发送写状态寄存器命令    Flash_WriteData8(Sr);               		//写入一个字节                             
}   void Flash_Write_Enable(void)   					//使能写入
{ Flash_CS_L();Flash_WriteData8(W25X_WriteEnable); 		//发送写使能Flash_CS_H();	
} void Flash_Write_Disable(void)  		 			//禁止写入
{                     Flash_WriteData8(W25X_WriteDisable);  //发送写禁止指令                             
}u32 Flash_ReadID(void)				//读取设备ID
{u32 Temp ;	 u8 TempL,TempM,TempH;	Flash_CS_L();Flash_WriteData8(W25X_JedecDeviceID);								//发送读取ID命令	    TempH=Flash_ReadData();			//接收高8位TempM=Flash_ReadData();			//接收中8位TempL=Flash_ReadData();			//接收低8位Temp=TempH;Temp<<=8;										//左移8位Temp|=TempM;								//高8位与低8位合并成16位(与运算后赋值)Temp<<=8;										//左移8位Temp|=TempL;								//高8位与低8位合并成16位(与运算后赋值)Flash_CS_H();return Temp;
} 
void Flash_ReadSector(u32 ReadAddr)  //读一个扇区
{u16 i;   										                              Flash_WriteData8(W25X_ReadData);         	//发送读取命令   Flash_WriteData8((ReadAddr>>16)&0xff);  	//发送24bit地址    Flash_WriteData8((ReadAddr>>8)&0xff);   Flash_WriteData8(ReadAddr&0xff);   for(i=0;i<4096;i++)				//一个扇区4096个Byte{ ARR2[i]=Flash_ReadData();   	//循环读取每个字节}
}void Flash_EraseSector(u32 Dst_Addr)   		//擦除扇区
{  	  Flash_Write_Enable();                  	//SET WEL 	 while((Flash_ReadSR()&0x01)==0x01);  		// 等待BUSY位清空{}Flash_WriteData8(W25X_SectorErase);      	//发送扇区擦除指令 Flash_WriteData8((Dst_Addr>>16)&0xff);  	//发送24bit地址    Flash_WriteData8((Dst_Addr>>8)&0xff);   Flash_WriteData8(Dst_Addr&0xff); while((Flash_ReadSR()&0x01)==0x01);  		// 等待BUSY位清空{}
} void Flash_WritePage(u32 WriteAddr,u8 NumByteToWrite)
{u8 i;  Flash_Write_Enable();                  		//写使能 Flash_CS_L();Flash_WriteData8(W25X_PageProgram);      	//发送写页命令   Flash_WriteData8((WriteAddr>>16)&0xff); 	//发送24bit地址    Flash_WriteData8((WriteAddr>>8)&0xff);   Flash_WriteData8(WriteAddr&0xff);   for(i=0;i<NumByteToWrite;i++){Flash_WriteData8(ARR1[i]);} Flash_CS_H();
}void Flash_WriteByte(u32 WriteAddr,u8 Data)		//写入一个字节
{Flash_Write_Enable();                  		//写使能 Flash_CS_L();Flash_WriteData8(W25X_PageProgram);      	//发送写页命令   Flash_WriteData8((WriteAddr>>16)&0xff); 	//发送24bit地址    Flash_WriteData8((WriteAddr>>8)&0xff);   Flash_WriteData8(WriteAddr&0xff);   Flash_WriteData8(Data); Flash_CS_H();
}

4.主函数。先读取设备ID,然后将数组ARR1的数据写入Flash的扇区0,再将扇区0的数据读取出来放在数组ARR2中,通过串口将ARR2的数据显示到电脑(串口调试助手)。

#include<stm32f10x.h>u8 ARR1[10]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A};//随意填入几个元素,后面将其元素写入Flash中
u8 ARR2[10];    //后面将Flash的数据读出来,复制到该数组中void USART_SendDatatoUSB( char ASCII[])    //串口发送字符串函数
{u8 i,j,Wait;for(i=0;i<12;i++){u8 Wait=0;				 	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) {Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待}USART_ClearFlag(USART1,USART_FLAG_TC);j=ASCII[i];USART_SendData(USART1,j);}Wait=0;while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) {Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待}USART_ClearFlag(USART1,USART_FLAG_TC);USART_SendData(USART1,10);	//换行	
}int main()
{SPI2_UserInit();			//SPI2初始化----控制SPI FlashUSART1_Userinit();			//USART1初始化--控制串口CH340Flash_Write_Enable();    //Flash写使能USART_SendDatatoUSB( "Flash:ID")Data=Flash_ReadID();    //读取ID(16位)while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET){}USART_SendData(USART1,(Data>>16)&0xFF);while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET){}USART_SendData(USART1,(Data>>8)&0xFF);Flash_EraseSector(0x000000);        //擦除扇区USART_SendDatatoUSB( "扇区擦除完成");Flash_WritePage(0x000000,10);        //写入数据USART_SendDatatoUSB( "数据写入成功");Flash_ReadSector(0x000000)  //读数据USART_SendDatatoUSB( "数据读取成功");for(i=0;i<10;i++){u8 Wait=0;				 	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) //检查指定的SPI标志位设置与否:发送缓存空标志位,RESET表示正在发送数据{Wait++;//循环计数200,计数200此(大概20us),不管是否标志位为空,都退出等待}USART_ClearFlag(USART1,USART_FLAG_TC);j=ARR2[i];USART_SendData(USART1,j);}
}

最后接收到数据如下。

相关文章:

STM32单片机初学8-SPI flash(W25Q128)数据读写

当使用单片机进行项目开发&#xff0c;涉及大量数据需要储存时&#xff08;例如使用了屏幕作为显示设备&#xff0c;常常需要存储图片、动画等数据&#xff09;&#xff0c;单靠单片机内部的Flash往往是不够用的。 如STM32F103系列&#xff0c;内部Flash最多只能达到512KByte&a…...

MS-SQL创建查询排序语句总结

重新捡起枪杆子&#xff0c;学习N年没用过的MS-SQL&#xff0c;整理一些学习笔记记录。 一、创建、修改和删除表 在SQL中&#xff0c;表有如下规则&#xff1a; 每张表都有一个名字&#xff0c;通常称为表名或关系名。表名必须以字母开头&#xff0c;最大长度为 30 个字符。一…...

subprocess—Python多进程模块

subprocess—Python多进程模块 1.概述 这篇文章介绍并行运算中的subprocess模块&#xff0c;subprocess 模块允许我们启动一个新进程&#xff0c;并连接到它们的输入/输出/错误管道&#xff0c;从而获取返回值。 subprocess 它可以用来调用第三方工具&#xff08;例如&#x…...

【APP渗透测试】 Android APP渗透测试技术实施以及工具使用(客户端服务端)

文章目录前言一、安全威胁分析二、主要风险项三、Android测试思维导图四、反编译工具五、Android客户端漏洞一、Jnaus漏洞漏洞二、数据备份配置风险漏洞漏洞三、Activity组件泄露漏洞漏洞四、BroadcastReceiver组件泄露漏洞漏洞五、允许模拟器Root环境登录漏洞漏洞六、未识别代…...

字符串匹配 - Overview

字符串匹配(String Matchiing)也称字符串搜索(String Searching)是字符串算法中重要的一种&#xff0c;是指从一个大字符串或文本中找到模式串出现的位置。字符串匹配概念字符串匹配问题的形式定义&#xff1a;文本&#xff08;Text&#xff09;是一个长度为 n 的数组 T[1..n]&…...

【IP课堂】Ip地址如何进行精准定位?

通过Ip地址定位&#xff0c;是目前网络上最常见的定位方式。当然&#xff0c;也是最简单的定位方式。其实方法大多都是雷同的&#xff0c;通过Ip定位&#xff0c;就目前网上公开的技术。如通过搜索关键词“定位&#xff0c;定位查询&#xff0c;Ip定位”等&#xff0c;只能查询…...

MySQL 临时表相关参数说明区别

MySQL 临时表参数innodb_temp_tablespaces_dir、innodb_temp_data_file_path、innodb_tmpdir、tmpdir 区分 解决方案 innodb_tmpdir&#xff1a; alter table生成中间表文件&#xff0c;innodb_tmpdir有指定效路径&#xff0c;优选选择innodb_tmpdir&#xff0c;没有则选择tm…...

第二章 变量和基本类型

1.string类型数据的另一种初始化方式 语法&#xff1a; string 变量名 (" 初始化内容 "); 2.C中的列表初始化 语法&#xff1a; 数据类型 变量名 { 变量初始化的值 } ; 数据类型 变量名 { 变量初始化的值 } ; 例&#xff1a; 3.引用常量 常量引…...

【Python】循环语句(while,for)、运算符、字符串格式化

一、while循环Python 编程中 while 语句用于循环执行程序&#xff0c;即在某条件下&#xff0c;循环执行某段程序&#xff0c;以处理需要重复处理的相同任务。其基本形式为&#xff1a;while 判断条件(condition)&#xff1a;执行语句(statements)执行语句可以是单个语句或语句…...

利用设计模式、反射写代码

软件工程师和码农最大的区别就是平时写代码时习惯问题&#xff0c;码农很喜欢写重复代码而软件工程师会利用各种技巧去干掉重复的冗余代码。 业务同学抱怨业务开发没有技术含量&#xff0c;用不到设计模式、Java 高级特性、OOP&#xff0c;平时写代码都在堆 CRUD&#xff0c;个…...

Spring Cloud Alibaba--seata微服务详解之分布式事务(三)

上篇讲述gateway的部署和使用&#xff0c;gateway统一管理和转发了HTTP请求&#xff0c;在互联网中大型项目一定存在复杂的业务关系&#xff0c;尤其在商城类软件中如淘宝、PDD等商城&#xff0c;尤其在秒杀场景中&#xff0c;并发量可以到达千万级别&#xff0c;此时数据库就会…...

[USACO2023-JAN-Bronze] T3 Moo Operations 题解

一、题目描述因为Bessie觉得玩平时经常玩的只包含C O和W的字符串无聊了&#xff0c;Farmer John 给了她Q个新的字符串(1≤Q≤100)&#xff0c;这Q个字符串只包含M和O。很明显&#xff0c;只包含M和O的单词里Bessie最喜欢的是”MOO”&#xff0c;所以她希望按照下面两个规则&…...

OKCC呼叫中心支持哪些接入方式?

使用OKCC系统开展呼叫中心业务&#xff0c;要将电话打通&#xff0c;需要什么样的设备接入到OKCC系统呢&#xff1f; 目前实际广泛使用的接入方式&#xff0c;既有硬件网关接入方式&#xff0c;也有软件接入方式&#xff0c;在生产实践中&#xff0c;我们须根据实际的需求及使…...

如何让手机共享电脑代理网络的WIFI热点

参考&#xff1a; 手机共享电脑的proxy网络 把电脑的网络代理给安卓设备如何将电脑的代理网络以WIFI热点的方式共享 电脑端设置代理&#xff1a; 打开电脑上的 proxy软件并设置其端口号&#xff08;例如&#xff1a;7890&#xff09;&#xff0c;且允许局域网&#xff08;例如…...

渲染有问题?怎么办?6种方法让你渲染无忧

简单点&#xff0c;解决问题的方式简单点。 日常工作中我们总会遇到各种各样的问题&#xff0c;比如渲不出图&#xff0c;速度太慢或效率太低&#xff0c;各种噪点和黑图等等&#xff0c;烦不胜烦&#xff0c;今天我就针对6个常见的问题给大家说下方法&#xff0c;一家之言仅供…...

中国人寿业务稳定性保障:“1+1+N” 落地生产全链路压测

引言 保险业务的数字化转型正如火如荼地进行&#xff0c;产品线上化、投保线上化、承保线上化、核保线上化等业务转型&#xff0c;导致系统的应用范围不断扩大&#xff0c;用户的高频访问也正在成为常态。同时&#xff0c;系统复杂性也呈指数上升&#xff0c;这些因素都增加了…...

2/17考试总结

时间安排 7:40–7:50 读题&#xff0c;T1 貌似是签到&#xff0c;T2,T4 DP,T3看起来很不可做。 7:50–8:00 T1,差分一下然后模拟就行了。 8:00–10:20 T2,注意到值域很小&#xff0c;可以考虑状压&#xff0c;想到一个状压状态数较少的 dp &#xff0c;然后挂得彻底。发现有一…...

零信任-360连接云介绍(9)

​360零信任介绍 360零信任又称360连接云安全访问平台(下文简称为&#xff1a;360连接云)&#xff0c;360连接云&#xff0c;是360基于零信任安全理念&#xff0c;以身份为基础、动态访问控制为核心打造的安全访问平台。 通过收缩业务暴露面、自适应增强身份认证、终端持续检…...

使用dlib进行人脸检测和对齐

最近在配置人脸属性识别的服务&#xff0c;用过faceboxes_detector&#xff08;faster rcnn的包&#xff09;&#xff0c;也用过face_recognition的&#xff0c;但是她们都没有做人脸对齐&#xff0c;而且检测人脸的范围也不太一样。没有做人脸对齐的时候&#xff0c;使用属性识…...

将python代码封装成c版本的dll动态链接库

前言 将python程序打包成DLL文件&#xff0c;然后用C调用生成的DLL文件&#xff0c;这是一种用C调用python的方法&#xff0c;这一块比较容易遇到坑。网上关于这一块的教程不是很多&#xff0c;而且大部分都不能完全解决问题。我在傻傻挣扎了几天之后&#xff0c;终于试出了一个…...

AI技术网关如何用于安全生产监测?有什么优势?

现代工业生产和运营的规模越来越庞大、系统和结构越来越复杂&#xff0c;现场的风险点多面广&#xff0c;给作业一线的安全监管带来极大的挑战。 针对工地、煤矿、危化品、加油站、烟花爆竹、电力等行业的安全生产监管场景&#xff0c;可以借助AI智能与物联网技术&#xff0c;…...

2|数据挖掘|关联规则|Association Rules|Apriori算法|Frequent-pattern tree和FP-growth算法|11.11

...

刷题记录:牛客NC53370 Forsaken的三维数点

传送门:牛客 题目描述: Forsaken现在在一个三维空间中&#xff0c;空间中每个点都可以用(x,y,z)表示。突然&#xff0c;三维空间的主人出现 了&#xff0c;如果Forsaken想要继续在三维空间中呆下去&#xff0c;他就必须回答三维空间主人的问题.主人会在空间 中坐标为(x,y,z)处…...

lombok的原理 和 使用

原理Lombok能以简单的注解形式来简化java代码&#xff0c;提高开发人员的开发效率。其实并没有改变字节码文件的任何内容&#xff0c;只是简化的程序员编写代码的方式。不使用lombok&#xff1a;使用lombok&#xff1a;lombok常用注解Setter &#xff1a;注解在类或字段&#x…...

UDP网络编程

UDP和TCP 前几节我们提到了计算机网络编程中的TCP编程&#xff0c;TCP和UDP都是计算机机网络通信的传输层中的传输协议&#xff0c;今天我们来学习计算机网络编程中的基于UDP传输协议的网络编程 首先我们要了解TCP和UDP的区别 它们是同属于计算机网络传输层的传输协议 TCP&…...

“合并区间”问题解析及其思考

合并区间题目以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。解析本题思路相对比较容易想先对各个区间按左…...

2023年理想新能源汽车核心部件解密

理想主要硬件清单(L9车型) 汽车结构 设置名称 规格 备注 价格 供应商 感知层...

C++ 将一个vector内容赋值给另一个vector,及swap与assign的区别

在本文中&#xff0c;我们将主要介绍5种将一个vector内容赋值给另一个vector的方式&#xff0c;顺便讨论下swap与assign的区别。 赋值 方式一、申明时赋值 vector<int> v2; v2.push_back(0); v2.push_back(1);vector<int> v1(v2); //声明方式二、使用assign赋值…...

PMP的价值有哪些?

我个人认为&#xff0c;考证只有两个出发点是正确的。一是为了提升自己或者满足自己的兴趣&#xff0c;另一个是和自己的职业规划相关。 比如&#xff0c;有同学想提升自己英语能力&#xff0c;可以考四六级&#xff0c;或者更厉害一点的考雅思、托福。比如&#xff0c;有的同…...

OnGUI label 控件||Unity 3D GUI教程||OnGUI Background Color 控件

Unity 3D Label 控件用于在设备的屏幕上创建文本标签和纹理标签&#xff0c;和Box 控件类似&#xff0c;可以显示文本内容或图片。Label 控件一般用于显示提示性的信息&#xff0c;如当前窗口的名称、游戏中游戏对象的名字、游戏对玩家的任务提示和功能介绍等。具体使用方法如下…...

dede 门户网站/推广产品的方法和步骤

共回答了13个问题采纳率&#xff1a;84.6%172.19.52.128/26代表该网段为172.19.52.128-172.19.52.191&#xff0c;段内2^8/2^(26-3*8)64个IP地址&#xff0c;划分为两个16IP地址的子网&#xff0c;一个32IP地址的子网&#xff0c;三个子网分别为172.19.52.128/28、172.19.52.14…...

视频发布播放网站建设/中山网站seo

例21题目&#xff1a;猴子吃桃问题&#xff1a;猴子第一天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不瘾&#xff0c;又多吃了一个第二天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时&a…...

wordpress地图在哪/安卓aso优化

进入某个目录的命令&#xff08;进入/shared/ods目录下&#xff09;&#xff1a;cd /shared/ods成功进入后是这样子的&#xff1a;slave1:/shared/ods #查看进入的目录下的文件的命令(输入两个小写字母ll)&#xff1a;slave1:/shared/ods # ll 退回上一层目录的命令&#xff…...

wordpress博客页修改/百度营销推广靠谱吗

一、废话 听说很多地方都降温了&#xff0c;希望大家注意身体&#xff0c;别冻着了&#xff01; 二、正文 在Android的网络通讯中&#xff0c;通常会使用Socket进行设备间数的数据通讯&#xff0c;使用Http来对网络数据进行请求。 1、Socket&#xff08;套接字&#xff09; 不管…...

软件搭建/宁波seo外包推广平台

class Solution:def maxSubArray(self, nums: [int]) -> int:res nums[0]res_max resfor num in nums[1:]:res num if res < 0 else res numres_max max(res_max, res)return res_max...

个人网站不备案做经营性质网站/上海网站推广广告

为了我们电脑的安全&#xff0c;一般我们都会给电脑设置开机密码&#xff0c;这样对电脑来说就比较安全了。但是&#xff0c;每次开机都的输入密码才可以进入系统桌面&#xff0c;这样比较麻烦&#xff0c;而且还浪费时间&#xff1b;想取消开机密码&#xff0c;但不知道怎么取…...