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

STM32F407实现SD卡的读写功能

文章目录

  • 前言
  • 一、SDIO简介
  • 二、SD卡操作
    • 1.读操作
    • 2.写数据
    • 3.擦除操作
    • 4.最终效果
    • 5.完整工程


前言

在STM32中存储空间是有限的,对于需要存储大量数据的项目就需要外扩存储空间,一般会选择FLASH、EEPROM或者SD卡。SD是这三种中可达空间最大的,所以SD卡在大容量存储中发挥着中要的作用,本次OTA项目是使用了SD卡存储云平台下发的数据流,再将所需的数据存在eflash内,SD卡还可以起到版本恢复的作用。

一、SDIO简介

想要操作SD卡可以通过SPI或者SDIO的方式,本实验使用的是SDIO的方式对SD卡操作,下图是SDIO和MCU接口示意图:
在这里插入图片描述
SD卡使用9-pin接口通信,其中3根电源线、1根时钟线、1根命令线和4根数据线,具体如下:
CLK:时钟线,由SDIO主机产生,即由STM32控制器输出;
CMD:命令控制线,SDIO主机通过该线发送命令控制SD卡,如果命令要求SD卡提供应答,SD卡也是通过该线传输应答信息;
D0-3:数据线,传输读写数据;SD卡可将D0拉低表示忙状态;
VDD、VSS1、VSS2:电源和地信号。

二、SD卡操作

1.读操作

SD数据是以块(Black)形式传输的,SDHC卡数据块长度一般为512字节,数据可以从主机到卡,也可以是从卡到主机。数据块需要CRC位来保证数据传输成功,还要注意:起始位使用0,结束位还使用1。
在这里插入图片描述
上图是SD卡读操作的框图,其中CRC是直接通过硬件的方式进行的。
代码如下:

