QueryPerformanceFrequency() - 技术特点

本文介绍Windows 9X下使用QueryPerformanceFrequency()与QueryPerformanceCounter()实现高精度定时的方法。通过这两个函数可以获取系统的时钟频率及计数值,进而计算出特定事件的精确耗时。

供WIN9X使用的高精度定时器:QueryPerformanceFrequency()和QueryPerformanceCounter(),要求计算机从硬件上支持高精度定时器。

函数的原形是:
  BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
  BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);

数据类型LARGE_INTEGER既可以是一个作为8字节长的整数,也可以是作为两个4字节长的整数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下:
  typeef union _ LARGE_INTEGER
  {
   struct
   {
   DWORD LowPart;
   LONG HighPart;
   };
   LONGLONG QuadPart;
  } LARGE_INTEGER;

在定时前应该先调用QueryPerformanceFrequency()函数获得机器内部计时器的时钟频率。接着在需要严格计时的事件发生前和发生之后分别调用QueryPerformanceCounter(),利用两次获得的计数之差和时钟频率,就可以计算出事件经历的精确时间。测试函数SLEEP(100)的精确持续时间方法:
  LARGE_INTEGER litmp;
  LONGLONG qt1,qt2;
  double dft,dff,dfm;
  QueryPerformanceFrequency(&litmp);//获得时钟频率
  dff=(double)litmp.QuadPart;
  QueryPerformanceCounter(&litmp);//获得初始值
  qt1=litmp.QuadPart;Sleep(100);
  QueryPerformanceCounter(&litmp);//获得终止值
  qt2=litmp.QuadPart;
  dfm=(double)(qt2-qt1);
  dft=dfm/dff;//获得对应的时间值

需要注意的是DFT计算的结果单位是秒。

