UEFI学习(四)-SuperIo的访问-程序员宅基地

技术标签: uefi  UEFI学习  嵌入式硬件  单片机  

一、什么是Super I/O?


Super I/O 芯片也叫 I/O 芯片。在 486 以上档次的主板上都有 I/O控制电路。因为在南桥这样的高速设备和串行、并行接口、软盘驱动器及键盘鼠标等大量低速设备之间必定存在资源的不匹配,而需要经过转换和管理。而 Super I/O 芯片则完成了该功能。
通常在硬件监控芯片硬件监控芯片中会整合超级 I/O 功能,可用于监控受监控对象的电压、温度、转速等。主板在附件中会提供某种软件,它和主板上的硬件配合使用就能实现对电压、温度、风扇转速等的监控,一旦检测到这些参数超出设定的指标时,它会自动作出调整,以保护元件的安全。常见的温度控制芯片有 ADT7463 等等;通用的通用硬件监控芯片有 Winbond W83697HF 和 W83627HF , SMSC 的 LPC47M172 , ITE 的IT8705F、IT8703F,ASUS 的 AS99172F 等等,这些芯片通常还整合了对于温度的监控需与温度传感元件配合;对风扇电机转速的监控,则需与 CPU 的散热风扇配合使用。

SIO是一个半可定制化的芯片,怎么说是半可定制化呢?比如上电时序,这一部分就是固化好的,而可定制化部分则是逻辑设备(Logic Device)部分。接入电源后SIO便根据固化的程序开始运作,等待power button触发。按下power button后SIO开始跑上电时序,CPU Reset后BIOS才开始跑,此时BIOS给SIO配置的Logic Device也才生效。
原链接:https://ay123.net/mystudy/673/

二、我们要用SuperIo实现什么

本文使用的SuperIo为NCT5581D,需要注意的是不同的厂家SuperIo的访问方式与配置都会有差异。学习的资料为对应的datasheet和program guide。
对于NCT5581D我们的学习目标为:

  • 了解NCT5581D的访问机制,学习 SIO Logic Device 相关内容。编写 一个 UEFI App(SIO.EFI),用于读写 SIO Logical Device 寄存器。
    命令格式
    读: SIO.EFI ldn //显示 ldn 这个 Device 下面的 256 个寄存器值
    写: SIO.EFI ldn,offset,value
    //把 value 值写到 ldn 这个 Device,地址为 offset
  • 了解NCT5581D的GPIO功能,选择一个 GPIO 引脚,用 SIO.EFI 工具控制输出电平。万用表测量,验证效果
  • 了解NCT5581D的Hardware Monitor的相关内容与其功能,编写 HardWareMonit 工具(HWM.efi)。实现一些功能:
    1、读取 CPU 温度、风扇转速、CPU 电压。循环显示出来
    2、控制 CPU 风扇转。
  • 了解NCT5581D的WDT功能,编写看门狗测试工具(WDT.efi)。
    运行命令:
    SIO.EFI nSec
    //设置 WDT Timer 为 nSec 秒。 nSec 秒后系统自动重启;实时显示 Timer 剩余时间

三、NCT5581D的访问机制

若要了解NCT5581D的访问机制需要对datasheet的第7章内容进行学习,从datasheet中可以了解到NCT5581D使用一个特殊的协议来访问配置寄存器来设置不同类型的配置。NCT5581D有十多个逻辑设备(from Logical Device 1 to Logical Device 16)如果将所有逻辑设备配置寄存器映射到正常的PC地址空间,则需要很大的地址空间。所以,NCT5581D使用两个I/O地址(2Eh/2Fh或4Eh/4Fh)映射所有配置寄存器,2Eh/2Fh或4Eh/4Fh作为index(索引)/data(数据)对进行对superio内部寄存器的访问。我们可以通过,对2Eh写入地址,去选中相应寄存器;而对2Fh进行读写数据,来达到我们对superio内部寄存器的目的。需要注意的是在NCT5581D中有一组全局寄存器位于0h-2fh的索引处,其中包含了整个芯片的信息和配置。而访问单个逻辑设备的控制寄存器的方法也很简单。只需将所需的逻辑设备号写入全局寄存器07h。索引为30h或更高的后续访问将直接访问到逻辑设备寄存器。

在我们开始着手去编写应用去验证功能之前,我们还需要了解一下,NCT5581D内部寄存器的结构,以便对上述提到的内容有更好的理解。

在这里插入图片描述

(其中A25/A26是访问HardwareMonitor的IOspace的index/data对,后面会解释。)
下面列出的是NCT5581D的全局寄存器,也就是对应于上图中00h-ffh的寄存器

在这里插入图片描述

从上图中的备注中可以看到全局变量寄存器CR20h和CR21h(注:CR的意思是control register就是控制寄存器的意思)中保存的是Chip-ID的高八位和低八位。其中低八位可能会因为版本变更而有变化,但高八位总是不变的。
看到这里我们可以对NCT5581D有一个验证的设想,我们可以通过上面描述的方式对全局变量寄存器CR20h和CR21h进行读取然后对照上表进行验证是否正确获得相应寄存器的值,从而证明上述的superio访问方式是正确的可行的。

配置顺序

根据第7章的内容想要读取寄存器的值需要按照一定的配置顺序:

  1. Enter the Extended Function Mode.(进入扩展功能模式)
  2. Configure the configuration registers(配置寄存器)
  3. Exit the Extended Function Mode.(退出扩展功能模式)

①进入扩展功能模式