u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt)
{u8 sta=SD_OK;long long lsector=sector;u8 n;lsector<<=9;if((u32)buf%4!=0){for(n=0;n<cnt;n++){sta=SD_ReadBlock(SDIO_DATA_BUFFER,lsector+512*n,512);//单个sector的读操作memcpy(buf,SDIO_DATA_BUFFER,512);buf+=512;} }else{if(cnt==1)sta=SD_ReadBlock(buf,lsector,512);    	//单个sector的读操作else sta=SD_ReadMultiBlocks(buf,lsector,512,cnt);//多个sector  }return sta;
}SD_Error SD_ReadBlock(u8 *buf,long long addr,u16 blksize)
{	  SD_Error errorstatus=SD_OK;u8 power;u32 count=0,*tempbuff=(u32*)buf;//转换为u32指针 u32 timeout=SDIO_DATATIMEOUT;   if(NULL==buf)return SD_INVALID_PARAMETER; SDIO->DCTRL=0x0;	//数据控制寄存器清零(关DMA) if(CardType==SDIO_HIGH_CAPACITY_SD_CARD)//大容量卡{blksize=512;addr>>=9;}   SDIO_DataInitStructure.SDIO_DataBlockSize= SDIO_DataBlockSize_1b ;//清除DPSM状态机配置SDIO_DataInitStructure.SDIO_DataLength= 0 ;SDIO_DataInitStructure.SDIO_DataTimeOut=SD_DATATIMEOUT ;SDIO_DataInitStructure.SDIO_DPSM=SDIO_DPSM_Enable;SDIO_DataInitStructure.SDIO_TransferDir=SDIO_TransferDir_ToCard;SDIO_DataInitStructure.SDIO_TransferMode=SDIO_TransferMode_Block;SDIO_DataConfig(&SDIO_DataInitStructure);if(SDIO->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//卡锁了if((blksize>0)&&(blksize<=2048)&&((blksize&(blksize-1))==0)){power=convert_from_bytes_to_power_of_two(blksize);	SDIO_CmdInitStructure.SDIO_Argument =  blksize;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);//发送CMD16+设置数据长度为blksize,短响应errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN);	//等待R1响应 if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	}else return SD_INVALID_PARAMETER;	  	 SDIO_DataInitStructure.SDIO_DataBlockSize= power<<4 ;//清除DPSM状态机配置SDIO_DataInitStructure.SDIO_DataLength= blksize ;SDIO_DataInitStructure.SDIO_DataTimeOut=SD_DATATIMEOUT ;SDIO_DataInitStructure.SDIO_DPSM=SDIO_DPSM_Enable;SDIO_DataInitStructure.SDIO_TransferDir=SDIO_TransferDir_ToSDIO;SDIO_DataInitStructure.SDIO_TransferMode=SDIO_TransferMode_Block;SDIO_DataConfig(&SDIO_DataInitStructure);SDIO_CmdInitStructure.SDIO_Argument =  addr;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);//发送CMD17+从addr地址出读取数据,短响应 errorstatus=CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);//等待R1响应   if(errorstatus!=SD_OK)return errorstatus;   		//响应错误	 if(DeviceMode==SD_POLLING_MODE)						//查询模式,轮询数据	 {INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)while(!(SDIO->STA&((1<<5)|(1<<1)|(1<<3)|(1<<10)|(1<<9))))//无上溢/CRC/超时/完成(标志)/起始位错误{if(SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)						//接收区半满,表示至少存了8个字{for(count=0;count<8;count++)			//循环读取数据{*(tempbuff+count)=SDIO->FIFO;}tempbuff+=8;	 timeout=0X7FFFFF; 	//读数据溢出时间}else 	//处理超时{if(timeout==0)return SD_DATA_TIMEOUT;timeout--;}} if(SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)		//数据超时错误{										   SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT); 	//清错误标志return SD_DATA_TIMEOUT;}else if(SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)	//数据块CRC错误{SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);  		//清错误标志return SD_DATA_CRC_FAIL;		   }else if(SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET) 	//接收fifo上溢错误{SDIO_ClearFlag(SDIO_FLAG_RXOVERR);		//清错误标志return SD_RX_OVERRUN;		 }else if(SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET) 	//接收起始位错误{SDIO_ClearFlag(SDIO_FLAG_STBITERR);//清错误标志return SD_START_BIT_ERR;		 }   while(SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)	//FIFO里面,还存在可用数据{*tempbuff=SDIO->FIFO;	//循环读取数据tempbuff++;}INTX_ENABLE();//开启总中断SDIO_ClearFlag(SDIO_STATIC_FLAGS);//清除所有标记}else if(DeviceMode==SD_DMA_MODE){TransferError=SD_OK;StopCondition=0;			//单块读,不需要发送停止传输指令TransferEnd=0;				//传输结束标置位,在中断服务置1SDIO->MASK|=(1<<1)|(1<<3)|(1<<8)|(1<<5)|(1<<9);	//配置需要的中断 SDIO->DCTRL|=1<<3;		 	//SDIO DMA使能 SD_DMA_Config((u32*)buf,blksize,DMA_DIR_PeripheralToMemory); while(((DMA2->LISR&(1<<27))==RESET)&&(TransferEnd==0)&&(TransferError==SD_OK)&&timeout)timeout--;//等待传输完成 if(timeout==0)return SD_DATA_TIMEOUT;//超时if(TransferError!=SD_OK)errorstatus=TransferError;  }   return errorstatus; 
}

在上面的代码中SD_ReadDisk()实际上是调用的是SD_ReadBlock(),该函数先发送 CMD16,用于设置块大小,然后配置 SDIO 控制器读数据的长度,这里我们用到函数 convert_from_bytes_to_power_of_two 求出 blksize 以 2 为底的指数,用于 SDIO 读数据长度设置。然后发送 CMD17(带地址参数 addr),从指定地址读取一块数据。最后,根据我们所设置的模式(查询模式/DMA 模式),从 SDIO_FIFO 读出数据。在这些函数中数据buf的地址必须4字节对齐。

2.写数据

在这里插入图片描述
上图是写数据的程序框图,可以看到左边的CMD,操作SD卡就是通过发送这个CMD来控制的,这里的内容比较多不做很详细的介绍,想要深入了解可以查看手册。
程序代码:

u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
{u8 sta=SD_OK;u8 n;long long lsector=sector;lsector<<=9;if((u32)buf%4!=0){for(n=0;n<cnt;n++){memcpy(SDIO_DATA_BUFFER,buf,512);sta=SD_WriteBlock(SDIO_DATA_BUFFER,lsector+512*n,512);//单个sector的写操作buf+=512;} }else{if(cnt==1)sta=SD_WriteBlock(buf,lsector,512);    	//单个sector的写操作else sta=SD_WriteMultiBlocks(buf,lsector,512,cnt);	//多个sector  }return sta;
}
SD_Error SD_WriteBlock(u8 *buf,long long addr,  u16 blksize)
{SD_Error errorstatus = SD_OK;u8  power=0,cardstate=0;u32 timeout=0,bytestransferred=0;u32 cardstatus=0,count=0,restwords=0;u32	tlen=blksize;						//总长度(字节)u32*tempbuff=(u32*)buf;					if(buf==NULL)return SD_INVALID_PARAMETER;//参数错误  SDIO->DCTRL=0x0;							//数据控制寄存器清零(关DMA)SDIO_DataInitStructure.SDIO_DataBlockSize= 0; ;//清除DPSM状态机配置SDIO_DataInitStructure.SDIO_DataLength= 0 ;SDIO_DataInitStructure.SDIO_DataTimeOut=SD_DATATIMEOUT ;SDIO_DataInitStructure.SDIO_DPSM=SDIO_DPSM_Enable;SDIO_DataInitStructure.SDIO_TransferDir=SDIO_TransferDir_ToCard;SDIO_DataInitStructure.SDIO_TransferMode=SDIO_TransferMode_Block;SDIO_DataConfig(&SDIO_DataInitStructure);if(SDIO->RESP1&SD_CARD_LOCKED)return SD_LOCK_UNLOCK_FAILED;//卡锁了if(CardType==SDIO_HIGH_CAPACITY_SD_CARD)	//大容量卡{blksize=512;addr>>=9;}    if((blksize>0)&&(blksize<=2048)&&((blksize&(blksize-1))==0)){power=convert_from_bytes_to_power_of_two(blksize);	SDIO_CmdInitStructure.SDIO_Argument = blksize;//发送CMD16+设置数据长度为blksize,短响应 	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);	errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN);	//等待R1响应  if(errorstatus!=SD_OK)return errorstatus;   	//响应错误	 }else return SD_INVALID_PARAMETER;	SDIO_CmdInitStructure.SDIO_Argument = (u32)RCA<<16;//发送CMD13,查询卡的状态,短响应 	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);	errorstatus=CmdResp1Error(SD_CMD_SEND_STATUS);		//等待R1响应  if(errorstatus!=SD_OK)return errorstatus;cardstatus=SDIO->RESP1;													  timeout=SD_DATATIMEOUT;while(((cardstatus&0x00000100)==0)&&(timeout>0)) 	//检查READY_FOR_DATA位是否置位{timeout--;  SDIO_CmdInitStructure.SDIO_Argument = (u32)RCA<<16;//发送CMD13,查询卡的状态,短响应SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);	errorstatus=CmdResp1Error(SD_CMD_SEND_STATUS);	//等待R1响应   if(errorstatus!=SD_OK)return errorstatus;		cardstatus=SDIO->RESP1;													  }if(timeout==0)return SD_ERROR;SDIO_CmdInitStructure.SDIO_Argument = addr;//发送CMD24,写单块指令,短响应 	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);	errorstatus=CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);//等待R1响应  if(errorstatus!=SD_OK)return errorstatus;   	 StopCondition=0;									//单块写,不需要发送停止传输指令 SDIO_DataInitStructure.SDIO_DataBlockSize= power<<4; ;	//blksize, 控制器到卡	SDIO_DataInitStructure.SDIO_DataLength= blksize ;SDIO_DataInitStructure.SDIO_DataTimeOut=SD_DATATIMEOUT ;SDIO_DataInitStructure.SDIO_DPSM=SDIO_DPSM_Enable;SDIO_DataInitStructure.SDIO_TransferDir=SDIO_TransferDir_ToCard;SDIO_DataInitStructure.SDIO_TransferMode=SDIO_TransferMode_Block;SDIO_DataConfig(&SDIO_DataInitStructure);timeout=SDIO_DATATIMEOUT;if (DeviceMode == SD_POLLING_MODE){INTX_DISABLE();//关闭总中断(POLLING模式,严禁中断打断SDIO读写操作!!!)while(!(SDIO->STA&((1<<10)|(1<<4)|(1<<1)|(1<<3)|(1<<9))))//数据块发送成功/下溢/CRC/超时/起始位错误{if(SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) != RESET)							//发送区半空,表示至少存了8个字{if((tlen-bytestransferred)<SD_HALFFIFOBYTES)//不够32字节了{restwords=((tlen-bytestransferred)%4==0)?((tlen-bytestransferred)/4):((tlen-bytestransferred)/4+1);for(count=0;count<restwords;count++,tempbuff++,bytestransferred+=4){SDIO->FIFO=*tempbuff;}}else{for(count=0;count<8;count++){SDIO->FIFO=*(tempbuff+count);}tempbuff+=8;bytestransferred+=32;}timeout=0X3FFFFFFF;	//写数据溢出时间}else{if(timeout==0)return SD_DATA_TIMEOUT;timeout--;}} if(SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)		//数据超时错误{										   SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT); 	//清错误标志return SD_DATA_TIMEOUT;}else if(SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)	//数据块CRC错误{SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);  		//清错误标志return SD_DATA_CRC_FAIL;		   }else if(SDIO_GetFlagStatus(SDIO_FLAG_TXUNDERR) != RESET) 	//接收fifo下溢错误{SDIO_ClearFlag(SDIO_FLAG_TXUNDERR);		//清错误标志return SD_TX_UNDERRUN;		 }else if(SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET) 	//接收起始位错误{SDIO_ClearFlag(SDIO_FLAG_STBITERR);//清错误标志return SD_START_BIT_ERR;		 }   INTX_ENABLE();//开启总中断SDIO_ClearFlag(SDIO_STATIC_FLAGS);//清除所有标记  }else if(DeviceMode==SD_DMA_MODE){TransferError=SD_OK;StopCondition=0;			//单块写,不需要发送停止传输指令 TransferEnd=0;				//传输结束标置位,在中断服务置1SDIO->MASK|=(1<<1)|(1<<3)|(1<<8)|(1<<4)|(1<<9);	//配置产生数据接收完成中断SD_DMA_Config((u32*)buf,blksize,DMA_DIR_MemoryToPeripheral);				//SDIO DMA配置SDIO->DCTRL|=1<<3;								//SDIO DMA使能.  while(((DMA2->LISR&(1<<27))==RESET)&&timeout)timeout--;//等待传输完成 if(timeout==0){SD_Init();	 					//重新初始化SD卡,可以解决写入死机的问题return SD_DATA_TIMEOUT;			//超时	 }timeout=SDIO_DATATIMEOUT;while((TransferEnd==0)&&(TransferError==SD_OK)&&timeout)timeout--;if(timeout==0)return SD_DATA_TIMEOUT;			//超时	 if(TransferError!=SD_OK)return TransferError;}  SDIO_ClearFlag(SDIO_STATIC_FLAGS);//清除所有标记errorstatus=IsCardProgramming(&cardstate);while((errorstatus==SD_OK)&&((cardstate==SD_CARD_PROGRAMMING)||(cardstate==SD_CARD_RECEIVING))){errorstatus=IsCardProgramming(&cardstate);}   return errorstatus;
}

可以看到SD_WriteDisk()函数调用的是SD_WriteBlock()函数,执行写数据块命令(CMD24-27)时,主机把一个或多个数据块从主机传送到卡中,同时在每个数据块的末尾传送一个CRC码。一个支持写数据块命令的卡应该始终能够接收由WRITE_BL_LEN定义的数据块。如果CRC校验错误,卡通过SDIO_D信号线指示错误,传送的数据被丢弃而不被写入,所有后续(在多块写模式下)传送的数据块将被忽略。

3.擦除操作

SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
{SD_Error errorstatus = SD_OK;uint32_t delay = 0;__IO uint32_t maxdelay = 0;uint8_t cardstate = 0;/*!< 检查SD卡是否支持擦除操作 */if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0){errorstatus = SD_REQUEST_NOT_APPLICABLE;return(errorstatus);}maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);    //延时,根据时钟分频设计来计算if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)    //卡上锁{errorstatus = SD_LOCK_UNLOCK_FAILED;return(errorstatus);}/* SDHC卡,地址参数为块地址,每块512字节,SDSC卡地址为字节地址 */if (CardType == SDIO_HIGH_CAPACITY_SD_CARD) {startaddr /= 512;endaddr /= 512;}/*!<  ERASE_GROUP_START (CMD32) and SD_CMD_SD_ERASE_GRP_END(CMD33) */if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType)){/*!< Send CMD32 SD_ERASE_GRP_START with startaddr  */SDIO_CmdInitStructure.SDIO_Argument = startaddr;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);if (errorstatus != SD_OK){return(errorstatus);}/*!< Send CMD33 SD_ERASE_GRP_END with endaddr  */SDIO_CmdInitStructure.SDIO_Argument = endaddr;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_END);if (errorstatus != SD_OK){return(errorstatus);}}/*!< Send CMD38 ERASE */SDIO_CmdInitStructure.SDIO_Argument = 0;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus = CmdResp1Error(SD_CMD_ERASE);if (errorstatus != SD_OK){return(errorstatus);}for (delay = 0; delay < maxdelay; delay++){}/*!< 等待SD卡的内部时序操作完成 */errorstatus = IsCardProgramming(&cardstate);while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))//SD卡编程 || SD卡接收{errorstatus = IsCardProgramming(&cardstate);    //检测SD卡是否在读写操作}return(errorstatus);
}

可以看到上面的函数是通过CMD32、33、38来对SD卡进行擦除的。

4.最终效果

在这里插入图片描述
在上图中可以看到mian函数对SDIO进行了初始化,之后打印出SD卡的基本信息,再写入1024个数据到两个扇区,当按下按键的时候再将两个扇区的数据读取出来并且打印到串口,在串口助手可以看到数据从0到FF之后又从0开始,这是因为我在存数据是定义的变量是u8,所以到达最大值就重新计数了。

5.完整工程

我用夸克网盘分享了「SD卡读写.rar」,点击链接即可保存。
链接:https://pan.quark.cn/s/98bdba7113e4

相关文章:

STM32F407实现SD卡的读写功能

文章目录 前言一、SDIO简介二、SD卡操作1.读操作2.写数据3.擦除操作4.最终效果5.完整工程 前言 在STM32中存储空间是有限的&#xff0c;对于需要存储大量数据的项目就需要外扩存储空间&#xff0c;一般会选择FLASH、EEPROM或者SD卡。SD是这三种中可达空间最大的&#xff0c;所…...

Vue 3中的setup【与Vue 2的区别】

一、前言 在Vue 3中&#xff0c;setup是组合式API&#xff08;Composition API&#xff09;的核心入口函数。其核心作用是为组件提供灵活的逻辑组织方式&#xff0c;解决复杂组件中逻辑碎片化的问题。 二、核心作用 1.初始化响应式数据 通过ref和reactive等API声明响应式状态…...

基于PySide6的YOLOv8/11目标检测GUI界面——智能安全帽检测系统

&#x1f4d6; 前言 在工业安全领域&#xff0c;智能安全帽检测是保障工人生命安全的重要技术手段。本文将介绍如何利用YOLOv8/YOLOv11目标检测算法与PySide6 GUI框架&#xff0c;开发一套功能完整的智能安全帽检测系统。系统支持&#xff1a; 动态切换检测模型&#xff08;Y…...

AF3 generate_chain_data_cache脚本解读

AlphaFold3 generate_chain_data_cache 脚本在源代码的scripts文件夹下。该脚本从指定目录中批量解析 mmCIF/PDB 文件的工具,并将每个链的基本信息(序列、分辨率、是否属于聚类等)提取并写入 JSON 文件,主要用于后续蛋白质建模、过滤或训练数据准备。 源代码: import ar…...

C/C++不透明指针

今天在ESP32编程中又看到了这个词&#xff0c;这个词出现在cursor回答中。回答如下&#xff1a; struct esp_netif_obj; typedef struct esp_netif_obj esp_netif_t;esp_netif_obj的具体实现细节被隐藏了用户代码只能通过esp_netif_t类型指针来操作网络接口这种封装方式被称为…...

电力实习中需要注意哪些安全用电问题

电力实习中需要注意哪些安全用电问题 在电工实习中&#xff0c;由于涉及到电力设备和电气设施&#xff0c;安全问题尤为重要。 以下是电工实习中需要注意的安全问题&#xff1a; 一、电气设备及线路安全 使用电气设备前&#xff0c;应确保设备具有良好的电气绝缘&#xff0c…...

【版本控制】git命令使用大全

大家好&#xff0c;我是jstart千语。今天来总结一下git的使用命令&#xff0c;上文会先将git命令都列出来&#xff0c;便于快速寻找&#xff0c;然后还会对部分常用命令图文讲解&#xff0c;适合新手&#xff0c;让你快速地理解。最后还会总结在idea中使用git。如果有缺失的&am…...

Day09【基于Tripletloss实现的简单意图识别对话系统】

基于Tripletloss实现的表示型文本匹配 目标数据准备参数配置数据处理Triplet Loss目标Triplet Loss计算公式公式说明 模型构建网络结构设计网络训练目标损失函数设计 主程序推理预测类初始化加载问答知识库文本向量化知识库查询主程序main测试测试效果 参考博客 目标 在此之前…...

什么是HIGG验厂,HIGG验厂有什么要求?HIGG验厂有什么作用

什么是Higg验厂&#xff1f; Higg验厂&#xff08;Higg Facility Environmental Module, FEM & Higg Facility Social & Labor Module, FSLM&#xff09;是由可持续服装联盟&#xff08;SAC, Sustainable Apparel Coalition&#xff09;开发的一套评估工具&#xff0c…...

SmolVLM新模型技术解读笔记

原文地址&#xff1a;https://huggingface.co/blog/zh/smolervlm 一、核心发布概要 新成员亮相&#xff1a;推出256M&#xff08;2.56亿参数&#xff09;与500M&#xff08;5亿参数&#xff09;视觉语言模型关键定位&#xff1a;目前全球最小VLM&#xff08;256M&#xff09;…...

解决USG5150防火墙web无法连接问题

参考 防火墙usg5500&#xff08;V300R001C00SPC700&#xff09;WEB界面无法登陆 现象 Web防火墙突然无法web登录&#xff0c;Ping通&#xff0c;但是Tcpping端口不通。无论是从外网、还是内网都一样。 Probing 192.168.100.1:1234/tcp - No response - time2047.528ms Prob…...

Resilience4j与Spring Cloud Gateway整合指南:构建弹性的API网关

什么是Resilience4j&#xff1f; Resilience4j是一个轻量级的容错库&#xff0c;专为Java 8和函数式编程设计。它借鉴了Netflix Hystrix的设计理念&#xff0c;但更加轻量且专注于Java 8的函数式编程风格。Resilience4j提供了多种容错机制&#xff0c;帮助开发者构建弹性强健的…...

Quipus,LightRag的Go版本的实现

1 项目简介 奇谱系统当前版本以知识库为核心&#xff0c;基于知识库可以快构建自己的问答系统。知识库的Rag模块的构建算法是参考了LightRag的算法流程的Go版本优化实现&#xff0c;它可以帮助你快速、准确地构建自己的知识库&#xff0c;搭建属于自己的AI智能助手。与当前LLM…...

怎样完成本地模型知识库检索问答RAG

怎样完成本地模型知识库检索问答RAG 目录 怎样完成本地模型知识库检索问答RAG使用密集检索器和系数检索器混合方式完成知识库相似检索1. 导入必要的库2. 加载文档3. 文本分割4. 初始化嵌入模型5. 创建向量数据库6. 初始化大语言模型7. 构建问答链8. 提出问题并检索相关文档9. 合…...

研发效率破局之道阅读总结(2)流程优化

研发效率破局之道阅读总结(2)流程优化 Author: Once Day Date: 2025年4月15日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 程序的艺术_Once-Day…...

解决PIP 安装出错ERROR: cp310-cp310-manylinux_2_28_x86_64.whl is not a supported wheel

ERROR: torch-2.8.0.dev20250325cu128-cp310-cp310-manylinux_2_28_x86_64.whl is not a supported wheel on this platform. 可以 pip debug --verbose | grep manylinux | grep cp310 WARNING: This command is only meant for debugging. Do not use this with automation f…...

b站golang后端开发一面

go和其他语言的对比 Golang&#xff08;也称为Go语言&#xff09;是一种静态类型、编译型语言&#xff0c;由Google开发&#xff0c;以其简洁、高效和强大的并发处理能力著称。 Golang的设计哲学强调简洁明了。与Python类似&#xff0c;Go语法简洁&#xff0c;易于学习和编写。…...

Vue3 SSR Serverless架构革命:弹性计算与量子加速

一、全维度Serverless SSR架构 1.1 蜂巢式弹性调度系统 1.2 冷启动时间优化表 优化策略Node.js冷启(ms)Deno冷启(ms)Bun冷启(ms)裸启动1800960420预编译二进制650380210内存快照预热22016090WASM实例池15011075量子状态预载453832 二、边缘渲染协议升级 2.1 流式SSR响应协议…...

深度大脑:AI大模型的设计与运行原理

AI大模型的设计与运行原理涉及多个复杂环节&#xff0c;以下是系统化的总结&#xff0c;结合核心要点与补充细节&#xff1a; 一、AI大模型的设计 1. 深度神经网络架构 Transformer&#xff1a;取代RNN/CNN&#xff0c;解决长程依赖问题。核心组件&#xff1a; 自注意力机制…...

Python网络编程从入门到精通:Socket核心技术+TCP/UDP实战详解

引言 网络编程是构建现代分布式系统的核心能力&#xff0c;而Socket作为通信的基石&#xff0c;其重要性不言而喻。本文将从零开始&#xff0c;通过清晰的代码示例、原理剖析和对比分析&#xff0c;带你彻底掌握Python中的Socket编程技术&#xff0c;涵盖TCP可靠连接、UDP高效…...

使用CMake生成Opencv对应库文件

opencv环境配置&#xff1a;版本3.4/3.2(OpenCV-3.4.3) CMake&#xff1a;3.12.1 D:\OpenCv\opencv\build\x64\vc16\bin路径添加至环境变量中 CMake环境配置&#xff1a; D:\Install_QT\bin路径添加至环境变量中&#xff08;path中即可&#xff09; QT5环境变量配置&#xff1a…...

MySQL 数据库备份和恢复全指南

MySQL 是一款常用的开源数据库系统&#xff0c;在日常运维中&#xff0c;数据备份和恢复是系统管理的重要一环。本文将细致介绍 MySQL 两大备份方案—— mysqldump 和 XtraBackup&#xff0c;包括备份方式、恢复步骤、定时脚本、远程备份和常见问题处理方案。 一、mysqldump 备…...

关于我的服务器

最近我买了台腾讯云服务器&#xff0c;然后新手小白只会用宝塔。。。 安装完之后默认的端口是8888&#xff0c;打开面板就会提示我有风险。然后 我改了端口之后&#xff0c;怎么都打不开。 于是 学到了几句命令可以使用&#xff1a; //查看端口是否已经修改成功 cat www/se…...

spring面试题

1&#xff0c;如何理解spring boot中的starter Starter是一种简化依赖管理和自动配置的核心机制&#xff0c;能快速集成特定功能模块&#xff0c;无需手动配置复杂依赖和xml文件。 依赖简化&#xff1a;将某个功能模块所需的所有依赖打包成一个“一站式”依赖&#xff0c;开发…...

python setup.py学习

Python-setup进阶打包命令 Python-setup进阶打包命令_python setup-CSDN博客 packages 需要处理的包目录&#xff08;包含__init__.py的文件夹&#xff09;&#xff0c;这里通常使用 find_packages()&#xff0c;它默认在和setup.py同一目录下搜索各个含有 __init__.py的包。…...

最简单的使用SDL2 播放原始音频数据程序

author: hjjdebug date: 2025年 04月 15日 星期二 14:02:05 CST description: 最简单的使用SDL2 播放原始音频数据程序 文章目录 1.最简单的播放音频的程序是什么样子的?2. 怎样用SDL 来编写音频播放器代码?2.1 SDL播放音频核心代码:混音函数2.2 先看看音频播放的可能的两种框…...

利用IDEA开发Spark-SQL

创建子模块Spark-SQL&#xff0c;并添加依赖 创建Spark-SQL的测试代码&#xff1a; 运行结果&#xff1a; 自定义函数&#xff1a; UDF&#xff1a; UDAF&#xff08;自定义聚合函数&#xff09; 强类型的 Dataset 和弱类型的 DataFrame 都提供了相关的聚合函数&#xff0c; …...

随身Wi-Fi能跑PCDN?

随身WiFi可以用于运行PCDN&#xff08;点对点内容分发网络&#xff09;&#xff0c;但存在技术限制和潜在风险&#xff0c;需谨慎操作。 可行性分析 技术基础 随身WiFi本质是便携式无线路由器&#xff0c;具备网络接入和分发能力&#xff0c;理论上可配置为PCDN节点。 部分用户…...

Google-A2A协议全面解析:一文掌握Agent-to-Agent协议的核心与应用

前言&#xff1a; 在当今人工智能技术飞速发展的时代&#xff0c;智能体&#xff08;Agent&#xff09;已悄然融入我们生活的各个角落。无论是个人智能助手&#xff0c;还是企业的自动化工具&#xff0c;各类AI代理的应用愈发广泛。但目前这些智能体之间大多处于孤立状态&…...

jmeter压测工具出现乱码

然后 prev.setDataEncoding(“utf-8”)...

大模型训练显存压缩实战:ZeRO-3 vs 梯度累积 vs 量化混合策略

一、显存瓶颈的本质与挑战 大模型训练面临的核心矛盾是模型参数量指数级增长与GPU显存容量线性提升之间的鸿沟。以175B参数模型为例&#xff0c;其显存消耗主要来自三个方面&#xff1a; 参数存储‌&#xff1a;FP32精度下需700GB显存‌梯度缓存‌&#xff1a;反向传播产生的…...

WPS JS宏编程教程(从基础到进阶)-- 第七部分:JS对象在WPS中的应用

目录 第7章 JS对象在WPS中的应用7-1 对象创建的几种方法从零理解对象&#xff1a;数据收纳盒两种基础创建方式代码解析表 7-2 对象属性的查、改、增、删像操作Excel单元格一样管理属性1. 点操作符&#xff08;静态键名&#xff09;2. 中括号操作符&#xff08;动态键名&#xf…...

网络编程(UDP)

server:服务器 # import socket # # 传递udp协议参数 # sk socket.socket(typesocket.SOCK_DGRAM) # # # 绑定ip及端口 # sk.bind(("127.0.0.1",8080)) # # print("等待客户端发送消息") # # # 直接发送 # msg,addr sk.recvfrom(1024) # # print(msg.d…...

深入讲解 CSS 选择器权重及实战

1. 权重计算规则详解 CSS 选择器的优先级由 三元组 (x, y, z) 决定&#xff0c;比较规则如下&#xff1a; 选择器类型权重值 (x, y, z)示例ID 选择器x 1#header → (1,0,0)类/伪类/属性y 1.active, :hover元素/伪元素z 1div, ::before 比较规则&#xff1a;从左到右逐级比…...

Mysql的查询

1.Mysql的基本查询 语法&#xff1a;select*from 表名;代表查询所有数据的所有列 SELECT * FROM classinfo; SELECT * FROM studentinfo; select 字段1&#xff0c;字段2.....from 表名;查询数据的指定字段 查询studentinfo表的学生姓名和年龄 SELECT stuname,age FROM stu…...

RaabitMQ 快速入门

&#x1f389;欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨ &#x1f389;感谢各位读者在百忙之中抽出时间来垂阅我的文章&#xff0c;我会尽我所能向的大家分享我的知识和经验&#x1f4d6; &#x1f389;希望我们在一篇篇的文章中能够共同进步&#xff01;&#xff01;&…...

LLM: 探索LLM视觉缺陷

文章目录 前言一、Constructing MMVP Benchmarks1、CLIP-blind pair 二、MMVP-VLM bench1、Model size influence2、correlation between CLIP MLLMs 三、Mixture of Features1、Additive MoF Experiment2、Interleaved MoF Experiment 总结 前言 在使用多模态大模型时候是否会…...

常用的 ​​SQL 语句分类整理​​

以下是常用的 ​​SQL 语句分类整理​​&#xff0c;覆盖数据查询、操作、表管理和高级功能&#xff0c;适用于大多数关系型数据库&#xff08;如 MySQL、PostgreSQL、SQL Server&#xff09;&#xff1a; 目录 ​​一、数据查询&#xff08;DQL&#xff09;​​ ​​1. 基础查…...

Go之defer关键字:优雅的资源管理与执行控制

在Go语言中&#xff0c;defer关键字是处理资源释放、错误恢复和代码逻辑清理的利器。它看似简单&#xff0c;却隐藏着许多设计哲学和底层机制。本文将深入剖析defer的执行原理、使用场景和常见陷阱&#xff0c;助你掌握这一关键特性。 一、defer基础&#xff1a;延迟执行的本质…...

T1结构像+RS-fMRI影像处理完整过程记录(数据下载+Matlab工具箱+数据处理)

最近需要仿真研究T1结构像RS-fMRI影像融合处理输出目标坐标的可行性。就此机会记录下来。 为了完成处理&#xff0c;首先需要有数据&#xff0c;然后需要准备对应的处理平台和工具箱。那么正文开始~ &#xff08;1&#xff09;下载满足要求的开源数据 去OpenNEURO https://open…...

Flowable进阶-网关、事件和服务

网关 并行网关 并行网关允许将流程拆分为多个分支&#xff0c;也可以将多个分支汇集到一起。并行网关的功能是基于流入流出的顺序流。fork分支&#xff1a;用于任务的开始。并行后所有外出的顺序流&#xff0c;为每个顺序流都创建一个并发分支。 join汇聚&#xff1a;用于任务…...

【三维重建与生成】GenFusion:SVD统一重建和生成

标题:《GenFusion: Closing the Loop between Reconstruction and Generation via Videos》 来源&#xff1a;西湖大学&#xff1b;慕尼黑工业大学&#xff1b;上海科技大学&#xff1b;香港大学&#xff1b;图宾根大学 项目主页&#xff1a;https://genfusion.sibowu.com 文章…...

常见的爬虫算法

1.base64加密 base64是什么 Base64编码&#xff0c;是由64个字符组成编码集&#xff1a;26个大写字母AZ&#xff0c;26个小写字母az&#xff0c;10个数字0~9&#xff0c;符号“”与符号“/”。Base64编码的基本思路是将原始数据的三个字节拆分转化为四个字节&#xff0c;然后…...

有序二叉树各种操作实现(数据结构C语言多文件编写)

1.先创建tree.h声明文件( Linux 命令&#xff1a;touch tree.h)。编写函数声明如下(打开文件 Linux 操作命令&#xff1a;vim tree.h): //树的头文件位置 #ifndef __TREE_H__ #define __TREE_H__ //节点 typedef struct node{int data;//数据struct node* left;//记录左侧子节…...

Nacos-Controller 2.0:使用 Nacos 高效管理你的 K8s 配置

作者&#xff1a;濯光、翼严 Kubernetes 配置管理的局限 目前&#xff0c;在 Kubernetes 集群中&#xff0c;配置管理主要通过 ConfigMap 和 Secret 来实现。这两种资源允许用户将配置信息通过环境变量或者文件等方式&#xff0c;注入到 Pod 中。尽管 Kubernetes 提供了这些强…...

特殊文件以及日志——特殊文件

一、特殊文件 必要性&#xff1a;可以用于存储多个用户的&#xff1a;用户名、密码。这些有关系的数据都可以用特殊文件来存储&#xff0c;然后作为信息进行传输。 1. 属性文件.properties&#xff08;键值对&#xff09; &#xff08;1&#xff09;特点&#xff1a; 都只能…...

Spark-SQL核心编程语言

利用IDEA开发spark-SQL 创建spark-SQL测试代码 自定义函数UDF 自定义聚合函数UDAF 强类型的 Dataset 和弱类型的 DataFrame 都提供了相关的聚合函数&#xff0c; 如 count()&#xff0c; countDistinct()&#xff0c;avg()&#xff0c;max()&#xff0c;min()。除此之外&…...

jdk 安装

oracle官网 : Java Archive | Oracle 中国 export JAVA_HOME/Users/xxxxx/app/services/x86jdk/jdk1.8.0_431.jdk/Contents/Home export PATH$JAVA_HOME/bin:$PATH 华为镜像网站&#xff1a;Index of java-local/jdk...

Missashe考研日记-day21

Missashe考研日记-day21 1 专业课408 学习时间&#xff1a;4h学习内容&#xff1a; 今天先把昨天学的内容的课后习题做了&#xff0c;整整75道啊&#xff0c;然后学了OS第二章关于CPU调度部分的内容&#xff0c;这第二章太重要了&#xff0c;以至于每一小节的内容都比较多&am…...

双重路由引入的环路,选路次优的产生以及解决方法

描述 在R2,R3上双向引入ospf,以及rip,R5修改静态的优先级为180,在ospf中引入该静态路由 路由分析 选路次优问题 R5引入了静态路由,优先级是150 R2->R5->100.1.1.0,优先级是150 R3->R4->100.1.1.0,优先级是150 R3->R4->R5->100.1.1.0,优先级是150 R2-…...