kehuadong

频谱模拟


#pragma once

#include <Windows.h>

#define DEFAULT_THREAD_TERMINATED_TIME 2000

class CAutoThread {
public:
	// 构造(初始化)
	CAutoThread(DWORD dwThreadTerminatedTime = DEFAULT_THREAD_TERMINATED_TIME) {
		m_bTerminated = TRUE;
		m_dwExitCode = (DWORD)-1;
		m_hThreadHandle = NULL;
		m_dwThreadTerminatedTime = dwThreadTerminatedTime;
	};

	// 析构(有需要时自动结束线程)
	virtual ~CAutoThread() { ThreadStop(); }

	// 启动线程
	BOOL ThreadStart() {
		ThreadTerminated(m_dwThreadTerminatedTime);

		if (NULL == m_hThreadHandle) {
			m_bTerminated = FALSE;
			m_hThreadHandle = ::CreateThread(NULL, 0, CAutoThread::ThreadProc, (LPVOID)this, 0, &m_dwThreadId);
		}

		return (m_hThreadHandle != NULL);
	}

	// 结束线程
	BOOL ThreadStop() {
		ThreadTerminated(m_dwThreadTerminatedTime);
		return (m_hThreadHandle == NULL);
	}

	// 设置线程优先级
	BOOL CeSetPriority(int nPriority) {
		if (m_hThreadHandle) {
#ifdef _WIN32_WCE
			return CeSetThreadPriority(m_hThreadHandle, nPriority);
#endif
		}
		return FALSE;
	}

	// 结束线程
	BOOL ThreadTerminated(DWORD dwMilliSeconds = DEFAULT_THREAD_TERMINATED_TIME) {
		m_bTerminated = TRUE;
		return WaitThreadComplete(dwMilliSeconds);
	}

	// 等待线程结束
	BOOL WaitThreadComplete(DWORD dwMilliSeconds) {
		if (m_hThreadHandle) {
			if (::WaitForSingleObject(m_hThreadHandle, dwMilliSeconds) == WAIT_OBJECT_0) {
				// thread dead
				::CloseHandle(m_hThreadHandle);
				m_hThreadHandle = NULL;
				return TRUE;
			}
			else {
				ForceTerminated();
			}
		}
		return FALSE;
	}

	// 强制结束线程
	BOOL ForceTerminated() {
		BOOL bReturn = FALSE;
		if (m_hThreadHandle) {
			bReturn = ::TerminateThread(m_hThreadHandle, (DWORD)-1);	 // terminate abnormal
			m_dwExitCode = -1;
			::CloseHandle(m_hThreadHandle);
			m_hThreadHandle = NULL;
			m_bTerminated = TRUE;
		}
		return bReturn;
	}

	// 获取线程ID
	DWORD GetThreadId() const { return m_dwThreadId; }

	// 线程是否结束
	BOOL IsTerminated() const { return m_bTerminated; }

	// 获取线程句柄
	HANDLE GetThreadHandle() const { return m_hThreadHandle; }

	// 获取线程退出码
	BOOL GetExitCodeThread(LPDWORD lpExitCode) {
		if (!m_hThreadHandle) {
			*lpExitCode = m_dwExitCode;
			return TRUE;
		}
		else {
			return FALSE;
		}
	};

	// 投递线程消息
	BOOL PostThreadMessage(DWORD u4Msg, WPARAM wParam, LPARAM lParam) {
		return ::PostThreadMessage(m_dwThreadId, u4Msg, wParam, lParam);
	}

private:
	virtual DWORD ThreadRun() = 0;	// User have to implement this function.

	static DWORD WINAPI ThreadProc(LPVOID dParam) {
		CAutoThread* pThreadPtr = (CAutoThread*)dParam;
		pThreadPtr->m_dwExitCode = pThreadPtr->ThreadRun();
		::ExitThread(pThreadPtr->m_dwExitCode);
		return pThreadPtr->m_dwExitCode;
	};

protected:
	// 线程是否处于终止状态
	BOOL m_bTerminated;

private:
	// 线程句柄
	HANDLE m_hThreadHandle;

	// 线程ID
	DWORD m_dwThreadId;

	// 线程退出码
	DWORD m_dwExitCode;

	// 等待线程退出的最长事件
	DWORD m_dwThreadTerminatedTime;
};

#define DEFAULT_SERIAL_BUFF_LEN 1024

#define DEFAULT_SERIAL_READ_WAIT 1

#define SAFE_DELETE(a) \
	{                  \
		if (a) {       \
			delete a;  \
			a = NULL;  \
		}              \
	}

#define SAFE_DELETE_ARRAY(a) \
	{                        \
		if (a) {             \
			delete[] a;      \
			a = NULL;        \
		}                    \
	}

