单片机通用Bootloader框架-优化

2023/9/30 17:56:09

单片机通用Bootloader框架-优化

  • 单片机通用Bootloader框架-优化
  • 定义一个标志位变量存储在不初始化的段
    • 在IAR中定义
    • 在MDK中定义
  • 修改跳转接口

这篇文章是对之前写的Bootloader的一次优化,之前博文

单片机通用Bootloader框架-优化

此次优化是看到这篇公众号推的(公众号写的真的太好了,每次必看):安富莱原文,主要讲了BL阶段需要跳转到APP程序执行前,如何省去,一些反初始化外设,那些复杂的操作,直接干净的跳转到APP区执行,文章中提到了RAM区一个段,不进行初始化的段,当单片机没有掉电的情况下,仅是软复位操作,不会改变这个段里面变量的数值,利用这个段去做一些事情,如BL需要的标志位,程序更新完毕了,改下这个段里面变量的标志位,复位一下在执行初始化硬件前,根据标志位的情况去执行跳转,免去由原来的跳转前反初始化外设(即恢复外设关闭中断这些操作)

另外片上一般都有BKP(即后备存储区),之前都是想到用来存放RTC是否是第一次配置的标志位,用它可以

比如下面的RTC配置,利用了RTC_BKP_DR0数据区域存储标志位,只要MCU不掉电,这个数据一直在,而且不编程(动它)不会被改变

/**
 *  @file RTC_Port.c
 *
 *  @date 2022年08月09日 11:20:01 星期二
 *
 *  @author aron566
 *
 *  @copyright Copyright (c) 2021 aron566 <aron566@163.com>.
 *
 *  @brief RTC时钟接口.
 *
 *  @details 需要将bool RTC_Port_Is_First_Config(void) 在初始化RTC硬件时调用判断是否需要设置时间.
 *
 *  @version V1.0
 */
/** Includes -----------------------------------------------------------------*/
#include <time.h>
/* Private includes ----------------------------------------------------------*/
#include "RTC_Port.h"
#include "main.h"
#include "rtc.h"
/** Use C compiler -----------------------------------------------------------*/
#ifdef __cplusplus ///< use C compiler
extern "C" {
#endif
/** Private macros -----------------------------------------------------------*/
#define RTC_FIRST_CONFIG_FLAG     0x0566/**< RTC首次配置标志 */
/** Private typedef ----------------------------------------------------------*/

/** Private constants --------------------------------------------------------*/
/** Public variables ---------------------------------------------------------*/
/** Private variables --------------------------------------------------------*/
static RTC_TIME_DATE_Typedef_t Time_Date;
/** Private function prototypes ----------------------------------------------*/

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


/** Private application code -------------------------------------------------*/
/*******************************************************************************
*
*       Static code
*
********************************************************************************
*/

/** Public application code --------------------------------------------------*/
/*******************************************************************************
*
*       Public code
*
********************************************************************************
*/
/**
  * @brief  Alarm A callback.
  * @param  hrtc RTC handle
  * @retval None
  */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hrtc);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_RTC_AlarmAEventCallback could be implemented in the user file
   */
}

/**
 * @brief 关闭闹钟
 *
 */
void RTC_Port_Set_Alarm_Close(void)
{
  /* 默认关闭闹钟 */
  HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A);
}

/**
 * @brief 设置闹钟
 *
 * @param H 时间
 * @param Min 分钟
 * @param S 秒
 * @param W 星期几 1 - 7
 * @param Mask 屏蔽字段 @ref RTC_AlarmMask_Definitions
 * @param Week_Day_Sel 1 选择 Week 0 选择 Day
 */
void RTC_Port_Set_Alarm(uint8_t H, uint8_t Min, uint8_t S, uint8_t W, uint32_t Mask, uint8_t Week_Day_Sel)
{
  RTC_AlarmTypeDef sAlarm = {0};

  sAlarm.AlarmTime.Hours = H;
  sAlarm.AlarmTime.Minutes = Min;
  sAlarm.AlarmTime.Seconds = S;
  sAlarm.AlarmTime.SubSeconds = 0;
  sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
  sAlarm.AlarmMask = Mask;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
  if(Week_Day_Sel == 1)
  {
    sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
  }
  else
  {
    sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
  }
  sAlarm.AlarmDateWeekDay = W;
  sAlarm.Alarm = RTC_ALARM_A;
  if(HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("Set Alarm Error\n");
  }
}

