【STM32】制作一个bootloader

2023-11-15

工作环境:STM32CubeMX+Keil

相关环境准备这里就不介绍了。

bootloader是什么

  • bootloader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件的更新,也就是单片机选择性的自己给自己下载程序。可以更新,可以不更新,更新的话,boot loader更新完程序后,跳转到新程序运行;不更新的话,boot loader直接跳转到原来的程序去运行。
  • boot loader更新完程序后并不擦除自己,下次启动后依然先运行boot loader程序,又可以选择性的更新或者不更新程序,所以boot loader就是用来管理单片机程序的更新。
  • 在实际的单片机工程项目中,如果加入了boot loader功能,就可以给单片机日后升级程序留出一个接口,方便日后单片机程序更新。当然,这就需要创建亮哥工程项目,一个位boot loader工程,一个为app工程。
  • bootloader工程生成的文件通常下载到ROM或者Flash中的首地址,这样可以保证上电后先运行boot loader程序。而app工程生成的文件则下载到ROM或者Flash中boot loader后面的地址中。也就是说,存在ROM或者Flash中的内容是分为两部分的。
  • 要实现同一个ROM或者Flash中保存两段程序,并且保证不能相互覆盖,则需要在下载程序时指定地址。如在keil中,可以进行如下调整(需要知道Flash的扇区地址)。

在这里插入图片描述

升级流程

在介绍升级流程之前。我们必须先搞清楚MCU型号对应的Flash分区情况,可以直接查看手册(当然,其实我们也可以在hal库代码中找到相关信息),如下图:

在这里插入图片描述

因为上文说到,我们有两个程序需要下载到Flash,bootloader文件一般下载在最开始的地址,application一般在后面的地址。

在这里插入图片描述

上图即为我们预定义的flash分配情况。我们一般是按照整个扇区进行利用的,这样便于擦除和写入。

  • bootloader从起始地址开始,这个没有疑问。我分配了扇区0给它(这个大小是具体情况而定)。

  • ota_flag这个是用于存放是否升级的标志位。存放在扇区1,ota_flag只占用4字节,当写入0x87654321的时候,表示需要升级,否则,无需升级。

  • application占用了两个连续的扇区,扇区2和扇区3。

  • ota_data也占用了两个连续的扇区,扇区4和扇区5。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oH0MLjqQ-1686299396663)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20230609161215006.png)]

代码示例

#ifndef BOOTLOADER_H
#define BOOTLOADER_H

#ifdef __cpluscplus
extern "c"
{
#endif

#define FLASH_SECTOR_STEP_SIZE  (128*1024)
#define FLASH_SECTOR_0_ADDRESS  (0x08000000)

#define MCU_OTA_FLAG_ADDRESS (FLASH_SECTOR_0_ADDRESS+ FLASH_SECTOR_STEP_SIZE * FLASH_SECTOR_1)
#define APPLICATION_ADDRESS  (FLASH_SECTOR_0_ADDRESS+ FLASH_SECTOR_STEP_SIZE * FLASH_SECTOR_2)

#define MCU_OTA_DATA_ADDRESS (FLASH_SECTOR_0_ADDRESS+ FLASH_SECTOR_STEP_SIZE * FLASH_SECTOR_4)
#define MCU_OTA_FLAG_VALUE   (0x87654321)

int erase_mcu_ota_flag();
int write_mcu_ota_flag();
int erase_application_sectors();
int write_application_sectors();
int mcu_reset();


#ifdef __cpluscplus
}
#endif
#endif
#include "bootloader.h"
#include "stm32h7xx_hal.h"
#include "stm32h7xx_hal_flash.h"
#include <stdint.h>


int erase_mcu_ota_flag()
{
	  int ret=0;
    uint32_t SectorError = 0;
	
		FLASH_EraseInitTypeDef sector;
		sector.TypeErase = FLASH_TYPEERASE_SECTORS;
		sector.VoltageRange = FLASH_VOLTAGE_RANGE_3;
		sector.Sector = FLASH_SECTOR_1;
		sector.NbSectors = 1;
	
		HAL_FLASH_Unlock();
		ret = HAL_FLASHEx_Erase(&sector, &SectorError);
		HAL_FLASH_Lock();
	  return ret;
}

int write_mcu_ota_flag()
{
	  int ret=0;
		HAL_FLASH_Unlock();
		ret = HAL_FLASH_Program(FLASH_PSIZE_WORD, MCU_OTA_FLAG_ADDRESS, MCU_OTA_FLAG_VALUE);
		HAL_FLASH_Lock();
	  return ret;
}

