STM32 TCP实现OTA

news2024/10/5 23:28:18

芯片:stm32f407

开发平台:stm32cubeide

上位机开发平台:visual studio 2017

1. FLASH分配

将flash划分为四个部分:

        bootloader:        0x8000000-0x800ffff

        app1:                0x8010000-0x805ffff

        app2:                0x8060000-0x80affff

        parameters:      0x80e0000-0x80fffff

        其中,bootloader程序为启动程序,app1是当前运行程序,app2为准备升级程序,parameters存储程序相关参数。

2. BOOTLOADER

        在app2中用最后一段flash地址0x80dfff0来存储升级标志Upgrade_Flag,比如有新程序下载到app2中,则0x80dfff0中数据为0xaaaaaa,如果为其他则表示没有新程序,开机后bootloader首先运行,启动后检测Upgrade_Flag是否为0xaaaaaa:

        如果是,则将app2中的程序拷贝到app1中,然后重置标志位Upgrade_Flag为0xffffffff且跳转到app1起始地址0x8010000开始执行新的程序;

        如果否,则直接跳转到app1起始地址0x8010000开始运行程序;

        bootloader主要代码如下:

#define IAP_ADDRESS			0x8000000		//
#define APP1_ADDRESS			0x8010000		//
#define APP1_ADDRESS_END		0x805FFFF		//
#define APP2_ADDRESS			0x8060000		//
#define APP2_ADDRESS_END		0x80AFFFF		//
#define PARA_ADDRESS			0x80E0000		//
#define UPGRADE_ADDRESS			0x80DFFF0		//


pFunction Jump_To_Application;
uint32_t JumpAddress;

pFunction Jump_To_Application;
void Jump_To_APP()
{
	uint32_t JumpAddress;
	printf("jump to app: %#x\r\n", APP1_ADDRESS);
	//HAL_DeInit();
	__disable_irq();
	if(((*(__IO uint32_t*)APP1_ADDRESS)&0x2ffe0000)==0x20020000)
	{
		/* Jump to user application */
		JumpAddress = *(__IO uint32_t*) (APP1_ADDRESS + 4);
		Jump_To_Application = (pFunction) JumpAddress;
		//printf("jump %#x success \r\n",APP1_ADDRESS);

		/* Initialize user application's Stack Pointer */
		  __set_MSP(*(__IO uint32_t*) APP1_ADDRESS);
		  Jump_To_Application();
	}
	else
	{
		printf("error [0x%08x]\r\n",(*(volatile uint32_t*)APP1_ADDRESS));
	}
}
uint8_t u8_Code_Buff[2048];//2k
uint16_t Num=0;
uint8_t NotUpgrade[4]={0xff,0xff,0xff,0xff};
uint8_t Upgrade[4]={0xaa,0xaa,0xaa,0xaa};
uint32_t Upgrade_Flag;

void Copy_APP2_to_APP1(void)
{

	if(((*(__IO uint32_t*)APP2_ADDRESS)&0x2ffe0000)!=0x20020000)
	{
		printf("invalid app in address [0x%08x]\r\n",APP2_ADDRESS);
		return;
	}
	MEM_IF_IniT();
	uint32_t APP1_Addr;
	uint32_t APP2_Addr;
	APP1_Addr=APP1_ADDRESS;
	APP2_Addr=APP2_ADDRESS;
	MEM_If_Erase(APP1_ADDRESS,APP1_ADDRESS_END);
	printf("Erase app1 flash\r\n");
	for(int i=0;i<150;i++)//copy 150*2k=300k, max size of app is 300k
	{
		printf("APP1_Addr = %x\r\n",APP1_Addr);
		printf("APP2_Addr = %x\r\n",APP2_Addr);
		MEM_If_Read(u8_Code_Buff,APP2_Addr,2048);
		HAL_Delay(10);
		MEM_If_Write_Byte(u8_Code_Buff,APP1_Addr,2048);
		HAL_Delay(10);
		APP1_Addr+=0x800;
		APP2_Addr+=0x800;
		memset(u8_Code_Buff,0,sizeof(u8_Code_Buff));
		Num=i;
		if(1)
		{
			printf("Successfully copied page %d\r\n",Num);
		}
		else
		{
			printf("Copy failed page %d\r\n",Num);
		}

	}
	MEM_If_Erase(UPGRADE_ADDRESS,UPGRADE_ADDRESS);
	MEM_If_Write_Byte(NotUpgrade,UPGRADE_ADDRESS,sizeof(NotUpgrade));
	MEM_IF_DeInit();
}

