О таймере

Компонент 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   
    { Private declarations }   
    BegTime: TDateTime;  // Начальное время цикла    
    Counter: Integer;    // Счетчик    цикла   
  public   
    { Public declarations }   
  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).

Листинг 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);   
  private   
    { Private declarations }   
  public   
    { Public declarations }   
  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.
Теги:
таймер, Timer, Interval
Добавлено: 08 Апреля 2018 06:51:12 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...