STM32CUBEMX+STM32F4+IAP串口升级应用,亲测可用,带详解

news2024/12/15 3:57:38
  • 一、IAP的基本概念

IAP,全名为in applacation programming,即在应用编程。

也就是在应用程序中升级。好处就太多了,比如远程在线升级,不用人到现场拆开,用烧写器连接升级。

实现IAP技术的核心是一段预先烧写在单片机内部的IAP程序。这段程序主要负责与外部的上位机软件进行握手同步,然后将通过外设通信接口将来自于上位机软件的程序数据接收后写入单片机内部指定的闪存区域,然后再跳转执行新写入的程序,最终就达到了程序更新的目的。

原理和ISP有点类似。简单的讲:

ISPST官方写的一段bootloader代码,通过UART1烧录程序。

此时BOOT0=1

IAP需要我们自己写一段bootloader代码,支持串口,网络,can等实现应用程序升级。这次就选择用串口实现IAP应用。

STM32微控制器上实现IAP程序之前首先要回顾一下STM32的内部闪存组织架构和其启动过程。STM32的内部闪存地址起始于0x8000000,一般情况下,程序文件就从此地址开始写入。此外STM32是基于Cortex-M3内核的微控制器,其内部通过一张中断向量表来响应中断,程序启动后,将首先从中断向量表取出复位中断向量执行复位中断程序完成启动。而这张中断向量表的起始地址是0x8000004,当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到中断向量表处,并根据中断源取出对应的中断向量执行中断服务程序。最后还需要知道关键的一点,通过修改STM32工程的链接脚本可以修改程序文件写入闪存的起始地址。

 

对图1解读如下:

1 STM32复位后,会从地址为0x8000004处取出复位中断向量的地址并跳转执行复位中断服务程序,如图1中标号○1所示。

2 复位中断服务程序执行的最终结果是跳转至C程序的main函数,如图1中标号○2所示,而main函数应该是一个死循环,是一个永不返回的函数。

3 main函数执行的过程中,生了一个中断请求,此时STM32的硬件机制会将PC指针强制指回中断向量表处,如图1中标号○3所示。

4 根据中断源进入相应的中断服务程序,如图1中标号○5所示

5 中断服务程序执行完毕后,程序再度返回至main函数中执行,如图1中标号○6所示。

打开一个stm32程序的bin文件。

如图,前面4字节是栈顶地址:0x20000648

接着4字节是复位中断函数地址0x080001A1。执行完复位中断函数后,回到主函数。

 

若在STM32中加入了IAP程序,则情况会如图2所示。

对图2的解读如下:

1 STM32复位后,从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序,随后跳转至IAP程序的main函数,如图2中标号○1○2所示。这个过程和图1相应部分是一致的。

