一: _TMP
根據 ACPI 規格書, _TMP 會傳回 thermal zone 的溫度. 但是只有少數電腦實作這項功能, 大部分電腦沒有實作 , 所以傳回的數值是定值. 在 Windows 作業系統, 你可以使用 MSAcpi_ThermalZoneTemperature 得到溫度.

二: 確認自己電腦有無 ACPI Embedded Controller
打開裝置管理員, 在系統裝置下看看有沒有 ACPI Embedded Controller 之類的字. 如果沒有, 代表你的電腦沒有 ACPI Embedded Controller.

三: 得到 ACPI Embedded Controller 的資源
在 Windows 下,查看 ACPI tables 的程式很難找到. 所以我使用 Linux 的 acpidump 指令
acpidump -o think_a
把結果放在 think_a
The ACPI Component Architecture Project 網站可下載 iASL compiler and Windows ACPI tools , 安裝此工具. 執行下列程式.
acpixtract -a think_a
從 think_a 取出全部表格. 會產生許多 dat 檔案, 檢查看看你有沒有 ECDT.dat 及 DSDT.dat . 如果你沒有 ECDT.dat , 則你的電腦不支援 ACPI Embedded Controller.
iasl -d ECDT.dat
反組譯 ECDT 表, 得到 ECDT.dsl. 以記事本開啟 ECDT.dsl, 你可以看到 Command/Status Register 及 Data Register 所使用的系統資源. 以 ThinkPad R50e 為例, Command/Status Register 是 IO port 0x66, Data Register 是 IO port 0x62 .

四: 讀取 ACPI Embedded Controller 重點提示
ACPI 規格書 Embedded Controller 章節已詳細介紹程式實作方法, 到 Advanced Configuration and Power Interface 網站下載規格書.
...
case IOCTL_Embedded_Control_Read:
 if(inBufLength < 1 || outBufLength < 1){
  status = STATUS_BUFFER_TOO_SMALL;
  break;
 }
 num = waitInputEmpty(fdoData);
 if(num == 0xFF){//如果 Controller 忙碌則離開
  status = STATUS_DEVICE_BUSY;
  break;
 }
//寫 0x80(讀命令) 到 command register
 _outp(fdoData->command_status,0x80);
 num = waitInputEmpty(fdoData);
 if(num == 0xFF){
  status = STATUS_DEVICE_BUSY;
  break;
 }
 ptr3 = buffer;
 _outp(fdoData->data_register,*ptr3);//把想要讀取的位址寫到 data register
 *ptr3 = num;
 Irp->IoStatus.Information = 1;
 break;
case IOCTL_Port_Read_Data:
 if(outBufLength < 1){
  status = STATUS_BUFFER_TOO_SMALL;
  break;
 }
 ptr3 = buffer;
 *ptr3 = (UCHAR)_inp(fdoData->data_register);//讀取 data register
 Irp->IoStatus.Information = 1;
 break;
...

先對驅動程式使用 IOCTL_Embedded_Control_Read, 1毫秒之後, 再用 IOCTL_Port_Read_Data 即可得到指定 address 的數值. 因為 CPU 速度比 Embedded Controller 快, 所以這裡等1毫秒 .
UCHAR waitInputEmpty(PFDO_DATA fdoData){
 UCHAR val,i,j;
 for(i=0; i < 253; ++i){
  val = (UCHAR)_inp(fdoData->command_status);//read status register
  if ((val & 2) == 0) return i;//如果 val 的 bit1 = 0, 表示 input buffer empty, CPU 可以送指令給 Embedded Controller.
  j = i + 3;//耗 CPU 時間
 }
 return 0xFF;
}

C# 部分程式
/* return * 0: success
* 1: embed_control is null
* 2: DeviceIoControl fail
*/
public byte EmbedControlRead(byte offset, ref byte answer){
 uint junk;
 bool win;
 byte[] trans, res;
 junk = 0;
 if (embed_control == IntPtr.Zero) return 1;
 trans = new byte[1];
 res = new byte[1];
 trans[0] = offset;//送出 offset 給 Embedded Controller
 win = DeviceIoControl(embed_control, IOCTL_Embedded_Control_Read, trans, 1, res, 1, ref junk, IntPtr.Zero);
 if (!win){
  errCode = GetLastError();
  return 2;
 }
 Application.DoEvents();
 Thread.Sleep(1);//等 Embedded Controller 執行指令
 win = DeviceIoControl(embed_control, IOCTL_Port_Read_Data, null, 0, trans, 1, ref junk, IntPtr.Zero);
//read data register
 if (!win){
  errCode = GetLastError();
  return 2;
 }
 answer = trans[0];
 return 0;
}

五: ThinkPad R50e 溫度感測器
反組譯 DSDT.dat , 得到 DSDT.dsl.
iasl -d DSDT.dat
用記事本開啟 DSDT.dsl , 查找 Device (EC) . 在 Field (ECOR, ByteAcc, NoLock, Preserve) 裡面, 可發現其中1段:
Offset (0x78),
TMP0, 8,
TMP1, 8,
TMP2, 8,
TMP3, 8,
TMP4, 8,
TMP5, 8,
TMP6, 8,
TMP7, 8,

故 R50e 最多有8個溫度感測器, 在 Embedded Controller 的位址是 0x78 ~ 0x7F. 想得到 TMP0 溫度 就輸入
EmbedControlRead(0x78,ref ans);
R50e TMP4 ~ TMP7 沒裝溫度感測器, 其數值永為 0x80.