要将芯片置入扩展功能模式,必须连续两次0x87写入应用于扩展功能启用注册器(EFERs,即2Eh或4Eh)。

②配置寄存器

芯片选择逻辑设备,并通过功能索引扩展功能数据寄存器(EFIR)和扩展功能数据寄存器(EFDR)激活所需的逻辑设备。EFIR与EFER位于相同的地址,而EFDR位于地址(EFIR+1)。首先,将逻辑设备编号(即0x07)写入EFIR,然后将所需的逻辑设备的编号写入EFDR。如果要访问芯片(全局)控制注册器,则不需要执行此步骤。其次,将逻辑设备中所需的配置寄存器的地址写入EFIR,然后通过EFDR写入(或读取)所需的配置寄存器。

③退出扩展功能模式

要退出扩展功能模式,需要向EFER写入0xAA。一旦芯片退出扩展功能模式,它将处于正常运行模式,并准备进入配置模式。

至此,了解了上述内容我们便可以开始着手并去验证这个访问机制了。

  1. 参照UEFI学习(二),我们创建一个edkii的标准应用工程采用MdeModulePkg包来编译,文件夹名字取为mySio,包含mySio.c和mySio.inf;

  2. mySio.c中加入以下内容:

    #include <Uefi.h>
    #include <Library/UefiLib.h>
    #include <Library/UefiApplicationEntryPoint.h>
    #include <Library/IoLib.h>
    //edkii中已包含了对io地址读写的库IoLib.h,从而我们可以使用IoWrite8/IoRead8进行写操作和读操作,
    //具体函数定义还需跳转到IoLib.h去查看。
    #define  IoIndexPort 0x2E
    #define  IoDataPort  0x2F
    
    
    EFI_STATUS
    EFIAPI
    UefiMain (
      IN EFI_HANDLE        ImageHandle,
      IN EFI_SYSTEM_TABLE  *SystemTable
      )
    {
      UINT8  ID_High=0;
      UINT8  ID_Low=0;
      //进入扩展模式
      IoWrite8(IoIndexPort,0x87);
      IoWrite8(IoIndexPort,0x87);
      //读取全局寄存器读取ID号:高位+地位
      IoWrite8(IoIndexPort,0x20);
      ID_High=IoRead8(IoDataPort);
      IoWrite8(IoIndexPort,0x21);
      ID_Low=IoRead8(IoDataPort);
      Print(L"SuperIO ID is %X %X\n",ID_High,ID_Low);
      //退出扩展模式
      IoWrite8(IoIndexPort,0xAA);
      return EFI_SUCCESS;
    }
    
    
  3. mySio.inf中加入以下内容:

    [Defines]
      INF_VERSION                    = 0x00010005
      BASE_NAME                      = mySio
      FILE_GUID                      = c3d8fe10-29fb-48bd-bf61-6fda693d7712
      MODULE_TYPE                    = UEFI_APPLICATION
      VERSION_STRING                 = 1.0
      ENTRY_POINT                    = UefiMain
    
    [Sources]
      mySio.c
    
    [Packages]
      MdePkg/MdePkg.dec
      MdeModulePkg/MdeModulePkg.dec
    
    [LibraryClasses]
      UefiApplicationEntryPoint
      UefiLib
      IoLib
    
    
    
  4. IoLib.h的路径在edk2\MdePkg\Include\Library\IoLib.h,其中我们可以从中查看到IoWrite8/IoRead8两个函数的描述而对于其底层实现,我们现在可以暂时不用去深入了解:

    /**
      Reads an 8-bit I/O port.
    
      Reads the 8-bit I/O port specified by Port. The 8-bit read value is returned.
      This function must guarantee that all I/O read and write operations are
      serialized.
    
      If 8-bit I/O port operations are not supported, then ASSERT().
    
      @param  Port  The I/O port to read.
    
      @return The value read.
    
    **/
    UINT8
    EFIAPI
    IoRead8 (
      IN      UINTN                     Port
      );
    
    /**
      Writes an 8-bit I/O port.
    
      Writes the 8-bit I/O port specified by Port with the value specified by Value
      and returns Value. This function must guarantee that all I/O read and write
      operations are serialized.
    
      If 8-bit I/O port operations are not supported, then ASSERT().
    
      @param  Port  The I/O port to write.
      @param  Value The value to write to the I/O port.
    
      @return The value written the I/O port.
    
    **/
    UINT8
    EFIAPI
    IoWrite8 (
      IN      UINTN                     Port,
      IN      UINT8                     Value
      );	
    

5.按照UEFI学习(二)将我们编写好的工程文件加入到MdeModulePkg的.dsc文件中并进行编译得到mySio.efi,将其转移到具备SHELL环境的u盘中,并在带有NCT5581D的测试机台上开机并进入shell环境,运行我们编译得到的mySio.efi文件。结果如下图所示,可以看到高八位的确如datasheet中描述的一样为default值D4h而低八位应该是因为版本更变。

在这里插入图片描述

目标一实现

至此,已经完成了我们的访问机制的验证。接下来我们可以开始去着手实现目标一了。对于参数的传入,我们可以通过创建shell工程应用去实现,通过学习《UEFI原理与编程》第三章的3.2.1,学习shell应用程序的特点与创建过程。接下来给大家分享一下个人对于目标一任务的实现并会在代码中作出一定解释帮助阅读者阅读。

SIO.c

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib/BaseLibInternals.h>

/*
ShellCEntryLib.h和UefiBootServicesTableLib.h是shell应用工程需要,IoLib.h是IO读取需要,
ShellLib.h是调用ShellPrintEx所需要包含的库,用于指定坐标打印。
*/