2 执行完IAP过程后(STM32内部多出了新写入的程序,图2中以灰色底纹方格表示,地址始于0x8000004+N+M跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,其过程如图2的标号○3所示。新程序的main函数应该也具有永不返回的特性。同时应该注意在STM32的内部存储空间在不同的位置上出现了2个中断向量表。

3 在新程序main函数执行的过程中,一个中断请求来临,PC指针仍会回转至地址为0x8000004中断向量表处,而并不是新程序的中断向量表,如图2中标号○5所示。注意到这是由STM32的硬件机制决定的。

4 根据中断源跳转至对应的中断服务,如图2中标号○6所示。注意此时是跳转至了新程序的中断服务程序中。

5 中断服务执行完毕后,返回main函数。如图2中标号○8所示。

从上述两个过程的分析可以得知,对将使用IAP过程写入的程序要满足2个要求:

1、新程序必须从IAP程序之后的某个偏移量为x的地址开始;

2、必须将新程序的中断向量表相应的移动,移动的偏移量为x

我们打开一个APP程序的bin文件

打开APP.bin文件,能够看到前面4字节是栈顶地址:0x20005460,接着4字节是复位中断函数入口地址:0x080081A1

 

  • 二、STM32F405RGT6FLASH

  • FLASH分为ROMRAM

ROM大小是1024KB。分为12个扇区。扇区大小不完全一样。

RAM大小介绍说192(112+16+64)KB其中192K的内存包括64KCCM(CPU可访问)112KSRAM1(RAM)16KSRAM2(外设使用)

CCM内存的基地址是0x10000000  Keil看不到

SRAM1内存的基地址是0x20000000大小0x1C000112K

SRAM2内存的基地址是0x2001C000,大小是0x4000 16K

我们把ROM空间划分为两部分。分别存放Bootloader程序和APPlication程序。下文简称为bootapp

由于IAP的功能需要在线升级程序,所以我们最核心的功能需求就是改写程序存储区中的内容。

MCU复位后,先从0x08000000地址开始运行boot程序;

如果需要更新app程序,则由boot程序获取app程序的烧写文件(二进制bin文件格式),将其写入到app对应的地址中;

如果需要运行app程序,则从boot程序中跳转到app对应的地址执行。

 

我们对ROM划分区域,分为三部分,前面两部分用于存放bootapp编译生成的文件。

预留多大空间合适呢?

一般boot预留16KB足够用。自己的boot编译生成的文件大小

Program Size: Code=8324   RO-data=528  RW-data=32  ZI-data=1576

code : 代码

RO-data :指的是程序中的指令和常量

RW-data :程序中已经初始化的变量

ZI-data :程序中未初始化的便利那个

烧录文件的大小=code + RO-data + RW-data=8324   +528  + 32=8884个字节

烧录文件大小指的是bin文件而非hex文件,bin的才是下载的二进制文件。

芯片需要的RAM大小=RW-data+ZI-data=32+1576=1608个字节

三、BOOT程序

1、产品功能

上电后,通过uart1接收升级指令,启动升级,通过uart1接收app编译生成的bin文件。接收完成后,运行app程序。

2、接收指令和判断指令

HAL_UART_Receive(&huart1,datatemp,256,1000);//接收指令 

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

Ifstrstr((const char *)datatemp,erasure_CMD) =NULL //判断指令

函数strstr是用于在一个字符串A中,查找第一次出现的另外一个字符串B的位置,查找到了,返回BA中的起始位置的指针;没有找到,就返回NULL

 

temp=HAL_UART_Receive(&huart1,datatemp,256,30*1000);//接收bin文件的256个字节到缓冲区。

For(i=0;i<64;i++)

{

Data_32=*(uisigned int *)(&datatemp[i<<2]) //256个字节 修改成6432位数据读取

}

3bin文件生成指令。

①$K\ARM\ARMCC\bin\fromelf.exe --bin --output=Bin\@L.bin !L   会在MDK目录下生成一个文件夹,里面是.bin格式文件

 

②fromelf.exe --bin -o "$L@L.bin" "#L  会在.hex同目录下生成一个.bin 格式文件

4FLASH写入

FLASH写入的时候,需要先解锁,接着擦除,然后写入,最后给FLASH上锁。

所以函数名称是flash_eraser_write()

 

解锁

HAL_FLASH_Unlock( )

擦除

HAL_FLASHEx_Erase&EraseInitStruct,&SectorError;

FLASH_EraseInitTypeDef  EraseInitStruct;

EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS  ;//擦除模式按照扇区

EraseInitStruct.Sector = FLASH_SECTOR_2;//APP占用的起始地址所在的扇区

EraseInitStruct.NbSectors=1; //APP占用的扇区数

EraseInitStruct.VoltageRange=  FLASH_VOLTAGE_RANGE_3;      //供电电压

写入

HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,Address,DATA32[i++]);

上锁

HAL_FLASH_Lock( )

 

STM32F103Flash擦除的最小单位是页,也就是每次一擦除,都会把擦除地址所在页的整页数据擦掉;

STM32F407Flash擦除的最小单位是扇区,也就是每次一擦除,都会把擦除地址所在扇区的扇区数据擦掉;

5、栈顶地址判断

if (((*(volatile u32*)ApplicationAddress) &amp; 0x2FFE0000 ) == 0x20000000)分析:

ApplicationAddress存放的是用户程序Flash的首地址,(*(volatile u32*)ApplicationAddress)

