由于工作的需求,需要做一个工控机的GPIO的输出,也就不可避免的接触到WinRing IO了。基本上要控制这种通用型的GPIO,有两种方式,一种是winRing0,一种就是WinIO了。但由于WinIO需要让系统进行windows签名模式,这就比较烦人了。但关于WinIO我之前也有一个项目设计到了,我也稍微记录了一下,大家如果感兴趣,可以去看看,地址为最新WinIO驱动测试GPIO口。所以,大家比较常用的还是WinRing0的方式。
所谓WinRing0 是一个完全免费开源的组件( BSD License ), 允许windows程序直接对I/O端口进行存取操作。通过使用一种内核模式的设备驱动器和其它几种底层编程技巧,它绕过了Windows系统的保护机制。
注意在使用的时候一定要注意版本:WinRing0 是一个强大的驱动程序,可以直接通过这个驱动读取CPUID,CPU MSR,TSC,IO port,PCI config等硬件信息。在WinRing0_1_3_1b版本之前是一个开源项目,之后就关闭了。而且目前的最新版本WinRing0 2.0.0.20已经阉割掉了全部的写寄存器的功能,之留下了读取CPUID,CPU MSR,TSC的功能。不能不说是一大遗憾。
2.0.0.20版本已经出来八年多了,比较明显的变化就是移除了WinRing0x64.dll,只能编译32位的程序,不然load dll会Fail。WinRing0使用还是比较简单的,只需要在自己的程序里面include OlsDef.h和OlsApiInit.h,然后Call WinRing0.dll里面的函数就好了。
所以,我使用的还是WinRing0-1.3.0的版本,那样我才能去设置对应的寄存器。
注意:我的编程环境是:win10+Qt 5.15+VS2019。编程语言是Qt.
首先,你应该要有一份关于GPIO 关于IO的寄存器的说明,这边我就不便给出了。但基本跟网上差不多,但可能会因为不同厂家,会有不同的文档。其次,就是要下载一下winring0_1.3.0的版本的驱动。CSDN的下载网址,希望有积分的能够支持下。
有两种调用库的方式,一种是静态库的调用,一种是动态库的调用。我这里使用的是静态库的调用。这边编译的时候,就可以把WinRing0x64.lib这个静态库编译到我的插件里面,发布程序的时候,就不用带着WinRing0x64.lib这个文件了。而你如果是动态调用的时候,发布程序的时候,你就需要带上那个文件了。静态库有好处也有坏处把。好处是发布程序的时候比较方便,不用带那么多个库。坏处是,如果程序一旦更新,与这个静态库相关的代码都得重新编译。
关于Qt调用静态库的方式,我这里就不详解了。网上一大堆,找一下即可。(当你编译程序的时候,出现错误的问题是:无法找到WinRing0x64.lib这个库的话,那么就是你引用这个库的方式或是路径不对,自查一下即可。)
LIBS += -L$$PROJECT_LIBDIR_BOARD
LIBS += -lWinRing0x64
上面的PROJECT_LIBDIR_BOARD是一个WinRing0x64.lib的路径,你可以弄弄绝对路径,或是相对路径,当然,最好还是相对路径。
#include
#include
#include "OlsApi.h"
你这个头文件"OlsApi.h"这个也要在pro文件中给出。
INCLUDEPATH += ./lib
这个lib的下面就是这些引用到的头文件。
WinRing0的初始化很简单,这里演示的是静态库的初始化,动态库的初始化也差不多,可以自己找找。
if (!InitializeOls()) {
ret = false;
}
简单一句话,就初始化了。
注意,后面在析构函数中,要记得结束这个初始化。
DeinitializeOls();
//InitSuperIO
WriteIoPortByte(0x4e, 0x87);
WriteIoPortByte(0x4e, 0x01);
WriteIoPortByte(0x4e, 0x55);
WriteIoPortByte(0x4e, 0xaa);
网上的很多资料都是0x2e。自己要注意甄别自己的版本是什么。
//select logic device
WriteIoPortByte(0x4e, 0x07);
WriteIoPortByte(0x4f, 0x07);
WriteIoPortByte(0x4e, 0x2c);
WriteIoPortByte(0x4f, 0x89);
WriteIoPortByte(0x4e, 0x27);
Data = ReadIoPortByte(0x4f);
Data |= BIT7;
WriteIoPortByte(0x4e, 0x27);
WriteIoPortByte(0x4f, Data);
这几句话的意思是先将要写入的0x27这个寄存器写入0x4e中,然后,读出4f中的内容。去做对应位的操作。然后,再对0x3f这个寄存器写入Data。这里的第4句其实意义不是很大的。
//退出Super IO 芯片配置模式
WriteIoPortByte(0x4e, 0x02);
WriteIoPortByte(0x4f, 0x02);
输入的话,就根据厂商给的那个文档,自行读取对应的寄存器的值即可。做个示例:
#define BIT0 0x01
#define BIT1 0x02
#define BIT2 0x04
#define BIT3 0x08
#define BIT4 0x10
#define BIT5 0x20
#define BIT6 0x40
#define BIT7 0x80
#define INBIT0 0xFE
#define INBIT1 0xFD
#define INBIT2 0xFB
#define INBIT3 0xF7
#define INBIT4 0xEF
#define INBIT5 0xDF
#define INBIT6 0xBF
#define INBIT7 0x7F
Data = ReadIoPortByte(0xA02);
quint8 n1 = (Data&BIT7)==BIT7?0:0x01;
输出的话,就设置一下对应的位即可。
操作跟上面写寄存器一样,只是寄存器不一样而已。
基本做了上述的操作,你就可以完美解决winring0访问IO的问题,祝一切顺利。有什么问题,可以私聊,但可能没办法及时回复,见谅~ 有关于新的版本更新,以及新的寄存器的更新的话,可以在评论区留言,让遇到这个问题的人能少走一些弯路。觉得有用,给个点赞哦~
WinRing0如何使用
[业余知识:PC 硬件监控]使用WinRing0 2.0.0.20读取CPU温度
GPIO-TOOL-WIN