#define  IoIndexPort 0x2E
#define  IoDataPort  0x2F
UINT8 symbol2=0;
UINT8 ROWNUM[16] = {0x00,0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0};

void StartToReadRC()//进入扩展模式
{
    IoWrite8(IoIndexPort, 0x87);
    IoWrite8(IoIndexPort, 0x87);
    return;
}

void QuitRCReader()//推出扩展模式
{
    IoWrite8(IoIndexPort, 0xAA);
    return;
}

UINT8 SelectLogicalDevice(IN CHAR16* LDn)//选择逻辑设备并屏蔽部分保留的逻辑设备号,NCT5581d包含逻辑设备号0-9,A-F,10-16
{
    UINT8 symbol = 0;
    Print(L"The input LD is %s\n",LDn);
    if(LDn[0]!='L'||LDn[1]!='D')
    {
        ShellPrintEx(-1, -1, L"%HTHE LD parameter Input illegal!!\n");
        return symbol;
    }
    switch (LDn[2])
    {
        case '0':Print(L"This LD is reserved!\n"); break;
        case '1':if(LDn[3]=='6')//case 1的特殊是因为10-16的逻辑设备中只有16是有用的其余为保留
                    {IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x16); symbol = 1;}
                 else
                    {Print(L"This LD is reserved!\n"); }
                 break;
        case '2':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x02); symbol = 1;break;
        case '3':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x03); symbol = 1;break;
        case '4':Print(L"This LD is reserved!\n"); break;
        case '5':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x05); symbol = 1;break;
        case '6':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x06); symbol = 1;break;
        case '7':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x07); symbol = 1;break;
        case '8':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x08); symbol = 1;break;
        case '9':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x09); symbol = 1;break;
        case 'A':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0A); symbol = 1;break;
        case 'B':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0B); symbol = 1;break;
        case 'C':Print(L"This LD is reserved!\n"); break;
        case 'D':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0D); symbol = 1;break;
        case 'E':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0E); symbol = 1;break;
        case 'F':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0F); symbol = 1;break;
        default:ShellPrintEx(-1, -1, L"%HInput illegal!!\n");break;
    }
    return symbol;//symbol是为了判断是否成功
}

void ReadRC()//表头与内容打印的实现
{
    UINT8 RCvalue = 0,Times=0;
    INT32 colinit=0, rowinit=0;
    INT32 col=0, row=0,a=1,b=17;
    //Print(L"The row's value is %d!\n",gST->ConOut->Mode->CursorRow);
    Print(L"\n");
    colinit=gST->ConOut->Mode->CursorColumn;
    rowinit=gST->ConOut->Mode->CursorRow;

	//打印行表头
    ShellPrintEx(colinit, rowinit, L"%H     00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F");
    for (row =rowinit+a; row < rowinit+b; row++)
        {
            col = 0;
            ShellPrintEx(col, row,L"%H%02x", ROWNUM[row - 1-rowinit]);//打印列表头
            for (col = 5; col < 66;)
            {
                IoWrite8(IoIndexPort, Times);
                RCvalue= IoRead8(IoDataPort);
                ShellPrintEx(col, row, L"%B%02x",RCvalue);
                Times++;
                col += 4;
            }
        }
    if (Times==0)//unit8最大为255,而打印结束后Times已经增加到256即溢出为0了,而这一步是为了验证是否打印完全并正确
    {
        ShellPrintEx(0, rowinit+19, L"%HRCread is complete!\n");
    }
    else
    {
        ShellPrintEx(0,rowinit+ 19, L"%HRCread error!\n");
        Print(L"The Times's value is %d!\n",Times); 
    }
   return;
}



UINT8 Transfer(IN CHAR16 value)
{
    UINT8 number=0;

    if(value>47&&value<58)
        number=(UINT8)(value-48);
    else if(value>64&&value<71)
        number=(UINT8)(value-55);
    else if(value>96&&value<103)
        number=(UINT8)(value-87);
    else
        {Print(L"The format of the parameter value is incorrect!\n");symbol2=1;}
    return number;
}

INTN
EFIAPI
ShellAppMain(
    IN UINTN Argc,
    IN CHAR16** Argv
)
{
    UINT8 offsetRT=0;
    UINT8 valueRT=0;
	//通过判断输入参数的个数来选择不同的功能
    switch (Argc)
    {
        case 1:Print(L"Argv[1] = NULL\n"); break;
        case 2:StartToReadRC();
               if(SelectLogicalDevice(Argv[1]))//?1
                ReadRC();
               else
                Print(L"Select Logical Device Fail!\n"); 
               QuitRCReader();
               break;
        case 3:Print(L"Missing Parameter!\n"); break;
        case 4:StartToReadRC();
               if(SelectLogicalDevice(Argv[1]))
                {
                    offsetRT=Transfer(Argv[2][0])*16+Transfer(Argv[2][1]);
                    Print(L"offsetRT is %x!\n",offsetRT); 
                    if(symbol2)
                        break;
                    else
                        {
                            
                            valueRT=Transfer(Argv[3][0])*16+Transfer(Argv[3][1]);
                            Print(L"valueRT is %x!\n",valueRT); 
                            if(symbol2)
                                break;
                            else
                                {IoWrite8(IoIndexPort,offsetRT );
                                IoWrite8(IoDataPort,valueRT );break;}
                        }
                }   
               QuitRCReader();
               break;
        default:Print(L"Not Ready Yet!\n"); 
            break;
    }


    return 0;
}

