STM32 TCP实现OTA

芯片: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.mfbz.cn/a/408368.html

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

相关文章

GEE入门篇|遥感专业术语(实践操作3):时间分辨率(Temporal Resolution)

目录 时间分辨率&#xff08;Temporal Resolution&#xff09; 1.Landsat 2.Sentinel-2 时间分辨率&#xff08;Temporal Resolution&#xff09; 时间分辨率是指特定传感器图像流的重访时间或时间节奏&#xff0c;重访时间是指卫星连续访问地球表面同一位置…

程序员副业接单做私活避坑指南

最近有不少读者私信我想接私活&#xff0c;想赚外快。 这篇文章系统的分享了对接单做私活这件事情的思考&#xff0c;也给出一些干货建议。希望让大家少走一些弯路&#xff0c;不要被坑。 先说结论 不建议大家在接单这个事情上投入太大精力&#xff0c;如果你“贼心不改”&am…

Nginx基本操作

目录 引言 一、Nginx配置文件详解 &#xff08;一&#xff09;配置文件 &#xff08;二&#xff09;模块 二、全局配置文件 &#xff08;一&#xff09;关闭版本或修改版本 1.关闭版本号 2.修改版本信息 &#xff08;二&#xff09;修改启动的进程数 &#xff08;三&…

MongoDB之客户端工具与核心概念及基本类型篇

MongoDB之客户端工具与核心概念及基本类型篇 文章目录 MongoDB之客户端工具与核心概念及基本类型篇1. MongoDB是什么?1. 关于MongoDB2. 相关客户端工具1. MongoDB Compass2. Studio 3T3. Navicat for MongoDB4. NoSQL Manager for MongoDB Professional 2.MongoDB相关概念2.1 …

Linux安装JDK,Tomcat,MySQL的安装以及项目部署

一、jdk安装配置 传入资源 连接后&#xff0c;创建存放资源的文件&#xff0c;将jdk&#xff0c;tomcat&#xff0c;Mysql的压缩包复制到文件中。 输入命令: cd javaCloudJun/software (进入要文件中) 输入命令 : pwd (查看当前的文件路径) 将文件路径复制到左边的搜索框中…

接口测试实战--自动化测试流程

一、项目前期准备 常见项目软件架构: springMvc:tomcat里运行war包(在webapps目录下) springboot:java -jar xx.jar -xms(**) 运行参数 springCloud:k8s部署,使用kubectl create -f xx.yaml 接口自动化测试介入需越早越好,只要api定义好就可以编写自动化脚本; 某个…

掌握BeautifulSoup4:爬虫解析器的基础与实战【第91篇—BeautifulSoup4】

掌握BeautifulSoup4&#xff1a;爬虫解析器的基础与实战 网络上的信息浩如烟海&#xff0c;而爬虫技术正是帮助我们从中获取有用信息的重要工具。在爬虫过程中&#xff0c;解析HTML页面是一个关键步骤&#xff0c;而BeautifulSoup4正是一款功能强大的解析器&#xff0c;能够轻…

typecho 给文章创建目录树

受益于 shortcode 短代码插件和泽泽短代码中目录树的显示样式&#xff0c;形成了自己实现添加文章目录的思路&#xff1a; 一、文章目录树的结构 <div id"toc"><div class"toc-left"><div class"toc-btn" type"button&quo…

1.QT简介(介绍、安装,项目创建等)

1. QT介绍 Qt&#xff08;官方发音 [kju:t]&#xff09;是一个跨平台的C开发库&#xff0c;主要用来开发图形用户界面&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;程序 Qt 是纯 C 开发的&#xff0c;正常情况下需要先学习C语言、然后在学习C然后才能使用…

三院院士 Michael I. Jordan 指出:大模型在两个方向仍需 “努力“,补充过度自信问题和逆转诅咒问题

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 三院院士 Michael I. Jordan 指出&#xff1a;大模型在两个方向仍需 “努力“ Michael I. Jordan 的个人主页&#xff1a;https://people.eecs.berkeley.edu/~jordan/ 回顾过去的一年&#xff0c;大模…

