Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

О таймере

01.01.2007

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

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

Начните новый проект с пустой формой и положите на нее компонент ttimer.
Установите в свойство enabled таймера значение false.
Напишите такой модуль главной формы (листинг 4):

Листинг 4

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} 
 
procedure tfmexample.bbrunclick(sender: tobject); 
// Запускает таймер. edinput содержит период его срабатывания. 
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; 

 

Здесь uid — идентификатор события таймера (см. об этом ниже); umsg — не используется; dwuser — произвольное число, передаваемое процедуре в момент срабатывания таймера; dw1, dw2 — не используются.

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

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

 

Здесь udelay — необходимый период срабатывания таймера (в мс); uresolution — разрешение таймера (значение 0 означает, что события срабатывания таймера будут возникать с максимально возможной частотой; в целях снижения нагрузки на систему вы можете увеличить это значение); lptimeproc — адрес процедуры обратного вызова; dwuser — произвольное число, которое передается процедуре обратного вызова и которым программист может распоряжаться по своему усмотрению; fuevent — параметр, управляющий периодичностью возникновения события таймера: time_oneshot (0) — событие возникает только один раз через udelay миллисекунд; time_periodic (1) — события возникают периодически каждые udelay мс. При успешном обращении функция возвращает идентификатор события таймера и 0, если обращение было ошибочным.

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

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, ureolution: 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. 

 

Источник: http://www.delphi.h5.ru/