SIO.inf

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = SIO
  FILE_GUID                      = 7a6ca3b8-ee1b-489c-b300-24544a7bd418
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = ShellCEntryLib

[Sources]
  SIO.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  ShellCEntryLib
  UefiLib
  IoLib
  ShellLib

结果演示

在这里插入图片描述

在这里插入图片描述

目标二实现

对于目标二的实现,首先需要对NCT5581D的GPIO具有一定的了解,掌握其配置所需要用到的寄存器。在datasheet的第17章中有对GPIO的详细介绍,The NCT5581D provides input/output ports that can be individually configured to perform a simple basic I/O function or alternative, pre-defined function. Users can configure each individual port to be an input or output port by programming respective bit in selection register (0 = output, 1 = input). Invert port value by setting inversion register (0 = non–inverse, 1 = inverse). Port value is read/write through data register.
而要实现具体的配置,以GP84为例,下面表中已经提示了要想实现GP84的GPIO功能需要先对其进行使能,配置相关的寄存器使GP84达到输入还是输出,极性等等。

在这里插入图片描述
在这里插入图片描述

  1. 首先是Logical Device 7 CR30h将GPIO8置active
    在这里插入图片描述

  2. 接下来需要注意的是在旧版的datasheet中可能很难查到的GPIO8使能位的设置,该设置位于全局变量寄存器CR2Ah中
    在这里插入图片描述

  3. 接下来要将GP84设置成需要的输入还是输出模式,电平极性是否需要反转,设置电平以及状态寄存器的读取以及功能复用的选择寄存器的设置寄存器位于 Logic Device 7, E4~E7 and ED
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 设置GPIO上拉还是开漏模式,寄存器位于 Logic Device F, CRE7
    在这里插入图片描述

了解了需要配置的寄存器,我们就可以开始实现目标二的要求了。因为是要使用万用表进行测量验证,所以在观察了原理图之后选择了GP84这个口,设想是将其配置GPIO输出模式,不用翻转极性,通过设置电平后用状态寄存器和万用表去验证功能,没有万用表时:在设置好相应寄存器后先让GPIO84清除状态寄存器,然后让GPIO84输出低电平再让GPIO84输出高电平,此时在状态寄存器的相应位可以读取到状态寄存器侦测到一个电平跳变,从而验证我们的设置是正确的。

下面列出我的GPIO.c:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib/BaseLibInternals.h>

#define  IoIndexPort 0x2E
#define  IoDataPort  0x2F

INTN
EFIAPI
ShellAppMain(
    IN UINTN Argc,
    IN CHAR16** Argv
)
{
	UINT8 GPIO84status=0;
	UINT8 GPIO84status2=0;
	//enter extern mode
	IoWrite8(IoIndexPort, 0x87);
    IoWrite8(IoIndexPort, 0x87);

    //select logical device 07 to enable gpio84
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);
    IoWrite8(IoIndexPort, 0x30);
    IoWrite8(IoDataPort, 0x04);
    GPIO84status2=(IoRead8(IoDataPort)>>2)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The cr30[2] is %x\n",GPIO84status2);

    //SET function to GPIO
    IoWrite8(IoIndexPort, 0x2A);
    GPIO84status2=IoRead8(IoDataPort)|0x80;
    IoWrite8(IoIndexPort, 0x2A);
    IoWrite8(IoDataPort, GPIO84status2);
    GPIO84status2=(IoRead8(IoDataPort)>>7)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The cr2A[8] is %x\n",GPIO84status2);

    // set gpio84 output
    IoWrite8(IoIndexPort, 0xE4);
    IoWrite8(IoDataPort, 0xEF);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The crE4[4] is %x\n",GPIO84status2);


    //set GPIO84 Push-Pull
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x0F);
    IoWrite8(IoIndexPort, 0xE7);
    IoWrite8(IoDataPort, 0xEF);
    //set gpio84 output highlevel
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);
    IoWrite8(IoIndexPort, 0xE5);
    IoWrite8(IoDataPort, 0x10);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The GPIO84status1 is %x\n",GPIO84status2);

    //read gpio8 status register to clear status
    IoWrite8(IoIndexPort, 0xE7);
    IoRead8(IoDataPort);

    //set GPIO84 OUT OF Push-Pull
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x0F);
    IoWrite8(IoIndexPort, 0xE7);
    IoWrite8(IoDataPort, 0xFF);
    //set gpio84 output lowlevel
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);
    IoWrite8(IoIndexPort, 0xE5);
    IoWrite8(IoDataPort, 0x00);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The GPIO84status2 is %x\n",GPIO84status2);

    //set GPIO84 Push-Pull
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x0F);
    IoWrite8(IoIndexPort, 0xE7);
    IoWrite8(IoDataPort, 0xEF);
    //set gpio84 output highlevel
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);    
    IoWrite8(IoIndexPort, 0xE5);
    IoWrite8(IoDataPort, 0x10);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The GPIO84status3 is %x\n",GPIO84status2);


    IoWrite8(IoIndexPort, 0xE7);
    GPIO84status=IoRead8(IoDataPort);
    ShellPrintEx(-1, -1, L"%B%H The E7status is %x\n",GPIO84status2);
    GPIO84status=(GPIO84status>>4)&0x01;
    if(GPIO84status)
    	ShellPrintEx(-1, -1, L"%B%H GPIO Setting Success!");
    else
    	ShellPrintEx(-1, -1, L"%B%H GPIO Setting Fail!");

    IoWrite8(IoIndexPort, 0xAA);


	return 0;
}