addr 强制转换为 volatile uint32_t 指针,然后取该指针所指向的地址的值,即得到了 addr地址的值。意思是取用户程序首地址里面的数据,这个数据就是用户代码的堆栈地址,堆栈地址指向RAM,而RAM的起始地址是0x20000000,因此上面的判断语句执行:判断用户代码的堆栈地址是否落在:0x20000000~0x2001ffff区间中,这个区间的大小为128K,笔者查阅STM32F4各型号的RAM大小,目前RAM最大的容量可以做到192K+4K,时钟频率为168MHZ

6APP跳转

JumpAddress = *(__IOuint32_t*) (APPLICATION_ADDRESS +4);// ②

JumpToApplication = (pFunction) JumpAddress;//③

/* Initialize user application's Stack Pointer */

__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);// ④

JumpToApplication(); // ⑤

程序跳转地址的确认,前面已经说过0x08008004处的4个字节存放的是复位函数的入口地址,该句的意思为获得ApplicationAddress + 4地址处的数据,即为获得新的复位函数入口地址。

Jump_To_Application这个函数指针指向复位函数入口地址。

1.这句的意思是将上一句取得的中断函数地址转为函数指针。void *pFunction)(void);是声明了一个函数指针。

2.此时,JumpToApplication指向了复位中断函数所在的地址。

堆栈的初始化,调用__set_MSP重新设定栈顶代地址,把栈顶地址设置为用户代码指向的栈顶地址。

跳转到新的复位函数。设置PC指针为复位地址。

  • 7、stm32cubemx配置

配置RCC

配置SYS烧写

配置串口

  • 8、Keil配置

选用微库,

配置IROM1IROM2

 

  • 9、代码

 

/* USER CODE BEGIN Header */

/**

******************************************************************************

  * @file           : main.c

  * @brief          : Main program body

******************************************************************************

  * @attention

  *

  * Copyright (c) 2024 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.

  *

******************************************************************************

  */

/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/

#include "main.h"

#include "dma.h"

#include "usart.h"

#include "gpio.h"

 

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include "stdio.h"

#include "string.h"

/* USER CODE END Includes */

 

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

 

/* USER CODE END PTD */

 

/* Private define ------------------------------------------------------------*/

/* USER CODE BEGIN PD */

 

/* USER CODE END PD */

 

/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PM */

#define NVIC_VectTab_RAM_Start((uint32_t)0x20000000)                                            //RAM起始地址

#define NVIC_VectTab_RAM_End((uint32_t)0x20020000)                                      //RAM结束地址,大小为128K,根据自己的实际芯片大小修改

#define NVIC_VectTab_FLASH((uint32_t)0x08000000)                                           //Flash起始地址

#define BOOT_SIZE                                                     0x8000                                         //Boot大小,32KB,占用前面两个扇区(其实上只需要占用第一个扇区16KB即可)

#define ApplicationAddress          (NVIC_VectTab_FLASH + BOOT_SIZE)                   //APP的起始地址 

#define Application_SIZE             0x4000                                  //APP大小为16KB。占用第三个扇区

#define FLASH_USER_END_ADDR       (ApplicationAddress+Application_SIZE)  //APP的结束地址                    

 

#define    UPDATE_CMD                    "update"                                                               //升级擦除指令

 

 

typedef  void (*iapfun)(void);

void SystemClock_Config(void);

 

 

/* USER CODE END PM */

 

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

 

/* USER CODE BEGIN PV */

//uint32_t FirstSector =1,NbofSectors=1;

 

uint32_t SectorError =0 ;//擦除错误的扇区地址,擦除成功时候该值为0xffffffff

iapfun jump2app;  //用函数指针指向复位函数入口地址

// unsigned int count2 = 0;

unsigned char datatemp[256] = {0};//用于存放串口接收缓冲区,存放256B数据。

unsigned char boot_flag = 0;//擦除完成标志

unsigned char time_out_flag = 0;//接收串口数据超时标志

 

/* USER CODE END PV */

 

/* Private function prototypes -----------------------------------------------*/

 

