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…...

Python在AOIP(Audio Over IP)方面的应用探讨
Python在AOIP(Audio Over IP)方面的应用探讨 引言 随着网络技术的发展,音频传输逐渐向基于IP的解决方案迁移。音频通过互联网进行传输被称为音频过IP(Audio Over IP,简称AOIP)。这种技术在广播、现场活动…...

C++20标准对线程库的改进:更安全、更高效的并发编程
引言 C20 是 C 语言的一个重要里程碑,它引入了许多新特性,其中就包括对线程库(thread)的重大改进。这些改进不仅增强了语言的并发编程能力,还解决了先前版本中的一些痛点问题。本文将详细介绍 C20 在线程方面的改进&a…...

外包干了三年,快要废了。。。
先简单说一下自己的情况,普通本科,在外包干了3年多的功能测试,这几年因为大环境不好,我整个人心惊胆战的,怕自己卷铺盖走人了,我感觉自己不能够在这样蹉跎下去了,长时间呆在一个舒适的环境真的会…...

微服务网关终极进化:设计模式驱动的性能与可用性优化(四)
时间:2024年09月12日 作者:小蒋聊技术 邮箱:wei_wei10163.com 微信:wei_wei10 希望大家帮个忙!如果大家有工作机会,希望帮小蒋推荐一下,小蒋希望遇到一个认真做事的团队,一起努力…...

Java中的服务端点日志记录:AOP与SLF4J
Java中的服务端点日志记录:AOP与SLF4J 大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿! 在Java后端服务开发中,日志记录是监控和调试应用的关键手段。通过合理使用AOP&…...

黑马头条第八天实战(上)
D8 1)登录功能需求说明 用户根据用户名和密码登录密码需要手动加盐验证需要返回用户的token和用户信息 2)模块搭建思路步骤 2.1)模块作用 先捋一下之前搭模块干了啥 feign-api 远程调用 自媒体保存时调用远程客户端进行增加文章&#x…...

swift qwen2-vl推理及加载lora使用案例
参考: https://swift.readthedocs.io/zh-cn/latest/Instruction/LLM%E5%BE%AE%E8%B0%83%E6%96%87%E6%A1%A3.html#%E5%BE%AE%E8%B0%83%E5%90%8E%E6%A8%A1%E5%9E%8B https://blog.csdn.net/weixin_42357472/article/details/142150209 SWIFT支持300+ LLM和50+ MLLM(多模态大模型…...

如何使用 Choreographer 进行帧率优化
Choreographer 是 Android 提供的一个工具类,专门用来协调 UI 帧的渲染。你可以通过 Choreographer 来精确控制帧的绘制时机,以优化帧率,确保应用的流畅度。以下是如何使用 Choreographer 进行帧率优化的详细步骤: 1. 理解 Chore…...

稳定驱动之选SiLM5350系列SiLM5350MDBCA-DG单通道隔离栅极驱动器(带内部钳位):工业自动化的可靠伙伴
SiLM5350系列SiLM5350MDBCA-DG是具体有10A峰值输出电流能力,单通道隔离式栅极驱动器。SiLM5350MDBCA-DG可提供内部钳位功能。驱动电源电压为4V至30V。3V至18V的宽输入VDDI范围使驱动器适合与模拟和数字控制器接口。所有电源电压引脚都有欠压锁定 (UVLO) 保护。 SiLM…...

鸿蒙OpenHarmony【轻量系统芯片移植】内核移植
移植芯片架构 芯片架构的移植是内核移植的基础,在OpenHarmony中芯片架构移植是可选过程,如果当前OpenHarmony已经支持对应芯片架构则不需要移植操作,在“liteos_m/arch”目录下可看到当前已经支持的架构,如表1: 表1 …...