// SPDX-License-Identifier: WTFPL // Code is written by Google AI #include #include #include // ログデータの構造体 typedef struct { LONGLONG rawCounter; // 高精度タイマーの生の値 bool cts; bool dsr; } LogData; // キューの最大バッファサイズ(2のべき乗にしておくと管理が楽です) #define QUEUE_SIZE 256 LogData logQueue[QUEUE_SIZE]; int queueHead = 0; int queueTail = 0; // 同期用のWin32オブジェクト CRITICAL_SECTION cs; // キューの競合を防ぐ排他ロック(C++のmutex相当) HANDLE hLogEvent; // サブスレッドをkickするためのイベント信号 bool isRunning = true; // 終了フラグ LONGLONG Ticks, prevCounter; double get_ms(LONGLONG count) { return ((double)count / Ticks) * 1000; } // 【サブスレッド:kickを待って画面出力する】 DWORD WINAPI LogThreadWorker(LPVOID lpParam) { while (1) { // メインスレッドからの kick(シグナル)を待つ(CPU負荷ゼロで待機) WaitForSingleObject(hLogEvent, INFINITE); while (1) { LogData data; bool hasData = false; // キューからデータを安全に取り出す EnterCriticalSection(&cs); if (queueHead != queueTail) { data = logQueue[queueTail]; queueTail = (queueTail + 1) % QUEUE_SIZE; hasData = true; } LeaveCriticalSection(&cs); // キューが空になったらkick待ちに戻る if (!hasData) { break; } // 64ビット整数(%lld)として生のタイマー値をそのまま出力 // 2つの行の差分を見れば、1ms未満の精密な間隔が手計算(またはExcel等)で割り出せます printf("[Counter: %f (%f)] CTS:%s DSR:%s\n", get_ms(data.rawCounter), get_ms(data.rawCounter - prevCounter), data.cts ? "ON" : "OFF", data.dsr ? "ON" : "OFF"); prevCounter = data.rawCounter; } // 終了フラグが立っていてキューも空ならスレッドを抜ける if (!isRunning) { break; } } return 0; } int main(int argc, char* argv[]) { int i; HANDLE hComm, hThread; LARGE_INTEGER freq, qpc; if (argc < 2) { printf("使い方: %s <ポート名> (例: COM1 や \\\\.\\COM10)\n", argv[0]); return 1; } hComm = CreateFileA(argv[1], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if (hComm == INVALID_HANDLE_VALUE) { printf("COMポート %s のオープンに失敗しました。", argv[1]); return 1; } // 周波数を取得 QueryPerformanceFrequency(&freq); printf("タイマー周波数: %lld Hz\n", freq.QuadPart); Ticks = freq.QuadPart; QueryPerformanceCounter(&qpc); prevCounter = qpc.QuadPart; // Win32 同期オブジェクトの初期化 InitializeCriticalSection(&cs); hLogEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // 自動リセットイベント // サブスレッドの起動 (Win32 API) hThread = CreateThread(NULL, 0, LogThreadWorker, NULL, 0, NULL); if (hThread == NULL) { printf("スレッド作成失敗\n"); return 1; } // 監視するシリアルイベントの設定 SetCommMask(hComm, EV_CTS | EV_DSR); printf("監視を開始します...\n"); // 【メインスレッド:シリアルイベント監視ループ】 while (1) { DWORD evtMask = 0; // 1. イベント待ち(ここでブロッキング待機) if (WaitCommEvent(hComm, &evtMask, NULL)) { LogData newData; DWORD modemStatus = 0; int nextHead; // 2. イベント発生直後に最優先で生のタイマー値を取得 QueryPerformanceCounter(&qpc); newData.rawCounter = qpc.QuadPart; // 3. 最新のピン状態を取得 GetCommModemStatus(hComm, &modemStatus); newData.cts = (modemStatus & MS_CTS_ON) != 0; newData.dsr = (modemStatus & MS_DSR_ON) != 0; // 4. キューへの記録(クリティカルセクションで保護) EnterCriticalSection(&cs); nextHead = (queueHead + 1) % QUEUE_SIZE; if (nextHead != queueTail) { // キュー溢れチェック logQueue[queueHead] = newData; queueHead = nextHead; } LeaveCriticalSection(&cs); // 5. サブスレッドをkick(シグナルを送る) SetEvent(hLogEvent); } } // 終了処理 isRunning = false; SetEvent(hLogEvent); // 最後のkick WaitForSingleObject(hThread, INFINITE); // スレッドの安全な終了を待つ // 後片付け CloseHandle(hThread); CloseHandle(hLogEvent); DeleteCriticalSection(&cs); CloseHandle(hComm); return 0; }