可以看到,我的GPIO.c就是按照前面提到的配置去设置好相应的寄存器,就可以实现这个功能。

目标三实现

NCT5581D 的Hardware Monitor 监控PC硬件中的几个关键参数,包括电源电压、风扇速度和温度,所有这些对于高端计算机系统稳定和正常工作都是非常重要的。此外,专有的硬件减少了编程和处理器干预的数量,以控制冷却风扇的速度,最小化环境噪音,最大化系统可靠性。基于Hardware Monitor功能的强大和复杂性,单单前文提到的逻辑设备上的寄存器是远远不够的,所以NCT5581D为其提供了一个block去存放Hardware Monitor功能配置所需要的众多寄存器,那么问题就来了,对于这个block我们要怎么去访问呢?从datasheet的第八章我们可以看到,“NCT5581D提供了两个接口,LPC和I2C,供微处理器读取或写硬件监视器的内部寄存器。”而对于从LPC的访问方式是与前文提到的index/data对的IO访问方式是相似的,只不过前文使用的是2E/2F,而Hardware Monitor需要用到另外的端口号。在datasheet中同样提到了Hardware Monitor所用到的索引/数据对的基地址由使用超级I/O协议访问CR[60h]和CR[61h]获得。即想要获取Hardware Monitor需要用到的索引/数据对,我们先要选中Hardware Monitor对应的逻辑设备号,并读取其CR[60h]和CR[61h]的值,使用我们前面实现的打印功能,我们可以很容易的获取到这两个寄存器的值。分别为0x0A和0x20,但是需要注意的是,此时获取的仅仅只是基地址,而且基地址要由这两个寄存器怎么转换得到我们尚未可知,当我们继续往下看datasheet,在第九章的开头我们可以看到“地址端口和数据端口的基本地址在硬件监视设备逻辑设备B的寄存器CR60h和CR61h中指定。60h是高字节,61h是低字节。地址端口和数据端口分别位于基本地址,再加上5小时和6小时。例如,如果CR[60h]为02h,CR[61h]为90h,则地址端口为0x295h,数据端口为0x296h。”所以我们根据读到的CR[60h]和CR[61h]的值我们可以求得Hardware Monitor所用到的索引/数据对应该为A25/A26;至此,我们可以按此去访问Hardware Monitor的专属block了(通常把这种block称为IOspace)。

另外,第八章内容中还提到了“由于内部登记器的数量,有必要将登记器集划分为登记器4Eh指定的“bank”。”对此,我的个人理解是对前面选择逻辑设备号的一种套娃方式,把众多寄存器进行bank的分区,通过固定的寄存器去选择相应bank再去选择bank中的寄存器从而减少地址数量的需求。而对于选择bank则是在bank0的CR4E,该寄存器的描述为:

在这里插入图片描述

读到这里的时候,我当初会产生很多疑问,就是当我已经跳转到其他bank之后,我要怎么再选择bank0的4E去实现跳转到其他bank?难道每个bank的4e寄存器都可以实现跳转?那为什么datasheet要强调bank选择寄存器位于bank0的4E呢?而当我查阅了datasheet的第九章Hardware Monitor的众多寄存器后我发现,只有bank0具有4E寄存器,那么我的理解就是,在其他bank的4E寄存器的位置都是bank04E寄存器的映射,所以当我们跳转到其他bank后我们对该bank的4E进行写入便可实现对bank0的4E寄存器的值的修改,从而实现跳转。

掌握了对Hardware Monitor IOSpace的访问机制后,我们接下来就是要像配置GPIO功能一样去理解目标三中提到的功能实现的相关寄存器的配置。

CPU温度读取

对于CPU温度的读取需要理解PECI接口,从第八章内容我们可以理解相关的内容,“PECI(平台环境控制接口)是一种新的数字接口,可以读取IntelCPU的CPU温度。PECI的带宽从2Kbps到2Mbps,它使用一根线进行自时钟和数据传输。通过与IntelCPU中的数字热传感器(DTS)接口,PECI报告相对于激活热控制电路(TCC)的处理器温度的负温度(计数)。在TCC激活温度下,英特尔CPU将以降低性能运行,以防止设备发生热损坏。”
而读取的配置顺序为:

  1. BIOS have to set Global Control Register CR2C bit 0 = 0 to select Pin function as PECI.
    在这里插入图片描述

  2. Set Bank 7 Index 03h = 10h for Agent 0 and Domain 0
    在这里插入图片描述

  3. Set Bank 7 Index 09h for Agent 0 Tbase
    在这里插入图片描述

  4. Set Bank 7 Index 01h = 95h to enable PECI3.0
    在这里插入图片描述

  5. Get temperature raw data from Bank 7 Index 17h (high byte) / 18h (low byte)
    在这里插入图片描述

  6. Set Bank 2 Index 00h = 10h to select PECI Agent 0 as CPUFAN monitoring source
    在这里插入图片描述

  7. Set Bank 0 Index AEh bit 0 to 1 to enable PECI Agent 0 mode
    在这里插入图片描述

  8. PECI Temperature Reading
    在这里插入图片描述

对于上述配置中的Tbase的配置和 raw data的配置可以从步骤8图中得到相应的关系,这两个步骤的意义在于当温度与我们自己测量到的温度有很大偏差时作调整用,而我们现在是为了练习对superio的访问,所以我们可以不对这两个步骤进行配置修改。以下是我的在CPU温度读取的代码实现:

