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);
}
相关推荐
ChillCoding1 小时前
更新中:C++ STL库,查找排序(基础算法),数据结构,数学算法,竞赛相关基础
数据结构·c++·算法
智者知已应修善业1 小时前
【51单片机使用IO组赋值方法实现无源蜂鸣器响时LED12亮不响时34亮】2024-3-7
c++·经验分享·笔记·算法·51单片机
.千余1 小时前
【C++】深挖STL list底层:解迭代器与节点存储逻辑
开发语言·c++·笔记·学习·其他
雪落漂泊1 小时前
C++ 继承与多态(上)
开发语言·c++
聆风吟º1 小时前
【C++11新章】列表初始化详解
开发语言·c++·列表初始化
alwaysrun1 小时前
C++之灵活易用的YAML解析库yaml-cpp
c++·后端·程序员
Shadow(⊙o⊙)2 小时前
进程间通信0.0-pipe()匿名管道,详细分析进程池调度队列执行逻辑,进程池模拟实现。
linux·运维·服务器·开发语言·c++
lcj25112 小时前
【list】【手撕 STL】List 容器全解析!迭代器 / 增删改查 / 去重排序,面试必背的核心考点!
c++·面试·list
指尖的爷2 小时前
C++头文件的作用
开发语言·c++