#include <windows.h> #include <stdio.h> #include <stdint.h> #include <stdbool.h> #pragma comment(lib, "winmm.lib") #define _UNICODE #define LOG_FILE L"vehicle_log.txt" #define IDC_PAUSE_BUTTON 101 // 暂停按钮的控件 ID // ------------------ 全局变量定义 ------------------ uint16_t current_vehicle_speed = 0; // 当前速度(km/h) uint64_t total_distance_mm = 0; // 累计行驶距离(mm) uint32_t last_update_time = 0; // 上次更新时间(毫秒) const uint32_t delta_T = 20; // 每20ms记录一次日志 const uint32_t can_update_period = 100; // CAN 信号更新间隔100ms bool is_paused = false; // 程序暂停状态 LARGE_INTEGER frequency, start_counter; uint8_t can_frame[8] = {0}; // CAN 数据帧 // ------------------ 函数声明 ------------------ void ClearLogFile(); void WriteLog(uint32_t current_time, uint16_t speed); void UpdateVehicleState(uint32_t current_time); void GenerateCANMessage(uint16_t speed_msg, uint8_t frame[8]); uint16_t ReadCANMessage(uint8_t frame[8]); DWORD WINAPI WorkerThread(LPVOID lpParam); LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // ------------------ 清空日志文件 ------------------ void ClearLogFile() { FILE* file = _wfopen(LOG_FILE, L"w"); total_distance_mm = 0; // 重置总里程 if (file) { fwprintf(file, L"Log initialized. Vehicle Logger started.\n"); fclose(file); } } // ------------------ 写入日志 ------------------ void WriteLog(uint32_t current_time, uint16_t speed) { FILE* file = _wfopen(LOG_FILE, L"a"); if (!file) return; SYSTEMTIME st; GetLocalTime(&st); int total_distance_m = (int)((total_distance_mm + 500) / 1000); // 四舍五入到米 fwprintf(file, L"%02d:%02d:%02d.%03d - Speed: %d km/h, Total Distance: %d m\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, speed, total_distance_m); fclose(file); } // ------------------ 更新车辆状态 ------------------ // 修改 UpdateVehicleState 函数 void UpdateVehicleState(uint32_t current_time) { static uint32_t last_can_update_time = 0; static uint32_t last_log_time = 0; static bool has_initialized = false; static uint16_t current_speed = 0; // 静态变量,确保初始值为0 static int16_t acceleration = 0; // 加速度 const uint64_t max_distance_mm = 999999000000; // 第一次调用时初始化所有状态 if (!has_initialized) { current_vehicle_speed = 0; // 确保全局变量初始值为0 total_distance_mm = 0; // 重置总里程为0 last_update_time = current_time; last_can_update_time = current_time; // 初始化CAN更新时间 last_log_time = current_time; // 初始化日志时间 has_initialized = true; memset(can_frame, 0, sizeof(can_frame)); // 确保CAN帧初始为全0 WriteLog(current_time, current_vehicle_speed); // 添加标志表示已完成初始化 printf("System initialized. Speed: %d\n", current_vehicle_speed); } if (is_paused) { return; // 暂停时不更新状态 } // --- 判断是否需要生成新的CAN信号(100ms周期) --- if (current_time - last_can_update_time >= can_update_period) { static uint16_t max_speed = 360; static uint16_t min_speed = 0; // 移除保持0速的特殊逻辑,直接使用随机加速度 acceleration = (rand() % 21) - 10; // 加速或减速(-10 到 +10 km/h) // 更新速度,同时确保速度保持在有效范围内 int32_t new_speed = current_speed + acceleration; if (new_speed > max_speed) new_speed = max_speed; if (new_speed < min_speed) new_speed = min_speed; current_speed = (uint16_t)new_speed; // 打印新速度 printf("Generated speed = %d\n", current_speed); // --- 生成 CAN 消息 --- GenerateCANMessage(current_speed, can_frame); last_can_update_time = current_time; // 更新 CAN 信号生成时间 } // --- 从 CAN 信号解码车辆速度 --- uint16_t decoded_speed = ReadCANMessage(can_frame); current_vehicle_speed = decoded_speed; printf("decoded_speed = %d\n", decoded_speed); // --- 计算时间差并累加行驶距离 --- uint32_t elapsed_time_ms = current_time - last_update_time; if (elapsed_time_ms > 0) { // 确保时间差有效 // 累加行驶距离,但不超过最大值 if (total_distance_mm < max_distance_mm) { total_distance_mm += (uint64_t)((double)current_vehicle_speed * elapsed_time_ms / 3.6); if (total_distance_mm > max_distance_mm) { total_distance_mm = max_distance_mm; // 如果超出最大值,锁定到最大值 } } last_update_time = current_time; // 更新最后的累计时间戳 } // --- 如果满足日志记录周期,则写入日志 --- if (current_time - last_log_time >= delta_T) { WriteLog(current_time, current_vehicle_speed); // 写入当前速度到日志 last_log_time = current_time; // 更新日志的时间戳 } } // ------------------ 生成 CAN 消息 (Motorola大端序) ------------------ void GenerateCANMessage(uint16_t speed_msg, uint8_t frame[8]) { memset(frame, 0, 8); // 清空帧数据 uint16_t mask = (1 << 13) - 1; // 限制速度信息到低 13 位 uint16_t encoded_speed = speed_msg & mask; // Motorola大端序映射:起始位是信号MSB,物理地址递增方向=信号权重递减方向 for (int i = 0; i < 13; i++) { uint16_t bit_pos = 16 + i; // 信号物理位置:第16位到第28位 uint8_t byte_pos = bit_pos / 8; // 字节索引 // 修正:直接使用bit_pos % 8(Motorola不需要字节内反转) uint8_t bit_in_byte = bit_pos % 8; // 修正:从信号MSB开始提取 (12-i) if (encoded_speed & (1 << (12 - i))) { frame[byte_pos] |= (1 << bit_in_byte); } } } // ------------------ 读取 CAN 消息 (Motorola大端序) ------------------ uint16_t ReadCANMessage(uint8_t frame[8]) { uint16_t decoded_speed = 0; // 从物理位读取并重构信号值 for (int i = 0; i < 13; i++) { uint16_t bit_pos = 16 + i; uint8_t byte_pos = bit_pos / 8; uint8_t bit_in_byte = bit_pos % 8; // 修正:直接使用物理位偏移 // 修正:将读取的位放入正确位置 (12-i) if (frame[byte_pos] & (1 << bit_in_byte)) { decoded_speed |= (1 << (12 - i)); } } return decoded_speed; } // ------------------ 后台工作线程 ------------------ DWORD WINAPI WorkerThread(LPVOID lpParam) { HWND hwnd = (HWND)lpParam; QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&start_counter); LARGE_INTEGER current_counter; uint32_t ideal_loop_time = 0; uint32_t delay_ms = 500; // 延迟时间(毫秒) // --- 延迟部分,延迟 500ms 稳定程序 --- current_vehicle_speed = 0; // 设置初速度为 0,防止累计距离 while (1) { QueryPerformanceCounter(&current_counter); uint32_t elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart); if (elapsed_ms >= delay_ms) break; // 延迟完成,继续进入主循环 } last_update_time = 0; // 重置最后更新时间,防止延迟影响累计逻辑 // --- 主循环 --- while (1) { QueryPerformanceCounter(&current_counter); uint32_t elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart); // 更新车辆状态 UpdateVehicleState(elapsed_ms); // 通知主线程刷新显示 PostMessage(hwnd, WM_USER + 1, 0, 0); // 控制日志节奏,保持日志打印间隔严格为 20ms ideal_loop_time += delta_T; while (1) { QueryPerformanceCounter(&current_counter); uint32_t temp_elapsed_ms = (uint32_t)((current_counter.QuadPart - start_counter.QuadPart) * 1000 / frequency.QuadPart); if (temp_elapsed_ms >= ideal_loop_time) break; } } } // ------------------ 窗口消息处理 ------------------ LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { ClearLogFile(); current_vehicle_speed = 0; // 创建暂停按钮 HWND pauseButton = CreateWindowW( L"BUTTON", L"Pause", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 160, 200, 100, 30, hwnd, (HMENU)IDC_PAUSE_BUTTON, NULL, NULL ); HANDLE thread = CreateThread(NULL, 0, WorkerThread, hwnd, 0, NULL); if (!thread) { MessageBox(NULL, L"Error", L"Failed to create thread", MB_ICONERROR); PostQuitMessage(-1); } CloseHandle(thread); break; } case WM_USER + 1: { wchar_t buffer[256]; // 将总行驶距离从毫米转换为米(四舍五入到米) uint64_t total_distance_m = (total_distance_mm + 500) / 1000; // 获取当前系统时间 SYSTEMTIME st; GetLocalTime(&st); swprintf(buffer, 256, L"Time: %02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); // 获取客户区大小 RECT clientRect; GetClientRect(hwnd, &clientRect); // 定义动态内容区域,避免覆盖按钮部分 RECT dynamicRect = clientRect; dynamicRect.bottom -= 110; // 保留底部按钮区域 // 获取设备上下文 HDC hdc = GetDC(hwnd); HDC memDC = CreateCompatibleDC(hdc); // 创建内存DC HBITMAP memBitmap = CreateCompatibleBitmap(hdc, dynamicRect.right - dynamicRect.left, dynamicRect.bottom - dynamicRect.top); SelectObject(memDC, memBitmap); // 将位图选入内存DC // 填充动态内容区域背景 HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW)); // 默认窗口背景色 FillRect(memDC, &dynamicRect, hBrush); DeleteObject(hBrush); // 设置字体 HFONT hFont = CreateFontW( 36, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial"); HFONT oldFont = SelectObject(memDC, hFont); // 打印当前时间 RECT timeRect = { dynamicRect.left + 50, dynamicRect.top + 10, dynamicRect.right - 50, dynamicRect.top + 50 // 时间显示区域 }; SetTextColor(memDC, RGB(0, 0, 0)); // 黑色字体 DrawTextW(memDC, buffer, -1, &timeRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); // 打印车辆速度 RECT speedRect = { dynamicRect.left + 50, dynamicRect.top + 70, dynamicRect.right - 50, dynamicRect.bottom - 50 }; swprintf(buffer, 256, L"Speed: %d km/h", current_vehicle_speed); SetTextColor(memDC, RGB(0, 0, 255)); // 蓝色字体 DrawTextW(memDC, buffer, -1, &speedRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); // 打印总行驶距离 RECT distanceRect = { dynamicRect.left + 50, dynamicRect.top + 150, dynamicRect.right - 50, dynamicRect.bottom - 30 }; if (total_distance_m >= 100000) { swprintf(buffer, 256, L"Distance: %.1f km", total_distance_m / 1000.0); // 显示为 km(保留1位小数) } else { swprintf(buffer, 256, L"Distance: %d m", (int)total_distance_m); // 显示为米 } SetTextColor(memDC, RGB(255, 0, 0)); // 红色字体 DrawTextW(memDC, buffer, -1, &distanceRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); // 将内存DC的内容复制到动态区域 BitBlt(hdc, dynamicRect.left, dynamicRect.top, dynamicRect.right - dynamicRect.left, dynamicRect.bottom - dynamicRect.top, memDC, 0, 0, SRCCOPY); // 清理资源 SelectObject(memDC, oldFont); DeleteObject(hFont); DeleteDC(memDC); DeleteObject(memBitmap); ReleaseDC(hwnd, hdc); break; } case WM_COMMAND: { if (LOWORD(wParam) == IDC_PAUSE_BUTTON) { is_paused = !is_paused; HWND pauseButton = GetDlgItem(hwnd, IDC_PAUSE_BUTTON); SetWindowTextW(pauseButton, is_paused ? L"Resume" : L"Pause"); } break; } case WM_DESTROY: { PostQuitMessage(0); break; } default: { return DefWindowProc(hwnd, uMsg, wParam, lParam); } } return 0; } // ------------------ 主程序入口 ------------------ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { WNDCLASSW wc = {0}; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = L"VehicleLogger"; if (!RegisterClassW(&wc)) return -1; HWND hwnd = CreateWindowW( L"VehicleLogger", L"CAN Signal Logger", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 450, 300, NULL, NULL, hInstance, NULL ); if (!hwnd) return -1; MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } 一行行解释
最新发布
08-16
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值