【UEFI基础】BIOS下的启动项管理

启动管理

启动管理(Boot Manager)是UEFI BIOS中重要的一部分,它通过一系列的变量来确定启动策略,包括:

  • 执行启动还是恢复操作
  • 启动顺序是如何

本文会介绍下面的内容:

  • 与启动管理相关的变量
  • 启动或恢复的流程策略
  • 加载项结构体
  • 创建加载项

注意本文只关注UEFI BIOS的启动项,涉及的是UEFI BIOS启动管理,至于Legacy的,这里不会描述。

另外,虽然是“启动管理”,但是这里的“启动”并不都是“启动操作系统”的意思,代码操作的也不叫“启动项”,而是“加载项”(Load Option),后面文档中出现的“启动”也并不一定是启动操作系统的意思,也可能仅仅是加载一个UEFI应用或UEFI驱动。

启动一般流程

以下是《UEFI Spec》中的一般规定:

  • 如果OsIndications指定了要执行恢复操作,则不会进入通常的启动流程,而是进入恢复选项。
  • Driver####会优先于Boot####执行。
  • 执行Boot加载项时,先执行BootNext对应的加载项,然后按照BootOrder中的顺序执行Boot####
  • 如果BootNextBoot####中的执行都失败了,则进入启动项恢复操作,主要就是PlatformRecovery####

实际上真实的BIOS中,其实现可以有很多中,因此并不完全按照上述的规则。比如最后一步BootNextBoot####中的执行都失败,一般会选择进入Setup,而不是PlatformRecovery####

启动管理变量

  1. 真正描述加载项的变量:
变量名作用域说明
Boot####NV、BS、RTA boot load option.
Driver####NV、BS、RTA driver load option.
SysPrep####NV、BS、RTA System Prep application load option containing a EFI_LOAD_OPTION descriptor.
OsRecovery####NV、BS、RT
PlatformRecovery####NV、BS、RTPlatform-specified recovery options. These variables are only modified by firmware and are read-only to the OS.

需要注意这些变量的名称是Load Option,并不是叫启动项,虽然流程上差不多,但是很多并不是为了启动操作系统,而是一些额外的操作:

  • Boot####:这些算是普通启动时的启动项。
  • Driver####:这些是用来加载驱动的。
  • SysPrep####:对应的时UEFI应用,用来为系统启动做准备的,它们会在Boot####之前执行。
  • PlatformRecovery####:这些是在最后执行的,所以其它的启动都失败之后就会执行这些。

这四种类型对应到代码中:

//
// Load Option Type
//
typedef enum {LoadOptionTypeDriver,LoadOptionTypeSysPrep,LoadOptionTypeBoot,LoadOptionTypePlatformRecovery,LoadOptionTypeMax
} EFI_BOOT_MANAGER_LOAD_OPTION_TYPE;

有几点需要注意的:

  • Boot####Driver####是正常启动的时候都是执行的。
  • 启动恢复项有两类,PlatformRecovery####OsRecovery####,后者没有出现在《UEFI Spec》的“Table 10. Global Variables”中,因为它使用的是其它的GUID(称为Vendor Specific VendorGuid)。

上述的变量因为有多个,所以还有一个顺序关系,对应到一个变量:

变量名作用域说明
BootOrderNV、BS、RTThe ordered boot option load list.
DriverOrderNV、BS、RTThe ordered driver load option list.
SysPrepOrderNV、BS、RTThe ordered System Prep Application load option list.
OsRecoveryOrderNV、BS、RTOS-specified recovery options.

但是没有PlatformRecoveryOrder,可以认为只有一个PlatformRecovery应用,所以不需要顺序吧。

另外OsRecoveryOrder出现在《UEFI Spec》的“Table 10. Global Variables”中也比较奇怪,因为前面已经说了它应该属于VendorGuid的变量才对,并且OsRecoveryOrder也没有出现在EDK代码中。

  1. 启动相关的变量:
变量名作用域说明
BootCurrentBS、RTThe boot option that was selected for the current boot.
BootNextNV、BS、RTThe boot option for the next boot only.