// 用于接收串口数据的回调对象
class CSerialObject {
public:
	virtual ~CSerialObject() {};

	virtual BOOL OnDataRecv(BYTE* pData, DWORD dwDataLen) = 0;
};


class CSerial : public CAutoThread {
public:
	CSerial(DWORD dwBufLen = DEFAULT_SERIAL_BUFF_LEN,
		DWORD dwReadWait = DEFAULT_SERIAL_READ_WAIT);

	virtual ~CSerial(void);

	BOOL InitSerial(CSerialObject* pObject);

	BOOL Open(BYTE uComPort, DWORD dwBaudRate, BYTE ByteSize = 8, BYTE Parity = NOPARITY, BYTE StopBits = ONESTOPBIT);

	BOOL ChangeBaud(DWORD dwBaudRate, BYTE ByteSize = 8, BYTE Parity = NOPARITY, BYTE StopBits = ONESTOPBIT);

	BOOL Close();

	BOOL WriteData(LPCVOID lpBuf, DWORD dwBufLen);

	BOOL IsOpen() const;

	BOOL ClearUart();

	BOOL IsTimeOut(DWORD preTime);

	// 接收数据的缓存区
	BYTE* m_pBuf;

	BOOL m_SendDataState;

	DWORD m_PreTime;

protected:
	DWORD ThreadRun();

private:

	// 读取数据后,等待多长时间(毫秒)
	DWORD m_dwReadWait;

	// 每次最多读取多少数据
	DWORD m_dwBufLen;

	// 串口设备句柄
	HANDLE m_hComDev;

	// 接收串口数据的对象
	CSerialObject* m_pObject;
};

 

 

 

#include <stdio.h>

#include <thread>
using namespace std;

#include "CSerial.h"

#define RETAILMSG(cond,printf_exp)

#define MIN_BUF_ARRAY_SIZE 2

// 构造串口
CSerial::CSerial(DWORD dwBufLen, DWORD dwReadWait)
	: CAutoThread()
	, m_hComDev(INVALID_HANDLE_VALUE)
	, m_dwBufLen(dwBufLen)
	, m_pBuf(NULL)
	, m_dwReadWait(dwReadWait)
{
	if (m_dwBufLen < MIN_BUF_ARRAY_SIZE) {
		m_dwBufLen = MIN_BUF_ARRAY_SIZE;
	}

	m_pBuf = new BYTE[m_dwBufLen];

	if (m_pBuf) {
		memset(m_pBuf, 0, m_dwBufLen);
	}

	m_SendDataState = FALSE;
}

// 析构串口
CSerial::~CSerial(void) {
	Close();
	SAFE_DELETE_ARRAY(m_pBuf);
}


// 初始化回调函数
BOOL CSerial::InitSerial(CSerialObject* pObject) {	
	m_pObject = pObject;

	return TRUE;
}