UINTN ReadCPUTemperature()
{
    UINTN CPUTemperature=0;
    //set Global Control Register CR2C bit 0 = 0 to select Pin function as PECI.
    IoWrite8(IoIndexPort, 0x2C);
    IoWrite8(IoDataPort, 0x00);
    //Set Bank 7 Index 03h = 10h for Agent 0 and Domain 0
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7);
    IoWrite8(baseAddress+0x05, 0x03);
    IoWrite8(baseAddress+0x06, 0x10);
    //Set Bank 7 Index 01h = 95h to enable PECI3.0
    IoWrite8(baseAddress+0x05, 0x01);
    IoWrite8(baseAddress+0x06, 0x95);
    //Set Bank 2 Index 00h = 10h to select PECI Agent 0 as CPUFAN monitoring source
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 2); 
    IoWrite8(baseAddress+0x05, 0x00); 
    IoWrite8(baseAddress+0x06, 0x10);   
    //Set Bank 0 Index AEh bit 0 to 1 to enable PECI Agent 0 mode
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 0); 
    IoWrite8(baseAddress+0x05, 0xAE); 
    IoWrite8(baseAddress+0x06, 0x01);    
    //Get temperature raw data from Bank 7 Index 20h 
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7); 
    IoWrite8(baseAddress+0x05, 0x20); 
    CPUTemperature= IoRead8(baseAddress+0x06);
    return CPUTemperature;
}

CPU电压读取

对于CPU电压的读取很简单,只需要读取相应的寄存器就能获得当前电压值,需要注意的是需要对读取到的数值进行转换才能得到正确值,即Detected Voltage = Reading * 0.008 V

在这里插入图片描述

UINTN ReadCPUVoltage()
{
    UINTN CPUVoltage=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0x80);
    CPUVoltage=IoRead8(baseAddress+0x06);
    return CPUVoltage*8;//此处不作除法运算是会丢失精度小数的实现在下面给出
}

//实现小数显示:将整数和小数部分分开显示,加上标点符号
void Specialtreatment(UINTN CPUVoltage)
{
    UINTN IntegerPart=0,FractionalPart=0;
    IntegerPart=CPUVoltage/1000;
    if (IntegerPart<10)
        ShellPrintEx(18,1,L"%d",IntegerPart);
    else if (IntegerPart<100)
        {
            ShellPrintEx(17,1,L"%d",IntegerPart);
            ShellPrintEx(25,1,L"This is impossible!!!!");
        }
    FractionalPart=CPUVoltage%1000; 
    ShellPrintEx(20,1,L"%03d",FractionalPart);
    return;
}

CPU风扇转速读取

对于CPU风扇转速的读取同样是简单的,而NCT5581D提供了两种方式去读取转速值
在这里插入图片描述

我采用的是第一种方式:

UINTN ReadCPUFanRPM()
{
    UINTN CPUFanRPM=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0xb2);
    CPUFanRPM=IoRead8(baseAddress+0x06);
    IoWrite8(baseAddress+0x05, 0xb3);
    CPUFanRPM=CPUFanRPM*32+((IoRead8(baseAddress+0x06)<<3)>>3);//根据上图说明读取13bit的数值
    return 1350000/CPUFanRPM;
}

CPU风扇转速控制

CPU风扇转速控制如下图所示,其中输出模式上拉还是开漏以及pwm的输出频率因为尚未对此功能进行了解,所以我让它保持原来的值,不作修改。

在这里插入图片描述

而对应的真实输出的占空比关系如下图:

在这里插入图片描述

   //Select bank2 index02 bit[7:4]->00000010=02h
   IoWrite8(baseAddress+0x05, 0x4E);
   IoWrite8(baseAddress+0x06, 2);
   IoWrite8(baseAddress+0x05, 0x02);
   IoWrite8(baseAddress+0x06, 0x02); 
   //set PWM output(Duty)bank2 index09 bit[7:0] 00-FF
   fanSpeedDuty=Transfer(Argv[3][0])*16+Transfer(Argv[3][1]);//将从命令行获取的参数进行转换成目标占空比输入值
   ShellPrintEx(-1,-1,L"%H%2X",fanSpeedDuty);
   IoWrite8(baseAddress+0x05, 0x09);
   IoWrite8(baseAddress+0x06, fanSpeedDuty); 

目标四实现

WATCH DOG TIMER的工作原理很简单,当开启了WATCH DOG功能后向计数器寄存器写入相应值,计时器便会进行倒数,当数值没有被打断递减而减为0后,会向相应位置触发一个低电平跳变,当硬件连接将重启时序连入,便可实现机器的重启。
去配置一个WATCH DOG TIMER以WDT1为例需要进行以下配置:

在这里插入图片描述

个人对于目标三和四的实现

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib/BaseLibInternals.h>

#define  IoIndexPort 0x2E
#define  IoDataPort  0x2F

UINTN baseAddress=0;

void StartToReadRC()
{
    IoWrite8(IoIndexPort, 0x87);
    IoWrite8(IoIndexPort, 0x87);
    return;
}

