最近碰到一個關于芯片測試過程中的問題,這顆芯片是用在筆記本端口上,筆記本客戶那邊會有一個壓力測試,就是頻繁的電腦電源狀态切換,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我是參考如下鍊接的:
希望可以幫到大家,上面代碼在我自己的筆記本以及客戶的筆記本是可以适用的。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!