/**
 * @brief 获取该时间下周几 1 - 7
 *
 * @param y 年 2000 + y
 * @param m 月 1 - 12
 * @param d 日 1 - 31
 * @return int32_t 周几 1 - 7
 */
int32_t RTC_Port_GetWeekDay(int y, int m, int d)
{
  int year = y + 2000;
  int month = m;
  int date = d;
  if(month == 1) month = 13, year--;
  if(month == 2) month = 14, year--;
  int weekdey = (date + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7;
  return weekdey + 1;
}

/**
 * @brief 获取至返回自 1970 年 1 月 1 日以来持续时间的秒数,格林威治时间GMT UTC世界标准时间(原子钟)+8小时就是东八区时间(北京时间)
 *
 * @return uint32_t秒数,Unix时间戳
 */
uint32_t RTC_Port_Get_TimeStamp(void)
{
  struct tm tm_data;
  const RTC_TIME_DATE_Typedef_t *T = RTC_Port_Get_Time_Date();
  tm_data.tm_year = (int)T->Year + 2000 - 1900;  /**< 自1900起的年数 */
  tm_data.tm_mon  = (int)T->Month - 1;           /**< 月份,范围从0到11 */
  tm_data.tm_mday = (int)T->Day;                 /**< 1 - 31 */
  tm_data.tm_hour = (int)T->Hour;                /**< 0-23 */
  tm_data.tm_min  = (int)T->Minute;              /**< 0-59 */
  tm_data.tm_sec  = (int)T->Second;              /**< 0-59 */

  return (uint32_t)mktime(&tm_data);
}

/**
 * @brief 获取时间日期
 *
 * @return const RTC_TIME_DATE_Typedef_t*
 */
const RTC_TIME_DATE_Typedef_t *RTC_Port_Get_Time_Date(void)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};
  /* 先获取时间,再获取日期,否则有时延 */
  if(HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("rtc get time error.\r\n");
    return &Time_Date;
  }
  Time_Date.Hour = sTime.Hours;
  Time_Date.Minute = sTime.Minutes;
  Time_Date.Second = sTime.Seconds;

  if(HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("rtc get date error.\r\n");
    return &Time_Date;
  }
  Time_Date.Year = 2000 + sDate.Year;
  Time_Date.Month = sDate.Month;
  Time_Date.Day = sDate.Date;
  Time_Date.Weekday = sDate.WeekDay;
  return &Time_Date;
}

/**
 * @brief 设置时间日期
 *
 * @param H 小时
 * @param Min 分钟
 * @param S 秒
 * @param D 多少号
 * @param Month 月
 * @param Y 年
 * @param W 星期 1 - 7
 */
void RTC_Port_Set_Time_Date(uint8_t H, uint8_t Min, uint8_t S, uint8_t D, uint8_t Month, uint8_t Y, uint8_t W)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};
  sTime.Hours = H;
  sTime.Minutes = Min;
  sTime.Seconds = S;
  sDate.Date = D;
  sDate.Month = Month;
  sDate.Year = Y;
  sDate.WeekDay = W;

  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if(HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("Set Time Error\n");
  }

  if(HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    printf("Set Date Error\n");
  }
}

/**
 * @brief 检测是否是首次配置RTC
 *
 * @return true 是
 * @return false 不是
 */
bool RTC_Port_Is_First_Config(void)
{
  /* 检测是否是首次配置 */
  if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != RTC_FIRST_CONFIG_FLAG)
  {
    return true;
  }
  return false;
}

/**
 * @brief RTC接口初始化
 *
 */
void RTC_Port_Init(void)
{
  /* 检测是否是首次配置 */
  if(RTC_Port_Is_First_Config() == true)
  {
    /* 设置默认时间参数 */
    RTC_Port_Set_Time_Date(12, 12, 12, 9, 8, 22, 2);

    /* 默认关闭闹钟 */
    RTC_Port_Set_Alarm_Close();

    /* 写入已配置标识 */
    HAL_PWR_EnableBkUpAccess();
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, RTC_FIRST_CONFIG_FLAG);
    printf("RTC Port First Config.\r\n");
    return;
  }
  printf("RTC Port Init ok.\r\n");
}

