О таймере
Материал из 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