int main(void)
{
  /* USER CODE BEGIN 1 */
	
	//SCB->VTOR = FLASH_BASE | 0x40000;

  /* 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();
  /* USER CODE BEGIN 2 */
  printf("\r\n");
  printf("*****************************************\r\n");
  printf("*         luckyzjian's ISP              *\r\n");
  printf("*                                       *\r\n");
  printf("*          www.cdxcjc.com               *\r\n");
  printf("*                                       *\r\n");
  printf("*       Version: v%d.%d.%d            *\r\n",version_board,version_year,version_no);
  printf("*                                       *\r\n");
  printf("*           026-82598116                *\r\n");
  printf("*****************************************\r\n");
  printf("************ flash table  ***************\r\n");
  printf("*bootloader  : %#x -------- %#x******\r\n",IAP_ADDRESS,APP1_ADDRESS-1);
  printf("*APP1        : %#x -------- %#x******\r\n",APP1_ADDRESS,APP1_ADDRESS_END);
  printf("*APP2        : %#x -------- %#x******\r\n",APP2_ADDRESS,APP2_ADDRESS_END);
  printf("*PRIVATE PARA: %#x -------- %#x******\r\n",PARA_ADDRESS,PARA_ADDRESS+0X1FFFF);
  printf("*****************************************\r\n");


	TM1629_init();
	Upgrade_Flag=*(__IO uint32_t*)(UPGRADE_ADDRESS);
	printf("Upgrade_Flag = 0x%x\r\n",Upgrade_Flag);

	if(0xaaaaaaaa==Upgrade_Flag)
	{
		printf("there is new app in address: %x\r\n",APP2_ADDRESS);
		printf("copy new app in address: %x to address: %x\r\n",APP2_ADDRESS,APP1_ADDRESS);
		TM1629_printRow1("-----",ShowType_Right);
		TM1629_printRow2("-ISP-",ShowType_Right);
		TM1629_DISPLAY();
		Copy_APP2_to_APP1();
		printf("copy finished\r\n",APP2_ADDRESS,APP1_ADDRESS);
		printf("------%#x\r\n",(APP1_ADDRESS+4)&0XFF000000);
		if(((APP1_ADDRESS+4)&0xff000000)==0x08000000)
		{
			printf("jump APP1 running");
			Jump_To_APP(APP1_ADDRESS);
		}
	}
	else
	{
		printf("there is no new app in address: %x\r\n",APP2_ADDRESS);
		printf("------%#x\r\n",(APP1_ADDRESS+4)&0xff000000);
		if(((APP1_ADDRESS+4)&0xff000000)==0x08000000)
		{
			printf("jump APP1 running\r\n");
			Jump_To_APP(APP1_ADDRESS);
		}
	}
	
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1)
	{
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	}

  /* USER CODE END 3 */
}

3. APP

APP程序因为是从0x08010000开始运行的,要注意修改如下两个地方

/Core/Src/system_stm32f4xx.c中:

#define VECT_TAB_OFFSET         0x00010000U     /*!< Vector Table base offset field.
                                                     This value must be a multiple of 0x200. */

STM32F407ZGTX_FLASH.ld:

MEMORY
{
  CCMRAM    (xrw)    : ORIGIN = 0x10000000,   LENGTH = 64K
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K
  FLASH    (rx)    : ORIGIN = 0x8010000,   LENGTH = 320K
}

