О таймере

Материал из DRKB

О таймере[править | править код]

Компонент timer (таймер) служит для отсчета интервалов реального времени. Его свойство interval определяет интервал временив миллисекундах , который должен пройти от включения таймера до наступления события ontimer. Таймер включается при установке значения true в его свойство enabled. Единожды включенный таймер все время будет возбуждать события ontimer до тех пор, пока его свойство enabled не примет значения false.

Следует учесть, что в силу специфики реализации стандартного аппаратного таймера ibm-совместимого компьютера минимальный реально достижимый интервал отсчета времени не может быть меньше 55 мс (этот интервал называется тиком), более того, любой интервал времени, отсчитываемый с помощью таймера, всегда кратен 55 мс. Чтобы убедиться в этом, проведите эксперимент, в котором подсчитывается среднее время между двумя срабатываниями таймера (timer.dpr):

Начните новый проект с пустой формой и положите на нее компонент TTimer.

unit unit1; 

interface 

uses 
  windows, messages, sysutils, classes, graphics, controls, forms, 
  dialogs, stdctrls, buttons, extctrls; 

type 
  TfmExample = class(TForm) 
    Panel1: TPanel; 
    bbRun: TBitBtn; 
    bbClose: TBitBtn; 
    edInput: TEdit; 
    lbOutput: TLabel; 
    mmOutput: TMemo; 
    Timer1: TTimer; 
    procedure bbRunClick(Sender: TObject); 
    procedure Timer1Timer(Sender: TObject); 
    procedure FormActivate(Sender: TObject); 
  private 
    BegTime: TDateTime; // Начальное время цикла 
    Counter: Integer; // Счетчик цикла 
  end; 

var
  fmExample: TfmExample; 

implementation 

{$r *.dfm} 

// Запускает таймер. edinput содержит период его срабатывания. 
procedure TfmExample.bbRunClick(Sender: TObject); 
var delay: Word; 
begin 
  // Проверяем задание интервала 
  if edInput.Text='' then Exit; 
  try 
    delay := StrToInt(edInput.Text); 
  except 
    ShowMessage('Ошибка в записи числа'); 
    edInput.SelectAll; 
    edInput.SetFocus; 
    Exit 
  end; 
  Counter := 0; // Сбрасываем счетчик 
  Timer1.Interval := delay; // Устанавливаем интервал 
  BegTime := Time; // Засекаем время 
  Timer1.Enabled := True; // Пускаем таймер 
  Screen.Cursor := crHourGlass; 
end; 

procedure TfmExample.Timer1Timer(Sender: TObject); 
  var h, m, s, ms: Word; // Переменные для декодирования времени 
  const maxCount = 55; // Количество срабатываний таймера 
begin 
  Counter := Counter + 1; // Наращиваем счетчик срабатываний 
  if Counter = maxCount then // Конец цикла? 
  begin // - Да 
    Timer1.Enabled := False; // Останавливаем таймер 
    // Находим среднее время срабатывания: 
    DecodeTime((Time-Begtime)/maxCount, h, m, s, ms); 
    // Выводим результат 
    mmOutput.Lines.Add(Format('Задано %s ms. Получено %d ms.', [edInput.Text, ms])); 
    edInput.Text := ''; // Готовим следующий запуск 
    edInput.SetFocus; 
    Screen.Cursor := crDefault;
  end; 
end; 

procedure TfmExample.FormActivate(Sender: TObject); 
begin 
  edInput.SetFocus();
end; 

end.


Необходимость нескольких (maxCount) срабатываний для точного усреднения результата связана с тем, что системные часы обновляются каждые 55 мс. После запуска программы и ввода 1 как требуемого периода срабатывания в редакторе mmoutput вы увидите строку

Задано 1 ms. Получено 55 ms.

в которой указывается, какое реальное время разделяет два соседних события OnTimer. Если вы установите период таймера в диапазоне от 56 до 110 мс, в строке будет указано 110 ms и т.д. (в силу дискретности обновления системных часов результаты могут несколько отличаться в ту или иную сторону).

