2014-09-25 3 views
0

Я работаю над приложением секундомера, специально для спортивной укладки и скоростного кубирования (разрешая куб Рубика в кратчайшие сроки).Таймер высокой точности

Я хочу, чтобы программа запускала секундомер, который отображает прошедшее время в формате 00: 00.00 с помощью минут, секунд и разделяет секунды на 2 десятичных знака при нажатии пробела, а затем останавливает таймер при повторном нажатии пробела ,

Я пробовал много вариантов и загружал исходный код и различные классы секундомера, чтобы попытаться решить мою проблему, но когда я запускаю программу параллельно реальному секундомеру, мой таймер отстает. Кажется, я не могу получить код правильно для точного таймера, который запускается и останавливается при нажатии пробела.

Я работаю в Delphi XE5. Я был бы очень признателен за любую помощь.

+1

Опубликовать то, что у вас есть, и кто-то поможет вам исправить ситуацию. –

+1

_ «Я пробовал много вариантов ...» _ показать нам это. Прямо сейчас, в этом вопросе не хватает исследовательских усилий. – NGLN

+3

Имейте в виду, что приложение на основе окон всегда будет иметь некоторую неконтролируемую величину ошибки в миллисекундах до десятков миллисекунд, если не больше. Причина в том, что в соревнованиях используются аппаратные таймеры, и это просто потому, что Windows не может гарантировать, сколько времени будет задерживаться между нажатием клавиши и уведомлением вашего приложения (и, кроме того, между уведомлением вашего приложения и фактически что-то с этим делать).Когда вы говорите о временах куба, где 10ms имеет большое значение, хорошо ... –

ответ

3

Первое отражение

Прежде чем я продолжу, некоторые размышления:

  • Если есть задержка при запуске таймера из-за задержки между пользователем - клавиатура - код тогда не должен 't есть относительно равное отставание , когда пробел снова нажат, чтобы остановить таймер?
  • Windows не является оперативной системой реального времени. Если ваше приложение требует точности в реальном времени, вы можете выбрать другую платформу.
  • Беспроводная клавиатура может увеличить отставание ...

Обнаружение нажатия клавиши

Я могу думать о двух различных механизмов обнаружения, если пробел нажата:

  • OnKeyDown - стандартное событие Delphi на основе обмена сообщениями окна (Slow ??)
  • GetAsyncKeyState() - Считывает физическое состояние клавиатуры, r независимо от состояния очереди ввода. (Быстрее ??)

Benchmarck

Ниже приведен пример, где я тест между двумя механизмами с использованием TStopwatch. TStopwatch использует функциональные возможности, зависящие от операционной системы, для получения доступа к таймерам с высоким разрешением, если они доступны; в противном случае используются обычные таймеры. Я поместил функцию GetAsyncKeyState в собственный поток.

unit main; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.AppEvnts, Winapi.ShellApi, Vcl.StdCtrls, Vcl.ExtCtrls, 

    //Add these two units 
    System.Diagnostics, System.TimeSpan; 

type 
    TThreadCheckSpaceKey = class(TThread) 
    private 
    FStopwatch : TStopwatch; 
    public 
    property Stopwatch : TStopwatch read FStopWatch; 
    constructor Create(); 
    procedure Execute(); override; 
end; 

    TFormMain = class(TForm) 
    ApplicationEvents1: TApplicationEvents; 
    procedure FormCreate(Sender: TObject); 
    procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean); 
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
    private 
    FThreadCheckSpaceKey : TThreadCheckSpaceKey; 
    FStopwatch : TStopwatch; 
    procedure DoDrawTimer; 
    public 

    end; 

var 
    FormMain: TFormMain; 

implementation 

{$R *.dfm} 

function TimeSpanToStr(aTimeSpan : TTimeSpan):string; 
begin 
    result := Format('%.*d', [2, aTimeSpan.Minutes])+ ':' + 
      Format('%.*d', [2, aTimeSpan.Seconds]) + ':' + 
      Copy(Format('%.*d', [3, aTimeSpan.Milliseconds]), 1, 3); 
end; 

procedure TFormMain.DoDrawTimer(); 
begin 
    //This is just an example 
    Caption := 'GetAsyncKeyState: ' + TimeSpanToStr(FThreadCheckSpaceKey.Stopwatch.Elapsed) + ' OnKeyDown: ' + TimeSpanToStr(FStopWatch.Elapsed);; 
end; 

procedure TFormMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean); 
begin 
    FThreadCheckSpaceKey.Terminate; 

    FreeAndNil(FThreadCheckSpaceKey); 
end; 

procedure TFormMain.FormCreate(Sender: TObject); 
begin 
    //If in the Delphi IDE then report memory leaks on shutdown 
    ReportMemoryLeaksOnShutdown := DebugHook <> 0; 

    FStopwatch := TStopWatch.Create; 

    FThreadCheckSpaceKey := TThreadCheckSpaceKey.Create; 

    //Clear any previos records of the space key being pressed 
    GetAsyncKeyState(VK_SPACE); 
end; 

procedure TFormMain.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean); 
begin 
    DoDrawTimer; 
end; 

procedure TFormMain.FormKeyDown(Sender: TObject; var Key: Word; 
    Shift: TShiftState); 
begin 
    if Key = VK_SPACE then begin 
    if FStopwatch.IsRunning then 
     FStopwatch.Stop 
    else 
     FStopwatch := TStopwatch.StartNew; 

    Key := 0; //Don't propogate to underlyning controls 
    DoDrawTimer; 
    end; 
end; 

{ TThreadCheckSpaceKey} 

function GetBit(Value: SHORT; Index: Byte): Boolean; 
begin 
    Result := ((Value shr Index) and 1) = 1; 
end; 

constructor TThreadCheckSpaceKey.Create; 
begin 
    inherited; 
    FStopwatch := TStopWatch.Create; 
end; 

procedure TThreadCheckSpaceKey.Execute; 
var IsDown : boolean; 
begin 
    inherited; 

    while not Terminated do begin 
     //If the lowest bit is set then the space bar has been pressed since last check 
     //If the higest bit is set then the space bar is down 
     if GetBit(GetAsyncKeyState(VK_SPACE), 15) then begin 

      if not IsDown then 
      if FStopwatch.IsRunning then 
       FStopwatch.Stop 
      else 
       FStopwatch := TStopwatch.StartNew; 
      IsDown := true; 
     end 
     else 
     IsDown := false; 
    end; 
end; 

end. 
+1

Как это ответить на вопрос? Вопрос заключается не в том, как построить секундомер, а в том, как получить точное измерение времени без отставания. Я не вижу ничего подобного в вашем ответе. –

+0

Пункт принял сэр Руфо. Я обновил свой ответ. – Lars

+0

'GetAsyncKeyState' может быть фактически менее результативным, так как вам нужно будет опросить цикл занятости, который мог бы накапливать ошибки до, по крайней мере, интервала среза времени потока. С другой стороны, сообщения с клавиатурой запускаются прерываниями, которые непосредственно ожидают потоки, ожидающие сообщений, что делает ответ довольно быстрым. Однако, независимо от реализации, ни один таймер программного обеспечения не может гарантировать точность в 100% случаев. Учитывая, что вы можете купить стопку за 25 долларов и даже дешевый ПК в двадцать раз по этой цене ... иногда лучше использовать правильный инструмент для правильной работы. –