程序通过TCP下载,因为我项目中已经有一套TCP的通讯接口,为了方便,便在原来的自定义接口协议中增加了下载程序的协议

通讯格式定义如下:

发送:
0x02起始码1
ADD地址1控制仪地址 0X80~0X8F 代表0~15号地址
CMD命令字节1
LB数据[DF]长度1
[DF]数据LB
CS校验码1=NOT(0x02+ADD+CMD+LB+[DF])+1
0X03结束码1
响应:
0x06起始码1
ADD地址1控制仪地址 0X80~0X8F 代表0~15号地址
CMD命令字节1
LB数据[DF]长度1
[DF]数据LB
CS校验码1=NOT(0x06+ADD+CMD+LB+[DF])+1
0X03结束码1

涉及下载的命令定义如下:

命令命令代码命令格式响应说明
开始下载固件0x510x02 ADD 0x51 0x02 [分包数量] CS  0x030x06 ADD 0x51  0x01 <STATUS> CS 0x03[分包数量],固件数据包按1000字节一个包进行分包,最后一包不足1000字节时,用0x00填充到1000字节,将总的分包数量在开始升级固件指令时写入
返回STATUS:
0X06 成功;
0x15 失败
写入固件0x520x02 ADD 0x52 [分包顺序][数据长度]{数据} CS 0x030x02 ADD 0x52 0x03 [分包顺序] <STATUS> CS 0x03[分包顺序][数据长度]《数据》三部分固定长度为1004个字节,其中:
[分包顺序]为两字节,代表该帧数据为整个固件包的第几包数据,从0开始计数;
[数据长度]为两字节,代表该包数据字节长度,应固定为1000字节;
《数据》为固件内容,当最后一包不足1000字节时,用0x00填充到1000字节
返回STATUS:
0X06 成功;
0x15 失败;
写入固件结束0x530x02 ADD 0x53 0x02 <CRCH>  <CRCL> CS 0x030x06 ADD 0x53 0x01 <STATUS> CS 0x03CRCH,CHRL组成固件全部数据的CRC16校验,下位机收到结束指令后,对整包数据进行CRC16校验,与该指令CRC校准字比对,如果比对成功,则标记为升级成功(向地址0x80DFFF0写入0xaaaaaa)
返回STATUS:
0X06 升级成功;
0x15 升级失败;
放弃写入固件0x5f0x02 ADD 0x5f 0x00 CS 0x030x06 ADD 0x5f 0x00 CS 0x03

#define CLIENTMAX 5
typedef struct
{
	struct netconn *conn;
	uint8_t num;
}tcp_client;
typedef struct{
	struct netconn *client[CLIENTMAX+2];
	uint8_t state[CLIENTMAX+2];
	osThreadId client_taskid[CLIENTMAX+2];
	Controller_StatusDef realdatastatus[CLIENTMAX+2];
}client_ad;
client_ad clientad;


uint8_t isIAPStart=0;
uint16_t IAP_frameTotalCount=0;
uint16_t IAP_frameCount=0;
uint16_t IAP_frameSeq=0;
uint16_t IAP_frameLength=0;
uint16_t IAP_frameCRC=0x0000;
uint8_t isIAPFinish=0;
uint8_t isIAPSuccess=0;

uint8_t NotUpgrade[4]={0xff,0xff,0xff,0xff};
uint8_t Upgrade[4]={0xaa,0xaa,0xaa,0xaa};

void svr_task(void const *arg);
void processCmd(Controller_AckDataDef frame,Controller_AckDataDef* ackframe,uint8_t clientindex);
osThreadDef(myTaskClient, tcp_server_thread, osPriorityNormal, CLIENTMAX, 1400);

