MFC开关量输出发脉冲实例

使用QueryPerformanceCounter(QPC)高精度计数器CPU 硬件计数器支持 微秒级,适合:步进电机脉冲、运动控制、IO 实时控制,完全满足发脉冲。采取子线程发脉冲,主线程(界面)实时响应停止按钮,设定转一圈脉冲数为3200个,程序功能步进电机正反转,可输入所要转的圈数,按停止按钮立即停止。

6408_demo_vcDlg.h

cpp 复制代码
#if !defined(AFX_6408_DEMO_VCDLG_H__6F86E9CB_0D17_495C_A3C1_6DD1860E26B1__INCLUDED_)
#define AFX_6408_DEMO_VCDLG_H__6F86E9CB_0D17_495C_A3C1_6DD1860E26B1__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif

#include "PC60002K.H"

class CMy6408_demo_vcDlg : public CDialog
{
public:
	CMy6408_demo_vcDlg(CWnd* pParent = NULL);
	enum { IDD = IDD_MY6408_DEMO_VC_DIALOG };

	// 线程函数
	static UINT SendPulseThread(LPVOID pParam);

protected:
	HICON m_hIcon;
	virtual void DoDataExchange(CDataExchange* pDX);
	virtual BOOL OnInitDialog();

	//{{AFX_MSG(CMy6408_demo_vcDlg)
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnBtnRunPos();
	afx_msg void OnBtnRunNeg();
	afx_msg void OnBtnStop();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()

private:
	unsigned short m_nAddr;
	double m_fCircle;
	volatile int m_nStopFlag;    // 停止标志
	CString m_strCircle;
	int m_nDir;                  // 方向 1正转 0反转
	CWinThread* m_pThread;       // 脉冲线程
};

#endif

6408_demo_vcDlg.cpp

cpp 复制代码
#include "stdafx.h"
#include "6408_demo_vc.h"
#include "6408_demo_vcDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define PUL_CH          11
#define DIR_CH          12
#define PUL_PER_CIRCLE  3200
#define PULSE_HIGH_US   156
#define PULSE_LOW_US    156

// 微秒延时
void DelayUs(unsigned long us, volatile int *pStopFlag)
{
	LARGE_INTEGER freq, now, end;
	QueryPerformanceFrequency(&freq);
	QueryPerformanceCounter(&now);
	end.QuadPart = now.QuadPart + freq.QuadPart * us / 1000000;

	while(1)
	{
		if(*pStopFlag) return;
		QueryPerformanceCounter(&now);
		if(now.QuadPart >= end.QuadPart) break;
	}
}

// ==============================================
// ? 子线程:发脉冲(界面不会卡!停止立即响应!)
// ==============================================
UINT CMy6408_demo_vcDlg::SendPulseThread(LPVOID pParam)
{
	CMy6408_demo_vcDlg* pDlg = (CMy6408_demo_vcDlg*)pParam;

	long total = (long)(pDlg->m_fCircle * PUL_PER_CIRCLE);
	DO6408Bit(pDlg->m_nAddr, DIR_CH, pDlg->m_nDir);

	for(long i=0; i<total; i++)
	{
		if(pDlg->m_nStopFlag) break;

		DO6408Bit(pDlg->m_nAddr, PUL_CH, 1);
		DelayUs(PULSE_HIGH_US, &pDlg->m_nStopFlag);

		DO6408Bit(pDlg->m_nAddr, PUL_CH, 0);
		DelayUs(PULSE_LOW_US, &pDlg->m_nStopFlag);
	}

	DO6408Bit(pDlg->m_nAddr, PUL_CH, 0);
	pDlg->m_pThread = NULL;
	return 0;
}

// ===================== 下面全是原来代码,只改按钮函数 =====================

CMy6408_demo_vcDlg::CMy6408_demo_vcDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMy6408_demo_vcDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_nAddr = 0x100;
	m_fCircle = 1.0;
	m_nStopFlag = 0;
	m_strCircle = _T("1.0");
	m_pThread = NULL;
}

void CMy6408_demo_vcDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT_CIRCLE, m_strCircle);
}