#ifdef __cplusplus ///<end extern c
}
#endif
/******************************** End of file *********************************/

定义一个标志位变量存储在不初始化的段

类型定义

/* BL跳转指示 */
typedef enum
{
  BL_UNKNOW_FLAG  = 0,
  BL_JUMP_APP     = 0x0566,
  BL_JUMP_BL      = 0x0577,
}BL_PORT_STEP_Typedef_t;

typedef struct
{
  BL_PORT_STEP_Typedef_t Flag;
  uint32_t App_Addr;
  uint32_t Stack_Base_Addr;
}BL_PORT_JUMP_Typedef_t;

在IAR中定义

我们打开IAR的分散加载文件***.icf,找到不初始化的段名.noinit
在这里插入图片描述

static BL_PORT_JUMP_Typedef_t BL_Static_Jump_Data MATH_PORT_SECTION(".noinit");

MATH_PORT_SECTION是为了指定该变量分配到哪个段中,宏定义如下:不同的编译器不同关键字实现

#ifndef MATH_PORT_SECTION
  #if defined (__CC_ARM)                /* ARM Compiler */
    #if ENABLE_SECTION_SPACE
      #define MATH_PORT_SECTION(x)                 __attribute__((section(x)))
    #else
      #define MATH_PORT_SECTION(x)
    #endif
      #define MATH_PORT_USED                       __attribute__((used))
      #define MATH_PORT_UNUSED                     __attribute__((unused))
  #elif defined (__IAR_SYSTEMS_ICC__)   /* for IAR Compiler */
    #if ENABLE_SECTION_SPACE
      #define MATH_PORT_SECTION(x)                 @ x
    #else
      #define MATH_PORT_SECTION(x)
    #endif
      #define MATH_PORT_USED                       __root
      #define MATH_PORT_UNUSED
  #elif defined (__GNUC__)              /* GNU GCC Compiler */
    #if ENABLE_SECTION_SPACE
      #define MATH_PORT_SECTION(x)                 __attribute__((section(x)))
    #else
      #define MATH_PORT_SECTION(x)
    #endif
      #define MATH_PORT_USED                       __attribute__((used))
      #define MATH_PORT_UNUSED                     __attribute__((unused))
  #else
      #define MATH_PORT_SECTION(x)
      #define MATH_PORT_USED
      #define MATH_PORT_UNUSED
  #endif /* __CC_ARM */
  /** @}*/

  #ifndef SECTION
    #ifdef __CC_ARM                        /* ARM Compiler */
        #define SECTION(x)                 __attribute__((section(x)))
        #define USED                       __attribute__((used))
        #define UNUSEDX                    __attribute__((unused))
    #elif defined (__IAR_SYSTEMS_ICC__)    /* for IAR Compiler */
        #define SECTION(x)                 @ x
        #define USED                       __root
    #elif defined (__GNUC__)               /* GNU GCC Compiler */
        #define SECTION(x)                 __attribute__((section(x)))
        #define USED                       __attribute__((used))
        #define UNUSED                     __attribute__((unused))
    #else
        #error not supported tool chain
    #endif /* __CC_ARM */
  #endif
#endif

找到map文件,可以看到变量被分配的地址及段:
在这里插入图片描述
在这里插入图片描述

在MDK中定义

在这里插入图片描述
为了统一都用.noinit
***.sct分散加载文件中,修改如下:关键字UNINIT

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