int erase_application_sectors()
{
	  int ret=0;
    uint32_t SectorError = 0;
	
		FLASH_EraseInitTypeDef sector;
		sector.TypeErase = FLASH_TYPEERASE_SECTORS;
		sector.VoltageRange = FLASH_VOLTAGE_RANGE_3;
		sector.Sector = FLASH_SECTOR_2;
		sector.NbSectors = 2;
	
		HAL_FLASH_Unlock();
		ret = HAL_FLASHEx_Erase(&sector, &SectorError);
		HAL_FLASH_Lock();
	  return ret;
}

int write_application_sectors()
{
	  int ret=0;
		HAL_FLASH_Unlock();
	  for(int i=0;i<MCU_OTA_DATA_ADDRESS;i+=4)
	  {
			ret = HAL_FLASH_Program(FLASH_PSIZE_WORD, APPLICATION_ADDRESS+i, *(__IO uint32_t*)(MCU_OTA_DATA_ADDRESS+i));
	  }
		HAL_FLASH_Lock();
	  return ret;
}

int mcu_reset()
{
	 __set_FAULTMASK(1);
   __NVIC_SystemReset();
}
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 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 "bootloader.h"
#include <stdio.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef void (*pFunction)();
/* 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 */


/* USER CODE END 0 */