BEGIN_MESSAGE_MAP(CMy6408_demo_vcDlg, CDialog)
	//{{AFX_MSG_MAP(CMy6408_demo_vcDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BTN_POS, OnBtnRunPos)
	ON_BN_CLICKED(IDC_BTN_NEG, OnBtnRunNeg)
	ON_BN_CLICKED(IDC_BTN_STOP, OnBtnStop)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CMy6408_demo_vcDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);

	if(!OpenDevice6K())
		AfxMessageBox(_T("打开PC6408板卡失败!"));

	m_strCircle = _T("1.0");
	UpdateData(FALSE);

	DO6408Bit(m_nAddr,DIR_CH,0);
	DO6408Bit(m_nAddr,PUL_CH,0);
	return TRUE;
}

void CMy6408_demo_vcDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	CDialog::OnSysCommand(nID,lParam);
}

void CMy6408_demo_vcDlg::OnPaint()
{
	if (IsIconic()){
		CPaintDC dc(this);
		SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);
		int cx=GetSystemMetrics(SM_CXICON);
		int cy=GetSystemMetrics(SM_CYICON);
		CRect rc;GetClientRect(&rc);
		int x=(rc.Width()-cx)/2,y=(rc.Height()-cy)/2;
		dc.DrawIcon(x,y,m_hIcon);
	}else CDialog::OnPaint();
}

HCURSOR CMy6408_demo_vcDlg::OnQueryDragIcon()
{
	return (HCURSOR)m_hIcon;
}

// ===================== 正转(启动线程) =====================
void CMy6408_demo_vcDlg::OnBtnRunPos()
{
	if(m_pThread != NULL) return; // 防止重复启动

	UpdateData(TRUE);
	m_fCircle = atof(m_strCircle);
	if(m_fCircle <= 0){
		AfxMessageBox(_T("圈数必须大于0!"));
		return;
	}

	m_nDir = 1;
	m_nStopFlag = 0;

	// ? 启动子线程发脉冲(界面不卡!)
	m_pThread = AfxBeginThread(SendPulseThread, this);
}

// ===================== 反转(启动线程) =====================
void CMy6408_demo_vcDlg::OnBtnRunNeg()
{
	if(m_pThread != NULL) return;

	UpdateData(TRUE);
	m_fCircle = atof(m_strCircle);
	if(m_fCircle <= 0){
		AfxMessageBox(_T("圈数必须大于0!"));
		return;
	}

	m_nDir = 0;
	m_nStopFlag = 0;

	m_pThread = AfxBeginThread(SendPulseThread, this);
}

// ===================== 停止(? 立即生效!) =====================
void CMy6408_demo_vcDlg::OnBtnStop()
{
	m_nStopFlag = 1;
	if(m_pThread)
	{
		WaitForSingleObject(m_pThread->m_hThread, 100);
		m_pThread = NULL;
	}
	DO6408Bit(m_nAddr, PUL_CH, 0);
}
相关推荐
qq_1998868723 分钟前
第7板块·第3节:CUTLASS 的 GEMM 实现与优化策略
c++·人工智能·gpu算力·cuda
hetao173383726 分钟前
2026-09-17 hetao1733837 的刷题记录
c++·算法
shada2 小时前
Boost.Process v2 在旧内核上报 `assign: Bad file descriptor`
c++
别动我齐刘海2 小时前
ROS2 Jazzy + C++ 实战路线——基础学习2
c++·人工智能·vscode·python·学习·机器学习·机器人
朝朝辞暮i3 小时前
C++第 2 课
c++
代码中介商3 小时前
C++ 预约系统实战(三):服务端实现——libevent 事件驱动与业务路由
c++·json·c/s
布莱克6054 小时前
C++中的内存分区详解
c++
All for pursuit.5 小时前
【链表-9】146.LRU缓存
数据结构·c++·算法·leetcode
j7~6 小时前
【C++微服务项目开发脚手架】(接口篇一)gflags + gtest + spdlog 接口学习笔记
c++·学习·gtest·项目开发·spdlog·gflags·c++项目微服务开发脚手架
星期八不上发条6 小时前
智能指针是什么?使用场景,循环引用解决办法,面试回答
开发语言·c++·stl