;LR_IROM1 0x08020000 0x001E0000  {    ; load region size_region
;  ER_IROM1 0x08020000 0x001E0000  {  ; load address = execution address
LR_IROM1 0x08040000 0x001C0000  {    ; load region size_region
  ER_IROM1 0x08040000 0x001C0000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_RAM1 0x00000000 0x00010000  {  ; 64KB ITCM Code Data 400MHz
	 *(USE_ITCM_SPACE)
	 *(.text.aidis.ro)
   *(.text.aidis.math)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 UNINIT 0x0000000C  { ; 12Bytes DTCM Data 400MHz
    *(.noinit)
   .ANY (+RW +ZI)
  }
    
  RW_IRAM1 0x2000000C 0x0001FFF4  { ; 128KB-12Bytes DTCM Data 400MHz
    *(USE_DTCM_SPACE)
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x24000000 0x00080000  { ; 512KB Main Data 200MHz
   .ANY (+RW +ZI)
  }
	RW_IRAM3 0x30000000 0x00020000  { ; 128KB D2 DMA Data 200MHz
    *(USE_LCD_DMA_BUF_SPACE)
  }
  RW_IRAM4 0x30020000 0x00020000  { ; 128KB D2 DMA Data 200MHz
    *(USE_IDEL1_DMA_BUF_SPACE)
  }
  RW_IRAM5 0x30040000 0x00008000  { ; 32KB D2 DMA Data 200MHz
    *(USE_IDEL2_DMA_BUF_SPACE)
    .ANY (+RW +ZI)
  }
  RW_IRAM6 0x38000000 0x00010000  { ; 64KB D3 DMA Data 200MHz
   *(USE_DMA_BUF_SPACE)
  }
	RW_IRAM7 0x38800000 0x00001000  {	; 4KB Low Power Save Data 200MHz
   *(USE_BACKUP_BUF_SPACE)
  }
}

修改跳转接口

原来跳转的地方,改为直接复位,这样在BL想要跳转到APP分区时,将APP分区的信息给到标志变量,然后软复位,软复位后先检查是否需要跳转,需要跳转直接跳转到APP分区,不需要进行硬件恢复操作


/**
  ******************************************************************
  * @brief   跳转至应用程序分区
  * @param   [in]App_Addr app程序起始地址
  * @param   [in]Stack_Base_Addr 栈底地址
  * @return  None.
  * @author  aron566
  * @version v1.0
  * @date    2022/11/11
  ******************************************************************
  */
static void Jump_To_Application(uint32_t App_Addr, uint32_t Stack_Base_Addr)
{
  uint32_t JumpAddress = 0;
  printf("jump to : %08X, StackBase : %08X\r\n", App_Addr, Stack_Base_Addr);
  printf("read top address of stack : %08X\r\n", (*(__IO uint32_t *)App_Addr));
  printf("check stack in:%08X\r\n\n", ((*(__IO uint32_t *)App_Addr) & STACK_ADDR_MASK));

  /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
  if(((*(__IO uint32_t *)App_Addr) & STACK_ADDR_MASK) == Stack_Base_Addr)
  {
    /*Jump to user application*/
    JumpAddress = *(__IO uint32_t *)(App_Addr + 4);
    JumpToApplication = (pJumpFunction)JumpAddress;

    if(App_Addr == USER_BL_ADDR)
    {
      BL_Static_Jump_Data.Flag = BL_JUMP_BL;
    }
    else
    {
      BL_Static_Jump_Data.Flag = BL_JUMP_APP;
    }

    BL_Static_Jump_Data.App_Addr = App_Addr;
    BL_Static_Jump_Data.Stack_Base_Addr = Stack_Base_Addr;

    printf("System Reset.\r\n");

    /* 软复位 */
    HAL_NVIC_SystemReset();
    return;
#if 0
    /* 关闭全局中断 */
    __disable_irq();

    /*关闭滴答定时器,复位到默认值*/
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

    /*设置所有时钟到默认状态,使用HSI时钟*/
    HAL_RCC_DeInit();

	  /*所有开启的外设恢复默认状态,否则产生中断标志在APP阶段导致硬件错误*/
    HAL_DeInit();
    Serial_Port_DeInit();

    /*关闭所有中断,清除所有中断挂起标志 */
    for(int i = 0; i < 8; i++)
    {
      NVIC->ICER[i] = 0xFFFFFFFF;
      NVIC->ICPR[i] = 0xFFFFFFFF;
    }

    /*使能全局中断*/
    __enable_irq();

    /*设置重映射到系统Flash*/
//    __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();

    /* Initialize user application's Stack Pointer */
    __set_MSP(*(__IO uint32_t*)App_Addr);
#if USE_RTOS_BOOTLOADER
    /*设置特权模式使用MSP*/
    __set_CONTROL(0);
#endif
    JumpToApplication();
#endif
  }
#if 0
  printf("Try Jump Faild.\r\n");
#endif
}

在初始化硬件之前,执行以下判断跳转:

/**
 * @brief Bl尝试跳转APP
 *
 * @return true 跳转成功不反回
 * @return false 未检测到可跳转标识,失败
 */
bool Bootloader_Port_Try_Jump_To_Application(void)
{
  /* 检测到BL是否可以直接跳转 */
  if(BL_JUMP_APP != BL_Static_Jump_Data.Flag)
  {
    return false;
  }
  uint32_t JumpAddress = 0;

  /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
  if(((*(__IO uint32_t *)BL_Static_Jump_Data.App_Addr) & STACK_ADDR_MASK) == BL_Static_Jump_Data.Stack_Base_Addr)
  {
    /* Jump to user application */
    JumpAddress = *(__IO uint32_t *)(BL_Static_Jump_Data.App_Addr + 4);
    JumpToApplication = (pJumpFunction)JumpAddress;

    /* 跳转到APP */
    JumpToApplication();
  }
  return true;
}

在这里插入图片描述


http://www.jnnr.cn/a/123523.html

相关文章

【毕业设计】基于Spark的海量新闻文本聚类(新闻分类)

文章目录0 前言1 项目介绍2 实现流程3 开发环境4 java目录功能介绍5 scala目录功能介绍5.1 求TF-IDF5.2 调用K-means模型5.3 评价方式6 聚类结果7 最后0 前言 &#x1f525; Hi&#xff0c;大家好&#xff0c;这里是丹成学长的毕设系列文章&#xff01; &#x1f525; 对毕设…

第五章第一节:树与二叉树

文章目录1. 树的基本概念1.1 树的定义1.2 基本术语1.2.1 结点之间的关系描述1.2.2 结点、树的属性描述1.2.3 有序树、无序树1.2.4 树、森林1.3 树的性质2. 二叉树的概念2.1 二叉树的定义及其主要特性2.2 二叉树的五种状态2.2.1 满二叉树2.2.2 完全二叉树2.2.3 二叉排序树2.2.4 …

VsCode配置c/c++环境

文章目录 1. vsCode配置C/C环境1. vsCode下载和安装1. 下载Microsoft vsCode2. 安装vsCode3. 下载中文插件 2. MinGW编译器下载和配置1. 下载MinGW2. 下载后放到自己方便的目录&#xff0c;并复制文件里bin目录的路径&#xff08;后面用得到&#xff09;3. 在系统环境变量配置…

Privacy Preserving Probabilistic Record Linkage Without Trusted Third Party论文总结

Privacy Preserving Probabilistic Record Linkage Without Trusted Third Party论文总结AbstractI. INTRODUCTIONII. RELATED WORK AND BACKGROUND不经意传输OT&#xff08;Oblivious transfer&#xff09;混淆电路Garbled CircuitA. Deterministic and Probabilistic PPRLB. …

数据结构系列学习(六) - 顺序栈(Stack)

目录 引言&#xff1a; 学习&#xff1a; 初始状态&#xff1a; 入栈状态&#xff1a; 代码实现&#xff1a; 头文件&#xff08;SqStack.h&#xff09;&#xff1a; 设置顺序栈的初始大小&#xff1a; 设置顺序栈中元素的范型&#xff1a; 结构体的设计&#xff1a; …

MySQL的卸载与安装

MySQL的卸载与安装 环境&#xff1a;Ubuntu20.04 MySQL版本&#xff1a; 5.7 注&#xff1a;Ubuntu 20.04 版本系统自带的 MySQL 版本是 8.0&#xff0c;本文给出 5.7 版本的安装教程。 MySQL的彻底卸载 1、删除mysql的数据文件 sudo rm /var/lib/mysql/ -R2、删除mysql的配…

【并发编程】线程池及Executor框架

1.为什么要使用线程池 诸如 Web 服务器、数据库服务器、文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务。请求以某种方式到达服务器&#xff0c;这种方式可能是通过网络协议&#xff08;例如 HTTP、FTP &#xff09;、通过 JMS队列…

CV:阿里在CV数据增强领域带来SOTA新范式(已被NeurIPS2022接收)—基于离散化对抗训练的鲁棒视觉新基准!

CV&#xff1a;阿里在CV数据增强领域带来SOTA新范式(已被NeurIPS2022接收)—基于离散化对抗训练的鲁棒视觉新基准&#xff01; 导读&#xff1a;本文中&#xff0c;来自阿里巴巴AAIG的研究团队在模型鲁棒性问题上进行了研究&#xff0c;包括对抗鲁棒、分布外泛化性等。他们提出…

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ 文章目录深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ一、前言二、我的环境三、前期工作1、导入依赖项并设置GPU2、导入数据四、数据预处理1、加载数据2、检查数据3、配置数据集并进行归一化处理4、可视化数据五、构建…

xv6源码解析(三)——内存管理

01 内存管理 内存管理&#xff1a;通过编写物理页分配器&#xff0c;以链表管理所有空闲页帧&#xff0c; 实现了对物理页帧的回收与分配&#xff1b;在xv6系统sbrk内存管理方式的基础上&#xff0c;添加了进程用户空间非连续分区的分配。 内存管理参考链接 mmap 02 sbrk机制…

[附源码]计算机毕业设计JAVAjsp学生宿舍管理系统

[附源码]计算机毕业设计JAVAjsp学生宿舍管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM myb…

多媒体数字展示技术解决方案

数字媒体是科技与文化相融合而产生的新兴产业&#xff0c;其精髓是“文化为体&#xff0c;科技为媒”。现代计算机技术、网络技术和数字通信技术的高速发展为数字媒体技术的发展带来广阔的空间&#xff0c;尤其随着计算机网络技术与感官体验的充分结合&#xff0c;仿真虚拟现实…

Ubuntu16.04搭建UbertoothOne环境

Ubuntu16.04搭建UbertoothOne环境 【支持原创&#xff0c;转载需经过作者同意&#xff0c;否则追究相关责任】 相关链接 ubertoothone 主页ubertoothone github 环境说明 操作系统&#xff1a;Ubuntu 16.04.3 LTSUbertooth软件版本&#xff1a;ubertooth 2020-12-R1Libbtb…

web前端课程设计 基于HTML+CSS+JavaScript汽车自驾游(10个页面)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

C++对象与对象的关系

目录 依赖 关联 关联与聚合 组合 对应的类型设计&#xff1a; 类型的组合&#xff1a; 在一个系统中&#xff0c;一个对象可能与不同的对象相关&#xff0c;以下是不同的关系&#xff1a; 依赖&#xff08;Dependency&#xff09;&#xff08;使用一个&#xff09;关联…

数据库的基本操作(1)

这个博客主要是用来写什么是数据库以及一些数据库的基本操作。 什么是数据库呢&#xff1f; 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题 文件不利于数据查询和管理 文件不利于存储海量数据 文件在程…

如何搭建普罗米修斯 Prometheus

如何搭建普罗米修斯 Prometheus 1.下载Prometheus 进到这个网址 https://github.com/prometheus/prometheus/releases不同的系统下载不同的文件就可以 如果你的系统是那么你要下载的文件是备注linux系统prometheus-2.40.1.linux-amd64.tar.gz版本号可以是2.40.1的&#xff…

Allegro在板内添加器件限高区操作指导

Allegro在板内添加器件限高区操作指导 当我们希望器件放在一个限高区时候,会以DRC的形式报出错误来的时候,类似下图 我们可以通过Allegro的Place Keepout功能实现,具体操作如下 点击画铜皮的命令 把铜皮画在Package keepout层 如果是给top层器件禁布,那么画在top层 如…

.NET -- 使用Dump文件分析异常

目录 1. Dump文件 2. 程序崩溃时自动生成Dump文件 2.1 注册表生成 2.2 代码生成 3. 手动生成Dump文件 3.1 任务管理器生成 3.2 VS生成 4. Dump文件调试分析 4.1 简易崩溃测试代码 4.2 VS2022调试 4.3 非本机测试 1. Dump文件 Dump文件是进程的内存镜像。可以把程序…

【目标检测】Fast R-CNN前言

目录&#xff1a;论文Fast R-CNN前言总结一、概述1. 背景2. R-CNN缺点3. SPPNet二、Fast R-CNN简介三、ROI pooling layer四、fine-tuning一、概述 1. 背景 简化了最先进的基于卷积网络的目标检测器的训练过程&#xff08;R-CNN&#xff09;。提出一个单阶段训练算法&#xf…
最新文章