В ряде практически важных областей применения (при разработке игр, в системах реального времени для управления внешними устройствам и т.п.) интервал 55 мс может оказаться слишком велик. Современный ПК имеет мультимедийный таймер, период срабатывания которого может быть от 1 мс и выше, однако этот таймер не имеет компонентного воплощения, поэтому для доступа к нему приходится использовать функции api.

Общая схема его использования такова. Сначала готовится процедура обратного вызова (call back) с заголовком:

procedure timeProc(uid, umsg: uint; dwuser, dw1, dw2: dword); stdcall;


Запуск таймера реализуется функцией:

function timeSetEvent(udelay, uresolution: uint; lptimeproc: pointer; dwuser: dword; fuevent: uint): uint; stdcall; external 'winmm.dll';

Таймер останавливается, и связанные с ним системные ресурсы освобождаются функцией:

function timeKillEvent(uid: uint): uint; stdcall; external 'winmm.dll';

Здесь uid - идентификатор события таймера, полученный с помощью timesetevent. В следующем примере (timer.dpr) иллюстрируется использование мультимедийного таймера (листинг 5).

unit unit1; 

interface 

uses 
  windows, messages, sysutils, classes, graphics, controls, forms, 
  dialogs, stdctrls, buttons, extctrls; 

type 
  TfmExample = class(TForm) 
  Panel1: TPanel; 
  bbRun: TBitBtn; 
  bbClose: TBitBtn; 
  edInput: TEdit; 
  lbOutput: TLabel; 
  mmOutput: TMemo; 
  procedure bbRunClick(Sender: TObject); 
  procedure FormActivate(Sender: TObject); 
end; 

var fmExample: TfmExample; 

implementation 

{$r *.dfm} 
// Объявление экспортируемых функций: 

function timeSetEvent(uDelay, uResolution: UInt; lpTimeProc: Pointer; 
  dwUser: DWord; fuEvent: UInt): Integer; stdcall; external 'winmm'; 

function timeKillEvent(uid: UInt): Integer; stdcall; external 'winmm'; 

// Объявление глобальных переменных 
var 
  ueventid: UInt; // Идентификатор события таймера 
  begtime: TDateTime; // Засекаем время< 
  counter: Integer; // Счетчик повторений 
  delay: Word; // Период срабатывания 

procedure procTime(uid, msg: UInt; dwuse, dw1, dw2: DWord); stdcall; 
// Реакция на срабатывание таймера (процедура обратного вызова) 
var h, m, s, ms: Word; // Переменные для декодирования времени 
const maxcount = 55; // Количество повторений 
begin 
  timeKillEvent(ueventid); // Останавливаем таймер 
  counter := counter+1; // Наращиваем счетчик 
  if counter = maxcount then // Конец цикла? 
  begin // - Да: декодируем время 
    DecodeTime((time-begtime)/maxcount, h, m, s, ms); 
    fmExample.mmOutput.Lines.Add( // Сообщаем результат 
      Format('Задано %s ms. Получено %d ms', 
        [fmExample.edInput.Text, ms])); 
    fmExample.edInput.Text := ''; // Готовим повторение 
    fmExample.edInput.SetFocus;
  end 
  else // - Нет: вновь пускаем таймер 
    ueventid := timeSetEvent(delay, 0, @proctime, 0, 1); 
end; 

procedure TfmExample.bbRunClick(Sender: TObject); 
// Запускает таймер. edinput содержит требуемый период. 
begin 
  // Проверяем задание периода 
  if edInput.Text = '' then Exit; 
  try 
    delay := StrToInt(edInput.Text) 
  except 
    ShowMessage('Ошибка ввода числа'); 
    edInput.SelectAll; 
    edInput.SetFocus; 
    Exit 
  end; 
  counter := 0; // Сбрасываем счетчик 
  begtime := Time; // Засекаем время 
  // Запускаем таймер: 
  ueventid := timeSetEvent(delay, 0, @proctime, 0, 1); 
  if ueventid = 0 then 
    ShowMessage('Ошибка запуска таймера') 
end; 

procedure TfmExample.formactivate(Sender: TObject); 
begin 
  edInput.SetFocus 
end; 

end.


Source: http://www.delphi.h5.ru/
ID: 00203