【数据结构和算法初阶(c语言)】数据结构前言,初识数据结构(给你一个选择学习数据结构和算法的理由)

1.何为数据结构 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的 数据元素的集合。本质来讲就是在内存中去管理数据方式比如我们的增删查改。在内存中管理数据的方式有很多种&#xff08;比如数组结构、链式结构、树型结…

【Spring MVC】处理器映射器:AbstractHandlerMethodMapping源码分析

目录 一、继承体系 二、HandlerMapping 三、AbstractHandlerMapping 四、AbstractHandlerMethodMapping 4.1 成员属性 4.1.1 MappingRegistry内部类 4.2 AbstractHandlerMethodMapping的初始化 4.3 getHandlerInternal()方法&#xff1a;根据当前的请求url&#xff0c;…

linux安装sqoop

目录 下载配置 下载 本地下载好上传&#xff0c;解压&#xff0c;重命名&#xff0c;注意路径 tar -zxvf /opt/sqoop/sqoop-1.4.6.tar.gz -C /opt/ mv /opt/sqoop-1.4.6.bin__hadoop-2.0.4-alpha /opt/sqoop配置 环境变量 echo export SQOOP_HOME/opt/sqoop/ >> /etc…

FISCO BCOS(二)———配置及使用控制台

一、前言 FISCO BCOS是由金融区块链合作联盟&#xff08;深圳&#xff09;与微众银行共同发起的开源区块链项目&#xff0c;支持多链多账本&#xff0c;满足金融行业复杂业务需求。本文将介绍如何在Ubuntu操作系统上使用Linux命令配置FISCO BCOS的控制台并进行get/set操作。 目…

文生视频模型调研

文生视频只有OpenAI的Sora&#xff0c;其他的&#xff08;&#xff09;都是动图。 OpenAI发布了可以生成60s视频的Sora模型。刚刚发布的google的Gemini pro 1.5就一下子变得无人问津了&#xff0c;太尴尬了。 在这之前视频生成的天花板是Runway&#xff0c;支持最多18s视频生成…

设计模式-创建型模式-建造者模式

建造者模式&#xff08;Builder Pattern&#xff09;&#xff1a;将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。 建造者模式一步一步地创建一个复杂的对象&#xff0c;它允许用户只通过指定复杂对象…

MBG(Mybatis-Generator)生成代码

目录 步骤&#xff1a; 1. 创建数据库表 2. 配置 MyBatis Generator 3. 运行 MyBatis Generator 4. 编写业务逻辑 在实际开发中&#xff0c;你会发现有很多重复的工作&#xff1a; 首先是PO对象&#xff0c;我们往往创建与数据库表字段一一对应的PO对象; 其次在Mapper里…

十四届蓝桥杯国赛考试计算思维 U10 组真题和答案

答案在底部 第一题 恐龙乐园的规划图中有 n 个小岛,m 座小桥,每座桥连接两个小岛。 下图是 n=5,m=8 的一个例子: 希希发现,如果拆除一些桥,仍然能使任何两个小岛都互通。最多可以拆除( )座 桥。 A. n-m B. m-n C. m-m-1 D. m-n+1 第二题

YOLOv8重磅升级!新增旋转目标检测功能

YOLOv8重磅升级&#xff01;新增旋转目标检测功能&#xff0c;基于DOTA数据集&#xff0c;支持15个类别目标航拍图像检测&#xff0c;模型&代码均已开源~ 新版亮点 YOLOv8-OBB&#xff1a;YOLOv8中OBB(Oriented Bounding Box)模型的引入标志着物体检测的重要一步&#xff…

简单mock server模拟用户请求给小程序提供数据

整理小程序代码时发现一此小程序离开了mock-server基本上没有办法显示了,因此用node,express来满足给小程序提供演示数据的功能 const express require(express); const { createCanvas, Image } require(canvas); const fs require(fs); const path require(path);…
最新文章