BootCurrent因为是随时可变的,所以不会存放到非易失变量中。

  1. 配置相关的变量:
变量名作用域说明
BootOptionSupportBS、RTThe types of boot options supported by the boot manager. Should be treated as read-only.
OsIndicationsNV、BS、RTAllows the OS to request the firmware to enable certain features and to take certain actions.
OsIndicationsSupportedBS、RTAllows the firmware to indicate supported features and actions to the OS.

BootOptionSupport对应的取值:

#define EFI_BOOT_OPTION_SUPPORT_KEY      0x00000001
#define EFI_BOOT_OPTION_SUPPORT_APP      0x00000002
#define EFI_BOOT_OPTION_SUPPORT_SYSPREP  0x00000010
#define EFI_BOOT_OPTION_SUPPORT_COUNT    0x00000300

这表示的其实是启动管理的能力级,其能力说明如下:

  • EFI_BOOT_OPTION_SUPPORT_KEY置位表示Boot####可以通过快捷键启动。
  • EFI_BOOT_OPTION_SUPPORT_APP置位表示支持启动带LOAD_OPTION_CATEGORY_APP属性的项。
  • EFI_BOOT_OPTION_SUPPORT_SYSPREP置位表示支持启动SysPrep####
  • EFI_BOOT_OPTION_SUPPORT_COUNT指定了快捷键支持的按键数。

OsIndicationsSupported表示BIOS告知OS的BIOS所支持的特性,而OsIndications是OS告知BIOS的目前使用的特性。所以OsIndicationsOsIndicationsSupported的一个子集,目前支持的值有:

//
// Firmware should stop at a firmware user interface on next boot
//
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI                    0x0000000000000001
#define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION             0x0000000000000002
#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED  0x0000000000000004
#define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED            0x0000000000000008
#define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED     0x0000000000000010
#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY          0x0000000000000040
#define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH         0x0000000000000080

其中有一些跟启动有关:

  • EFI_OS_INDICATIONS_BOOT_TO_FW_UI:表示进入启动管理菜单。
  • EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY:表示进入到平台恢复项。

启动项结构体

其结构体位于edk2\MdePkg\Include\Uefi\UefiSpec.h:

#pragma pack(1)
typedef struct _EFI_LOAD_OPTION {////// The attributes for this load option entry. All unused bits must be zero/// and are reserved by the UEFI specification for future growth.///UINT32    Attributes;////// Length in bytes of the FilePathList. OptionalData starts at offset/// sizeof(UINT32) + sizeof(UINT16) + StrSize(Description) + FilePathListLength/// of the EFI_LOAD_OPTION descriptor.///UINT16    FilePathListLength;////// The user readable description for the load option./// This field ends with a Null character.///// CHAR16                        Description[];////// A packed array of UEFI device paths. The first element of the array is a/// device path that describes the device and location of the Image for this/// load option. The FilePathList[0] is specific to the device type. Other/// device paths may optionally exist in the FilePathList, but their usage is/// OSV specific. Each element in the array is variable length, and ends at/// the device path end structure. Because the size of Description is/// arbitrary, this data structure is not guaranteed to be aligned on a/// natural boundary. This data structure may have to be copied to an aligned/// natural boundary before it is used.///// EFI_DEVICE_PATH_PROTOCOL      FilePathList[];////// The remaining bytes in the load option descriptor are a binary data buffer/// that is passed to the loaded image. If the field is zero bytes long, a/// NULL pointer is passed to the loaded image. The number of bytes in/// OptionalData can be computed by subtracting the starting offset of/// OptionalData from total size in bytes of the EFI_LOAD_OPTION.///// UINT8                         OptionalData[];
} EFI_LOAD_OPTION;
#pragma pack()

其成员说明如下(注意代码中后面三个成员注释掉了,但是并不表示它们不存在,只是因为Description的长度是不确定的,所以后面成员没有固定位置,导致这个结构体是不定长结构体):

  • Attributes:启动项的属性,包括:
//
// EFI Load Options Attributes
//
#define LOAD_OPTION_ACTIVE           0x00000001	// 该属性决定启动管理会不会自动从这个启动项启动
#define LOAD_OPTION_FORCE_RECONNECT  0x00000002	// 如果DRIVER####类型的启动项有该属性,则当所有DRIVER####类型的启动项执行完毕之后// 会对所有的UEFI Dirver进行disconnect和reconnect
#define LOAD_OPTION_HIDDEN           0x00000008	// 表示这个选项不会出现在启动项选择界面
#define LOAD_OPTION_CATEGORY         0x00001F00	// 这个要结合下面两个一起使用,相当于一个掩码// 例如 if ((Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT)
#define LOAD_OPTION_CATEGORY_BOOT  0x00000000	// 表示这个启动项是普通启动的一部分
#define LOAD_OPTION_CATEGORY_APP   0x00000100	// 表示这个启动项虽然不用于普通启动,但是可以通过快捷键或者启动选项选择界面启动
  • FilePathListLengthFilePathList的长度,这个成员的存在是为了计算OptionalData在结构体中的偏移,并且根据整个结构体的大小就可以知道OptionalData的大小。
  • Description:描述启动项的字符串,会在启动项选择界面显示,以供选择。
  • FilePathList:一个Device Path数组,第一个成员(即FilePathList[0])是必须的,表示该启动项对应镜像(比如bootx64.efi)所在的设备及位置,BIOS本身只需要使用这一项即可,但是OS可能使用该数组的其它选项。
  • OptionalData:可选的数组,会传递给加载后的镜像。

以上是在《UEFI Spec》中定义的启动项,是作为BOOT####Driver####启动项存放到非易失介质中的就是这个结构体。当启动管理使用这些启动项的时候,会有进一步的扩展:

//
// Common structure definition for DriverOption and BootOption
//
typedef struct {//// Data read from UEFI NV variables//UINTN                                OptionNumber;     // #### numerical value, could be LoadOptionNumberUnassignedEFI_BOOT_MANAGER_LOAD_OPTION_TYPE    OptionType;       // LoadOptionTypeBoot or LoadOptionTypeDriverUINT32                               Attributes;       // Load Option AttributesCHAR16                               *Description;     // Load Option DescriptionEFI_DEVICE_PATH_PROTOCOL             *FilePath;        // Load Option Device PathUINT8                                *OptionalData;    // Load Option optional data to pass into imageUINT32                               OptionalDataSize; // Load Option size of OptionalDataEFI_GUID                             VendorGuid;//// Used at runtime//EFI_STATUS                           Status;          // Status returned from boot attempt gBS->StartImage ()CHAR16                               *ExitData;       // Exit data returned from gBS->StartImage ()UINTN                                ExitDataSize;    // Size of ExitData
} EFI_BOOT_MANAGER_LOAD_OPTION;

成员说明如下:

  • OptionNumber:就是####的值。
  • OptionType:启动项的属性,对应的取值:
//
// Load Option Type
//
typedef enum {LoadOptionTypeDriver,LoadOptionTypeSysPrep,LoadOptionTypeBoot,LoadOptionTypePlatformRecovery,LoadOptionTypeMax
} EFI_BOOT_MANAGER_LOAD_OPTION_TYPE;

实际上就对应到了不同的启动项变量。

  • Attributes:同EFI_LOAD_OPTION中的同名成员变量。
  • Description:同EFI_LOAD_OPTION中的同名成员变量,不过需要注意一个是数组,一个是指针。显然数组才能存放到非易失介质中。
  • FilePath:指向EFI_LOAD_OPTION中的成员FilePathList[0]
  • OptionalData:同EFI_LOAD_OPTION中的同名成员变量。
  • OptionalDataSizeOptionalData的大小。
  • VendorGuid:启动项变量对应的GUID,它一般是固定的EFI_GLOBAL_VARIABLE
  • StatusExitDataExitDataSizegBS->StartImage()的返回值,返回数据和返回数据大小。

启动管理实际上用的更多的是这个结构体。从EFI_LOAD_OPTIONEFI_BOOT_MANAGER_LOAD_OPTION的转变可以参考函数(位于edk2\MdeModulePkg\Library\UefiBootManagerLib\BmLoadOption.c):

/**Build the Boot#### or Driver#### option from the VariableName.@param  VariableName          Variable name of the load option@param  VendorGuid            Variable GUID of the load option@param  Option                Return the load option.@retval EFI_SUCCESS     Get the option just been created@retval EFI_NOT_FOUND   Failed to get the new option**/
EFI_STATUS
EFIAPI
EfiBootManagerVariableToLoadOptionEx (IN CHAR16                            *VariableName,IN EFI_GUID                          *VendorGuid,IN OUT EFI_BOOT_MANAGER_LOAD_OPTION  *Option)

创建启动项

BIOS进入到BDS阶段,其流程中跟启动项有关的操作大致包含如下的内容(edk2\MdeModulePkg\Universal\BdsDxe\BdsEntry.c):

启动项相关变量处理
创建PlatformDefaultBootOption
PlatformBootManagerBeforeConsole
EfiBootManagerStartHotkeyService
PlatformBootManagerAfterConsole
根据启动策略执行各种启动项

当然这只是一个简单的版本,实际上它包含很多变量获取以及根据变量值执行不同的启动策略的操作,不过这里主要关心的是启动项的创建,所以比较重要的是函数PlatformBootManagerBeforeConsole()PlatformBootManagerAfterConsole(),它们是库函数(库名PlatformBootManagerLib),这意味着不同的平台代码可以有自己的实现,在EDK代码中就有很多的不同实现:

在这里插入图片描述

不过无论是哪种实现,创建启动项都是其中很重要的一部分。

启动项的创建依赖于特定的几个Protocol:

  • gEfiBlockIoProtocolGuid
  • gEfiSimpleFileSystemProtocolGuid
  • gEfiLoadFileProtocolGuid

在《UEFI Spec》中只描述了后面两个,但是在EDK代码中会首先处理BlockIoProtocol(不过不同版本的EDK代码可能处理方式也不一样)。由于目前的启动主要是通过BootLoader或者网络启动,所以重要的还是后面两个,SimpleFileSystemProtocol表明了有文件系统可以获取BootLoader然后启动,而LoadFileProtocol说明了有网络可以进行PXE或Http等启动。代码处理主要在如下的函数:

/**The function creates boot options for all possible bootable medias in the following order:1. Removable BlockIo            - The boot option only points to the removable mediadevice, like USB key, DVD, Floppy etc.2. Fixed BlockIo                - The boot option only points to a Fixed blockIo device,like HardDisk.3. Non-BlockIo SimpleFileSystem - The boot option points to a device supportingSimpleFileSystem Protocol, but not supporting BlockIoprotocol.4. LoadFile                     - The boot option points to the media supportingLoadFile protocol.Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot BehaviorThe function won't delete the boot option not added by itself.
**/
VOID
EFIAPI
EfiBootManagerRefreshAllBootOption (VOID);

除了Protocol对应的启动设备之后,还有一种常见的启动方式就是进入Setup、启动管理菜单和UEFI Shell,它们也同样是通过创建启动项来完成,下面是创建Shell启动项的一个例子:

  //// Register UEFI Shell//PlatformRegisterFvBootOption (&gUefiShellFileGuid,L"EFI Internal Shell",LOAD_OPTION_ACTIVE);

创建启动管理菜单也是要给创建的例子:

    //// If not found the BootManagerMenuApp, create it.//OptionNumber = (UINT16)RegisterBootManagerMenuAppBootOption (&mBootMenuFile, L"UEFI BootManagerMenuApp", (UINTN)-1, FALSE);

最终两者调用的函数都是:

/**Initialize a load option.@param Option           Pointer to the load option to be initialized.@param OptionNumber     Option number of the load option.@param OptionType       Type of the load option.@param Attributes       Attributes of the load option.@param Description      Description of the load option.@param FilePath         Device path of the load option.@param OptionalData     Optional data of the load option.@param OptionalDataSize Size of the optional data of the load option.@retval EFI_SUCCESS           The load option was initialized successfully.@retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL.
**/
EFI_STATUS
EFIAPI
EfiBootManagerInitializeLoadOption (IN OUT EFI_BOOT_MANAGER_LOAD_OPTION    *Option,IN  UINTN                              OptionNumber,IN  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  OptionType,IN  UINT32                             Attributes,IN  CHAR16                             *Description,IN  EFI_DEVICE_PATH_PROTOCOL           *FilePath,IN  UINT8                              *OptionalData,IN  UINT32                             OptionalDataSize);

不过这个函数并不会设置变量,该操作有下面的函数完成:

/**This function will register the new Boot####, Driver#### or SysPrep#### option.After the *#### is updated, the *Order will also be updated.@param  Option            Pointer to load option to add. If on inputOption->OptionNumber is LoadOptionNumberUnassigned,then on output Option->OptionNumber is updated tothe number of the new Boot####,Driver#### or SysPrep#### option.@param  Position          Position of the new load option to put in the ****Order variable.@retval EFI_SUCCESS           The *#### have been successfully registered.@retval EFI_INVALID_PARAMETER The option number exceeds 0xFFFF.@retval EFI_ALREADY_STARTED   The option number of Option is being used already.Note: this API only adds new load option, no replacement support.@retval EFI_OUT_OF_RESOURCES  There is no free option number that can be used when theoption number specified in the Option is LoadOptionNumberUnassigned.@return                       Status codes of gRT->SetVariable ().**/
EFI_STATUS
EFIAPI
EfiBootManagerAddLoadOptionVariable (IN OUT EFI_BOOT_MANAGER_LOAD_OPTION  *Option,IN     UINTN                         Position)

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

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

相关文章

关于STM32项目面试题02:ADC与DAC篇(输入部分NTC、AV:0-5V、AI:4-20mA和DAC的两个引脚)

博客的风格是:答案一定不能在问题的后面,要自己想、自己背;回答都是最精简、最精简、最精简,可能就几个字,你要自己自信的展开。 面试官01:什么是模数转换/ADC?说说模数转换的流程? …

『功能项目』靠近Npc显示可对话标识【60】

我们打开上一篇59窗口可拖拽脚本的项目, 本章要做的事情是在资源加载时加载Npc01并且实现主角靠近Npc01时,显示可对话标识,当按键盘G键时弹出对话内容 首先在资源场景中加载一个Npc模型 导入(除脚本)外的资源 在资源文…

PAT甲级-1055 The World‘s Richest

题目 题目大意 输入给出富人的总数以及富人的姓名、年龄、财富,接下来的k行给出需要排序的个数,每个排序要求输出m个富人,并且限制了年龄段,[Amin, Amax]。要求输出所有的排序。如果满足年龄段的人数为0,就输出None。…

Windows目录监控部署

1.前提 Cell_Directory_Monitoring.bat脚本用到的du命令,请协调Windows系统管理员提供。 下述du命令部署配置方式仅供参考,如要部署,请协调Windows系统管理员协助确认其不会对系统造成异常。 1.1.du.exe部署 1.将x32位du.exe文件放入如下目录 目录:C:\Windows\System3…

如何在win10Docker安装Mysql数据库?

1.拉取镜像 docker pull mysql 2.查看镜像 使用以下命令来查看是否已安装了 mysql镜像。 3.运行镜像 命令: docker run -p 3306:3306 --name mysql --restartalways --privilegedtrue \ -v /usr/local/mysql/log:/var/log/mysql \ -v /usr/local/mysql/data:/var…

机器学习-点击率预估-论文速读-20240916

1. [经典文章] 特征交叉: Factorization Machines, ICDM, 2010 分解机(Factorization Machines) 摘要 本文介绍了一种新的模型类——分解机(FM),它结合了支持向量机(SVM)和分解模型的优点。与…

Linux下的CAN通讯

CAN总线 CAN总线简介 CAN&#xff08;Controller Area Network&#xff09;总线是一种多主从式 <font color red>异步半双工串行 </font> 通信总线&#xff0c;它最早由Bosch公司开发&#xff0c;用于汽车电子系统。CAN总线具有以下特点&#xff1a; 多主从式&a…

解决使用阿里云DataV Geo在线地图路径访问403问题

文章目录 1. DataV Geo在线地图路径访问403问题2. 解决方法3. 重启生效 1. DataV Geo在线地图路径访问403问题 最近在写一个省市下钻的demo&#xff0c;用到的是 阿里云DataV Geo在线地图 去动态获取GeoJSON 省市的数据&#xff0c;如下代码 axios.get("https://geo.dat…

Golang | Leetcode Golang题解之第414题第三大的数

题目&#xff1a; 题解&#xff1a; func thirdMax(nums []int) int {var a, b, c *intfor _, num : range nums {num : numif a nil || num > *a {a, b, c &num, a, b} else if *a > num && (b nil || num > *b) {b, c &num, b} else if b ! ni…

马匹行为识别系统源码分享

马匹行为识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

C语言程序设计(进阶)

行到水穷处&#xff0c;坐看云起时。 中秋快乐呀&#xff01; 数据在内存中的存储 1.数据类型的介绍 &#xff08;1&#xff09;基本的内置类型&#xff1a; char //字符数据类型 short //短整型 int //整型 long //长整型 …

说一说Zookeeper的应用场景及其原理

一 ZooKeeper简介 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名…

K8S - Access Control 机制介绍

作为开发人员&#xff0c; 我们通常会直接用root 帐号操作 k8s master node 里的kubectl 命令&#xff0c;并不能感知k8s 多用户权限管理存在。 即使自动化&#xff0c; 我们也会考虑用ansible 来远程操作master node… 所以大部分开发人员默认上是不用深入研究k8s的Access c…

基于AlexNet实现猫狗大战

卷积神经网络介绍 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;简称CNN&#xff09;&#xff0c;是一种深度学习模型&#xff0c;特别适用于处理图像、视频等数据。它的核心思想是利用卷积层&#xff08;Convolutional layers&#xff09;来提取输入数据…

[C语言]连子棋游戏

文章目录 一、前言二、游戏思路三、游戏方法1、初始化2、判断胜利3、交互4、电脑下棋 四、核心方法说明1、初始化游戏2、销毁棋盘3、显示游戏4、电脑下棋5、用户下棋6、判断游戏状态7、游戏交互 五、游戏效果展示与源码分享1、游戏效果2、源代码 一、前言 对于指针和数组理解尚…

关于std::swap原理

swap 操作交换两个相同类型容器的内容。调用swap之后&#xff0c;两个容器中的元素将会 交换&#xff1a; vector<striong> svec1(10); //10个元素的vector vector<string> svec2(24); //24个元素的vector swap(svec1,svec2); 调…

C++ | Leetcode C++题解之第413题等差数列划分

题目&#xff1a; 题解&#xff1a; class Solution { public:int numberOfArithmeticSlices(vector<int>& nums) {int n nums.size();if (n 1) {return 0;}int d nums[0] - nums[1], t 0;int ans 0;// 因为等差数列的长度至少为 3&#xff0c;所以可以从 i2 开…

一款免费开源且功能强大的思维导图软件-思绪思维导图

思绪思维导图是一款免费开源的思维导图软件&#xff0c;旨在帮助用户有效地组织和表达思想。它提供了丰富的功能&#xff0c;包括支持富文本、图片、图标、超链接、备注、标签等内容&#xff0c;以及关联线、概要等特性。 思绪思维导图下载&#xff1a;https://pan.quark.cn/s…

在STM32工程中使用Mavlink与飞控通信

本文讲述如何在STM32工程中使用Mavlink协议与飞控通信&#xff0c;特别适合自制飞控外设模块的项目。 需求来源&#xff1a; 1、增稳云台里的STM32单片机需要通过串口接收飞控传来的云台俯仰、横滚控制指令和相机拍照控制指令&#xff1b; 2、自制的有害气体采集器需要接收飞…

基于Springboot的医疗健康助手开题报告

文未可获取一份本项目的java源码和数据库参考。 一&#xff0e;选题意义, 研究现状,可行性分析 选题意义&#xff1a;随着科技的高速发展&#xff0c;人们的生活水平也正在稳步提高&#xff0c;解决温饱问题以后&#xff0c;广大人民群众也越来越注重自己的身体健康&#xff0…