高精度定时器编写一个占空比可调的方波发生器时间精确到0.01秒,两个编辑框分别输入高电平时间、低电时间,两个文本控件分别实时显示高电平时间和低电平时间,点击启动按钮高电平时间从0.00开始计时达到编辑框输入的高电平时间停止,低电平时间从0.00开始计时达到编辑框输入的低电平时间停止,再从高电平时间从0.00开始计时循环往复,点击停止按钮时停止计时文本控件显示当前实时间,另外有两个高低电平指示灯文本控件。
我们在资源编辑器中添加:
IDC_EDIT_HIGH:高电平时间编辑框
IDC_EDIT_LOW:低电平时间编辑框
IDC_STATIC_HIGH:高电平当前时间显示
IDC_STATIC_LOW:低电平当前时间显示
IDC_STATIC_HIGH_DISPLAY:高电平指示灯
IDC_STATIC_LOW_DISPLAY:低电平指示灯
我们定义两个状态:高电平状态和低电平状态。在每个状态中,我们分别计时,当达到设定时间后切换状态,并将另一个状态的计时器重置。状态0:高电平计时状态使用m_elapsedHigh计时,当计时时间大于等于设定高电平时间,则切换到低电平状态,并重置低电平计时器。状态1:低电平计时状态使用m_elapsedLow计时,当计时时间大于等于设定低电平时间,则切换到高电平状态,并重置高电平计时器。使用一个线程来不断检查当前状态,并更新当前状态的计时值,同时检查是否超时。
MyTimer_add_5Dlg.h
cpp
// MyTimer_add_5Dlg.h : header file
//
#include "CElapsed.h"
#if !defined(AFX_MYTIMER_ADD_5DLG_H__3DDDEF88_51D3_46A7_8FDC_54A483F962CD__INCLUDED_)
#define AFX_MYTIMER_ADD_5DLG_H__3DDDEF88_51D3_46A7_8FDC_54A483F962CD__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CMyTimer_add_5Dlg dialog
class CMyTimer_add_5Dlg : public CDialog
{
// Construction
public:
CMyTimer_add_5Dlg(CWnd* pParent = NULL); // standard constructor
// 去掉析构函数声明,使用默认析构函数
// Dialog Data
//{{AFX_DATA(CMyTimer_add_5Dlg)
enum { IDD = IDD_MYTIMER_ADD_5_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMyTimer_add_5Dlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
virtual BOOL DestroyWindow(); // 添加这个来替代析构函数
//}}AFX_VIRTUAL
void UpdateDisplay();
void StopTimer();
// 专门更新指示灯的函数
void UpdateIndicators(int nState);
// 新增成员变量
enum {
STATE_HIGH = 0, // 高电平状态
STATE_LOW = 1 // 低电平状态
};
CElapsed m_elapsedTimer; // 高精度计时器
CElapsed m_stateTimer; // 状态计时器
double m_dHighTime; // 高电平时间(秒)
double m_dLowTime; // 低电平时间(秒)
double m_dStateElapsed; // 当前状态已过去的时间(秒)
int m_nCurrentState; // 当前状态
bool m_bRunning; // 计时器是否在运行
bool m_bThreadRunning; // 更新线程是否在运行
static UINT TimerThreadProc(LPVOID pParam); // 计时器线程函数
CWinThread* m_pTimerThread; // 计时器线程指针
// Implementation
protected:
HICON m_hIcon;
private:
// 新增:两个消息定义
enum {
WM_UPDATE_DISPLAY = WM_USER + 100, // 更新时间显示
WM_UPDATE_INDICATORS = WM_USER + 101 // 更新指示灯
};
// 添加用于减少闪烁的变量
CString m_strLastHighTime;
CString m_strLastLowTime;
// Generated message map functions
//{{AFX_MSG(CMyTimer_add_5Dlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnButtonStart();
afx_msg void OnButtonStop();
afx_msg LRESULT OnUpdateDisplay(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnUpdateIndicators(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_MYTIMER_ADD_5DLG_H__3DDDEF88_51D3_46A7_8FDC_54A483F962CD__INCLUDED_)
MyTimer_add_5Dlg.cpp
cpp
// MyTimer_add_5Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "MyTimer_add_5.h"
#include "MyTimer_add_5Dlg.h"
#include <math.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// ... (原有的CAboutDlg类保持不变)
/////////////////////////////////////////////////////////////////////////////
// CMyTimer_add_5Dlg dialog
CMyTimer_add_5Dlg::CMyTimer_add_5Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CMyTimer_add_5Dlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMyTimer_add_5Dlg)
m_bRunning = false;
m_bThreadRunning = false;
m_pTimerThread = NULL;
m_dHighTime = 0.0;
m_dLowTime = 0.0;
m_dStateElapsed = 0.0;
m_nCurrentState = STATE_HIGH; // 初始状态为高电平
//}}AFX_DATA_INIT
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
// 初始化用于减少闪烁的变量
m_strLastHighTime = _T("");
m_strLastLowTime = _T("");
}
// 添加DestroyWindow函数来清理资源
BOOL CMyTimer_add_5Dlg::DestroyWindow()
{
// 确保线程停止
m_bThreadRunning = false;
if (m_pTimerThread)
{
WaitForSingleObject(m_pTimerThread->m_hThread, 500);
}
return CDialog::DestroyWindow();
return CDialog::DestroyWindow();
}
void CMyTimer_add_5Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyTimer_add_5Dlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
BEGIN_MESSAGE_MAP(CMyTimer_add_5Dlg, CDialog)
//{{AFX_MSG_MAP(CMyTimer_add_5Dlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_START, OnButtonStart)
ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
ON_MESSAGE(WM_UPDATE_DISPLAY, OnUpdateDisplay)
ON_MESSAGE(WM_UPDATE_INDICATORS, OnUpdateIndicators)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyTimer_add_5Dlg message handlers
BOOL CMyTimer_add_5Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// 初始化指示灯状态
UpdateIndicators(STATE_HIGH);
return TRUE;
}
void CMyTimer_add_5Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
void CMyTimer_add_5Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
// 使用双缓冲绘图减少闪烁
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap);
// 绘制背景
dcMem.FillSolidRect(&rect, GetSysColor(COLOR_3DFACE));
// 让父类绘制对话框内容
CWnd::DefWindowProc(WM_PAINT, (WPARAM)dcMem.m_hDC, 0);
// 将内存DC内容复制到屏幕DC
dc.BitBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, SRCCOPY);
dcMem.SelectObject(pOldBitmap);
bitmap.DeleteObject();
dcMem.DeleteDC();
}
}
HCURSOR CMyTimer_add_5Dlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CMyTimer_add_5Dlg::OnButtonStart()
{
if (m_bRunning) return;
// 获取高电平时间
CString strHighTime;
GetDlgItemText(IDC_EDIT_HIGH, strHighTime);
m_dHighTime = atof(strHighTime);
// 获取低电平时间
CString strLowTime;
GetDlgItemText(IDC_EDIT_LOW, strLowTime);
m_dLowTime = atof(strLowTime);
if (m_dHighTime <= 0.0 || m_dLowTime <= 0.0)
{
MessageBox(_T("请输入有效的时间(大于0)"), _T("错误"), MB_ICONERROR);
return;
}
// 重置状态
m_nCurrentState = STATE_HIGH;
m_dStateElapsed = 0.0;
m_bRunning = true;
m_bThreadRunning = true;
// 重置用于减少闪烁的变量
m_strLastHighTime = _T("");
m_strLastLowTime = _T("");
// 启动计时器
m_elapsedTimer.Start();
m_stateTimer.Start();
// 初始化显示
UpdateDisplay();
// 启动计时器线程
m_pTimerThread = AfxBeginThread(TimerThreadProc, this, THREAD_PRIORITY_NORMAL);
// 禁用编辑框,防止运行时修改
GetDlgItem(IDC_EDIT_HIGH)->EnableWindow(FALSE);
GetDlgItem(IDC_EDIT_LOW)->EnableWindow(FALSE);
}
void CMyTimer_add_5Dlg::OnButtonStop()
{
if (!m_bRunning) return;
// 停止计时器
StopTimer();
// 获取当前状态计时器的时间并更新显示
m_dStateElapsed = m_stateTimer.NowSec();
UpdateDisplay();
// 启用编辑框
GetDlgItem(IDC_EDIT_HIGH)->EnableWindow(TRUE);
GetDlgItem(IDC_EDIT_LOW)->EnableWindow(TRUE);
}
void CMyTimer_add_5Dlg::StopTimer()
{
if (!m_bRunning) return;
m_bThreadRunning = false;
m_bRunning = false;
if (m_pTimerThread)
{
// 等待线程退出
WaitForSingleObject(m_pTimerThread->m_hThread, 1000);
m_pTimerThread = NULL;
}
}
// 更新时间显示消息处理
LRESULT CMyTimer_add_5Dlg::OnUpdateDisplay(WPARAM wParam, LPARAM lParam)
{
// 使用简单的参数传递方式
// wParam: 状态值 (0:高电平, 1:低电平)
// lParam: 时间值乘以1000后的整数部分
int nState = (int)wParam;
double dTime = (double)lParam / 1000.0;
m_nCurrentState = nState;
m_dStateElapsed = dTime;
UpdateDisplay();
return 0;
}
// 更新指示灯消息处理
LRESULT CMyTimer_add_5Dlg::OnUpdateIndicators(WPARAM wParam, LPARAM lParam)
{
// wParam直接就是状态值
UpdateIndicators((int)wParam);
return 0;
}
// 更新时间显示 - 优化版本,减少闪烁
void CMyTimer_add_5Dlg::UpdateDisplay()
{
CString strHighTime, strLowTime;
if (m_nCurrentState == STATE_HIGH)
{
// 高电平状态:显示高电平计时
strHighTime.Format(_T("%.2f"), min(m_dStateElapsed, m_dHighTime));
strLowTime = _T("0.00");
}
else
{
// 低电平状态:显示低电平计时
strLowTime.Format(_T("%.2f"), min(m_dStateElapsed, m_dLowTime));
strHighTime = _T("0.00");
}
// 只有当文本发生变化时才更新,减少闪烁
if (strHighTime != m_strLastHighTime)
{
SetDlgItemText(IDC_STATIC_HIGH_DISPLAY, strHighTime);
m_strLastHighTime = strHighTime;
}
if (strLowTime != m_strLastLowTime)
{
SetDlgItemText(IDC_STATIC_LOW_DISPLAY, strLowTime);
m_strLastLowTime = strLowTime;
}
}
// 更新指示灯 - 优化版本,减少闪烁
void CMyTimer_add_5Dlg::UpdateIndicators(int nState)
{
static int nLastState = -1; // 记录上次状态
// 只有状态改变时才更新,避免闪烁
if (nState != nLastState)
{
if (nState == STATE_HIGH)
{
// 高电平激活
SetDlgItemText(IDC_STATIC_HIGH_INDICATOR, _T("● 高电平"));
SetDlgItemText(IDC_STATIC_LOW_INDICATOR, _T("○ 低电平"));
}
else
{
// 低电平激活
SetDlgItemText(IDC_STATIC_HIGH_INDICATOR, _T("○ 高电平"));
SetDlgItemText(IDC_STATIC_LOW_INDICATOR, _T("● 低电平"));
}
nLastState = nState;
}
}
// 计时器线程函数
UINT CMyTimer_add_5Dlg::TimerThreadProc(LPVOID pParam)
{
CMyTimer_add_5Dlg* pDlg = (CMyTimer_add_5Dlg*)pParam;
const double UPDATE_INTERVAL = 0.01; // 0.01秒更新间隔
int nLastState = -1; // 记录上次状态
while (pDlg->m_bThreadRunning)
{
double dCurrentTime = pDlg->m_elapsedTimer.NowSec();
double dCycleTime = pDlg->m_dHighTime + pDlg->m_dLowTime;
if (dCycleTime <= 0.0)
{
Sleep(10);
continue;
}
// 计算在一个周期内的时间
double dTimeInCycle = fmod(dCurrentTime, dCycleTime);
// 判断当前状态
int nCurrentState;
double dStateElapsed;
if (dTimeInCycle < pDlg->m_dHighTime)
{
// 高电平状态
nCurrentState = STATE_HIGH;
dStateElapsed = dTimeInCycle;
}
else
{
// 低电平状态
nCurrentState = STATE_LOW;
dStateElapsed = dTimeInCycle - pDlg->m_dHighTime;
}
// 如果状态改变,发送指示灯更新消息并重置状态计时器
if (nCurrentState != nLastState)
{
pDlg->m_stateTimer.Start(); // 重置状态计时器
// 发送指示灯更新消息 - 使用PostMessage确保不阻塞线程
pDlg->PostMessage(WM_UPDATE_INDICATORS, (WPARAM)nCurrentState, 0);
nLastState = nCurrentState;
}
// 获取状态计时器的值
dStateElapsed = pDlg->m_stateTimer.NowSec();
// 发送时间显示更新消息 - 使用PostMessage确保不阻塞线程
pDlg->PostMessage(WM_UPDATE_DISPLAY,
(WPARAM)nCurrentState,
(LPARAM)(int)(dStateElapsed * 1000));
// 精确等待
double dWaitTime = UPDATE_INTERVAL - fmod(pDlg->m_elapsedTimer.NowSec(), UPDATE_INTERVAL);
if (dWaitTime > 0 && dWaitTime < UPDATE_INTERVAL)
{
Sleep(static_cast<DWORD>(dWaitTime * 1000));
}
else
{
Sleep(static_cast<DWORD>(UPDATE_INTERVAL * 1000));
}
}
return 0;
}
运行程序
