我用过的最快的CPU是AMD 的K7。那时候我在一家主板厂上班,刚入行不久,上面要我测试AMDK7的问题。我跑去测试部门借到了一颗K7,小心翼翼的插在板子上(那时候CPU还有针脚),盖上风扇,上电发现风扇转动一下就停了,再次实验还是这样的现象。急忙找来老员工请教缘由,只见他推了推风扇,然后就诊断到:你没有锁扣,这个CPU烧掉了。听到这里,我大吃一惊,惊异于CPU如此脆弱,另外刚参加工作就出了这样的“篓子”心中也着实没底。不过去测试部门说明一下情况,把这个CPU挂账给我,后来也没有什么问题。只是从此之后,见到了AMD的CPU更加提心吊胆。从此,我也知道温度是关乎CPU生死的参数。 因此,这次使用 Arduino尝试制作一个能够将CPU温度,CPU使用率,内存使用率和风扇转速显示出来的小装置。选择使用Leonrado来实现,因为它本身有原生的USB支持,方便实现和电脑的接口。 通常的USB 设备在 Windows下都需要安装驱动,在实际使用中,相同的USB驱动在不同的Windows版本间有兼容性的问题。并且为了兼容32位和64位Windows,还需要准备2套驱动。此外,从Windows 7 X64开始,微软强制要求进行数字签名,给 USB 驱动的发行制造了很大困难。 鉴于此,无需特别驱动的 USB 设备需求强烈。总结下来主要有下面三种简单易行的方式: 1. 直接使用 LibUSB 这样的第三方库。优点是可以实现跨平台,一套代码可以在Linux和Windows 下运行。缺点是对于开发者要求较高,仍然会遇到签名上的问题; 2. 产品开发上使用 USB 串口芯片,借用芯片厂商提供的驱动。优点是开发简单,调试方便,对于产品来说,直接串口通讯即可,对于上位机来说也是串口编程。缺点是有时候芯片厂商提供的驱动也并不完美,客户在安装驱动时会有一些麻烦; 3. 直接设计免驱动的固件。Windows中集成了很多USB设备的驱动,最常见的就是 Mass Storage ,我们插入 U盘之后,系统自动加载驱动,无需额外安装即可正常工作。 对于第三种方法来说,最标准的就是声明自身为 HID 设备,Windows内置了对于 HID 设备驱动,时我们可以定义设备为 HID RAW 格式,这样可以很方便的使用。本文介绍如何使用Arduinio Leonrado制作一个能够显示当前CPU 温度和风扇转速的设备,而这个设备是通过 USBHID和上位机进行通讯的。 Arduino代码使用NicoHood 的HID库来完成【参考1】。此外,还选用了RSCG12864B01的 12864 LCD模块,使用这个模块的原因是:接线少,相对于其他12864动辄十根以上的接线,这个模块只需要I2C的 SDA和SCL,外加一个BUSY即可。这样我们可以讲注意力集中在“显示什么”,而不是“如何显示”。同时,桃子老师提供了这个模块的库,用起来也十分方便【参考2】。硬件连接如下,可以看到硬件部分足够简单:
接下来介绍软件部分,相比之下比硬件复杂多了。 我们通过使用一个开源的程序 open Hardware Monitor 来完获得 CPU温度和风扇转速【参考2】。这里先介绍一下获得CPU温度的原理:很久之前的主板,是采用在CPUSlot 下面放置热敏二极管的方式来获得CPU温度,这种方法存在显而易见的缺点就是不准确。有一段时间,我更新BIOS给测试Team做验证,他们测试不出来其他问题就喜欢报告“当前CPU温度不准”。后来我也每次都根据他们给出来的偏差进行修改,过了一段大约他们也觉得太无聊就不再把这项列为必须需修改的项目。随着功耗的增长,CPU几乎是系统中功耗最高的部件(特别是只有集成显卡的主板),系统需要严格监视温度防止烧毁,当温度达到设定的阈值时,首先是增加风扇转速进行散热,其次会采取各种方法降低功耗,比如,自动降频,直至关机。后来以Intel为首的处理器公司将这样的温度传感器集成在CPU中。同时,引入了平台环境式控制接口(PlatformEnvironment Control Interface,英文缩写PECI), 这样使得外围的 Super IO(台式机主板)/ EmbeddedController(笔记本)/BMC (服务器)都有机会获得当前的CPU温度。 除了CPU温度,还有系统温度和风扇转速之类的系统状态值。这些数值和主板的设计相关,BIOS中也没有统一的接口,例如:很多主机BIOS中有系统温度这个栏位,但是具体这个温度传感器放置在什么位置如何取值只有硬件工程师和BIOS工程师知道,不同主板差别很大。网上一直流传着从 WMI中可以获得当前CPU温度的说法。但是经过笔者研究,因为这个并非微软强制要求,因此绝大多数主板并不支持这个功能。也正是因为这一点,通过自己编程直接读取温度很难实现通用。试想一下,你在一台机器上编译调试后,想给女神露一手,结果确实无法抓取结果会是多么可悲的情景。 具体代码如下: [kenrobot_code]1. Arduino
#include "HID-Project.h"
#include "RSCG12864B.h"
//判断当前是否为 BUSY 的状态Pin
const int BUSYPIN = 7;
//初始化 LCD
RAYLIDLCD LCD12864(BUSYPIN);
//HID 的缓冲区
uint8_t rawhidData[64];
//接收到数据的结构体,总长度和缓冲区一样大
typedef struct sdata
{
unsignedint CPUTemperature; //CPU温度
unsigned int CPULoad; //CPU负载
unsigned int MemoryLoad; //内存负载
unsigned int FanSpeed; //风扇转速
unsigned char Reserved[56]; //暂时无用
};
//String: "CPU 温度"
char cpuStr1[] = {'C','P','U',' ',0XCE, 0XC2, 0XB6, 0XC8, 0x00};
//String: "℃"
char cpuStr2[] = {0xA1,0xE6,0x00};
//String: "CPU负载"
char cpuStr3[] = {'C','P','U',' ',0xB8, 0xBA, 0xD4, 0xD8, 0x00,};
//String: "内存负载"
char RamStr1[] = {0xC4, 0xDA, 0xB4, 0xE6, 0xB8, 0xBA, 0xD4, 0xD8,0x00,};
//String: "N/A"
char NAStr[] = {'N','/','A', 0x00};
//String: "风扇转速"
char FanSpeedStr1[] = {0xB7, 0xE7, 0xC9, 0xC8, 0xD7, 0xAA, 0xCB, 0xD9,0x00};
//String: ""
char FanSpeedStr2[] = {'R','P','M',0x00};
void setup() {
LCD12864.begin();
LCD12864.setBrightness(200);
LCD12864.clear();
//初始化 HID 设备
RawHID.begin(rawhidData, sizeof(rawhidData));
}
//在 12864 上显示String
void showstring(int x, int y, String str)
{
char buffer[33];
str.toCharArray(buffer,str.length());
LCD12864.print(x,y, buffer, VLARGE);
}
//在字符串s末尾填充空格,保证总长度等于16个字符
String fillblank(String s)
{
intt=s.length();
for(int i=0;i<17-t;i++)
{
s=s+' ';
}
return s;
}
void loop() {
char tempstring[12];
float tempfloat;
sdata Data; //接收缓冲
char *p;
String s;
//检查是否有新数据
auto bytesAvailable = RawHID.available();
if(bytesAvailable)
{
//直接将收到的64Byte填充到结构体中
p=(char *)&Data;
while (bytesAvailable--) {
*p=RawHID.read();
p++;
}
Serial.print(Data.CPUTemperature);
Serial.print(" ");
Serial.print(Data.CPULoad);
Serial.print(" ");
Serial.print(Data.MemoryLoad);
Serial.print(" ");
Serial.println(Data.FanSpeed);
//处理CPU温度
s=cpuStr1;
if (0xFFFF!=Data.CPUTemperature) {
itoa(Data.CPUTemperature, tempstring, 10);
s=s+tempstring+cpuStr2;
}
else //如果收到为0xFFFF表示当前无此项
s=s+NAStr;
s=fillblank(s);
showstring(0, 0, s);
//处理CPU使用率
s=cpuStr3;
if (0xFFFF!=Data.CPULoad) {
//例如:当前系统上CPU 使用率为49.83%,
//这里将会收到十进制的4983
tempfloat=((float) Data.CPULoad) / 100;
dtostrf(tempfloat,2,2,tempstring);
s=s+tempstring+'%';
}
else //如果收到为0xFFFF表示当前无此项
s=s+NAStr;
s=fillblank(s);
showstring(0, 16, s);
//处理当前内存使用率
s=RamStr1;
if (0xFFFF!=Data.MemoryLoad) {
tempfloat=((float) Data.MemoryLoad) / 100;
dtostrf(tempfloat,2,2,tempstring);
s=s+tempstring+'%';
}
else //如果收到为0xFFFF表示当前无此项
s=s+NAStr;
s=fillblank(s);
showstring(0, 32, s);
//处理风扇转速
s=FanSpeedStr1;
if (0xFFFF!=Data.FanSpeed) {
itoa(Data.FanSpeed, tempstring, 10);
s=s+tempstring+FanSpeedStr2;
}
else //如果收到为0xFFFF表示当前无此项
s=s+NAStr;
s=fillblank(s);
showstring(0, 48, s);
}
delay(500);
}[/kenrobot_code] 2.
|