正点原子STM32F407 U盘升级程序(IAP)OTA Bootloader APP USB升级+FATFS+USB Host

  • Chapter0 解决STM32 Bootloader跳转APP失败问题
    • 问题背景
    • 问题描述
    • 问题解决
      • 原APP跳转的函数为:
      • 修改APP程序main入口处
  • Chapter1 MDK如何生成*.bin格式的文件
  • Chapter2 正点原子STM32F407 U盘升级程序(IAP)OTA Bootloader APP USB升级+FATFS+USB Host+FreeRTOS 附上源码
    • 一、引言
    • 二、软硬件准备
    • 三、CUBEmx配置
    • 四、分区管理
    • 五、Bootloader层开发
      • 5.1 U盘接口实现
      • 5.2 升级流程控制
      • 5.3 bootloader.c
      • 5.4 bootloader.h
    • 六、App层开发
      • 6.1 App程序设计
      • 6.2 App魔术棒配置
      • 6.3底层代码修改
      • 6.4 Bin文件生成
    • 七、注意事项
    • 八、实验过程
    • 九、总结
    • 十、源码分享
  • Chapter3 HAL库U盘升级 STM32F407 CUBEMX:FATFS + USB_HOST + USB_OTG_FS
    • 一、测试平台:
    • 二、实验目的:
    • 三、BootLoader:
      • 1 下载器配置
      • 2 时钟源配置
      • 3 LED配置
      • 4 串口配置 开启全局中断
      • 5 USB_OTG_FS配置
      • 6 USB_HOST配置
      • 7 FATFS配置
      • 8 时钟树配置 48M是用来操作U盘的,所以必须要有
      • 9 工程配置
      • 10 生成工程
      • 11 创建两个空白文件文件
      • BootLoader.h
      • BootLoader.c
    • 四、APP:
      • 1 APP程序只进行LED闪烁,只配置这三个功能就够了
      • 2 时钟树配置
      • 3 工程配置
      • 4 接下来是APP程序
    • 五、实验现象:
    • 六、文件篇:
  • Chapter4 01-STM32+Air724UG远程升级篇OTA(自建物联网平台)-STM32如何实现的升级程序
  • Chapter5 02-STM32+Air724UG远程升级篇OTA(自建物联网平台)-什么是http,怎么通过http下载文件数据
    • 说明
    • 搭建好web服务器(Windows)
  • Chapter6 一个简单粗暴易用的远程调试方案——OTA http update
  • Chapter7 在线升级:OTA升级的原理和实现方式
    • 1、OTA 在线升级
    • 2、实现方式
    • 3、操作方式
      • 3.1、后台式升级
      • 3.2、非后台式式更新
    • 4、STM32 的在线升级


Chapter0 解决STM32 Bootloader跳转APP失败问题

原文链接:https://blog.csdn.net/2303_79637659/article/details/149101151

问题背景

在STM32开发中,通过Bootloader跳转到用户应用程序(APP)是常见需求。但在实际开发中,可能会遇到 Bootloader能正常启动,但跳转到APP后卡死或进入Error_Handler 的问题。本文将详细记录一个典型案例:因时钟配置冲突导致跳转失败,并给出完整解决方案。

问题描述

现有一个Bootloader程序,和一个APP程序,Bootloader程序存储在地址0x08000000,APP程序存储在0x08010000,将两个程序通过Keil烧录到对应的地址后,Bootloader程序能够正常运行,但APP程序不能运行。
但是如果单独下载运行APP程序,程序是可以正常运行的。

起初,我是认为程序没有正常跳转,于是在网上搜素了很多相关资料,这些文章很多都讲了MSP指针对程序跳转的影响和解决方案,根据这些资料排查问题后,发现还是不能成功跳转,于是,我使用Keil的断点调试功能对代码进行单步调试。

单步调试时发现,程序是能够正常跳转的,但在APP程序的SystemClock_Config函数中,HAL_RCC_OscConfig函数会配置失败,导致进入Error_Handler中断。

void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 4;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}

进一步分析发现,在Bootloader中由于使用了USB等外设,所以Bootloader和APP的时钟树配置有些许不同,导致APP时钟配置失败,程序卡死。

问题解决

原APP跳转的函数为:

typedef void (*appfun)(void);void APP_JUMP(uint32_t APP_ADDR)
{__disable_irq();uint32_t JUMP_ADDR = APP_ADDR;uint32_t RESET_IRQ_ADDR = JUMP_ADDR + 4;appfun boot2app;boot2app = (appfun)*(__IO uint32_t *)RESET_IRQ_ADDR;__set_MSP(*(__IO uint32_t *)JUMP_ADDR);boot2app();}

在APP跳转之前,将时钟和外设全部复位