void MX_FREERTOS_Init(void) {
  
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 1024);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  osThreadDef(tcpmultiTask, svr_task, osPriorityNormal, 0, 1024);
  osThreadCreate(osThread(tcpmultiTask), NULL);
  clientad.state[CLIENTMAX]=1;
  clientad.realdatastatus[CLIENTMAX]=RealPass_Stop;
  comackframe.originaldata_data=comackdata;	
  isCOMHasACK=0;
	

}
err_t client_init(void *arg,uint8_t* clientnumindex)
{
	uint8_t          clientnum;                             //?????? TCP client ????
	err_t          err;                             //????(UCOSIII)
	uint8_t ifreceiveclient=0;
	for(clientnum=0;clientnum<CLIENTMAX;clientnum++)   //???????????(????20????)
	{
		if(clientad.state[clientnum]==0)                 //???? clientnum ?????????0(???)
		{			
			
			ifreceiveclient=1;
			break;                                         //???? ????  ????????
		}
	}		
	if(ifreceiveclient==0)
	{
		UsrLog("This no space to accept client");
		return 1;
	}
	*clientnumindex=clientnum;
	return ERR_OK;                                     //??????
}

void svr_task(void const *arg)
{
	err_t         oserr;                       
	struct netconn *conn,*newconn;             
	while(!isLWIPInitSuccess)
		osDelay(100);


	for(int i=0;i<CLIENTMAX;i++)   
	{
		clientad.state[i]=0;
		clientad.realdatastatus[i]=RealPass_Stop;
		//clientad.iapstatus[i]=IAPStatus_app;
	}	
	conn = netconn_new(NETCONN_TCP);            
	netconn_bind(conn,IP_ADDR_ANY,sysParameterStruct._Para_ControlIP_PORT); 
	netconn_listen(conn);  		                
	sprintf((char*)iptxt, "%d", sysParameterStruct._Para_ControlIP_PORT);  
	UsrLog("tcp start listen on port: %s",iptxt);

	while(1)
	{
			uint8_t clientnumindex=0;
			//if(get_clientindex(&clientnumindex) != ERR_OK)
			//	osDelay(100);				
			if(netconn_accept(conn,&newconn) == ERR_OK) 
			{		
				if(client_init((void *)newconn,&clientnumindex) != ERR_OK)
				{                                         
					netconn_close(newconn);                 
					netconn_delete(newconn);                
				}
				else
				{
					
					clientad.client[clientnumindex]=newconn;             			
					clientad.state[clientnumindex]=1;                       
					clientad.client_taskid[clientnumindex] = osThreadCreate(osThread(myTaskClient),  (void *)clientnumindex);
					osDelay(100);
					if(clientad.client_taskid[clientnumindex]==NULL)
					{
						UsrLog("Failed to create the recv thread with id: %d",clientnumindex);
					}
					else
					{
						UsrLog("create the recv thread with id: %x",(int)(clientad.client_taskid[clientnumindex]));
					}
					
				}
			}
			osDelay(100);
	}
}
void tcp_server_thread(void const *arg)
{
	err_t err;
	struct netbuf *clientrecvbuf;
	Controller_AckDataDef recvframe;
	Controller_AckDataDef ackframe;
	uint8_t recvdata[40];
	uint8_t ackdata[80];
	/*recv tcp data buf*/
	ackframe.originaldata_head=0x06;
	ackframe.originaldata_address=sysParameterStruct._Para_ControlEquiAddress;
	ackframe.originaldata_end=0x03;
	ackframe.originaldata_data=ackdata;
	ip_addr_t addr; u16_t port;
	uint8_t *app_flash_buf;
	/* get remote IP address and port*/
	uint8_t clientindex=(uint8_t)arg;	
	if((err=netconn_getaddr(clientad.client[clientindex],&addr, &port, 0))==ERR_OK)
	{
		sprintf((char*)iptxt, "%d.%d.%d.%d:%d", (uint8_t)(addr.addr),(uint8_t)(addr.addr>>8),(uint8_t)(addr.addr>>16),(uint8_t)(addr.addr>>24),port);  
		UsrLog("one client is connected:%s",iptxt);
	}
	uint8_t *data;
	u16_t len;
	while(1)
	{		
		if((err=netconn_recv(clientad.client[clientindex],&clientrecvbuf))==ERR_OK)
		{
			do{
				netbuf_data(clientrecvbuf,&data,&len);
				//printf("recevie one frame: length=%d, data=0x%x--0x%x\r\n",len,data[0],data[len-1]);
				if(len>=6)
				{
					if(data[0]==0x02&&data[len-1]==0x03)
					{
						recvframe.originaldata_head=data[0];
						recvframe.originaldata_address=data[1];
						recvframe.originaldata_cmd=data[2];
						if(recvframe.originaldata_cmd==0x52)//app upload data frame:0x02 add 0x52 [frame seq] [frame len] {app data} cs 0x03
						{
							IAP_frameSeq=(uint16_t)((data[3]<<8)|data[4]);
							IAP_frameLength=(uint16_t)((data[5]<<8)|data[6]);
							app_flash_buf=data+7;
							printf("recevie one frame data: frame seq=%d, frame data length=%d\r\n",IAP_frameSeq,IAP_frameLength);
							uint8_t frameRight=1;
							if(isIAPStart)
							{
								if(IAP_frameSeq==IAP_frameCount&&IAP_frameSeq<IAP_frameTotalCount)
								{
									if(IAP_frameLength!=500)
									{
										printf("wrong frame length(%d), length should be 1000\r\n",IAP_frameLength);
										frameRight=0;
									}
									if(frameRight)
									{
										printf("valid frame data\r\n");
										uint32_t flashdestination=APP2_ADDRESS+500*IAP_frameSeq;
										MEM_IF_IniT();
										if (MEM_If_Write_Word(app_flash_buf,flashdestination,IAP_frameLength)  == HAL_OK)
										{
											printf("write frame data seq(%d)(data:0x%x..) to flash(addr:0x%08x) success\r\n",IAP_frameSeq,*app_flash_buf,flashdestination);
											IAP_frameCount++;
										}
										else /* An error occurred while writing to Flash memory */
										{
											printf("write frame data seq(%d) to flash fail\r\n",IAP_frameSeq);
					                    	frameRight=0;
										}
										MEM_IF_DeInit();
									}
								}
								else
								{
									frameRight=0;
								}

							}
							else
							{
								frameRight=0;
							}
							if(!frameRight)
							{
								printf("invalid frame data or process data fail\r\n");
							}
							uint8_t realdatalength=0;
							ackframe.originaldata_head=0x06;
							ackframe.originaldata_address=sysParameterStruct._Para_ControlEquiAddress;
							ackframe.originaldata_end=0x03;
							ackframe.originaldata_cmd=recvframe.originaldata_cmd;
							*(ackframe.originaldata_data+realdatalength)=IAP_frameSeq>>8;
							realdatalength++;
							*(ackframe.originaldata_data+realdatalength)=IAP_frameSeq;
							realdatalength++;
							*(ackframe.originaldata_data+realdatalength)=frameRight?0x06:0x15;
							realdatalength++;
							ackframe.originaldata_length=realdatalength;
							netconn_ack(&ackframe,clientad.client[clientindex]);

						}
						else
						{
							recvframe.originaldata_length=data[3];
							recvframe.originaldata_cs=data[len-2];
							recvframe.originaldata_end=data[len-1];
							if(recvframe.originaldata_length+6==len)
							{
								for(int i=0;i<recvframe.originaldata_length;i++)
								{
									recvdata[i]=data[4+i];
								}
								recvframe.originaldata_data=recvdata;
								processCmd(recvframe,&ackframe,clientindex);
								netconn_ack(&ackframe,clientad.client[clientindex]);
								continue;
							}
						}
					}
				}
				ackframe.originaldata_cmd=0xf3;
				ackframe.originaldata_length=0;
				netconn_ack(&ackframe,clientad.client[clientindex]);				
			}
			while(netbuf_next(clientrecvbuf)>=0);
			netbuf_delete(clientrecvbuf);
		}
		else if(err==ERR_CLSD||err==ERR_RST)
			break;	
	}
	if(clientad.state[clientindex]==1)
	{
		if((err=netconn_getaddr(clientad.client[clientindex],&addr, &port, 0))==ERR_OK)
		{
			sprintf((char*)iptxt, "%d.%d.%d.%d:%d", (uint8_t)(addr.addr),(uint8_t)(addr.addr>>8),(uint8_t)(addr.addr>>16),(uint8_t)(addr.addr>>24),port);  
			UsrLog("one ramote is closed:%s",iptxt); 
		}
		clientad.state[clientindex]=0;
		netconn_close(clientad.client[clientindex]);
		netconn_delete(clientad.client[clientindex]);			
		vTaskDelete(clientad.client_taskid[clientindex]);
	}
}