/**
  * @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. */
  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 JumpAddress;
    pFunction Jump_To_Application;
	if(*(__IO uint32_t*)MCU_OTA_FLAG_ADDRESS != MCU_OTA_FLAG_VALUE)
	{
		  JumpAddress = *(__IO uint32_t*)(APPLICATION_ADDRESS+4);
      Jump_To_Application = (pFunction)JumpAddress;

      __set_MSP(*(__IO uint32_t*)APPLICATION_ADDRESS);
      Jump_To_Application();
	}
	else
	{ 
		  printf("mcu_ota");  
		
		  erase_application_sectors();
		
		  write_application_sectors();
		
		  erase_mcu_ota_flag();
		
		  mcu_reset();
	}
	
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  /** 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 = 1;
  RCC_OscInitStruct.PLL.PLLN = 75;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  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_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

总结

关于地址跳转部分,可以参考STM32 Bootloader程序中Jump2App函数分析

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【STM32】制作一个bootloader 的相关文章

  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 在 Atollic TrueStudio、STM32CubeMX 中导入 C 库

    我目前正在开发 STM32F767ZI Nucleo 板和一个小安全芯片 microchip atecc508a 通过 i2c 连接进行连接 该芯片有一个可用的库加密验证库 https github com MicrochipTech cr
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • 你好世界引导加载程序不工作

    我一直在学习教程在这个网页上 http viralpatel net taj tutorial hello world bootloader php这将逐步创建一个显示 Hello World 的引导加载程序 第二个教程 我们尝试输出 A
  • 将 NASM 和 64 位 C 代码编译并链接到引导加载程序中 [重复]

    这个问题在这里已经有答案了 我制作了一个非常简单的一级引导加载程序 它执行两个主要操作 从 16 位实模式切换到 64 位长模式 并从硬盘读取接下来的几个用于启动基本内核的扇区 对于基本内核 我尝试用 C 而不是汇编编写代码 对此我有一些疑
  • 将bootloader和内核制作成iso? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 如何创建将内核加载到 iso 的简单引导加载程序 已经过去 5 天了 我在 google 中搜索并进行
  • 无法使用 OpenOCD 找到脚本文件

    我正在尝试按照本教程将 OpenOCD 与我的 ST 发现板一起使用 https japaric github io discovery README html https japaric github io discovery READM
  • STM32F4XX的12位ADC采集数值超过4096&右对齐模式设置失败

    文章目录 一 前言 二 问题1 数值超过4096 三 问题1的排错过程 四 问题2 右对齐模式设置失败 五 问题2的解决方法 5 1 将ADC ExternalTrigConv设置为0 5 2 使用ADC StructInit 函数 一 前
  • 特殊寄存器

    特殊寄存器 文章目录 前言 一 背景 二 2 1 2 2 总结 前言 前期疑问 STM32特殊寄存器到底是什么 特殊寄存器怎么查看和调试代码 本文目标 记录和理解特殊寄存器 一 背景 最近在看ucosIII文章是 里面提到特殊寄存器 这就进
  • systick定时器

    systick定时器 文章目录 前言 一 前期疑惑 二 解答 1 关于systick是阻塞的吗 2 非阻塞 三 软件编写 总结 前言 这边记录systick相关知识点 一 前期疑惑 在学习systick志气啊 其实对于systick还是一脸
  • ARM 系统上的 Bootrom 与引导加载程序有什么区别

    我主要来自 x86 系统背景 其中 BIOS 固件 负责从 PowerON 加载引导加载程序 如 GRUB 进而加载操作系统 我现在一直在阅读 ARM 系统上的等效启动顺序 网上似乎有文章提到了两个术语 bootrom 和 bootload
  • STM32 Nucleo 上的上升沿中断多次触发

    我正在使用 STM32 NUCLEO F401RE 微控制器板 我有一个扬声器 经过编程 当向上 向下推操纵杆时 可以按设定的量改变频率 我的问题是 有时 通常 当向上 向下推动操纵杆时 频率会增加 减少多次 这意味着 ISR 正在执行多次
  • 从没有中断引脚并且在测量准备好之前需要一些时间的传感器读取数据的最佳方法

    我正在尝试将压力传感器 MS5803 14BA 与我的板 NUCLEO STM32L073RZ 连接 根据 第 3 页 压力传感器需要几毫秒才能准备好读取测量值 对于我的项目 我对需要大约 10 毫秒来转换原始数据的最高分辨率感兴趣 不幸的
  • 使用 int 13h 读取的扇区数量多于磁道上的扇区数量

    顺序是什么int 13h with ah 02h会读19部门起始于 C H S 0 0 1 提供了 2 个磁头 每磁道 18 个扇区 每面 80 个磁道的 软盘 磁盘几何结构 或者 更一般地说 当它到达磁道 0 的末尾 磁头 0 时会发生什
  • 嵌入式 C++11 代码 — 我需要 volatile 吗?

    采用 Cortex M3 MCU STM32F1 的嵌入式设备 它具有嵌入式闪存 64K MCU固件可以在运行时重新编程闪存扇区 这是由闪存控制器 FMC 寄存器完成的 所以它不像a b那么简单 FMC 获取缓冲区指针并将数据刻录到某个闪存
  • 使用BIOS int 13h访问不同磁头的扇区

    我的磁盘每磁道有 63 个扇区 根据我的观察 我假设 我想使用 int 13h 读取 16 位引导加载程序上的扇区 例如 如果我想读取扇区号 63 我将执行以下操作 mov dl 0x80 Drive number mov dh 0 Thi
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能
  • 将引导加载程序存储在软盘映像上的哪里?

    我将编写并测试引导加载程序 为了做到这一点 我计划将引导加载程序复制到软盘映像文件上并将其安装在虚拟机中 但是 我不确定将引导加载程序的机器代码放在哪里 它是否只是转储到文件的前几个字节中 软盘的引导扇区是第一个扇区 如果您谈论的是原始软盘
  • 近调用/跳转表并不总是在引导加载程序中工作

    一般问题 我一直在开发一个简单的引导加载程序 并在某些环境中偶然发现了一个问题 在这些环境中 此类指令不起作用 mov si call tbl SI Call table pointer call call tbl Call print c

随机推荐

  • 扩散模型与生成模型详解

    扩散模型与其他生成模型 什么是扩散模型 扩散模型的简介 生成建模是理解自然数据分布的开创性任务之一 VAE GAN和Flow系列模型因其实用性能而在过去几年中占据了该领域的主导地位 尽管取得了商业上的成功 但它们的理论和设计缺陷 棘手的似然
  • 中国传统节日端午节网页HTML代码 学生网页课程设计期末作业下载 春节大学生网页设计制作成品下载 DW春节节日网页作业代码下载

    HTML5期末大作业 节日网站设计 中国传统节日端午节网页HTML代码 7页 HTML CSS JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码 常见网页设计作业题材有 个人 美食 公司
  • visio技巧(曲线、连接点、自制模具)

    一 画曲线 1 1 铅笔 任意多边形 弧形都可以画曲线 但曲度不好更改 1 2 鼠标选中连接线 在画布上画一个直角线 选中该线 点击右键 选曲线连接线 随意拉动该线上的连接点可以调整成任意曲度 二 增加 移动 删除图形上的连接点 1 1 增
  • 【Python 基础篇】Python代码 之 程序结构

    目录 前言 一 顺序结构 1 1 分支结构 1 2 双向分支 1 3 多路分支 1 4 if语句补充 二 顺序结构 三 循环结构 while while else for in for else 四 流程控制语句 break continu
  • SparkStreaming知识总结

    一 流式计算的概述 1 1 什么是流式计算 1 数据流与静态数据的区别 数据流指的就是不断产生的数据 是源源不断 不会停止 静态数据指的就是存储在磁盘中的固定的数据 2 流式计算的概念 就是对数据流进行计算 由于数据是炼苗不断的产生的 所以
  • VMware15.5安装win7旗舰版系统

    1 启动vmware 文件 新建虚拟机 2 选择自定义安装 下一步 3 兼容性默认不做修改 下一步 4 安装来源选择稍后安装操作系统 5 选择windows7 64 6 安装位置选择非系统盘位置 放在C盘会拖累系统运行速度 7 8 取决于物
  • PostgreSQL_row_number() over()

    语法 row number over partition by col1 order by col2 desc row number 为返回的记录定义各行编号 pritition by 分组 order by 排序 实例 实例数据来源 利用
  • vue实现文件下载

    原理 a href url 复制代码 实际使用场景 上面的原理中适合开放的资源下载 http请求中无需验证时使用 在实际使用过程中 a标签中的url中直接设置header比较麻烦且不安全 而且从开发规范上api一般上要封装一下 header
  • 华为OD机试 - 最多颜色的车辆(Java)

    题目描述 在一个狭小的路口 每秒只能通过一辆车 假设车辆的颜色只有 3 种 找出 N 秒内经过的最多颜色的车辆数量 三种颜色编号为0 1 2 输入描述 第一行输入的是通过的车辆颜色信息 0 1 1 2 代表4 秒钟通过的车辆颜色分别是 0
  • LeetCode 2545. 根据第 K 场考试的分数排序

    班里有 m 位学生 共计划组织 n 场考试 给你一个下标从 0 开始 大小为 m x n 的整数矩阵 score 其中每一行对应一位学生 而 score i j 表示第 i 位学生在第 j 场考试取得的分数 矩阵 score 包含的整数 互
  • git忽略指定文件夹

    git忽略指定文件夹 如下结构 总共有三个文件夹 假设要忽略第一层的B文件夹 在目录下新建一个 gitignore文件 并填写下面内容 B 假设要忽略第一层的A文件夹 在文件中填写A 的话 会把B文件夹下的A文件夹也忽略了 这个时候可以加上
  • 要称王,先做行业破坏者

    author skate time 2010 06 18 高端阅读78期 原标题为 世界 油王 的职场启示 我的人生 狠 字当头 有极强的故事性 白手起家 狂赚几亿美元 后遭朋友暗算 被踢出一手创办并成功发展40年的公司 同期不得不应对麻烦
  • Linux nrm 运行失败,解决:npm中 下载速度慢 和(无法将“nrm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确, 然后再试一次)...

    1 解决下载速度 因为我们npm下载默认是 连接国外的服务器 所以网速不是特别好的时候 可能下不了包 安装nrm 使用 npm i nrm g 我们的一般工具包都是下载到全局 安装完毕之后 可以运行 命令 nrm ls ls 表示 list
  • Django-Model层ORM之查询操作(六)

    目录 一 Django查询相关API all 查询所有记录 返回一个集合对象 filter 属性 根据条件查询 返回一个集合对象 first 和 last 查询第一个和最后一个记录 返回单个对象 get id 2 根据id查询 返回一个对象
  • Ubuntu 15.04 下编译Caffe2

    深度学习大神贾扬清在四月底发布了最新框架Caffe2 最近在Ubuntu15 04下编译了它的源代码 遇到一些坑 记录下来以供参考 基本安装次序如官网所述 https caffe2 ai docs getting started html
  • k8s Trouble Shooting 故障排除

    本文要讲的是k8s的故障排除 比较浅 最近刚入门 主要涵盖的内容是查看k8s对象的当前运行时信息 对于服务 容器的问题是如何诊断的 对于某些复杂的问题例如pod调度问题是如何排查的 1 查看系统的Event事件 在对象资源 pod serv
  • 一起写一个 Web 服务器

    http my oschina net leejun2005 blog 486771 一起写一个 Web 服务器 2 2015 06 06 实践项目 9 评论 Web服务器 分享到 8 本文由 伯乐在线 高世界 翻译 艾凌风 校稿 未经许可
  • java实现评论功能_Java实现评论回复功能的完整步骤

    前言 使用递归循环开发评论回复功能 适用于大部分的简单单体应用 评论功能或许是大多数的单体应用之中会用到的功能 我们会在自己所开发的项目之中进行集成该功能 大多数时候我们会将评论功能划分成以下几种 单一型 嵌套型 两层型 一 分类方式 1
  • SAP B/P 初步研究(二)

    从开发人员角度来看 B P客户创建可以试用两种方法 第一种是使用BAPI FUNCTION 第二种是使用BAPI CALL METHOD 个人更倾向于使用METHOD 因为METHOD方法只需要填充一个嵌套结构就可以实现B P所有业务视图的
  • 【STM32】制作一个bootloader

    工作环境 STM32CubeMX Keil 相关环境准备这里就不介绍了 bootloader是什么 bootloader就是单片机启动时候运行的一段小程序 这段程序负责单片机固件的更新 也就是单片机选择性的自己给自己下载程序 可以更新 可以