UINTN ReadCPUTemperature()
{
    UINTN CPUTemperature=0;
    //set Global Control Register CR2C bit 0 = 0 to select Pin function as PECI.
    IoWrite8(IoIndexPort, 0x2C);
    IoWrite8(IoDataPort, 0x00);
    //Set Bank 7 Index 03h = 10h for Agent 0 and Domain 0
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7);
    IoWrite8(baseAddress+0x05, 0x03);
    IoWrite8(baseAddress+0x06, 0x10);
    //Set Bank 7 Index 01h = 95h to enable PECI3.0
    IoWrite8(baseAddress+0x05, 0x01);
    IoWrite8(baseAddress+0x06, 0x95);
    //Set Bank 2 Index 00h = 10h to select PECI Agent 0 as CPUFAN monitoring source
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 2); 
    IoWrite8(baseAddress+0x05, 0x00); 
    IoWrite8(baseAddress+0x06, 0x10);   
    //Set Bank 0 Index AEh bit 0 to 1 to enable PECI Agent 0 mode
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 0); 
    IoWrite8(baseAddress+0x05, 0xAE); 
    IoWrite8(baseAddress+0x06, 0x01);    
    //Get temperature raw data from Bank 7 Index 20h 
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7); 
    IoWrite8(baseAddress+0x05, 0x20); 
    CPUTemperature= IoRead8(baseAddress+0x06);
    return CPUTemperature;


}

UINTN ReadCPUVoltage()
{
    UINTN CPUVoltage=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0x80);
    CPUVoltage=IoRead8(baseAddress+0x06);
    return CPUVoltage*8;
}

UINTN ReadCPUFanRPM()
{
    UINTN CPUFanRPM=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0xb2);
    CPUFanRPM=IoRead8(baseAddress+0x06);
    IoWrite8(baseAddress+0x05, 0xb3);
    CPUFanRPM=CPUFanRPM*32+((IoRead8(baseAddress+0x06)<<3)>>3);
    return 1350000/CPUFanRPM;
}


UINTN ReadSYSTemperature()
{
    UINTN SYSTemperature=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 0);
    IoWrite8(baseAddress+0x05, 0xAE);
    if(IoRead8(baseAddress+0x06)!=0)
        IoWrite8(baseAddress+0x06, 0);
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 6);
    IoWrite8(baseAddress+0x05, 0x22);
    IoWrite8(baseAddress+0x06, 0x02);
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 1);
    IoWrite8(baseAddress+0x05, 0x50);
    SYSTemperature=IoRead8(baseAddress+0x06);
    IoWrite8(baseAddress+0x05, 0x51);
    SYSTemperature=SYSTemperature*2+(IoRead8(baseAddress+0x06)>>7);
    SYSTemperature=SYSTemperature/2;
    return SYSTemperature;
}

void Specialtreatment(UINTN CPUVoltage)
{
    UINTN IntegerPart=0,FractionalPart=0;
    IntegerPart=CPUVoltage/1000;
    if (IntegerPart<10)
        ShellPrintEx(18,1,L"%d",IntegerPart);
    else if (IntegerPart<100)
        {
            ShellPrintEx(17,1,L"%d",IntegerPart);
            ShellPrintEx(25,1,L"This is impossible!!!!");
        }
    FractionalPart=CPUVoltage%1000; 
    ShellPrintEx(20,1,L"%03d",FractionalPart);
    return;
}

UINT8 Transfer(IN CHAR16 value)
{
    UINT8 number=0;

    if(value>47&&value<58)
        number=(UINT8)(value-48);
    else if(value>64&&value<71)
        number=(UINT8)(value-55);
    else if(value>96&&value<103)
        number=(UINT8)(value-87);
    else
        Print(L"The format of the parameter value is incorrect!\n");
    return number;
}

UINT8 isNumber(IN CHAR16 value)
{

    if(value>47&&value<58)
        return 1;
    else
        return 0;
}

// void GPIOinit()
// {
//     //select logical device 09 to enable gpio24
//     IoWrite8(IoIndexPort, 0x07);
//     IoWrite8(IoDataPort, 0x09);
//     IoWrite8(IoIndexPort, 0x30);
//     IoWrite8(IoDataPort, 0x01);

//     //SET function to GPIO
//     IoWrite8(IoIndexPort, 0x1B);
//     IoWrite8(IoDataPort, IoRead8(IoDataPort)&EF);
//     IoWrite8(IoIndexPort, 0x27);
//     IoWrite8(IoDataPort, IoRead8(IoDataPort)&F7);


//     // set gpio24 output
//     IoWrite8(IoIndexPort, 0x07);
//     IoWrite8(IoDataPort, 0x09);
//     IoWrite8(IoIndexPort, 0xE0);
//     IoWrite8(IoDataPort, IoRead8(IoDataPort)&EF);

//     return ;
// }