// 打开串口
BOOL CSerial::Open(BYTE uComPort, DWORD dwBaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits) {
	BOOL bRet = FALSE;

	// 先关闭已经打开的串口
	if (INVALID_HANDLE_VALUE != m_hComDev) {
		Close();
	}

	// 打开串口
	char wzPort[MAX_PATH] = { 0 };
	snprintf(wzPort, MAX_PATH, "\\\\.\\COM%d", uComPort);
	m_hComDev = CreateFileA(wzPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	// 成功打开
	if (m_hComDev != INVALID_HANDLE_VALUE) {
		// 设置
		bRet = SetCommMask(m_hComDev, EV_RXCHAR | EV_TXEMPTY | EV_ERR);
		RETAILMSG(!bRet, ("SetCommMask:%d ERROR:%d\r\n", bRet, GetLastError()));

		// 设置
		bRet = PurgeComm(m_hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
		RETAILMSG(!bRet, ("SetupComm:%d ERROR:%d\r\n", bRet, GetLastError()));

		// 设置
		COMMTIMEOUTS CommTimeOuts = { 0 };
		CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
		// CommTimeOuts.ReadIntervalTimeout = 1;
		CommTimeOuts.ReadTotalTimeoutConstant = 0;
		CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
		CommTimeOuts.WriteTotalTimeoutConstant = 0;
		CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
		bRet = SetCommTimeouts(m_hComDev, &CommTimeOuts);
		RETAILMSG(!bRet, ("SetCommTimeouts:%d ERROR:%d\r\n", bRet, GetLastError()));

		// 设置
		DCB dcb = { 0 };
		dcb.DCBlength = sizeof(DCB);
		bRet = GetCommState(m_hComDev, &dcb);
		RETAILMSG(!bRet, ("GetCommState:%d ERROR:%d\r\n", bRet, GetLastError()));

		dcb.fBinary = TRUE;
		dcb.fParity = FALSE;
		dcb.BaudRate = dwBaudRate;
		dcb.ByteSize = ByteSize;
		dcb.Parity = Parity;
		dcb.StopBits = StopBits;
		dcb.fDtrControl = DTR_CONTROL_ENABLE;
		dcb.fRtsControl = RTS_CONTROL_ENABLE;
		dcb.fOutxCtsFlow = 0;
		dcb.fOutxDsrFlow = 0;
		if (SetCommState(m_hComDev, &dcb)) {
			bRet = SetCommMask(m_hComDev, EV_RXCHAR | EV_TXEMPTY | EV_ERR);
			RETAILMSG(bRet, ("SetCommState success dwBaudRate:%d, wzPort:%s\r\n", dwBaudRate, wzPort));
			bRet = TRUE;
			ThreadStart();
		}
		else {
			RETAILMSG(1, ("SetCommState %s FAILED! ERROR:%d\r\n", wzPort, GetLastError()));
			Close();
		}

	}
	else {
		RETAILMSG(1, ("Open %s FAILED! ERROR:%d\r\n", wzPort, GetLastError()));
	}

	return bRet;
}


BOOL CSerial::ChangeBaud(DWORD dwBaudRate, BYTE ByteSize, BYTE Parity, BYTE StopBits) {
	BOOL bRet;
	DCB dcb = { 0 };
	dcb.DCBlength = sizeof(DCB);

	// 获取串口状态
	bRet = GetCommState(m_hComDev, &dcb);
	RETAILMSG(!bRet, ("GetCommState:%d ERROR:%d\r\n", bRet, GetLastError()));

	// 设置串口状态
	dcb.fBinary = TRUE;
	dcb.fParity = FALSE;
	dcb.BaudRate = dwBaudRate;
	dcb.ByteSize = ByteSize;
	dcb.Parity = Parity;
	dcb.StopBits = StopBits;
	dcb.fDtrControl = DTR_CONTROL_DISABLE;
	dcb.fRtsControl = RTS_CONTROL_ENABLE;
	dcb.fOutxCtsFlow = 0;
	dcb.fOutxDsrFlow = 0;
	if (SetCommState(m_hComDev, &dcb)) {
		bRet = SetCommMask(m_hComDev, EV_RXCHAR | EV_TXEMPTY | EV_ERR);
	}
	else {
		bRet = FALSE;
	}

	return bRet;
}

// 关闭串口
BOOL CSerial::Close() {
	BOOL bRet = FALSE;
	PurgeComm(m_hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
	ThreadStop();
	CloseHandle(m_hComDev);
	m_hComDev = INVALID_HANDLE_VALUE;
	bRet = TRUE;
	return bRet;
}

// 发送数据
BOOL CSerial::WriteData(LPCVOID lpBuf, DWORD dwBufLen) {
	BOOL bRet = FALSE;
	if (lpBuf && dwBufLen && m_hComDev) {
		DWORD dwWrite = 0;
		m_SendDataState = TRUE;
		m_PreTime = GetTickCount();
		bRet = WriteFile(m_hComDev, lpBuf, dwBufLen, &dwWrite, NULL);
		if (bRet && dwWrite != dwBufLen) {
			bRet = FALSE;
		}
	}
	return bRet;
}

// 串口是否被打开了
BOOL CSerial::IsOpen() const { return INVALID_HANDLE_VALUE != m_hComDev; }


// 清理串口
BOOL CSerial::ClearUart() {
	PurgeComm(m_hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
	return 0;
}


// 是否超时
BOOL CSerial::IsTimeOut(DWORD preTime) {
	return (abs((int)(GetTickCount() - preTime)) >= 3000);
}

DWORD CSerial::ThreadRun() {
	while (!IsTerminated() && m_pBuf && m_pObject && m_hComDev != INVALID_HANDLE_VALUE) {
		DWORD dwRead = 0;
		if (ReadFile(m_hComDev, m_pBuf, m_dwBufLen, &dwRead, NULL) && (dwRead > 0)) {
			m_pObject->OnDataRecv(m_pBuf, dwRead);			
		}
		else {
			this_thread::sleep_for(chrono::milliseconds(1));
		}
	}

	return 0;
}

 

 

#include <Windows.h>

#include <stdlib.h>

#include <vector>
#include <thread>
#include <mutex>
using namespace std;

#include "CSerial.h"

enum {
	SPECTRUM_DATA_CNT = 63,
};

struct SDisplay {
	int			Width;
	int			Height;
	HDC			OffScreenDC;	
	HBITMAP		OffScreenBMP;
	HBRUSH		BlackBrush;
	HBRUSH		WhiteBrush;
};

struct SSpectrum {
	uint8_t		Current[SPECTRUM_DATA_CNT];
	uint8_t		Draw[SPECTRUM_DATA_CNT];
	recursive_mutex Mutex;
};

static struct {
	SDisplay	Display;
	SSpectrum	Spectrum;
} _G;

static void _DoPaint() {
	SDisplay& D = _G.Display;
	HDC& hdc = D.OffScreenDC;
	int w = D.Width;
	int h = D.Height;
	// uint8_t 
	
	int col_w = w / SPECTRUM_DATA_CNT - 2;
	SelectObject(hdc, D.BlackBrush);
	Rectangle(hdc, 0, 0, w, h);

	SSpectrum& S = _G.Spectrum;
	unique_lock<recursive_mutex> lock(S.Mutex);
	SelectObject(hdc, D.WhiteBrush);
	for (int i = 0; i < SPECTRUM_DATA_CNT; i++) {
		int x = i * w / SPECTRUM_DATA_CNT;
		int y = h - S.Draw[i] * h / 256;
		Rectangle(hdc, x, y, x + col_w, h);
	}
}

static void _OnPaint(HWND hWnd) {
	PAINTSTRUCT ps;
	BeginPaint(hWnd, &ps);

	RECT rc;
	GetClientRect(hWnd, &rc);
	int w = rc.right - rc.left;
	int h = rc.bottom - rc.top;

	SDisplay& d = _G.Display;
	HDC& hdc = d.OffScreenDC;
	HBITMAP& hbmp = d.OffScreenBMP;
	if (d.Width != w || d.Height != h) {
		d.Width = w;
		d.Height = h;		
		if (hdc == NULL) {
			hdc = CreateCompatibleDC(ps.hdc);
		}
		HBITMAP newBmp = CreateCompatibleBitmap(hdc, w, h);
		HBITMAP oldBmp = (HBITMAP)SelectObject(hdc, newBmp);
		if (hbmp && hbmp == oldBmp) {
			DeleteObject(hbmp);
		}
		hbmp = newBmp;
	}

	_DoPaint();

	BitBlt(ps.hdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
	EndPaint(hWnd, &ps);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	switch (uMsg) {
	case WM_PAINT:
		_OnPaint(hWnd);
		printf("WM_PAINT\n");
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	return 0;
}

static const wchar_t* _GetWindowClassName() {
	return L"SpectrumWindow";
}

static void _InitWindowClass() {
	WNDCLASS wc = { 0 };
	wc.lpfnWndProc = WndProc;
	wc.hInstance = GetModuleHandle(NULL);
	wc.lpszClassName = _GetWindowClassName();
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);
}

static void _RandomSpectrum() {
	SSpectrum& S = _G.Spectrum;
	unique_lock<recursive_mutex> lock(S.Mutex);
	for (int i = 0; i < SPECTRUM_DATA_CNT; i++) {		
		S.Current[i] = rand() % 256;
		if (S.Draw[i] > S.Current[i]) {
			uint8_t d = S.Draw[i] - S.Current[i];
			if (d > 10) {
				d = 10;
			}
			S.Draw[i] -= d;
		}
		else if (S.Draw[i] < S.Current[i]) {
			uint8_t d = S.Current[i] - S.Draw[i];
			if (d > 10) {
				d = 10;
			}
			S.Draw[i] += d;
		}
	}
}

class MySerialObject : public CSerialObject {
public:
	BOOL OnDataRecv(BYTE* pData, DWORD dwDataLen) override {
		return TRUE;
	}
};

static MySerialObject _SerialObject;
static CSerial _Serial;

int main() {
	_InitWindowClass();

	_G.Display.BlackBrush = CreateSolidBrush(RGB(0, 0, 0));
	_G.Display.WhiteBrush = CreateSolidBrush(RGB(255, 255, 255));

	HWND hWnd = CreateWindow(_GetWindowClassName(), L"Spectrum", WS_OVERLAPPEDWINDOW,
		0, 0, 1024, 600, NULL, NULL, GetModuleHandle(NULL), NULL);
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	_Serial.InitSerial(&_SerialObject);
	_Serial.Open(11, 115200);
	_Serial.ThreadStart();

#if 0
	thread([hWnd] {
		for (int i = 0; i < 40*60; i++) {
			this_thread::sleep_for(chrono::milliseconds(25));
			_RandomSpectrum();
			InvalidateRect(hWnd, NULL, FALSE);
		}
	}).detach();
#else
	
#endif

	MSG msg = { 0 };
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

 

posted on 2023-02-15 11:10  kehuadong  阅读(13)  评论(0编辑  收藏  举报

导航