/* USER CODE BEGIN PFP */

 

/* USER CODE END PFP */

 

/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

int fputc(int ch, FILE *f)

{

  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);

  return ch;

}

 

/* USER CODE END 0 */

 

/**

  * @briefThe 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_DMA_Init();

  MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */

  

   unsigned char i;

   printf("boot start\r\n");

  printf("input \"update\"\t to erasure user flash, or wait 10s to start user app\r\n");//提示输出指令“update”,10s内没接收到指令,开始启动APP

   

  for(i = 0; i<10; i++)

  {

         //上电后每秒阻塞接收升级指令,连续10秒未收到则跳转App,10秒内收到则接收App数据并写入

         HAL_UART_Receive(&huart1, datatemp, 256, 1000);

 

         if(strstr((const char *)datatemp, UPDATE_CMD) != NULL)    //查询接收的数据是否包含指令。

         {

                // 擦除App区域

                FLASH_EraseInitTypeDef EraseInitStruct;

                EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS  ;//擦除模式按照扇区

                EraseInitStruct.Sector = FLASH_SECTOR_2;//APP占用的起始地址所在的扇区

                EraseInitStruct.NbSectors=1; //APP占用的扇区数

                EraseInitStruct.VoltageRange=       FLASH_VOLTAGE_RANGE_3;        //供电电压

 

                HAL_FLASH_Unlock();//解锁

                            

                if(HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK)//如果擦除不成功,上锁

                {

                       HAL_FLASH_Lock();

                       printf("Erase fail at:0x%x\n\r",SectorError);//返回擦除失败的扇区位置

                       return 0;

                }

 

                printf("当SectorError=0x%X\t;表示擦除成功\r\n",SectorError);

 

                boot_flag = 1;//擦除成功,开始升级

                printf("Erase OK\n\r");                    

                break;                 

         }

  }

 

  if(boot_flag == 1)

  {

         HAL_StatusTypeDef  temp;//状态标志符

         unsigned int Address;//地址变量

         unsigned int data_32;//按照字写入,也就是4B

         unsigned char j = 0;//

        

         printf("ready to receive bin, please send in 30s\n\r");//提示在30s内发送2进制APP文件

         Address = ApplicationAddress;              //指向预留存放APP的FLASH地址

         temp = HAL_UART_Receive(&huart1, datatemp, 256, 30*1000); //在30s内通过串口1发送2进制APP文件

        

         if(temp == HAL_TIMEOUT)

         {

                //阻塞30S,未收到App数据则退出

                printf("time out, end wait to receive bin\n\r");

                return 0;

         }

         else

         if(temp == HAL_OK)

         {

                //收到则循环接收,每秒阻塞接收256字节,并写入Flash

                while(1)

                {

                       unsigned char i;                       

                       HAL_FLASH_Unlock();//解锁

                       for(i=0; i<64; i++)//把接收的256字节,按照每次写入4字节,写入64次

                       {

                              data_32 = *(unsigned int *)(&datatemp[i<<2]);//很巧妙,字节转换成字

                              if(Address < FLASH_USER_END_ADDR)//写入地址未超出预留区域

                              {

                                if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address,data_32)==HAL_OK)//写入函数,按照字格式写入

                                     {

                                            Address = Address + 4;

                                     }

                                     else

                                     {

                                            HAL_FLASH_Lock();

                                            printf("write fail at: 0x%x\n\r", Address);

                                            return 0;

                                     }                                        

                              }

                              else 

                                     printf("写入的升级程序超出预留APP区域\r\n");

                       }

                      

                       HAL_FLASH_Lock();

                       printf("write 256 btye OK: 0x%x\t%d\n\r",Address,j++);

 

                       //循环调用串口接收函数。最后一包数据不足256字节时会接收超时,防止app数据不完整,不足256字节数据接收完以后处理一次,下一次接收超时认为接收完成,跳转App

                       temp = HAL_UART_Receive(&huart1, datatemp, 256, 2*1000);

                       if(temp == HAL_TIMEOUT)

                       {

                              time_out_flag++;

                              if(time_out_flag == 2)

                              {

                                     printf("End write OK\n\r");

                                     goto START_APP;

                              }

                       }

                }

         }

  }     

  else if(boot_flag == 0)  //没有收到升级命令

  {     

START_APP:       

         printf("start user app\n\r");

         HAL_Delay(10);

        

         /*

                判断App的栈顶指针是否合法(即是否有App)。

         ApplicationAddress为App在flash中的地址,(*(volatile u32*)ApplicationAddress)的意思

         取用户程序首地址里面的数据,这个数据就是用户代码的堆栈地址,堆栈地址指向RAM,而RAM的起始地址是0x20000000,

         因此上面的判断语句执行:判断用户代码的堆栈地址是否落在:0x20000000~0x2001ffff区间中

           这里的目的是判断App的栈顶指针是否在0x20000000到0x2001FFFF之间,在的话就认为有App,不在就没有

         */

         printf("ApplicationAddress:%0x\r\n", (*(unsigned int *)ApplicationAddress));

 

               

         if(((*(unsigned int *)ApplicationAddress)>= NVIC_VectTab_RAM_Start) &&

         ((*(unsigned int *)ApplicationAddress)<= NVIC_VectTab_RAM_End))

         {     

//                     // disable irq, if use this, must enable irq at app

//                     //__disable_irq();

//                    

                __set_MSP(*(unsigned int *)ApplicationAddress); //调用__set_MSP重新设定栈顶代地址,把栈顶地址设置为APP代码指向的栈顶地址。

                jump2app=(iapfun)*(unsigned int *)(ApplicationAddress+4);//获得新的复位函数入口地址,让jump2这个函数指针指向复位函数入口地址。

 

                jump2app();  //跳转到新的复位函数

         }

         else

         {

                printf("no user app\n\r");

                return 0;

         }

  }

          

  /* USER CODE END 2 */

 

  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

    /* USER CODE END WHILE */

 

    /* USER CODE BEGIN 3 */

  }

  /* USER CODE END 3 */

}

四、APP程序

  • 1、偏移地址

设置APPFLASH中的起始地址和设置中断向量偏移地址

#define NVIC_VectTab_FLASH        ((uint32_t)0x08000000)     //Flash起始地址

#define BOOT_SIZE                                0x8000                            //Boot大小

SCB->VTOR = NVIC_VectTab_FLASH | BOOT_SIZE; /* 设置中断向量偏移地址 */

  • 2、Keil配置

 

  • 3、程序

 

/* USER CODE BEGIN Header */

/**

  ******************************************************************************

  * @file           : main.c

  * @brief          : Main program body

******************************************************************************

  * @attention

  *

  * Copyright (c) 2024 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.

  *

******************************************************************************

  */

/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/

#include "main.h"

#include "usart.h"

#include "gpio.h"

 

/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include <stdio.h>

/* USER CODE END Includes */

 

/* Private typedef -----------------------------------------------------------*/

/* USER CODE BEGIN PTD */

 

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

 

/* USER CODE BEGIN PV */

 

/* USER CODE END PV */

 

/* Private function prototypes -----------------------------------------------*/

void SystemClock_Config(void);

/* USER CODE BEGIN PFP */

 

/* USER CODE END PFP */

 

/* Private user code ---------------------------------------------------------*/

/* USER CODE BEGIN 0 */

#define NVIC_VectTab_FLASH((uint32_t)0x08000000)                             //Flash起始地址

#define BOOT_SIZE                                0x8000                                                   //Boot大小

 

int fputc(int ch, FILE *f)

{

  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);

  return ch;

}

 

/* USER CODE END 0 */

 

/**

  * @briefThe 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();

  /* USER CODE BEGIN 2 */

 

uint32_t Count = 0;

  /* 设置中断向量偏移地址 */

  SCB->VTOR = NVIC_VectTab_FLASH | BOOT_SIZE;

printf("\n\r app start\n\r");

 

 

 

  /* USER CODE END 2 */

 

  /* Infinite loop */

  /* USER CODE BEGIN WHILE */

  while (1)

  {

    /* USER CODE END WHILE */

 

    /* USER CODE BEGIN 3 */

         HAL_Delay(10);

         if(Count%100 == 0)

         {

                printf("this is app!\t%d\n\r",Count/100);

         }

         Count++;

        

  }

  /* USER CODE END 3 */

}

 

五、运行结果

  • 1、烧写boot程序

sscom打开串口。输入指令,注意只要输入的内容包含update即可匹配

  • 2、发送设置

  • 3、发送bin文件

  • 4、app烧录完成,并运行

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

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

相关文章

CTFHub 命令注入-综合练习(学习记录)

综合过滤练习 命令分隔符的绕过姿势 ; %0a %0d & 那我们使用%0a试试&#xff0c;发现ls命令被成功执行 /?ip127.0.0.1%0als 发现一个名为flag_is_here的文件夹和index.php的文件&#xff0c;那么我们还是使用cd命令进入到文件夹下 http://challenge-438c1c1fb670566b.sa…

深入探索 JVM:原理、机制与实战

一、JVM 概述 JVM&#xff08;Java Virtual Machine&#xff09;是 Java 程序运行的核心组件&#xff0c;它提供了一个独立于硬件和操作系统的执行环境&#xff0c;使得 Java 程序能够在不同平台上具有跨平台的特性。 JVM 主要由以下几部分组成&#xff1a; 类装载器&#xf…

视频推拉流EasyDSS无人机直播技术巡查焚烧、烟火情况

焚烧作为一种常见的废弃物处理方式&#xff0c;往往会对环境造成严重污染。因此&#xff0c;减少焚烧、推广绿色能源和循环经济成为重要措施。通过加强森林防灭火队伍能力建设与长效机制建立&#xff0c;各地努力减少因焚烧引发的森林火灾&#xff0c;保护生态环境。 巡察烟火…

挺详细的记录electron【V 33.2.0】打包vue3项目为可执行程序

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、直接看效果 二、具体步骤 1.安装配置electron 1.将 electron 包安装到应用的开发依赖中。 2.安装electron-packager依赖&#xff08;打包可执行文件&#…

基本分页存储管理

一、实验目的 目的&#xff1a;熟悉并掌握基本分页存储管理的思想及其实现方法&#xff0c;熟悉并掌握基本分页存储管理的分配和回收方式。 任务&#xff1a;模拟实现基本分页存储管理方式下内存空间的分配和回收。 二、实验内容 1、实验内容 内存空间的初始化——可以由用户输…

Vue Web开发(五)

1. axios axios官方文档 异步库axios和mockjs模拟后端数据&#xff0c;axios是一个基于promise的HTTP库&#xff0c;使用npm i axios。在main.js中引入&#xff0c;需要绑定在Vue的prototype属性上&#xff0c;并重命名。   &#xff08;1&#xff09;main.js文件引用 imp…

论文概览 |《IJAEOG》2024.08 Vol.132(下)

本次给大家整理的是《International Journal of Applied Earth Observation and Geoinformation》杂志2024年08月第132期的论文的题目和摘要&#xff0c;一共包括88篇SCI论文&#xff01;由于论文过多&#xff0c;我们将通过两篇文章进行介绍&#xff0c;本篇文章介绍第45--第8…

「数据结构详解·十五」树状数组

「数据结构详解一」树的初步「数据结构详解二」二叉树的初步「数据结构详解三」栈「数据结构详解四」队列「数据结构详解五」链表「数据结构详解六」哈希表「数据结构详解七」并查集的初步「数据结构详解八」带权并查集 & 扩展域并查集「数据结构详解九」图的初步「数据结构…

【sgFileLink】自定义组件:基于el-link、el-icon标签构建文件超链接组件,支持垃圾桶删除、点击预览视频/音频/图片/PDF格式文件

sgFileLink源代码 <template><div :class"$options.name"><el-link click.stop"clickFile(data)"><img :src"getSrc(data)" /><span>{{ getFileNameAndSize(data) }}</span></el-link><el-linkcl…

电机驱动模块L9110S详解

电机驱动模块是一种用于控制和驱动电机的设备&#xff0c;它能够将控制信号转化为适合电机操作的电流和电压。通过电机驱动模块&#xff0c;可以实现对电机的速度、方向等参数进行精确控制。 今天我们要介绍的 L9110S 电机驱动适合大学生、工程师、个人DIY、电子爱好者们学习和…

Unity 获取鼠标点击位置物体贴图颜色

实现 Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) {textureCoord hit.textureCoord;textureCoord.x * textureMat.width;textureCoord.y * textureMat.height;textureColor textureMat.GetPixel(Mathf.Flo…

openlayers+vite+vue3实现在地图上画线(四)

在前几期实现离线地图初始化以及规划某一特定区域、打点、出现弹窗的基础上&#xff0c;本文主要阐述如何实现在所规划的区域地图上画线&#xff0c;如果你实现了打点的效果&#xff0c;其实这个相对来说还是算比较简单的&#xff0c;因为和打点的代码大差不差。使用openlayers…

游戏引擎学习第45天

仓库: https://gitee.com/mrxiao_com/2d_game 回顾 我们刚刚开始研究运动方程&#xff0c;展示了如何处理当人物遇到障碍物时的情况。有一种版本是角色会从障碍物上反弹&#xff0c;而另一版本是角色会完全停下来。这种方式感觉不太自然&#xff0c;因为在游戏中&#xff0c;…

类与对象以及ES6的继承

认识class定义类 类的声明用的比较多 类与构造函数的异同 类的构造函数 类的实例方法 类的访问器方法 在类里面写拦截方法 类的静态方法 通过类名直接访问 es6类的继承-extends super关键字 子类可以重写父类方法包括父类的静态方法也可以继承父类的静态方法 babel可以将新的代…

通过IKE协商方式建立IPSec隧道

我们前面学习了H3C的IPsec VPN配置&#xff08;为什么IPsec两端内网的网段能不能重复&#xff1f;分明可以实现&#xff01;&#xff09;&#xff0c;学习了Juniper的IPsec VPN配置&#xff0c;学习了Windows的IPsec VPN配置&#xff08;配置Juniper虚墙vSRX基于策略的IPsec VP…

文献分享: EMVB——PLAID后期交互引擎的进一步优化

&#x1f449;前情提要&#xff1a; 神经网络自然语言模型概述 Transformer \text{Transformer} Transformer与注意力机制概述 &#x1f4da;相关论文&#xff1a; BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding \text{BERT: Pre-train…

vue2+element-ui实现多行行内表格编辑

效果图展示 当在表格中点击编辑按钮时:点击的行变成文本框且数据回显可以点击确定按钮修改数据或者取消修改回退数据: 具体实现步骤 1. 行数据定义编辑标记 行数据定义编辑标记 当在组件中获取到用于表格展示数据的方法中,针对每一行数据添加一个编辑标记 this.list.f…

介绍几个Linux下的杀毒软件

一&#xff1a;chkrootkit 是一个用于检测Linux系统下可能被攻击者植入的后门程序或恶意代码的扫描工具。 &#xff08;1&#xff09;安装方法&#xff08;ubuntu) sudo apt update sudo apt install chkrootkit &#xff08;2&#xff09;使用方法&#xff1a; chkrootkit -…

JS 中请求队列与锁的巧妙结合

一、引言 在 JavaScript 开发中&#xff0c;尤其是在涉及到异步操作和对共享资源的并发访问时&#xff0c;有效地控制请求顺序和资源访问权限至关重要。例如&#xff0c;在多个网络请求同时针对一个有限制访问频率的 API 或者多个异步任务竞争同一个文件写入权限的场景下&#…

MYSQL索引的分类和创建

目录 1、聚簇索引和非聚簇索引 tips&#xff1a; 小问题&#xff1a;主键为什么建议使用自增id? 2、普通索引 &#xff08;常规索引&#xff09;(normal) 3、唯一索引&#xff08;UNIQUE &#xff09; 唯一索引和主键的区别&#xff1a; 唯一约束和唯一索引的区别&#…