void processCmd(Controller_AckDataDef frame,Controller_AckDataDef* ackframe,uint8_t clientindex)
{
	//Controller_AckDataDef ackframe;
	ackframe->originaldata_head=0x06;
	ackframe->originaldata_address=sysParameterStruct._Para_ControlEquiAddress;
	ackframe->originaldata_end=0x03;
	ackframe->originaldata_cmd=frame.originaldata_cmd;
	ackframe->originaldata_length=0;
	uint8_t *databuf=ackframe->originaldata_data;
	uint8_t realdatalength=0;
	switch(frame.originaldata_cmd)
	{
	case 0x51://start download app
		printf("start download app\r\n");
		IAP_frameTotalCount=(uint16_t)(frame.originaldata_data[0] << 8 | frame.originaldata_data[1]);
		printf("app frames total count is %d\r\n",IAP_frameTotalCount);
		MEM_IF_IniT();
		if(MEM_If_Erase(APP2_ADDRESS,APP2_ADDRESS_END)!=HAL_OK)
		{
			printf("erase flash fail, abort app downloading\r\n");
			isIAPStart=false;
			*(databuf+realdatalength)=0x15;
			realdatalength++;
			ackframe->originaldata_length=realdatalength;
		}
		else
		{
			printf("erase flash from %#x to %#x, wait for frame transmitting\r\n",APP2_ADDRESS,APP2_ADDRESS_END);
			IAP_frameCount=0;
			isIAPStart=true;
			*(databuf+realdatalength)=0x06;
			realdatalength++;
			ackframe->originaldata_length=realdatalength;
		}
		MEM_IF_DeInit();
		break;
	case 0x53://finish download app
		printf("finish download app\r\n");
		uint16_t IAP_crc16=(uint16_t)(frame.originaldata_data[0] << 8 | frame.originaldata_data[1]);
		printf("receive app crc16 is %x\r\n",IAP_crc16);

		if(1)//(IAP_crc16==IAP_frameCRC)
		{
			printf("pass crc check, finish download app\r\n");
			MEM_IF_IniT();
			MEM_If_Erase(UPGRADE_ADDRESS,UPGRADE_ADDRESS);
			MEM_If_Write_Byte(Upgrade,UPGRADE_ADDRESS,sizeof(Upgrade));
			MEM_IF_DeInit();

			*(databuf+realdatalength)=0x06;
			realdatalength++;
			ackframe->originaldata_length=realdatalength;
			isIAPStart=false;
		}
		else
		{
			printf("crc check fail, finish download app\r\n");
			*(databuf+realdatalength)=0x15;
			realdatalength++;
			ackframe->originaldata_length=realdatalength;
			isIAPStart=false;
		}
		break;
	case 0x5f:
		printf("receive abort download app command\r\n");
		isIAPStart=false;
		IAP_frameCount=0;
		break;
    /*
other cmd process...
    */
    default:break;
    }
}