typedef void (*appfun)(void);void APP_JUMP(uint32_t APP_ADDR)
{HAL_RCC_DeInit();   //复位时钟HAL_DeInit();  // 复位所有外设__disable_irq();uint32_t JUMP_ADDR = APP_ADDR;uint32_t RESET_IRQ_ADDR = JUMP_ADDR + 4;appfun boot2app;boot2app = (appfun)*(__IO uint32_t *)RESET_IRQ_ADDR;__set_MSP(*(__IO uint32_t *)JUMP_ADDR);boot2app();}

修改APP程序main入口处

/**************这三句必须加上,否则APP程序无法运行************************/
__enable_irq();
HAL_DeInit();
HAL_Init();
/**************这三句必须加上,否则APP程序无法运行************************/
/*** @brief  The 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. *//**************这三句必须加上,否则APP程序无法运行************************/__enable_irq();HAL_DeInit();HAL_Init();/**************这三句必须加上,否则APP程序无法运行************************//* 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();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);HAL_Delay(300);/* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

修改之后,APP能够成功跳转运行,至此,问题解决。

Chapter1 MDK如何生成*.bin格式的文件

  1. 【单片机开发】KEIL如何生成*.bin格式的文件

Chapter2 正点原子STM32F407 U盘升级程序(IAP)OTA Bootloader APP USB升级+FATFS+USB Host+FreeRTOS 附上源码

原文链接:https://blog.csdn.net/2202_75941163/article/details/146256031

一、引言

    在嵌入式开发领域,程序的现场升级功能是非常实用的,它允许用户在产品已经部署到现场后,仍能方便地对程序进行更新和维护。使用STM32实现U盘升级程序(IAP)功能,可以极大地提高产品的可维护性和用户体验。本文将详细介绍如何实现基于STM32的U盘IAP功能,主要分为Bootloader层和App层的开发。

-------------------------------------------------------------结尾附上源码---------------------------------------------------------------

二、软硬件准备

硬件:正点原子STM32F407ZGT6最小系统板
软件:CUBEMx版本:MX.6.12.0
调试工具:sscom串口调试助手

三、CUBEmx配置

     本文暂不详细介绍CUBEmx的配置步骤,因为网上资源丰富,大家可以参考上一篇(读写U盘)配置教程,里面有提到大佬的配置链接:

单片机读取U盘 FATFS文件系统 USB MSC STM32f105 GD32f305 读取U盘 exFAT FAT32_gd32f105usb例程-CSDN博客
https://blog.csdn.net/2202_75941163/article/details/145942897

注:读写U盘 与 U盘IAP升级所需的HAL库基本配置是一样的

四、分区管理

    为了实现IAP功能,需要对STM32的Flash存储器进行分区管理。一般将Flash存储器分为两个主要区域:Bootloader区和App区。Bootloader区用于存放Bootloader程序,而App区用于存放用户应用程序。
#define BOOTLOADER_START_ADDR    0x08000000    // Bootloader起始地址#define BOOTLOADER_SIZE            0x00010000    // Bootloader大小(64KB)#define FLASH_USER_START_ADDR      0x08010000   // App起始地址#define FLASH_USER_END_ADDR       (0x08010000 + APP_Size)  // APP结束地址#define filename "APP.bin"                     // APP_Size为U盘bin文件大小

五、Bootloader层开发

5.1 U盘接口实现

    为了实现U盘升级功能,需要在Bootloader中实现USB设备接口。STM32提供了丰富的USB外设功能,通过FatFS文件管理系统与USB Host功能,可以实现识别U盘升级Bin文件。在代码中,通过调用f_mount函数挂载U盘,f_open函数打开U盘中的固件文件,然后使用f_read函数将固件数据读取到RAM缓冲区中。

5.2 升级流程控制

在Bootloader中,升级流程控制的实现基于对U盘检测和用户操作的响应。具体流程如下:

U盘检测与初始化:系统上电后,Bootloader首先检测U盘是否插入。这是通过USB主机功能实现的,一旦检测到U盘,便初始化FatFS文件系统,为后续的文件操作做准备。
固件文件读取:在成功挂载U盘后,Bootloader尝试打开并读取固件文件。文件读取操作通过FatFS的f_read函数完成,将固件数据从U盘读取到内部RAM缓冲区中。
固件数据校验:读取固件数据后,需要对数据进行校验,确保数据的完整性和正确性。这一步骤对于防止因数据损坏导致的升级失败至关重要。
Flash擦除与写入:如果固件数据校验通过,Bootloader将执行Flash擦除操作,为新的固件写入腾出空间。擦除操作针对应用程序区域的Flash扇区进行。擦除完成后,将RAM缓冲区中的固件数据写入Flash。
跳转到应用程序:在固件成功写入Flash后,Bootloader设置好应用程序的堆栈指针和程序计数器,然后跳转到应用程序的入口点,开始运行新的应用程序。
错误处理与重试:如果在升级过程中任何一步出现错误,例如U盘读取失败、数据校验错误或Flash写入失败,Bootloader将留在当前模式下,等待用户重新发起升级操作或进行故障排除。

5.3 bootloader.c

#include "bootloader.h"
#include "main.h"extern ApplicationTypeDef Appli_state;
FRESULT res;
static FLASH_EraseInitTypeDef EraseInitStruct;uint8_t RAM_Buffer[RAM_BUFFER_SIZE];                         // 用于暂存从U盘读取的固件数据
uint32_t APP_Size;                                           // 从U盘读取的固件大小
uint32_t FirstSector = 0;
uint32_t NbOfSectors = 0;
uint32_t SectorError = 0;
uint32_t Address = 0;volatile uint32_t data32 = 0 ;
volatile uint32_t MemoryProgramStatus = 0 ;
uint8_t errorcode;
uint32_t *p;uint8_t SystemUpdateFlag = 0, state = 0;                     // 状态标志变量
uint16_t t = 0;typedef  void (*pFunction)(void);
pFunction Jump_To_Application;uint32_t JumpAddress;uint32_t FLASH_Erase_Write(void)
{uint32_t i = 0;HAL_FLASH_Unlock();                                                // 解锁FlashFirstSector = GetSector(FLASH_USER_START_ADDR);                    // 获取应用程序起始地址所在的扇区编号NbOfSectors = GetSector(FLASH_USER_END_ADDR) - FirstSector + 1;    // 计算需要擦除的扇区数量printf("擦除的扇区数量为%d",NbOfSectors);// 配置 Flash 擦除结构体EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;              // 设置擦除类型为扇区擦除EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;             // 设置电压范围EraseInitStruct.Sector = FirstSector;                             // 设置起始扇区EraseInitStruct.NbSectors = NbOfSectors;                          // 设置扇区数量// 执行Flash擦除if(HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {errorcode = HAL_FLASH_GetError();  // 获取错误码printf("errorcode %d", errorcode);Error_Handler();}// 禁用和清除Flash缓存__HAL_FLASH_DATA_CACHE_DISABLE();__HAL_FLASH_INSTRUCTION_CACHE_DISABLE();__HAL_FLASH_DATA_CACHE_RESET();__HAL_FLASH_INSTRUCTION_CACHE_RESET();__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();__HAL_FLASH_DATA_CACHE_ENABLE();Address = FLASH_USER_START_ADDR;              // 设置Flash写入起始地址// 遍历 RAM_Buffer,将数据写入 Flashprintf("正在写入数据 请稍后... ...\r\n");while (Address < FLASH_USER_END_ADDR){p = (uint32_t *)&RAM_Buffer[i];             // 获取要写入的数据if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, *p) == HAL_OK)           // 将数据写入 Flash{Address = Address + 4;   // 地址递增 4 字节(32位)i = i + 4;}else{printf("Address-error\r\n");Error_Handler();}}printf("数据写入完毕\r\n");HAL_FLASH_Lock();                                 // 锁定Flash// 验证Flash写入是否成功Address = FLASH_USER_START_ADDR;                  // 重置地址指针MemoryProgramStatus = 0x0;                        //初始化验证状态变量// 遍历 Flash 写入范围,验证数据while (Address < FLASH_USER_END_ADDR){data32 = *(__IO uint32_t*)Address;      // 读取Flash中的数据if (data32 != *(uint32_t*)RAM_Buffer)   // 比较Flash数据和原始数据{MemoryProgramStatus++;}Address = Address + 4;}return HAL_OK;
}/**********************************************************************
**** 函数名: GetSector()
**** 功  能: 获取Flash的扇区
**** 参  数: Address   Flash的地址
**** 返回值: 扇区编号
**** 时  间: 2025年3月10日
**** 设  计: 
**** 备  注: STM32F407的Flash大小(1M)    1个扇区16KB(0x4000字节)   一共11个扇区
**********************************************************************/static uint32_t GetSector(uint32_t Address)
{uint32_t sector = 0;              // 初始化扇区编号为0// 判断地址所在的扇区if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)){sector = FLASH_SECTOR_0;  }else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)){sector = FLASH_SECTOR_1;  }else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)){sector = FLASH_SECTOR_2;  }else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)){sector = FLASH_SECTOR_3;  }else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4)){sector = FLASH_SECTOR_4;  }else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5)){sector = FLASH_SECTOR_5;  }else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6)){sector = FLASH_SECTOR_6;  }else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7)){sector = FLASH_SECTOR_7;  }else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8)){sector = FLASH_SECTOR_8;  }else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9)){sector = FLASH_SECTOR_9;  }else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10)){sector = FLASH_SECTOR_10;  }else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11) */{sector = FLASH_SECTOR_11;}return sector;      // 返回扇区编号
}/**********************************************************************
**** 函数名: jumpToApp()
**** 功  能: 跳转到appa运行程序
**** 参  数:
**** 返回值: 无
**** 时  间: 2025年3月11日
**** 设  计: 
**** 备  注: 
**********************************************************************/
void jumpToApp()
{ // 检查应用程序的栈顶地址是否有效// 应用程序的栈顶地址存储在FLASH_USER_START_ADDR处// 有效栈顶地址的高16位必须是0x2000(即位于SRAM区域)if (((*(__IO uint32_t*)FLASH_USER_START_ADDR) & 0x2FFE0000 ) == 0x20000000){printf("跳转到应用程序\r\n");HAL_RCC_DeInit(); //复位时钟HAL_DeInit(); //复位所有外设__disable_irq(); //关闭所有终端//    printf("ADDR == 0x20000000\r\n");JumpAddress = *(__IO uint32_t*) (FLASH_USER_START_ADDR + 4);    // 应用程序的入口地址存储在FLASH_USER_START_ADDR + 4处Jump_To_Application = (pFunction) JumpAddress;                  // 将入口地址转换为函数指针// 设置栈指针(MSP)为应用程序的栈顶地址 __set_MSP(*(__IO uint32_t*) FLASH_USER_START_ADDR);             //应用程序的栈顶地址存储在FLASH_USER_START_ADDR处Jump_To_Application();                                          // 跳转到应用程序}printf("ADDR != 0x20000000\r\n");     //  栈顶地址无效printf("跳转到应用程序失败!\r\n");
}void UP_Data(void)
{while(t < 1010){MX_USB_HOST_Process();HAL_Delay(1);if(SystemUpdateFlag == 0 && Appli_state == APPLICATION_READY)       // 检查是否准备好进行固件更新{printf("检测到升级程序(U盘已经插入)\r\n");SystemUpdateFlag = 1;t = 1000;state = 1;//挂载U盘res = f_mount(&USBHFatFS, (TCHAR const*)USBHPath, 0);if(res != FR_OK){printf("U盘挂载失败  %d\r\n", res);Error_Handler();}else{printf("U盘挂载成功\r\n");}//打开U盘文件res = f_open(&USBHFile, filename, FA_READ);if(res != FR_OK){printf("打开U盘文件失败  %d\r\n", res);Error_Handler();}else{printf("打开U盘文件成功\r\n");}//读取U盘文件           读取固件数据到RAM_Bufferres = f_read(&USBHFile, RAM_Buffer, sizeof(RAM_Buffer), (void *)&APP_Size);if(res != FR_OK){printf("读取U盘文件失败  %d\r\n", res);Error_Handler();}else{printf("读取U盘文件成功\r\n");}   // 检查固件大小是否合法if((0<APP_Size) && (APP_Size<FLASH_USER_END_ADDR))     // 确保固件大小在合理范围内{printf("APP_Size大小为 : %d \r\n",APP_Size);printf("FLASH开始擦除\r\n");    //FLASH擦除//正在升级,指示灯常快速闪烁10次for(int k = 0; k < 20; k++){HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);HAL_Delay(50);}FLASH_Erase_Write();  // 调用Flash擦除和写入函数//升级完成,指示灯常亮1秒钟HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);HAL_Delay(1000);HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);jumpToApp();                  // 跳转到新应用程序            }else{printf("APP_Size_Erase\r\n");   //bin文件大小不符合}f_close(&USBHFile);}else{t++;}// 如果t超过1000且未进入更新流程,直接跳转到应用程序if(state == 0 && t > 1000){state = 1;printf("\r\n未检测到升级程序\r\n");jumpToApp();}}
}

5.4 bootloader.h

#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H#include "stm32f4xx_hal.h"
#include "usb_host.h"
#include "fatfs.h"
#include "usart.h"#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */#define RAM_BUFFER_SIZE               ((uint32_t)30*1024)       /*KBytes*/#define filename "APP.bin"             //识别U盘文件名称#define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_4   /* Start @ of user Flash area */
#define FLASH_USER_END_ADDR     ADDR_FLASH_SECTOR_4 + APP_Size 
uint32_t FLASH_Erase_Write(void);
static uint32_t GetSector(uint32_t Address);
void jumpToApp(void);
void UP_Data(void);#endif

六、App层开发

6.1 App程序设计

   APP程序设计可以按照自己的需要进行书写(试验过程建议先进行 升级亮灯)本例程设计:使用FreeRTOS 随机写了几个外设任务,串口通信、LED亮灭、按键检测、DA信号DMA转换等任务供学习使用;

6.2 App魔术棒配置

在这里插入图片描述
IRAM1 (0x20000000 起始地址):

这是片上SRAM,通常用于存储变量、数据结构以及堆栈等。STM32F407ZET6中,SRAM的大小是128KB(0x20000字节)。SRAM是通用的随机存取存储器,用于程序运行时的数据存储。

IRAM2 (0x10000000 起始地址):

这是CCM RAM,它是一种紧耦合存储器,直接连接到CPU,具有更快的访问速度。在STM32F407ZGT6中,CCM RAM的大小是64KB(0x10000字节)。CCM RAM通常用于存储需要快速访问的数据,例如实时数据处理或缓存。

6.3底层代码修改

在这里插入图片描述
在这里插入图片描述

6.4 Bin文件生成

在这里插入图片描述
使用fromelf.exe --bin -o “$L@L.bin” “#L”

这条命令的含义是:在工程编译完成后,自动调用fromelf.exe工具,将生成的elf格式的可执行文件(通常是.axf文件)转换为bin格式的文件。其中,–bin参数指定输出为bin格式,-o参数指定输出文件的路径和名称,"#L"表示输入的elf文件路径和名称

注:直接在工程文件中搜索.Bin文件即可

七、注意事项

在实现STM32 U盘IAP功能时,需要注意以下几点:

  1. 数据校验:在数据传输过程中,要进行严格的数据校验,确保数据的完整性和正确性。
  2. 分区大小:合理设置Bootloader区和App区的大小,确保App区有足够的空间存放用户程序。
  3. 兼容性:确保Bootloader和App之间的接口兼容,避免因接口不匹配导致的问题。
  4. 稳定性:在升级过程中,要确保系统的稳定性,避免因意外断电等因素导致升级失败。

八、实验过程

从APP中复制 .Bin文件到U盘中
在这里插入图片描述
在这里插入图片描述
然后将U盘插入USB OTG口,重新上电或复位,即可实现U盘IAP升级,实验现象如下所示
在这里插入图片描述

九、总结

   通过以上步骤,可以实现基于STM32的U盘IAP功能。该功能允许用户通过U盘方便地对设备进行程序升级,极大地提高了产品的可维护性和用户体验。在实际开发中,可以根据具体需求对上述方案进行优化和扩展,以满足不同的应用场景。

十、源码分享

U-disk_IAP: STM32 U盘升级程序(IAP)是一种实用的嵌入式开发技术,允许用户通过U盘对设备进行程序升级,提高产品的可维护性和用户体验。本文详细介绍基于STM32的U盘IAP功能实现,涵盖Bootloader和App层开发。通过合理分区管理Flash存储器,确保数据传输的完整性和正确性,实现稳定可靠的升级过程。该功能适用于需要现场升级的嵌入式产品,具有较高的实用价值。
https://gitee.com/Lucky_17wow/U-disk_IAP


Chapter3 HAL库U盘升级 STM32F407 CUBEMX:FATFS + USB_HOST + USB_OTG_FS

原文链接:https://blog.csdn.net/qq_44742284/article/details/123132331

一、测试平台:

MCU:STM32F407VET6
固件库:CUBEMX
IDE:MDK

二、实验目的:

将U盘里面的bin文件插入要升级的设备,通过BootLoader来进行升级

在这是用板载的LED灯来显示升级情况:
不进行升级:LED灯是灭的状态
升级成功:LED灯以100ms在闪烁

接下来先进行BootLoader的配置以及程序编写,再配置APP

三、BootLoader:

1 下载器配置

在这里插入图片描述

2 时钟源配置

在这里插入图片描述

3 LED配置

在这里插入图片描述

4 串口配置 开启全局中断

在这里插入图片描述

5 USB_OTG_FS配置

在这里插入图片描述

6 USB_HOST配置

在这里插入图片描述

7 FATFS配置

在这里插入图片描述

8 时钟树配置 48M是用来操作U盘的,所以必须要有

在这里插入图片描述

9 工程配置

在这里插入图片描述

10 生成工程

在这里插入图片描述

11 创建两个空白文件文件

在这里插入图片描述

BootLoader.h

#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H#include "stm32f4xx_hal.h"
#include "usb_host.h"
#include "fatfs.h"
#include "usart.h"#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */#define RAM_BUFFER_SIZE               ((uint32_t)30*1024)       /*KBytes*/#define filename "LED.bin"#define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_4   /* Start @ of user Flash area */
#define FLASH_USER_END_ADDR     ADDR_FLASH_SECTOR_4  +  0x10000uint32_t FLASH_Erase_Write(void);
static uint32_t GetSector(uint32_t Address);
void jumpToApp(void);
void UP_Data(void);#endif 

BootLoader.c

#include "BootLoader.h"extern ApplicationTypeDef Appli_state;
FRESULT res;
static FLASH_EraseInitTypeDef EraseInitStruct;uint8_t RAM_Buffer[RAM_BUFFER_SIZE];
uint32_t APP_Size;
uint32_t FirstSector = 0, NbOfSectors = 0, Address = 0;
uint32_t SectorError = 0;
__IO uint32_t data32 = 0 , MemoryProgramStatus = 0;
uint8_t errorcode;
uint32_t *p;uint8_t SystemUpdateFlag = 0, state = 0;
uint16_t t = 0;typedef  void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress;int fputc(int ch, FILE *f)		//用来打印信息的 便于观察
{HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 100);return ch;
}
uint32_t FLASH_Erase_Write(void)		//FLASH擦写
{uint32_t i = 0;HAL_FLASH_Unlock();FirstSector = GetSector(FLASH_USER_START_ADDR);NbOfSectors = GetSector(FLASH_USER_END_ADDR) - FirstSector + 1;EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;EraseInitStruct.Sector = FirstSector;EraseInitStruct.NbSectors = NbOfSectors;if(HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK){errorcode = HAL_FLASH_GetError();		//擦除失败的扇区printf("errorcode %d", errorcode);Error_Handler();}__HAL_FLASH_DATA_CACHE_DISABLE();__HAL_FLASH_INSTRUCTION_CACHE_DISABLE();__HAL_FLASH_DATA_CACHE_RESET();__HAL_FLASH_INSTRUCTION_CACHE_RESET();__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();__HAL_FLASH_DATA_CACHE_ENABLE();printf("HAL_OK\r\n");Address = FLASH_USER_START_ADDR;while (Address < FLASH_USER_END_ADDR){p = (uint32_t *)&RAM_Buffer[i];if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, *p) == HAL_OK)	//FLASH写入{Address = Address + 4;i = i + 4;}else{printf("Address-error\r\n");Error_Handler();}}HAL_FLASH_Lock(); Address = FLASH_USER_START_ADDR;		//校验MemoryProgramStatus = 0x0;printf("CHEAK\r\n");while (Address < FLASH_USER_END_ADDR){data32 = *(__IO uint32_t*)Address;if (data32 != *(uint32_t*)RAM_Buffer){MemoryProgramStatus++;}Address = Address + 4;}printf("CHEAK-finish\r\n");return HAL_OK;
}
static uint32_t GetSector(uint32_t Address)		//获取扇区
{uint32_t sector = 0;if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)){sector = FLASH_SECTOR_0;  }else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)){sector = FLASH_SECTOR_1;  }else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)){sector = FLASH_SECTOR_2;  }else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)){sector = FLASH_SECTOR_3;  }else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4)){sector = FLASH_SECTOR_4;  }else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5)){sector = FLASH_SECTOR_5;  }else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6)){sector = FLASH_SECTOR_6;  }else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7)){sector = FLASH_SECTOR_7;  }else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8)){sector = FLASH_SECTOR_8;  }else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9)){sector = FLASH_SECTOR_9;  }else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10)){sector = FLASH_SECTOR_10;  }else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11) */{sector = FLASH_SECTOR_11;}return sector;
}
void jumpToApp()
{if (((*(__IO uint32_t*)FLASH_USER_START_ADDR) & 0x2FFE0000 ) == 0x20000000){printf("ADDR == 0x20000000\r\n");JumpAddress = *(__IO uint32_t*) (FLASH_USER_START_ADDR + 4);Jump_To_Application = (pFunction) JumpAddress;__set_MSP(*(__IO uint32_t*) FLASH_USER_START_ADDR);__HAL_UART_DISABLE(&huart3);				//关闭相应中断__HAL_RCC_USB_OTG_FS_CLK_DISABLE();__HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_TC);__HAL_UART_DISABLE_IT(&huart3, UART_IT_RXNE);Jump_To_Application();}printf("ADDR != 0x20000000\r\n");
}
void UP_Data(void)
{if(state == 0){if(t < 2010){if(SystemUpdateFlag == 0 && Appli_state == APPLICATION_READY){printf("APPLICATION_READY\r\n");SystemUpdateFlag = 1;t = 2000;state = 1;res = f_mount(&USBHFatFS, (TCHAR const*)USBHPath, 0);if(res != FR_OK){printf("U盘挂载失败  %d\r\n", res);Error_Handler();}else{printf("U盘挂载成功\r\n");}res = f_open(&USBHFile, filename, FA_READ);if(res != FR_OK){printf("打开U盘文件失败  %d\r\n", res);Error_Handler();}else{printf("打开U盘文件成功\r\n");}res = f_read(&USBHFile, RAM_Buffer, sizeof(RAM_Buffer), (void *)&APP_Size);if(res != FR_OK){printf("读取U盘文件失败  %d\r\n", res);Error_Handler();}else{printf("读取U盘文件成功\r\n");}	if((0<APP_Size) && (APP_Size<FLASH_USER_END_ADDR)){printf("FLASH_Erase\r\n");FLASH_Erase_Write();jumpToApp();}else{printf("APP_Size错误\r\n");}f_close(&USBHFile);//		FATFS_UnLinkDriver(USBHPath);		}else{printf("%d\r\n", t);		t++;HAL_Delay(1);}if(state == 0 && t > 2000)		//超过2s 读取U盘失败{state = 1;printf("DISCONNECT\r\n");jumpToApp();}}}
}	

四、APP:

1 APP程序只进行LED闪烁,只配置这三个功能就够了

在这里插入图片描述

2 时钟树配置

在这里插入图片描述

3 工程配置

在这里插入图片描述
在这里插入图片描述

4 接下来是APP程序

(1)main.c 只放LED闪烁的功能
在这里插入图片描述
(2)中断偏移地址 这个地址要和BootLoader程序里面flash的起始地址要一样
在这里插入图片描述
(3)魔术棒配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
注意:下面这是一整行命令语句,不是分三行。

C:\Keil_v5\ARM\ARMCLANG\bin\fromelf.exe --bin -o C:\Users\Administrator\Desktop\USB_BootLoader\APP\MDK-ARM\APP.bin C:\Users\Administrator\Desktop\USB_BootLoader\APP\MDK-ARM\APP\APP.axf

配置好后点击OK,编译一下工程,bin文件就生成了
在这里插入图片描述
变量前面有个逗号 英文的

在这里插入图片描述
将生成的bin文件放到U盘里
在这里插入图片描述

五、实验现象:

BootLoader第一次下载是最好是进行一下全片擦除,之后使用扇区擦除

在这里插入图片描述
(1)没有检测到U盘现象
蓝色是电源指示灯,红框才是实验灯(红色的)
在这里插入图片描述

六、文件篇:

U盘升级STM32F407(文件篇)

Chapter4 01-STM32+Air724UG远程升级篇OTA(自建物联网平台)-STM32如何实现的升级程序

原文链接:https://blog.csdn.net/qq_14941407/article/details/115594577

说明
这节提供给用户一份实现更新STM32的程序(兼容STM32f103全系列)

主要说明STM32是如何实现的升级程序.后面的章节都是在这节的基础上进行优化.

该节源码开源: https://gitee.com/yang456/STM32_IAP_Learn.git

请用户认真学习此节!该代码只使用了5字节数组接收程序文件!

测试
1.说明
在这里插入图片描述

BootLoader作为引导程序,负责把接收的程序文件写入flash,然后加载执行.
STM32F10xTemplate 是用户程序,这套程序采用串口升级进去.然后执行

Chapter5 02-STM32+Air724UG远程升级篇OTA(自建物联网平台)-什么是http,怎么通过http下载文件数据

原文链接:https://blog.csdn.net/qq_14941407/article/details/115594623

说明

什么是http?http的实质是什么?

大家都在说GET指令,POST指令.这又是什么?

其实没什么!继续看!

搭建好web服务器(Windows)

1.按照基本控制篇以下两节搭建好web服务器;

注意:如果只是做远程升级不需要安装mqtt软件,主需要购买云主机,然后安装上Nginx

当然安装tomcat也可以
在这里插入图片描述

2.网站根目录

在这里插入图片描述

Chapter6 一个简单粗暴易用的远程调试方案——OTA http update

原文链接:https://blog.csdn.net/tiandiren111/article/details/107421355

文字简单描述一下思路,8266定时或主循环轮询服务器(树莓派)的一个文件(随便个文件,我用的txt),文件中的内容是标志,我用的是时间如:200716即昨天程序日期的版本号,今天我如果要更新8266的程序,就将最新的bin文件通过ftp发送到树莓派上,然后修改程序日期版本号。8266定时去询问服务器,并比较程序版本号,如果服务器程序的版本号大于当前的就更新,反之就不更新。就这么简单
在这里插入图片描述

在这里插入图片描述

Chapter7 在线升级:OTA升级的原理和实现方式

原文链接:https://blog.csdn.net/weixin_43866583/article/details/127706079

在平常的项目开发和调试中,下载程序一般使用的是外部下载器或者串口的方式实现对单片机的程序下载和刷新,这种方法在项目的开发阶段是常用的方式。

但是当项目开发完成推向市场的时候,很多时候需要对产品进行升级,而这个时候产品又已经是加了外壳的或者被封装起来了,一般也不会在外面预留出来下载接口之类的。

如果这个时候我想要更新产品的程序的话,可能就得要重新打开产品的外壳,然后通过下载器更新程序,更新完成之后再把外壳装上,这种做法显然是不太现实的。但是我们又必须要给产品进行升级,那该怎么办呢?这个时候就可以考虑使用产品本身预留的一些外部通信接口(如:USB、RS232、ES485、以太网口等)或者内部无线(如:wifi、蓝牙、4/5G网络)等对产品进行升级。

上面介绍的这种通过外部有线接口或者无线通信的方式进行的更新其实是一种在线更新的方式,即OTA升级技术。

那问题来了,到底什么是OTA升级技术呢?待我慢慢道来!

1、OTA 在线升级

  • OTA:Over-the-Air Technology,字面意思理解为:空中下载技术。

  • OTA 在线升级:通过OTA的方式实现产品软件更新的一种方式。

所以,简单而言,通过外部的方式(有线 / 无线)对产品进行更新,而不是用传统的编程器刷入固件的方式就可以称之为 OTA 在线升级。

严格意义上来讲,OTA 指的是空中下载,即只有通过无线的方式进行更新的才称之为 OTA 升级;而那种通过外部的接口接线来实现的更新,应该称之为本地升级。这两者还是有点区别的,只是一般我们都没有那么严格去区分罢了!

2、实现方式

那既然理解了升级的概念了,该怎么去实现呢?

我们一般的做法是会将这个升级功能进行划分,分为两部分:

1)接收新的升级固件并完成新旧固件的替换,这部分代码为 BootLoader;

2)产品功能的正常程序,用于执行各种应用功能,这部分程序称为 App。

那就是说,要实现在线升级,就需要准备两份程序,一份是BootLoader ,另一份是App。其中 bootloader 用于将外部传入的新固件(应用程序App)接收到内部并存储,接收完成以后,由 bootloader 用新接收到的固件去替换旧的固件,替换完成之后跳转到新的应用程序中进行执行。这样就完成了产品的固件更新。

注意:需要将 bootloader 和应用程序App的空间分开,两者是不能发生重叠的。

3、操作方式

3.1、后台式升级

后台式升级的意思是:在进行升级的时候,接收新固件包的方式是在后台进行的,不会影响功能的正常执行。等到固件更新完成之后,再跳转到Bootloader中去用新的固件替换旧的固件,替换完成之后呢再跳转到App去执行。

比如,现在的智能手机的在线更新就是后台式升级的方式。在你升级系统的时候,接收升级包的过程中,你还是可以正常使用的手机的,打电话、看视频、玩游戏等都不耽误,直到下载完成,你点击了开始更新之后,手机才进入更新状态,不让你操作,等更新完毕之后重启就又可以继续操作了。

3.2、非后台式式更新

非后台式升级的意思是:在进行升级的时候,接收固件时需要跳转到Bootloader,这个时候你不能在使用这个产品的任何功能,只能一直等着它接收并完成更新,完成之后你才能继续操作其他的功能。

4、STM32 的在线升级

本文以STM32为例展开讲解怎么实现OTA升级和操作的方法。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 8.0 在 Ubuntu 22.04 中如何将启用方式改为mysql_native_password(密码认证)

MySQL 8.0 在 Ubuntu 22.04 中默认启用了 auth_socket 认证方式(而非密码认证),导致 mysql_secure_installation 跳过了 root 密码设置。这会直接影响后续用 Navicat 连接 MySQL(因为 Navicat 需要密码登录),必须手动调整 root 用户的认证方式并设置密码。 核心问题:au…

七层网络协议-面试

七层网络协议概述七层网络协议&#xff0c;即OSI&#xff08;Open Systems Interconnection&#xff09;模型&#xff0c;是由国际标准化组织&#xff08;ISO&#xff09;提出的网络通信框架。它将网络通信过程划分为七个层次&#xff0c;每一层负责特定的功能&#xff0c;并通…

【Blender】二次元人物制作【二】:五官的制作

一、制作眼睛 选中眼眶内部的一圈线。shiftD复制出来调整成圆形&#xff0c;然后F快捷键填充将眼睛放在眼框内合适的位置&#xff0c;并用i键进行几次内插&#xff0c;做出瞳孔&#xff0c;并且将内部的眼瞳做得稍微向内凹陷一点。二、制作睫毛 选中眼眶上半部分的面&#xff0…

Deepin 25 系统安装 Docker:完整教程 + 常见问题解决

Deepin 25 系统安装 Docker&#xff1a;完整教程 常见问题解决 作为基于 Debian 的 Linux 发行版&#xff0c;Deepin 25 因系统目录&#xff08;如/usr&#xff09;默认只读的特性&#xff0c;安装 Docker 时需特殊处理 GPG 公钥存储路径。本文结合社区实践&#xff0c;整理出…

Redis MySQL小结

问题1&#xff1a;Redis为什么高效&#xff1f;答&#xff1a;基于内存&#xff0c;reactor&#xff0c;value的数据组织&#xff08;五种数据结构&#xff09;&#xff0c;KV的数据组织方式&#xff08;渐进hash&#xff09;问题2&#xff1a;跳表是什么&#xff1f;和红黑树的…

Flink on YARN 实战问题排查指南(精华版)

一、客户端常见问题速查 ‌1. JAR加载失败终极解法‌报错提示&#xff1a;"Could not build the program from JAR file" 核心原因&#xff1a;80%的情况是Hadoop依赖缺失 黄金配置&#xff1a;export HADOOP_CONF_DIR${HADOOP_HOME}/etc/hadoop export HADOOP_CLASS…

迅为RK3576开发板Android12制作使用系统签名

配套资料在网盘资料“iT0P-RK3576 开发板\02_【iTOP-RK3576 开发板】开发资料\ 08Android 系统开发配套资料\ 07 Android 制作使用系统签名”目录下制作签名文件 在 Android 源码 build/make/target/product/security/下存放着签名文件&#xff0c;如下所示&#xff1a;将北京迅…

django连接minio实现文件上传下载(提供接口示例)

django连接minio实现文件上传下载&#xff08;提供接口示例&#xff09;项目环境前提1.模型创建2. 在 settings.py 中添加 MINIO 配置3.创建 MINIO 工具类4.创建序列化器5. 创建视图6. 配置 URL 路由7.接口测试项目环境前提 已安装python3.8以上环境已安装djangorestframework…

Kafka消息队列进阶:发送策略与分区算法优化指南

Kafka消息队列进阶&#xff1a;发送策略与分区算法优化指南 目录Kafka消息队列进阶&#xff1a;发送策略与分区算法优化指南摘要1. Kafka消息发送模式概述1.1 消息发送的核心流程1.2 三种发送模式对比2. 同步发送模式详解2.1 同步发送实现原理2.2 同步发送性能优化3. 异步发送模…

【VScode】ssh报错

【VScode】ssh报错1. ssh报错2. 解决1. ssh报错 Failed to parse remote port from server output 2. 解决 windows电脑删除 C:\Users\username\.ssh\known_hosts linux cd /home/username/.vscode-server/ rm -rf ~/.vscode-server重新回到Vscode连接ok

Grafana+Loki+Alloy构建企业级日志平台

1.日志系统介绍日志系统&#xff1a;GLA、ELK、数仓 ⽇志处理流程&#xff1a;采集 > 存储 > 检索 > 可视化日志系统工作流程&#xff1a;日志平台的目的&#xff1a;统一聚合分散的日志日志平台搭建方案&#xff1a;ELK&#xff1a;ElasticSearch:存储日志&#xff0…

老梁聊全栈系列:(阶段一)现代全栈的「角色边界」与「能力雷达图」

JAVA Vue/React 双栈工程师的「T 型→E 型」进化指南 接上篇《从单体到云原生的演进脉络》 大家好&#xff0c;我是技术老梁&#xff0c;这是系列文章的第五篇。欢迎大家讨论&#xff0c;分享经验。如果知识对你有用&#xff0c;关注我&#xff0c;多多支持老梁&#xff0c;鼓…

使用 C# 设置 Excel 单元格格式

在实际报表开发中&#xff0c;Excel 的可读性和美观性与数据本身同样重要。合理的单元格格式设置不仅能让数据一目了然&#xff0c;还能让报表显得更专业。通过使用 C#&#xff0c;开发者可以精确控制 Excel 文件的单元格样式&#xff0c;无需依赖 Microsoft Office。 本文演示…

Redis篇章3:Redis 企业级缓存难题全解--预热、雪崩、击穿、穿透一网打尽

在企业级应用场景中&#xff0c;Redis 作为高性能缓存利器&#xff0c;极大提升了系统响应速度&#xff0c;但随着业务复杂度和并发量的攀升&#xff0c;缓存相关的各类挑战也接踵而至。比如系统启动时缓存缺失导致的数据库压力、大量缓存同时失效引发的连锁故障、热点数据过期…

【数值分析】02-绪论-误差

参考资料&#xff1a; 书籍&#xff1a; 数值分析简明教程/王兵团&#xff0c;张作泉&#xff0c;张平福编著. --北京&#xff1a;清华大学出版社&#xff1b;北京交通大学出版社&#xff0c;2012.8 视频&#xff1a;学堂在线APP中北京交通大学“数值分析I” 前期回顾 【数值分…

P3918 [国家集训队] 特技飞行

P3918 [国家集训队] 特技飞行 - 洛谷 思路&#xff1a; 因为如果连续进行相同的动作&#xff0c;乘客会感到厌倦&#xff0c;所以定义某次动作的价值为(距上次该动作的时间) ci​&#xff0c;若为第一次进行该动作&#xff0c;价值为 0。同一个动作&#xff0c;价值为ci*(最后一…

Python爬虫实战:研究Pandas,构建期货数据采集和分析系统

1. 引言 1.1 研究背景 期货市场作为金融市场的重要组成部分,具有价格发现、风险管理和资源配置的重要功能。上海期货交易所(Shanghai Futures Exchange, SHFE)作为中国四大期货交易所之一,上市交易的品种包括铜、铝、锌、黄金、白银等多种大宗商品期货,其交易数据反映了…

Linux第十七讲:应用层自定义协议与序列化

Linux第十七讲&#xff1a;应用层自定义协议与序列化1.什么是序列化和反序列化2.重新理解read、write为什么支持全双工3.网络版计算器的实现3.1socket的封装 -- 模板方法模式引入3.2序列化和反序列化 && json3.3协议的实现3.4 服务端整体看 -- 所有代码3.5七层协议&…

附录:Tomcat下载及启动

一、打开Tomcat官网windows下载第四个压缩包&#xff0c;下载完成后解压缩。&#xff08;安装路径不要带有中文和特殊符号&#xff09;二、启动Tomcat进入bin文件夹&#xff1a;\Tomcat\apache-tomcat-11.0.11\bin&#xff0c;找到startup.bat文件点击&#xff0c;黑窗口常驻即…

【CTF-WEB】表单提交(特殊参数:?url=%80和?url=@)(通过GBK编码绕过实现文件包含读取flag)

题目 寻找这个单纯的网站的flag 前端代码&#xff1a; <!DOCTYPE html> <head><title>CAT</title> </head><body> <h1>Cloud Automated Testing</h1> <p>输入你的域名&#xff0c;例如&#xff1a;loli.club</p>…