近碰到一個關于芯片測試過程中的問題,這顆芯片是用在筆記本端口上,筆記本客戶那邊會有一個壓力測試,就是頻繁的電腦電源狀態切換,S0(正常使用的開機狀態),S3(睡眠模式),S4(休眠模式)以及S5(關機模式)。
當然,主要是客戶在壓力測試過程中,發現了芯片會不正常的死鎖,客戶那邊將機臺寄回來,那么該如何復現呢?客戶那邊會有自己的一套壓力測試系統,不過會測試很多東西,不太方便給我們,而且每一次循環耗時比較久。那么,能不能自己搭建一套控制電腦睡眠,休眠,關機以及喚醒的程序呢?
上面講的是一個應用背景,告訴大家這其實也是有需求的,只是平時不太用而已,將其記錄下來:
首先,從電腦開機狀態S0切換到S3,S4甚至是S5,都是比較容易實現的,見下面代碼:
Application.SetSuspendState(PowerState.Suspend, false, false);//從S0進入S3
Application.SetSuspendState(PowerState.Hibernate,false,false);//從S0進入S4
Process.Start("shutdown","/s /t 0"); // 參數 /s 的意思是要關閉計算機
// 參數 /t 0 的意思是告訴計算機 0 秒之后執行命令
Process.Start("shutdown", "/r /t 0"); // 參數 /r 的意思是要重新啟動計算機
只要調用上述語句即可實現從S0到其他的電源狀態,那么反過來喚醒呢?
喚醒的難點在于:當處于S3,S4以及S5的狀態下,我的上位機程序是不會運行的,因此,在上位機軟件的定時喚醒也是沒法工作的。那么筆記本客戶那邊是怎么操作的呢?他們會通過底層的EC控制來顯示上述的功能,可是,我們是不知道底層EC的接口,而且,我們需要一個通用的程式,那要怎么實現呢?
在筆記本的設計中,在S3,S4,S5通常不是所有的東西都會關掉,通常會有一個硬件定時器還在開著,如果我們能操作這個定時器,那是不是就可以實現我們想要的功能呢?
可以調用下面的兩個函數,即CreateWaitableTimer以及SetWaitableTimer,這兩個函數就可以控制電腦里面開的硬件定時器,當然這個硬件定時器是CPU里面的還是EC里面的,我也不太清楚,沒研究過,如果有大神研究過,可以留言,我也學習學習。
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume);
另外,需要說明的一點是,使用這個定時器也是有條件的,你需要先設置筆記本,"Control Panel > Power Options > Change Plan Settings > Change Advanced Power Settings > Sleep > Allow Wake Timers", 使能定時器喚醒,還有就是,"Control Panel > Power Options > Change Plan Settings > Change Advanced Power Settings > Brad / Additional Settings > Require a password on wakeup",關閉喚醒需要密碼。
完成上面的設置,其實已經可以實現電腦從S3,S4,S5喚醒了,但在我使用的過程中,其實還碰到了一個問題,就是喚醒之后,屏幕不亮,你就會誤認為沒有喚醒,因此我增加了控制鼠標移動的命令,這樣,喚醒之后,屏幕就會亮起。
[DllImport("user32.dll")]
public static extern void mouse_event(Int32 dwFlags, Int32 dx, Int32 dy, Int32 dwData, UIntPtr dwExtraInfo);
mouse_event(0x0001,0,1,0,UIntPtr.Zero);
mouse_event(0x0001, 0, -1, 0, UIntPtr.Zero);
另外還有一點需要注意,就是筆記本從S0->S3/S4/S5->S0這個循環里面,S0,S3/S4/S5這幾個狀態的停留時間一定要足夠,因為,每個筆記本的完全進入各個狀態的時間會不一樣,比如,我用我自己的筆記本,這幾個狀態的停留時間要至少20s,否則,筆記本還沒有完全進入就要退出,就會導致,電腦把WaitableTimer關掉,而筆記本還沒有喚醒,導致程式死鎖。而新的剛買的筆記本,只需要設置10s即可完全進入。
廢話不多說,直接上代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
namespace AutoSwitchGUI
{
public partial class AutoSwitchGUI : Form
{
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume);
[DllImport("kernel32.dll")]
public static extern uint SetThreadExecutionState(uint esFlags);
[DllImport("user32.dll")]
public static extern void mouse_event(Int32 dwFlags, Int32 dx, Int32 dy, Int32 dwData, UIntPtr dwExtraInfo);
//public event EventHandler Woken;
private BackgroundWorker bgWorker=new BackgroundWorker();
public struct auto_switch_gui_status_t
{
public bool test_status;
public UInt64 test_times_cnt;
public UInt64 test_times;
public byte cur_state;
public int s0_duration;
public int s3_duration;
}
public auto_switch_gui_status_t auto_switch_status;
public AutoSwitchGUI()
{
InitializeComponent();
bgWorker.DoWork +=new DoWorkEventHandler(bgWorker_Dowork);
bgWorker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
private void bgWorker_Dowork(object sender, DoWorkEventArgs e)
{
long waketime=(long)e.Argument;
using (SafeWaitHandle handle=CreateWaitableTimer(IntPtr.Zero, true, this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
{
if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
{
using (EventWaitHandle wh=new EventWaitHandle(false, EventResetMode.AutoReset))
{
wh.SafeWaitHandle=handle;
wh.WaitOne();
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
mouse_event(0x0001,0,1,0,UIntPtr.Zero);
mouse_event(0x0001, 0, -1, 0, UIntPtr.Zero);
auto_switch_status.test_times_cnt++;
TestTimes.Text=auto_switch_status.test_times_cnt.ToString();
SystemTimer.Interval=auto_switch_status.s0_duration * 1000;
SystemTimer.Start();
}
public void SetWakeUpTime(UInt64 time)
{
bgWorker.RunWorkerAsync(System.DateTime.Now.AddSeconds(time).ToFileTime());
}
private void StartButton_Click(object sender, EventArgs e)
{
try
{
auto_switch_status.test_times=UInt64.Parse(SetTestTimes.Text);
auto_switch_status.s0_duration=int.Parse(S0Duration.Text);
auto_switch_status.s3_duration=int.Parse(S3Duration.Text);
if (auto_switch_status.test_times > 0)
{
//SetThreadExecutionState(0x00000001 | 0x00000002 | 0x80000000 | 0x00000040);
TestStatus.BackColor=Color.Green;
auto_switch_status.test_status=true;
TestTimes.Text="0";
auto_switch_status.test_times_cnt=0;
SystemTimer.Interval=auto_switch_status.s0_duration*1000;
auto_switch_status.cur_state=0;
SystemTimer.Start();
return;
}
}
catch
{
}
MessageBox.Show("Configuration Failed!");
}
private void StopButton_Click(object sender, EventArgs e)
{
SystemTimer.Stop();
auto_switch_status.test_status=true;
TestStatus.BackColor=Color.Red;
}
private void SystemTimer_Tick(object sender, EventArgs e)
{
if (auto_switch_status.cur_state==0)
{
auto_switch_status.cur_state=0;
SystemTimer.Stop();
if (auto_switch_status.test_times_cnt >=auto_switch_status.test_times)
{
}
else
{
SetWakeUpTime((UInt64)auto_switch_status.s3_duration);
Application.SetSuspendState(PowerState.Suspend, false, false);
//Application.SetSuspendState(PowerState.Hibernate, false, false);
}
}
else if (auto_switch_status.cur_state==1)
{
auto_switch_status.test_times_cnt++;
TestTimes.Text=auto_switch_status.test_times_cnt.ToString();
auto_switch_status.cur_state=0;
SendKeys.Send(" ");
MessageInfo.Text +="TEST1\r\n";
}
}
}
}
另外聲明,關于SetWaitableTimer和CreateWaitableTimer我是參考如下鏈接的:
希望可以幫到大家,上面代碼在我自己的筆記本以及客戶的筆記本是可以適用的。
.鍵盤或鼠標所使用的中斷沒有被設置成可用于喚醒,解決的方法是進入BIOS的“Power Management Setup”設置界面,將“PM Events”選項下的相關設備的IRQ喚醒功能都打開,即設置IRQ3、IRQ4、IRQ5、IRQ6、IRQ12、IRQ14和IRQ15為“Enabled”。
2.設備驅動程序發生沖突,建議卸載老的驅動程序;驅動程序與所安裝的硬件不兼容,請更換或升級驅動程序。
3.主板BIOS版本較老,可下載新版本的BIOS文件,并對主板BIOS進行刷新升級。
4.操作系統存在問題,或許安裝補丁程序可以解決問題。
5.如果休眠后時間稍微長一點就不能喚醒,則很可能是CPU風扇停轉,而CPU又未按要求進入對應的休眠模式,引起CPU溫度過高,從而導致電腦無法正常工作。這種現象大多發生在對CPU溫度和CPU風扇有監控功能的電腦系統中。
不能喚醒的親們趕快去試試以上的方法吧~
點擊更改計劃設置
休眠一般出現在長時間不操作電腦或者筆記本電池電量不足的時候進行保護性的休眠,開機后可以進入上次工作狀態。
但是休眠的缺點是恢復的時間較長,一方面長時間不操作電腦最好關閉電腦節省電量,另一方面筆記本的話使用電池對電池和正在進行的工作也會產生一定的影響,為了減少出乎意料的事情發生,所以建議在使用電腦的時候使用睡眠功能,或者直接關閉顯示屏即可.
可以使用工具關閉休眠模式。
果您的計算機已經設定了從不進入睡眠狀態,但是電源選項設定為幾分鐘后關閉屏幕,當屏幕關閉后仍然會進入睡眠狀態。
或是部分機種有支持關閉屏幕的快捷鍵,當按下快捷鍵關閉屏幕后也會進入睡眠狀態。
1. 請先確認您的計算機是否為Windows 10且支持新式待機(Modern Standby)。
如何判斷計算機是否支持新式待機,參考win10新式待機Modern Standby介紹
2. 如果您的計算機有支持Modern Standby模式,這個現象是正常的。
由于Microsoft的原生設計,
從Windows 10開始導入Modern Standby,當屏幕關閉后隨即會啟動Modern Standby,系統將會進入”快速睡眠”。
● Modern Standby與 S3(睡眠)差異
在 Windows 10 中,有兩種適用于 Pc 的電源模式: S3 和新式待機。
S3 電源型號是一種較舊的標準,不能從現代設備上獲得消費者預期的瞬間。
新式待機版能夠利用新式芯片組的所有功能,并且可在目前的平板電腦和電腦范圍內集成。
新式待機的第一次迭代是連接備用的,它最初在 Windows 8 和 Windows 8.1 中附帶。
新式待機擴展了 Windows 3.x 連接備用概念,允許在組件選擇中靈活,并使 OS 能夠在備用模式下管理網絡連接。
在任何新式備待機系統上,系統在處于待機狀態時仍保留在 S0(正常工作狀態),這允許以下方案工作:
1. 后臺活動
2. 從低功耗狀態恢復速度更快
在處于待機狀態的系統上,基于特定網絡模式的喚醒也可能由操作系統設置,使應用能夠接收最新的內容,例如傳入電子郵件、VoIP 呼叫或新聞文章。