3. 上位机实现升级工具(C# winform)

    

 4. 实现效果

升级工具通过TCP与板卡进行通讯,之前版本是V20.2024.3

打开升级程序包bin文件

点击下载

开始下载,显示整片程序被分成399个包

下载完成,提示重启板卡完成升级

重启后,开始将程序从APP2拷贝到APP1

拷贝完成,跳转到APP1运行,程序已经更新成V20.2024.4

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt Android sdk配置报错解决

使用的jdk8总是失败&#xff0c;报错command tools run以及platform sdk等问题。后来主要是设置jdk版本为17&#xff0c;就配置生效了。Android sdk路径可以选用Android Studio自带的&#xff0c;但是也要在Qt中点击“设置SDK”按钮做必要的下载更新等。 编译器这里会自动检测到…

Java设计模式 | 简介

设计模式的重要性&#xff1a; 软件工程中&#xff0c;设计模式&#xff08;design pattern&#xff09;是对软件设计中普遍存在&#xff08;反复出现&#xff09;的各种问题&#xff0c;所提出的解决方案。 这个术语由埃里希 伽玛&#xff08;Erich Gamma&#xff09;等人在1…

每日一题——LeetCode1512.好数对的数目

方法一 暴力循环 var numIdenticalPairs function(nums) {let ans 0;for (let i 0; i < nums.length; i) {for (let j i 1; j < nums.length; j) {if (nums[i] nums[j]) {ans;}}}return ans; }; 消耗时间和内存情况&#xff1a; 方法二&#xff1a;组合计数 var …

【GPTs分享】GPTs分享之consensus

大家好&#xff0c;元宵节快乐&#xff0c;今天给大家分享的GPTs是consensus。consensu号称无需关键字即可搜索2亿文章&#xff0c;而且给出的链接绝对保真&#xff0c;不再是胡编乱造的&#xff0c;而且能够根据指定主题辅助编写论文或者博客。 简介 consensus使用chat.cons…

GPT Pilot - 编写 95% 代码的开发工具!

在这篇博客介绍了GPT-pilot的研发细节&#xff0c;原作者将探讨GPT Pilot的技术内核 —— 一款基于GPT-4编写的开发工具&#xff0c;可以生成生产使用代码的应用。 你有没有想过&#xff0c;95%的应用代码&#xff0c;可以由AI编写&#xff0c;就像《钢铁侠》里的贾维斯一样&a…

C# 中 SQLite 查询数据库表中字段(列)是否存在的方法

查询SQLite数据库表中字段&#xff08;列&#xff09;存在的方法 使用SQL语句为&#xff1a;PRAGMA table_info([DeviceTrees]); 其中“DeviceTrees”为数据库表的名称。 使用SQLite Expert Professional工具&#xff0c;查看该语句是否起作用&#xff0c;这里使用的版本是…

基于Java SSM框架实现问卷调查系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现问卷调查系统演示 B/S结构 BROWSER/SERVER程序架构方式是使用电脑中安装的各种浏览器来进行访问和使用的&#xff0c;相比C/S的程序结构不需要进行程序的安装就可以直接使用。BROWSER/SERVER架构的运行方式是在远程的服务器上进行安装一个&#xff0c;然…

绿盾限制终端网络访问权限会恢复后,别的网站访问正常就是无法访问钉钉网站和下载东西

环境&#xff1a; Win10 专业版 钉钉7.5.5 绿盾7.0 问题描述&#xff1a; 绿盾限制终端网络访问权限会恢复后&#xff0c;别的网站访问正常就是无法访问钉钉网站和下载东西 解决方案&#xff1a; 排查方法 1.重置浏览器或者更换浏览器测试&#xff08;未解决&#xff09…

linux卸载mysql8重装5

目录 背景操作卸载重装配置启动 背景 在linux&#xff08;阿里云ECS&#xff09;安装部署Hive时初始化Hive元数据库&#xff0c;遇到报错前一天两三小时没解决&#xff0c;问题定位为mysql&#xff0c;次日打算重装 操作 卸载 停止 MySQL 服务 systemctl stop mysql yum卸载…

Map集合特点、遍历方式、TreeMap排序及Collections和Arrays

目录 ​编辑 一、集合框架 二、 Map集合 特点 遍历方式 HashMap与Hashtable的区别 TreeMap Collections Arrays 一、集合框架 二、 Map集合 Map集合是一种键值对的集合&#xff0c;其中每个键对应一个值。在Java中&#xff0c;Map接口定义了一种将键映射到值的数据结…

前后端分离vue.js+nodejs学生考勤请假系统 _fbo36

此系统设计主要采用的是nodejs语言来进行开发&#xff0c;采用vue框架技术&#xff0c;框架分为三层&#xff0c;分别是控制层Controller&#xff0c;业务处理层Service&#xff0c;持久层dao&#xff0c;能够采用多层次管理开发&#xff0c;对于各个模块设计制作有一定的安全性…

网络原理——HTTP

1. 什么是HTTP协议 HTTP是应用层的协议。Java最主要的应用场景是做网站&#xff0c;而网站由 后端&#xff08;HTTP服务器&#xff09; 和 前端&#xff08;浏览器&#xff09;组成&#xff0c;HTTP协议就是负责这里后端和前端的数据交互。 HTTP3.0 之前在传输层是通过 TCP传…

什么是智慧公厕?如何打造智慧公厕?

近年来&#xff0c;随着城市信息化建设的不断推进&#xff0c;智慧公厕的建设成为我国城市发展的重要一环。以智能化管理为核心&#xff0c;将公厕纳入互联互通的“智慧城市”大数据平台&#xff0c;使得公厕管理更加高效便捷&#xff0c;为市民提供更好的公共服务。本文将以智…

git最全总结

文章目录 Git 分布式版本控制工具内容1. 前言1.1 什么是Git1.2 使用Git能做什么 2. Git概述2.1 Git简介2.2 Git下载与安装 3. Git代码托管服务3.1 常用的Git代码托管服务3.2 码云代码托管服务3.2.1 注册码云账号3.2.2 登录码云3.2.3 创建远程仓库3.2.4 邀请其他用户成为仓库成员…

excel标记文本中的关键词加红加粗

任务&#xff1a; 有这么一张表&#xff0c;关键词为 word&#xff0c;文本内容为 text&#xff0c;现在想把 text 中的 word 标红加粗&#xff0c;如果数据量少&#xff0c;文本段手动标还可以&#xff0c;多起来就不太方便了 代码&#xff1a; import pandas as pd import x…

8-pytorch-损失函数与反向传播

b站小土堆pytorch教程学习笔记 根据loss更新模型参数 1.计算实际输出与目标之间的差距 2.为我们更新输出提供一定的依据&#xff08;反向传播&#xff09; 1 MSEloss import torch from torch.nn import L1Loss from torch import nninputstorch.tensor([1,2,3],dtypetorch.fl…

WEB相关工具(wget、curl、ab)

目录 一、wget 1、wget基本语法 2、wget帮助的更多选项 二、curl 1、curl基本语法 2、curl命令基本用法 2.1 curl伪装 2.2 提取状态码 2.3 提取本地IP地址 2.4 提取远端服务器IP地址 2.5 提取本地端口 2.6 提取远端服务器端口 三、压力测试工具 1、常用的httpd压…

数据结构与算法相关题解20240225

数据结构与算法相关题解20240225 一、58. 最后一个单词的长度二、48. 旋转图像三、69. x 的平方根四、50. Pow(x, n) 一、58. 最后一个单词的长度 简单 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度…

基于springboot+vue的租房管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

快速查找/打开host文件的方法

hosts文件是一个没有扩展名的文件&#xff0c;主要作用是&#xff1a;保存与域名的映射关系。 配置格式&#xff1a; ip 域名 windows系统里的保存位置&#xff1a; C:\Windows\System32\drivers\etc 下面介绍快速打开的方法。 第一步 [winR]打开运行&#xff0c;输入下面的…