频谱模拟
#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);
}
}