//同样通过检测命令行参数个数分为不同的功能
INTN
EFIAPI
ShellAppMain(
    IN UINTN Argc,
    IN CHAR16** Argv
)
{   
    UINTN counter = 0;
    UINTN CPUTemperature=0;
    UINTN CPUVoltage=0;
    UINTN CPUFanRPM=0;
    UINTN SYSTemperature=0;
    UINT8 fanSpeedDuty=0;
    UINT8 num=0;
    UINTN sec=0;
	if (Argc<=1)
    {
        Print(L"The input parament is not right!");
        return EFI_SUCCESS;
    }
    else if(Argv[1][0]=='D')//D代表display即显示CPU温度、电压、风扇转速
    {
        //extern mode 
        StartToReadRC();
        //read logical device B 60h and 61h
        IoWrite8(IoIndexPort, 0x07);
        IoWrite8(IoDataPort, 0x0B);
        IoWrite8(IoIndexPort, 0x60);
        baseAddress=IoRead8(IoDataPort);
        IoWrite8(IoIndexPort, 0x61);
        baseAddress=baseAddress*256+IoRead8(IoDataPort);
        gST->ConOut->ClearScreen (gST->ConOut);
        ShellPrintEx(0,0,L"CPU Temperature:");//16
        ShellPrintEx(0,1,L"CPU Voltage:");
        ShellPrintEx(0,2,L"CPU Fan RPM:");
        ShellPrintEx(0,3,L"SYS Temperature:");
        ShellPrintEx(19,1,L".");

        while(counter<12580)//循环显示12580次
        {
            ShellPrintEx(18,0,L"      ");
            CPUTemperature=ReadCPUTemperature();
            ShellPrintEx(18,0,L"%d",CPUTemperature);
            ShellPrintEx(18,1,L" .    ");
            CPUVoltage=ReadCPUVoltage();
            Specialtreatment(CPUVoltage);
            ShellPrintEx(18,2,L"      ");
            CPUFanRPM=ReadCPUFanRPM();
            ShellPrintEx(18,2,L"%d",CPUFanRPM);
            ShellPrintEx(18,3,L"      ");
            SYSTemperature=ReadSYSTemperature();
            ShellPrintEx(18,3,L"%d",SYSTemperature);
            counter++;
        }
        gST->ConOut->ClearScreen (gST->ConOut);

        return EFI_SUCCESS;
    }
    else if(Argv[1][0]=='C')//C是风扇转速控制
    {
        //extern mode 
        StartToReadRC();
        //read logical device B 60h and 61h
        IoWrite8(IoIndexPort, 0x07);
        IoWrite8(IoDataPort, 0x0B);
        IoWrite8(IoIndexPort, 0x60);
        baseAddress=IoRead8(IoDataPort);
        IoWrite8(IoIndexPort, 0x61);
        baseAddress=baseAddress*256+IoRead8(IoDataPort);
        //configuration registers CR24 bit3(keep)
        //configuration registers outputfrequency bank0,index02(keep)
        //Fan Control Mode Select bank2 index02 bit[7:4]
        if(Argc<4||(Argv[2][0]!='M'&& Argv[2][0]!='S'&& Argv[2][0]!='W'))
            {
                Print(L"The input parament to select fan mode and control fan speed is not right!");
                return EFI_SUCCESS;
            }
        else if(Argv[2][0]=='M')
            {
                //Select bank2 index02 bit[7:4]->00000010=02h
                IoWrite8(baseAddress+0x05, 0x4E);
                IoWrite8(baseAddress+0x06, 2);
                IoWrite8(baseAddress+0x05, 0x02);
                IoWrite8(baseAddress+0x06, 0x02); 
                //set PWM output(Duty)bank2 index09 bit[7:0] 00-FF
                fanSpeedDuty=Transfer(Argv[3][0])*16+Transfer(Argv[3][1]);
                ShellPrintEx(-1,-1,L"%H%2X",fanSpeedDuty);
                IoWrite8(baseAddress+0x05, 0x09);
                IoWrite8(baseAddress+0x06, fanSpeedDuty); 
                return EFI_SUCCESS;
            }
        else if(Argv[2][0]=='S')//这里是后面实现SmartFan需要
        {
            //Trying
            return EFI_SUCCESS;
        }
        else      
            return EFI_SUCCESS;
    }
    else if(Argv[1][0]=='W')//W代表看门狗实现
        {
            //
            while(isNumber(Argv[2][num]))
            {
                sec=sec*10+(Argv[2][num]-48);
                num++;
            }
            ShellPrintEx(-1,-1,L"setting second is :%H%d\n",sec);
            if(sec>255)
            {
                ShellPrintEx(-1,-1,L"setting second :%H%d is too big!Change the unit to make it!\n",sec);
                return EFI_SUCCESS;
            }
            StartToReadRC();
            IoWrite8(IoIndexPort, 0x07);
            IoWrite8(IoDataPort, 0x0D);
            IoWrite8(IoIndexPort, 0xF0);
            IoWrite8(IoDataPort, 0x00);

            IoWrite8(IoIndexPort, 0x07);
            IoWrite8(IoDataPort, 0x08);

            //active WDT1
            IoWrite8(IoIndexPort, 0x30);
            IoWrite8(IoDataPort,(IoRead8(IoDataPort)&0xEF)|0X01);
            //set second mode 
            IoWrite8(IoIndexPort, 0xF5);
            IoWrite8(IoDataPort,(IoRead8(IoDataPort)&0xF7)|0x02);
            IoWrite8(IoIndexPort, 0xF8);//WDT3
            IoWrite8(IoDataPort,(IoRead8(IoDataPort)&0xF6)|0x02);
            //set count down value
            IoWrite8(IoIndexPort, 0xF6);
            IoWrite8(IoDataPort, (UINT8)sec);
            IoWrite8(IoIndexPort, 0xF9);//WDT3
            IoWrite8(IoDataPort, (UINT8)sec);
            //read count down value
            gST->ConOut->ClearScreen (gST->ConOut);
            ShellPrintEx(0,0,L"WDT1 is counting down:   s");
           // ShellPrintEx(0,1,L"WDT3 is counting down:   s");
            
            while(1)
            {
                ShellPrintEx(21,0,L"   ");
                IoWrite8(IoIndexPort, 0xF6);
                ShellPrintEx(21,0,L"%H%d",IoRead8(IoDataPort));
                // ShellPrintEx(21,1,L"   ");
                // IoWrite8(IoIndexPort, 0xF9);
                // ShellPrintEx(21,1,L"%H%d",IoRead8(IoDataPort));
            }
            

            return EFI_SUCCESS;
        }
    else
        {
            Print(L"The input parament is not right!");
            return EFI_SUCCESS;
        }
}

至此,对NCT5581D的读取写入操作有了一定的了解,也是对edk2的一些应用的了解。
路漫漫其修远兮吾将上下而求索!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42951246/article/details/121266189

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan