UEFI——访问PCI/PCIE设备(二)
一、支持访问PCI/PCIE设备的Protocol
UEFI中提供了两个主要的模块来支持PCI总线,一是PCI Host Bridge(PCI主桥)控制器驱动,另一个是PCI总线驱动。这两个模块是和特定的平台硬件绑定的,在这种机制下,屏蔽了不同的CPU架构差异,为软件开发者提供了比较一致的Protocol接口。
UEFI标准中提供了两类访问PCI/PCIE设备的Protocol——EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL和EFI_PCI_IO_PROTOCOL。前者为PCI根桥提供了抽象的IO功能,它由PCI Host Bus Controller(PCI主总线驱动器)产生,一般由PCI/PCIe总线驱动用来枚举设备、获得Option ROM、分配PCI设备资源等;后者由PCI/PCIE总线驱动为PCI/PCIE设备产生,一般由PCI/PCIE设备驱动用来访问PCI/PCIE设备的IO空间、Memory空间和配置空间。
1.1 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL提供了基本的内存、输入/输出(I/O)、PCI配置和直接访问内存的接口,这些接口用于将PCI控制器后面的PCI根桥控制器的访问进行抽象。
typedef struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL;
/// Provides the basic Memory, I/O, PCI configuration, and DMA interfaces that are
/// used to abstract accesses to PCI controllers behind a PCI Root Bridge Controller.
///提供了基本内存,I/O,PCI配置和直接内存访问(DMA)接口,这些接口用于抽象PCI控制器后PCI根桥的访问
struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL {////// The EFI_HANDLE of the PCI Host Bridge of which this PCI Root Bridge is a member./// 这是PCI根桥所属的PCI主机桥的EFI_HANDLEEFI_HANDLE ParentHandle; //包含这个PCI根桥的PCI主机桥的HandleEFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollMem; //一个函数指针,指向用于轮询内存映射I/O空间的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollIo; //一个函数指针,指向用于轮询I/O空间的协议的方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Mem; //一个函数指针,指向用于访问内存的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Io; //一个函数指针,指向用于访问I/O空间的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Pci; //一个函数指针,指向用于访问PCI配置空间的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM CopyMem; //一个函数指针,指向用于复制内存的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP Map; //一个函数指针,指向用于映射内存或I/O空间的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP Unmap; //一个函数指针,指向用于取消映射内存或I/O空间的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer; //一个函数指针,用于指向分配内存缓冲区的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER FreeBuffer; //一个函数指针,指向释放内存缓冲区的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH Flush; //一个函数指针,指向用于刷新缓存的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES GetAttributes; //一个函数指针,指向用于获取PCI根桥属性的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES SetAttributes; //一个函数指针,指向用于设置PCI根桥属性的协议方法EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION Configuration; //一个函数指针,指向配置PCI根桥的协议方法////// The segment number that this PCI root bridge resides. PCI根桥所在的段号///UINT32 SegmentNumber;
};
在这里只介绍访问内存的接口Mem、访问I/O空间的接口Io、访问PCI配置空间的接口Pci。这三个接口的参数类型是一样的,都是EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS
typedef struct {/// Read PCI controller registers in the PCI root bridge memory space./// 读数据EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Read;/// Write PCI controller registers in the PCI root bridge memory space./// 写数据EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Write;
} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS;typedef
EFI_STATUS
(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM)(IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, //指向protocol实例IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width, //标识内存操作的宽度,一般有8位、16位、32位、64位几种IN UINT64 Address, //内存操作的基地址IN UINTN Count, //读写的数据个数,以width为单位IN OUT VOID *Buffer);
其中,参数Adress在访问I/O空间、内存空间、配置空间时是不一样的,
对于配置空间,Address由BDF地址和Register偏移决定,与一般使用宏EFI_PCI_ADDRESS来组合BDF和Register偏移,在EDK2中,宏定义如下:
#define EFI_PCI_ADDRESS(bus, dev, func, reg) \(UINT64) ( \(((UINTN) bus) << 24) | \(((UINTN) dev) << 16) | \(((UINTN) func) << 8) | \(((UINTN) (reg)) < 256 ? ((UINTN) (reg)) : (UINT64) (LShiftU64 ((UINT64) (reg), 32))))
对于IO空间而言,参数Address是指PCI设备IO空间的IO地址;
对于Memory空间而言,参数Address是指PCI设备Memory空间的Memory地址。
它们是由BAR和偏移量决定的 。
1.2 EFI_PCI_IO_PROTOCOL
在PCI/PCIE设备驱动中,一般使用EFI_PCI_IO_PROTOCOL来访问设备的内部资源,Protocol挂载在PCI/PCIE控制器上,运行在EFI启动环境中,对PCI/PCIE设备进行Memory空间和I/O空间访问。
/// Global ID for the PCI I/O Protocol
///
#define EFI_PCI_IO_PROTOCOL_GUID \{ \0x4cf5b200, 0x68b8, 0x4ca5, {0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a } \}typedef struct _EFI_PCI_IO_PROTOCOL EFI_PCI_IO_PROTOCOL;
EFI_PCI_IO_PROTOCOL的接口如下:
///
/// The EFI_PCI_IO_PROTOCOL provides the basic Memory, I/O, PCI configuration,
/// and DMA interfaces used to abstract accesses to PCI controllers.
/// There is one EFI_PCI_IO_PROTOCOL instance for each PCI controller on a PCI bus.
/// A device driver that wishes to manage a PCI controller in a system will have to
/// retrieve the EFI_PCI_IO_PROTOCOL instance that is associated with the PCI controller.
///
struct _EFI_PCI_IO_PROTOCOL {EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollMem;EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollIo;EFI_PCI_IO_PROTOCOL_ACCESS Mem;EFI_PCI_IO_PROTOCOL_ACCESS Io;EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS Pci;EFI_PCI_IO_PROTOCOL_COPY_MEM CopyMem;EFI_PCI_IO_PROTOCOL_MAP Map;EFI_PCI_IO_PROTOCOL_UNMAP Unmap;EFI_PCI_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer;EFI_PCI_IO_PROTOCOL_FREE_BUFFER FreeBuffer;EFI_PCI_IO_PROTOCOL_FLUSH Flush;EFI_PCI_IO_PROTOCOL_GET_LOCATION GetLocation;EFI_PCI_IO_PROTOCOL_ATTRIBUTES Attributes;EFI_PCI_IO_PROTOCOL_GET_BAR_ATTRIBUTES GetBarAttributes;EFI_PCI_IO_PROTOCOL_SET_BAR_ATTRIBUTES SetBarAttributes;////// The size, in bytes, of the ROM image.///UINT64 RomSize;////// A pointer to the in memory copy of the ROM image. The PCI Bus Driver is responsible/// for allocating memory for the ROM image, and copying the contents of the ROM to memory./// The contents of this buffer are either from the PCI option ROM that can be accessed/// through the ROM BAR of the PCI controller, or it is from a platform-specific location./// The Attributes() function can be used to determine from which of these two sources/// the RomImage buffer was initialized.///VOID *RomImage;
};
【注!!!】在这里需要注意的是EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL的实例,在大部分办公用的个人电脑中只存在一个,因此直接用全局指针变量gPCIRootBridgeIO存储;而EFI_PCI_IO_PROTOCOL的实例存在多个,一般有多少个PCI/PCIE设备,就存在多少个实例,因此使用全局指针数组gPCIIOArray[256]来存储这些实例。
二、访问PCI/PCIE设备的简单实现
2.1 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL遍历PCI设备
在使用EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL获取PCI/PCIE设备的时,首先需要获取EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL的所有句柄,获取句柄可以利用gBS->LocateHandleBuffer()函数,当获取到句柄后,利用gBS->HandleProtocol()函数获取到protocol实例,然后就可以调用其中的函数接口了。利用BDF找到某个设备,读取设备的配置空间。
PCI设备的配置空间用结构体PCI_TYPE00表示,其代码原型为:
/// PCI Device Configuration Spacetypedef struct {PCI_DEVICE_INDEPENDENT_REGION Hdr;PCI_DEVICE_HEADER_TYPE_REGION Device;
} PCI_TYPE00;
其中Hdr表示PCI配置空间的通用头部区域,其包括的字段:
// Common header region in PCI Configuration Spacetypedef struct {UINT16 VendorId;UINT16 DeviceId;UINT16 Command;UINT16 Status;UINT8 RevisionID;UINT8 ClassCode[3];UINT8 CacheLineSize;UINT8 LatencyTimer;UINT8 HeaderType;UINT8 BIST;
} PCI_DEVICE_INDEPENDENT_REGION;
Device表示PCI设备配置空间中的头部区域
/// PCI Device header region in PCI Configuration Spacetypedef struct {UINT32 Bar[6];UINT32 CISPtr;UINT16 SubsystemVendorID;UINT16 SubsystemID;UINT32 ExpansionRomBar;UINT8 CapabilityPtr;UINT8 Reserved1[3];UINT32 Reserved2;UINT8 InterruptLine;UINT8 InterruptPin;UINT8 MinGnt;UINT8 MaxLat;
} PCI_DEVICE_HEADER_TYPE_REGION;
利用EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL来访问PCIe设备的应用简单实现:
编写MyPCIEProtocol.c代码:
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/DebugLib.h>#include <Protocol/PciIo.h>
#include <Protocol/PciRootBridgeIo.h>
#include <IndustryStandard/Pci.h>EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *gPciRootBridgeIo; //创建一个EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL类型的指针变量EFI_STATUS LocatePciRootBridgeIo(void); //函数声明,告诉编译器函数名称、返回值类型、参数类型;如果函数定义在函数调用之后,那么在函数调用之前必须进行函数声明//函数声明
EFI_STATUS PciDevicePresent(IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL * PciRootBridgeIo,OUT PCI_TYPE00 * Pci,IN UINT8 Bus,IN UINT8 Device,IN UINT8 Func);EFI_STATUS ListPciInformation(void); //函数声明EFI_STATUS
EFIAPI
UefiMain (IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_STATUS Status;Status = LocatePciRootBridgeIo(); //调用LocatePciRootBridgeIo()函数,定位根桥if(EFI_ERROR(Status)){DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciRootBridgeIo failed,Can't find protocol!\n"));}else{DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciRootBridgeIo successed,Find protocol!\n"));}//列出PCI设备的信息ListPciInformation();return EFI_SUCCESS;
}EFI_STATUS LocatePciRootBridgeIo()
{EFI_STATUS Status;EFI_HANDLE *PciHandleBuffer = NULL;UINTN HandleIndex = 0;UINTN HandleCount = 0;//获取 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL的所有句柄Status = gBS->LocateHandleBuffer(ByProtocol, //查找句柄的方式,这里采用返回指定协议的句柄&gEfiPciRootBridgeIoProtocolGuid, //要查找的协议的GUIDNULL,&HandleCount, //输出参数,缓冲区中返回的句柄数量&PciHandleBuffer //输出参数,指向用于返回支持协议的请求句柄数组的缓冲区的指针。);if(EFI_ERROR(Status)) return Status; //如果没成功找到,直接结束程序//查找支持 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL句柄的protocol实例for(HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++){Status = gBS->HandleProtocol(PciHandleBuffer[HandleIndex],&gEfiPciRootBridgeIoProtocolGuid,(VOID **)&gPciRootBridgeIo);if(EFI_ERROR(Status)) continue; //如果没找到就继续寻找else return EFI_SUCCESS; //如果找到了就退出程序//这里找到就退出的原因是大部分办公用的个人电脑中就只存在一个EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL}return Status;}EFI_STATUS ListPciInformation()
{EFI_STATUS Status = EFI_SUCCESS;PCI_TYPE00 Pci; //PCI_TYPE00是一个结构体,表示PCI的配置空间UINT16 Dev,Func,Bus,PciDevicecount = 0;DEBUG((DEBUG_ERROR, "[CSDN] PciDeviceNo\tBus\tDev\tFunc | Vendor.Device.ClassCode\n"));for(Bus=0; Bus<256; Bus++)for(Dev=0; Dev<= PCI_MAX_DEVICE; Dev++)for(Func=0; Func<=PCI_MAX_FUNC; Func++){//判断设备是否存在Status = PciDevicePresent(gPciRootBridgeIo,&Pci,(UINT8)Bus,(UINT8)Dev,(UINT8)Func);if(Status == EFI_SUCCESS){//如果找到了设备,PCI设备数量加1PciDevicecount++;//打印设备信息DEBUG((DEBUG_ERROR, "[CSDN] %d\t\t%x\t%x\t%x\t",PciDevicecount,(UINT8)Bus,(UINT8)Dev,(UINT8)Func));DEBUG((DEBUG_ERROR, "%x\t%x\t%x\n",Pci.Hdr.VendorId,Pci.Hdr.DeviceId,Pci.Hdr.ClassCode[0]));}}return EFI_SUCCESS;
}EFI_STATUS
PciDevicePresent (IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo,OUT PCI_TYPE00 *Pci,IN UINT8 Bus,IN UINT8 Device,IN UINT8 Func)
{UINT64 Address;EFI_STATUS Status;//// Create PCI address map in terms of Bus, Device and Func//Address = EFI_PCI_ADDRESS (Bus, Device, Func, 0);//// Read the Vendor ID register//Status = PciRootBridgeIo->Pci.Read (PciRootBridgeIo,EfiPciWidthUint32,Address,1,Pci);if (!EFI_ERROR (Status) && (Pci->Hdr).VendorId != 0xffff) { //0xffff通常标识一个无效的,未分配的VentorId,如果能读取设备信息就进行打印。//// Read the entire config header for the device//Status = PciRootBridgeIo->Pci.Read (PciRootBridgeIo,EfiPciWidthUint32,Address,sizeof (PCI_TYPE00) / sizeof (UINT32),Pci);return EFI_SUCCESS;}return EFI_NOT_FOUND;
}
编写INF文件,并运行,运行结果为:
2.2 EFI_PCI_IO_PROTOCOL遍历PCI设备
使用EFI_PCI_IO_PROTOCOL遍历设备比较简单,因此为之前所得到的Protocol的实例,就是为PCI/PCIE设备产生的,实际上相当于找到了宿设备,只需要将设备的信息打印出来即可。
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/DebugLib.h>#include <Protocol/PciIo.h>
#include <Protocol/PciRootBridgeIo.h>
#include <IndustryStandard/Pci.h>EFI_PCI_IO_PROTOCOL *gPciIoArray[256];
UINTN gPciIoCount = 0;
EFI_STATUS LocatePciIo(void);EFI_STATUS ListPciInformation(void);EFI_STATUS
EFIAPI
UefiMain (IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable)
{EFI_STATUS Status;Status = LocatePciIo();if(EFI_ERROR(Status)){DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciIo failed,Can't find protocol!\n"));}else{DEBUG((DEBUG_ERROR, "[CSDN]Call LocatePciIo successed,Find protocol!\n"));}ListPciInformation();return EFI_SUCCESS;
}EFI_STATUS LocatePciIo()
{EFI_STATUS Status;EFI_HANDLE *PciHandleBuffer = NULL;UINTN HandleIndex = 0;UINTN HandleCount = 0;Status = gBS->LocateHandleBuffer(ByProtocol,&gEfiPciIoProtocolGuid,NULL,&HandleCount,&PciHandleBuffer );if(EFI_ERROR(Status)) return Status;gPciIoCount = HandleCount;DEBUG((DEBUG_ERROR, "[CSDN] the number of PCIn devices is %d", HandleCount));for(HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++){Status = gBS->HandleProtocol(PciHandleBuffer[HandleIndex],&gEfiPciIoProtocolGuid,(VOID **)&(gPciIoArray[HandleIndex]));}return Status;}EFI_STATUS ListPciInformation()
{UINTN i, count = 0;PCI_TYPE00 Pci;DEBUG((DEBUG_ERROR, "[CSDN] PciDeviceNo VendorID DeviceID ClassCode\n"));for (i = 0; i < gPciIoCount;i++){gPciIoArray[i]->Pci.Read(gPciIoArray[i],EfiPciIoWidthUint32,0,sizeof(Pci)/sizeof(PCI_TYPE00),&Pci);++count;DEBUG((DEBUG_ERROR, "[CSDN] %d\t\t%x\t%x\t%x\n", count, Pci.Hdr.VendorId, Pci.Hdr.DeviceId, Pci.Hdr.ClassCode[0]));}return EFI_SUCCESS;
}
运行结果如下,发现读到的Class Code[0]的数据不一样,不知道为什么?

相关文章:
UEFI——访问PCI/PCIE设备(二)
一、支持访问PCI/PCIE设备的Protocol UEFI中提供了两个主要的模块来支持PCI总线,一是PCI Host Bridge(PCI主桥)控制器驱动,另一个是PCI总线驱动。这两个模块是和特定的平台硬件绑定的,在这种机制下,屏蔽了…...
决策树算法的介绍与应用
目录 引言 决策树算法的基本原理 表格总结:决策树的构建步骤 决策树算法的 MATLAB 实现 示例:使用决策树进行分类预测 决策树的应用场景 表格总结:决策树的主要应用领域 决策树的优势与局限 结论 引言 决策树是一种广泛应用于数据挖掘…...
杰发科技Bootloader(3)—— 基于7801的APP切到Boot
为了方便在APP中跳转到Boot重新进行升级,有两种办法,7840同样可以使用。 1. 调用reset接口进行复位,复位后会先进Boot,再自动跳转到App。 NVIC_SystemReset(); 2. 直接使用跳转指令,参考Boot跳转到App代码࿰…...
Leetcode面试经典150题-138.随机链表的复制
题目比较简单,重点是理解思想,random不管,copy一定要放在next 而且里面的遍历过程不能省略 解法都在代码里,不懂就留言或者私信 /* // Definition for a Node. class Node {int val;Node next;Node random;public Node(int val…...
freemarker模板学习笔记
文章目录 freemarker常用指令if-elseif-else指令switch, case, default, break指令list, else, items, sep, break 指令<#list>指令语法<#else> 指令<#items> 指令<#sep> 指令<#break> 指令 include 指令<#include> 基础知识<#include&…...
高亚科技与广东海悟携手,打造全流程电子竞标管理平台!
近日,中国企业管理软件资深服务商高亚科技与广东海悟科技有限公司(以下简称“海悟”)正式签署合作协议,双方将基于高亚科技的8Manage SRM系统,推进海悟采购管理的数字化升级,实现全流程在线电子竞标管理&am…...
240908-结合DBGPT与Ollama实现RAG本地知识检索增强
A. 最终效果 B. 背景说明 DBGPT在0.5.6版本中开始支持Ollama:v0.5.6 版本更新 网友对其Web端及界面端的设置进行了分享: feat(model): support ollama as an optional llm & embedding proxy by GITHUBear Pull Request #1475 eosphoros-ai/DB-G…...
AMD ThinkSystem服务器上的 Linux 和 C 状态设置 - Lenovo ThinkSystem
受影响的配置 该系统可以是以下任何Lenovo服务器: ThinkSystem 、SR645( ThinkSystem )ThinkSystem ,SR645 V3( ThinkSystem )ThinkSystem ,SR635 V3( ThinkSystem )Th…...
Redis过期删除和缓存淘汰
1. 过期删除 在 Redis 中,键的过期删除机制主要包括惰性删除(Lazy Deletion)和定期删除(Periodic Deletion)。这两种策略有各自的优缺点,Redis 最终会结合这两种方法来管理过期键。 1.1 惰性删除…...
Golang | Leetcode Golang题解之第401题二进制手表
题目: 题解: func readBinaryWatch(turnedOn int) (ans []string) {for i : 0; i < 1024; i {h, m : i>>6, i&63 // 用位运算取出高 4 位和低 6 位if h < 12 && m < 60 && bits.OnesCount(uint(i)) turnedOn {ans …...
TON智能合约stdlib_ext库:扩展功能一览
TON(TheOpenNetwork)作为一个去中心化的区块链平台,其智能合约功能强大而灵活。在TON智能合约的开发过程中,stdlib.fc库提供了基础的功能支持。然而,对于一些高级或特定的需求,stdlib.fc可能无法满足。为此…...
LabVIEW开发FPGA方法与FIFO数据丢失处理
开发基于NI 7975R FPGA的系统涉及一系列流程,包括驱动安装、LabVIEW项目设置、开发调试、编译和与Windows系统的通信。重点在于FIFO的正确配置,避免数据丢失是关键环节之一,尤其是在使用高速数据流传输时。以下将详细介绍这些过程,…...
Python中的内存池机制
在Python中,内存管理是一个复杂但至关重要的主题,它直接关系到程序的性能和稳定性。Python的内存管理机制包括对象的分配、追踪以及回收,其中内存池(Memory Pool)是这一机制中的一个重要组成部分。内存池机制通过预先分…...
智能家居系统(基于STM32F103C8T6标准库+FreeRTOS+Qt串口开发实现)
视频演示:基于STM32F103C8T6标准库FreeRTOSQt串口开发实现的智能家居项目_哔哩哔哩_bilibili 基于STM32F103C8T6标准库FreeRTOSQt串口开发实现的智能家居项目: https://pan.baidu.com/s/1f41gAfOOnlcQoKoMx3o84A?pwd6j2g 提取码: 6j2g 注:本项目为学习完…...
[数据集][目标检测]脊椎检测数据集VOC+YOLO格式1137张1类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):1137 标注数量(xml文件个数):1137 标注数量(txt文件个数):1137 标注…...
大受欢迎的游戏却又意外被作者下架的《Flappy Bird》将重返iPhone
据"Flappy Bird 基金会"官网称,标志性的侧卷轴滚动游戏《Flappy Bird》将很快回归 iPhone。《Flappy Bird》于 2013 年发布,很快就获得了数千万次下载。然而,这款游戏在2014 年突然从 App Store 下架,原因是其越南开发者…...
Flutter类
Dart中的对象都继承自 Object 类,单继承(extend关键字)。Dart与Java、kotlin不同的是其无public、private、protected修饰符,默认public ,通过在属性名、方法名前加 _下划线 来定义是否私有。 实现一个简单的类 class…...
深入解析全连接层:PyTorch 中的 nn.Linear、nn.Parameter 及矩阵运算
文章目录 数学概念(全连接层,线性层)nn.Linear()nn.Parameter()Q1. 为什么 self.weight 的权重矩阵 shape 使用 ( out_features , in_features ) (\text{out\_features}, \text{in\_features}) (out_features,in_features)而不是 ( in_featur…...
缓存对象反序列化失败
未定义serialVersionUID,会自动生成序列化号 新增了属性,序列号就变了,导致缓存对象反序列化失败。 所有缓存对象必须指定序列化id! 那我如何找到未添加字段前 对象的序列化号呢?默认的序列化号是如何生成的呢&#…...
F28335的存储器与寄存器
1 存储器及CMD文件的编写 1 F28335的存储器 1.1 F28335存储器的结构 1.2 F28335存储器的映像 存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程称为存储器映射,如果再分配一个地址就叫重映射。 我们将《tms320f28335 数据手册》中“3.1…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
