MFC高精度方波发生器实现

高精度定时器编写一个占空比可调的方波发生器时间精确到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;
}

运行程序

相关推荐
Cinema KI9 小时前
一键定位,哈希桶的极速魔法
数据结构·c++·算法·哈希算法
D_evil__9 小时前
【Effective Modern C++】第三章 转向现代C++:8. 优先选用nullptr,而非0或NULL
c++
楼田莉子9 小时前
Linux系统小项目——“主从设计模式”进程池
linux·服务器·开发语言·c++·vscode·学习
从此不归路10 小时前
Qt5 进阶【7】网络请求与 REST API 实战:QNetworkAccessManager 深度应用
开发语言·c++·qt
试剂小课堂 Pro10 小时前
mPEG-Silane:mPEG链单端接三乙氧基硅的亲水性硅烷偶联剂
java·c语言·网络·c++·python·tomcat
mjhcsp10 小时前
P14992 取模(题解)
c++
老歌老听老掉牙10 小时前
16宫格属性分析系统:打造专业级科学数据可视化工具
c++·qt·可视化
橘子师兄10 小时前
C++AI大模型接入SDK—API接入大模型思路
开发语言·数据结构·c++·人工智能
CSDN_RTKLIB10 小时前
【字符编码】拷贝的是字符还是字节序列
c++
历程里程碑11 小时前
哈希3 : 最长连续序列
java·数据结构·c++·python·算法·leetcode·tornado