Что такое часы реального времени
Часы реального времени
В данной статье рассматривается пример создания часов рального времени. На индикатор будет выводиться точное время, а двоеточие на нем будет моргать раз в секунду. Точное время будет автоматически устанавливаться во вермя компиляции прошивки.
Описание компонентов
В нашем проекте мы используем:
Часы реального времени
Мы используем модуль с часами реального времени от Seeed Studio. Они построены на базе микросхемы DS1307 от Maxim Integrated. Из элементов обвязки она требует три резистора, часовой кварц и батарейку, которые уже имеются на данном модуле. Модуль обладает следующими свойствами:
Суть часов реального времени в том, что при наличии батарейки, они могут идти даже если основное устройство обесточено. Мы с такими часами сталкиваемся постоянно в ноутбуках или цифровых фотоаппаратах. Если достать из этих устройств аккумулятор, а через некоторое время вернуть их обратно, то время не сбросится. В этом заслуга часов реального времени, Real Time Clock (RTC).
Все необходимые библиотеки можно скачать с официального сайта.
Индикатор
Мы используем четырёхразрядный индикатор от Seeed Studio. Основное в индикаторе — микросхема TM1637, представляющая собой драйвер для отдельных 7-сегментных разрядов. В данном модуле используется 4 разряда. Модуль обладает следующими свойствами:
Данный модуль мы используем для показа времени: часов и минут. Удобство модуля в том, что подключается он всего по двум проводам и не требует программной реализации динамической индикации, поскольку все уже реализовано внутри модуля.
Динамическая индикация — это процесс, при котором индикаторы в нашем модуле загораются последовательно. Но мерцания мы не видим, поскольку человеческой глаз обладает большой инертностью. Данный метод позволяет очень хорошо экономить количество соединений между индикаторами и контроллером:
Библиотека для данного модуля также может быть скачана с сайта производителя.
Подключение
Модуль часов реального времени необходимо подключить к выводам SCL/SDA, относящимся к шине I²C. Также необходимо подключить линии питания (Vcc) и земли (GND).
Линии SDA/SCL имеют собственные отдельные пины на Arduino, однако внутри они так или иначе подключены к пинам общего назначения. Если рассмотреть Arduino Uno, линии SDA соответствует пин A4, а SCL — A5.
В комплекте с модулем поставляется шлейф с мама-контактами, которые удобнее подключать к Troyka Shield. Однако отдельные пины SDA и SCL на ней не выведены, поэтому мы осуществили подключение прямо через пины A5 и A4.
В плане подключения индикатора — все гораздо проще. Выводы CLK и DIO можно подключить к любым цифровым выводам. В данном случае используются 12-й и 11-й выводы соответственно.
Написание прошивки
Код для этого выглядит следующим образом:
Теперь загружаем этот код в среду разработки, компилируем и заливаем. Смотрим на дисплей — бинго! Время на дисплее — время компиляции.
Объяснение функции getInt
Для начала необходимо понять, откуда же в массиве compileTime появляется время. Оно появляется в этой строчке:
unsigned char compileTime[] = __TIME__;
Вернемся к коду, который необходимо объяснить:
Проблемы
Да, этот код рабочий, и часы будут идти. Однако, если отключить питание, а через несколько минут включить, то после включения время время вновь станет тем, которое было при компиляции.
Чтобы этого избежать, нам необходимо еще чуть-чуть модифицировать код. Каждый раз в функции setup будет происходить подсчет «хэша» времени компиляции — будет рассчитываться количество секунд, прошедшее с 00:00:00 до времени компиляции. И этот хэш будет сравниваться с хэшем в EEPROM. Напомним EEPROM — память, которая не обнуляется при отключении питания.
Если значения посчитанного и сохранённого ранее хэша совпадают, то это значит, что перезаписывать время в модуль часов нет необходимости: это уже было сделано. А вот если эта проверка не проходит, то происходит перезапись времени в RTC.
Заключение
В данной статье был показан пример работы с микросхемой часов реального времени RTC DS1307 и микросхемой-драйвером индикатора TM1637, также мы научились получать дату и время на этапе компиляции. Теперь, если выставить нужное время на часах, а потом отключить питание хоть на несколько часов, то после включения время вновь будет точным. Проверено!
СОДЕРЖАНИЕ
Терминология
Хотя учет времени может выполняться без RTC, его использование имеет преимущества:
Источник питания
Сроки
Некоторые RTC используют микромеханический резонатор на кремниевом кристалле RTC. Это уменьшает размер и стоимость RTC за счет уменьшения количества его частей. Микромеханические резонаторы намного более чувствительны к температуре, чем кварцевые резонаторы. Таким образом, они компенсируют изменения температуры с помощью электронного термометра и электронной логики.
Примеры
RTC на основе радио
Программные RTC
время = время + скорость.
Когда переменная «время» превышает константу, обычно степень двойки, номинальное вычисленное время часов (скажем, 1/50 секунды) вычитается из «времени», и вызывается программное обеспечение временной цепочки часов для считать доли секунды, секунды и т. д. С 32- битными переменными для времени и скорости математическое разрешение «скорости» может превышать одну часть на миллиард. Часы остаются точными, потому что иногда они пропускают долю секунды или увеличиваются на две доли. Крошечный скачок (« дрожание ») незаметен почти для всех реальных случаев использования RTC.
Другой подход измеряет температуру кристалла с помощью электронного термометра (например, термистора и аналого-цифрового преобразователя ) и использует полином для вычисления «скорости» примерно один раз в минуту. Наиболее распространенными кристаллами кварца в системе являются кристаллы SC-среза, и их скорость по температуре может быть охарактеризована полиномом 3-й степени. Итак, для них скорость измеряется при четырех температурах. Обычные кристаллы в виде камертона, используемые в часах и многих компонентах RTC, имеют параболические (2-й степени) уравнения температуры и могут быть охарактеризованы только тремя измерениями. Тогда с помощью линейной регрессии можно найти уравнение температуры. Что-то вроде этого подхода можно было бы использовать в коммерческих ИС RTC, но фактические методы эффективного высокоскоростного производства являются патентованными.
Исторические RTC
Некоторые старые разработки компьютеров, такие как Novas и PDP-8, использовали часы реального времени, которые отличались высокой точностью, простотой, гибкостью и низкой стоимостью. Блок питания компьютера выдает импульс логического напряжения либо для каждой полуволны, либо для каждого перехода через ноль сети переменного тока. Провод передает импульс на прерывание. Программа обработки прерывания считает циклы, секунды и т. Д. Таким образом, она может предоставить полные часы и календарь.
Часы также обычно составляли основу программных временных цепей компьютеров; например, обычно это был таймер, используемый для переключения задач в операционной системе. Счетные таймеры, используемые в современных компьютерах, предоставляют аналогичные функции с более низкой точностью и могут прослеживать свои требования к этому типу часов. (например, в PDP-8 первыми были сетевые часы модели DK8EA, а затем последовали кварцевые часы DK8EC.)
В Европе, Северной Америке и некоторых других сетях этот RTC работает, потому что частота сети переменного тока регулируется для обеспечения долговременной точности частоты, такой же хорошей, как и национальные стандартные часы. То есть в таких сетях эти RTC лучше кварцевых часов и менее затратны.
Такая конструкция RTC неприменима в портативных компьютерах или сетях (например, в Южной Азии), которые не регулируют частоту сети переменного тока. Также может показаться неудобным без доступа в Интернет устанавливать часы.
Бесчасовые процессоры
Некоторые материнские платы сделаны без часов реального времени. Часы реального времени не используются либо из желания сэкономить (как в системной архитектуре Raspberry Pi ), либо потому, что часы реального времени могут вообще не понадобиться (как в системной архитектуре Arduino ).
Arduino Mega Server и часы реального времени
В этой статье вы узнаете как Arduino Mega Server работает со временем и как можно создавать проекты на Ардуино, которые имеют привязку к реальному времени, вне зависимости от того, установлен ли в них «железный» RTC-модуль или нет. Все вопросы работы с реальным временем на Ардуино будут подробно разобраны и после прочтения этой статьи вы станете настоящим «мастером часовых дел».
Суть вопроса
Любой мало-мальски серьёзный проект на Ардуино должен иметь представление о текущем реальном времени. Например, показания датчиков должны быть привязаны ко времени (иначе никакой статистики и даже элементарных графиков невозможно будет построить), контроллер должен производить те или иные действия в зависимости от текущего времени суток, выходных, праздников и т. д. Если ваш контроллер не имеет представления о реальном времени, то он превращается в простой автомат, который может производить только элементарные действия по жёстко заданной программе.
Поскольку Arduino Mega Server это мощная и развитая система, то такое положение дел (отсутствие работы с реальным временем) меня, да и всех остальных пользователей системы, никак не могло устроить. Поэтому вопрос интеграции в систему RTC был одним из первых на повестке дня.
Виртуальные часы реального времени
Всё бы ничего, но ни у меня, ни у большинства пользователей AMS не было того самого «железного» модуля RTC, поэтому было принято решение сделать «ход конём» и, в качестве временной меры, организовать часы реального времени, работающие внутри системы, без настоящего физического модуля. Что и было с успехом реализовано.
Итак, как организовать виртуальный RTC, без настоящего модуля. Существует замечательная библиотека Time Library которая и выполняет львиную долю работы по обеспечению нас точным временем. Для начала работы с ней, её нужно скачать, разархивировать и поместить на стандартное место всех библиотек среды Arduino, а именно, в папку:
После этого нам становятся доступны все возможности работы со временем, которые она предоставляет.
Как это работает
Принцип очень простой. Библиотека «запускает» виртуальные часы «внутри» контроллера и предоставляет возможность синхронизировать их множеством способов, на выбор. Вы можете выбрать тот способ, который вам больше подходит. Поскольку Arduino Mega Server это сетевое устройство, то был выбран вариант синхронизации часов через сеть с серверами точного времени. Это могут быть сервера в Интернет или сервера в локальной сети, на которых работает соответствующая служба. Например, в базовом варианте AMS часы синхронизируются с сервером MajorDoMo, и для этого ничего настраивать не нужно, всё работает «из коробки».
Итак, для того, чтобы это заработало, нужно в начале скетча подключить соответствующие библиотеки.
Файл Time.h это собственно библиотека для работы со временем, а остальные файлы необходимы для работы с сетью и для синхронизации времени по протоколу NTP (библиотека Ethernet тоже должна быть у вас установлена).
Далее, вам нужно указать IP-адрес сервера, с которым вы хотите синхронизировать время
и соответствующий порт
но тут есть один момент: порт 8888 подходит для синхронизации в локальной сети, а в Интернет большинство серверов по нему не отвечает, поэтому, если вы планируете синхронизировать время с серверами точного времени в Интернет, то лучше установить порт 123:
осталось только указать временную зону
и создать объект EthernetUDP
На этом подготовительные операции можно считать законченными и можно описывать нужную вам функциональность работы со временем. Функция инициализации:
Здесь нужно обратить внимание на функцию
Эта функция устанавливает источник синхронизации времени (в данном случае это NTP синхронизация через сеть). Но это может быль любой другой источник, например, физический модуль RTC. Выполнение этой функции приводит к установке источника синхронизации (на будущее) и, одновременно, к самой синхронизации времени через этот источник. Именно в момент выполнения этой функции у вас в системе «появляется» точное время.
В самой библиотеке есть ещё одна интересная функция,
которая позволяет задать нужный интервал между синхронизациями (задаётся в секундах, сами синхронизации происходят автоматически, без какого-либо участия с вашей стороны).
Теперь вы можете пользоваться точным временем внутри скетча Ардуино, например, выводить в Serial монитор события не просто, а привязанными к конкретному точному времени. Делается это при помощи функции timeStamp():
которая является обёрткой для функции serialRTC():
Разбор механизма передачи и отображения времени в веб-интерфейсе AMS выходит за рамки данного повествования и достоин отдельной статьи и, если будет интерес, то можно будет написать продолжение и во всех подробностях объяснить, как происходит «магия» отображения времени в веб-интерфейсе Arduino Mega Server.
Собственно, всё. Так были организованы виртуальные часы реального времени в AMS вплоть до 0.12 версии включительно и так же вы можете организовать работу с точным временем в своих проектах, даже если у вас нет физического модуля часов реального времени. Но это ещё не конец истории, а скорее, только начало.
/*
Modul Virtual RTC
part of Arduino Mega Server project
*/
IPAddress timeServer(192, 168, 2, 8);
unsigned int localPort = 8888; // local port to listen for UDP packets
EthernetUDP Udp;
const int timeZone = 4;
time_t prevDisplay = 0; // when the digital clock was displayed
void rtcInit() <
Udp.begin(localPort);
Serialprint(«Waiting for NTP sync… \n»);
setSyncProvider(getNtpTime);
modulRtc = 1;
>
void printDigits(int digits) <
if(digits 0); // discard any previously received packets
Serialprint(«Transmit NTP request\n»);
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() — beginWait = NTP_PACKET_SIZE) <
Serialprint(«Receive NTP response\n»);
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] = SECS_PER_DAY) <
Serial.print(duration / SECS_PER_DAY);
Serialprint(» day «);
duration = duration % SECS_PER_DAY;
>
if(duration >= SECS_PER_HOUR) <
Serial.print(duration / SECS_PER_HOUR);
Serialprint(» hour «);
duration = duration % SECS_PER_HOUR;
>
if(duration >= SECS_PER_MIN) <
Serial.print(duration / SECS_PER_MIN);
Serialprint(» min «);
duration = duration % SECS_PER_MIN;
>
Serial.print(duration);
Serialprint(» sec) \n»);
>
void checkEvent(time_t* prevEvent) <
time_t duration = 0;
time_t timeNow = now();
if (*prevEvent > 0) <
duration = timeNow — *prevEvent;
>
if (duration > 0) <
showDuration(duration);
>
*prevEvent = timeNow;
>
Приятная неожиданность
Я бы ещё долго не занялся интеграцией модулей RTC в систему (хватает и других актуальных задач), но тут, в рамках технологического сотрудничества с нашим проектом, компания CHIPSTER предоставила для тестирования и интеграции в AMS оборудование, среди которого оказались Ethernet модули на чипе W5500 и… модуль часов реального времени на чипе DS3231, что оказалось как нельзя более кстати и послужило толчком для интеграции модулей RTC в систему.
Оказалось, что компания CHIPSTER не только торгует электронным оборудованием, но и разрабатывает собственные изделия для Arduino и автоматизации под торговой маркой Geegrow и имеет большие планы на будущее в этом направлении, в частности, у неё есть проект по выпуску специализированной версии Arduino Mega 2560 с расширенными возможностями и «заточенной» специально под Arduino Mega Server. И, если эта плата будет выпущена, то это будет очень интересное событие. Но вернёмся к часам реального времени.
Реальные часы реального времени
Поскольку модуль RTC оказался у меня под руками, то грех было бы не интегрировать его в систему. Благо это оказалось совсем несложно благодаря всё той же Time Library. Но обо всём по порядку.
Для тех, кто не знает, модули реального времени бывают двух типов — «обычные» (как правило, на чипе DS1307) и «продвинутые» (на чипе DS3231, который мне и достался). Разница между ними заключается в том, что первые не очень точные и могут «убегать» очень быстро и очень сильно, а вторые это высокоточные часы с нормированным уходом не более двух минут в год, то есть реально применимые на практике. А точность достигается благодаря более сложной схемной реализации и встроенной термокомпенсации.
Но программно обе версии модулей совместимы и работать с библиотекой и кодом будут и те и другие. Разница будет только в точности хода.
И конечно, одним из главных свойств часов реального времени является возможность работы при отключении напряжения питания, за счёт встроенной батарейки.
Физическое подключение
Теперь давайте поговорим о том, как физически подключить модуль RTC к Arduino Mega Server или к вашему проекту на Ардуино. Сразу скажу, что это очень просто и вам понадобятся всего два резистора и несколько проводов.
Подключение тривиально: вам нужно найти на своём модуле четыре контакта — GND («земля»), VCC (напряжение питания), SCL (синхросигнал), SDA (данные). Остальные контакты используются в редких и специфических случаях и вы на них можете не обращать внимания.
Итак, вывод GND подключаем к «земле», вывод VCC — к напряжению питания контроллера. Здесь всё просто и никаких вопросов возникать не должно.
С остальными выводами дело обстоит ненамного сложнее. Модуль RTC общается с контроллером по интерфейсу I2C, у которого всего два провода: синхронизация и данные и в контроллерах Arduino уже предусмотрены контакты для подключения этого интерфейса. У Arduino Uno это A4 (SDA) и A5 (SCL), а у arduino Mega это D20 (SDA) и D21 (SCL).
Единственная тонкость заключается в том, что выводы SCL и SDA нужно «подтянуть» к источнику питания через резисторы 4,7 КОм. Если у вас нет точно такого номинала, то можно использовать резисторы из диапазона 2 КОм — 10 КОм.
Программная поддержка
Теперь осталось только дописать поддержку модуля в коде AMS или вашего проекта. Как я уже сказал, это будет очень просто потому, что с модулем будет работать всё та же библиотека Time Library. Правда нам нужно будет добавить ещё одну библиотеку, а именно DS1307RTC Library. Её тоже распаковываем и помещаем в стандартную папку для библиотек:
Добавляем в код вашего скетча следующие строки
Теперь мы во всеоружии и можем приступать к написанию кода самого скетча, работающего с физическим модулем RTC. В функции
и внутреннее время Arduino Mega Server (или вашего контроллера) будет синхронизироваться с «железным» контроллером RTC, а не с серверами в Интернет или локальной сети. Таким образом, вызывая функции setSyncProvider(getNtpTime) и setSyncProvider(RTC.get) вы можете манипулировать источниками синхронизации времени и синхронизировать время так, как вам будет угодно, в зависимости от различных условий.
Ещё одна функция, о которой вам необходимо знать, это
которая позволяет узнать синхронизировано ли время и в зависимости от данного условия предпринять нужные действия.
Тонкий момент
Нужно различать две вещи: время, идущее в «железном» модуле RTC и время, идущее в вашем контроллере. Это не одно и то же. «Главным» для вас является время в контроллере, а время в модуле является лишь источником для синхронизации.
Но! поскольку время в физическом RTC тоже постепенно «уходит», то его тоже нужно подстраивать, синхронизируя с более точными источниками, например, с серверами в Интернет.
Поэтому, оптимальный алгоритм должен быть такой: если есть возможность, то синхронизируем все часы с серверами в Интернет, если сеть недоступна, то начинаем синхронизировать время в контроллере с модулем RTC, как только появляется сеть — переходим опять на синхронизацию через Интернет.
Если вы находитесь в экстремальных условиях, без доступа к каким-либо источникам синхронизации, то можно вручную время от времени корректировать ход «железных» часов.
Давайте, для примера, рассмотрим функцию синхронизации внутренних часов контроллера и модуля RTC через сеть:
Здесь мы сначала получаем точное время по сети
затем, в случае удачи, устанавливаем его в модуль RTC
а затем уже из этого модуля устанавливаем время контроллера
Начальный запуск
Но и это ещё не всё. Существует ещё проблема начального запуска, когда модуль RTC только подключён, но время в нём не выставлено и синхронизироваться с ним поэтому нельзя. Нужно каким-то образом выставить в нём правильное время. В Arduino Mega Server существует два способа решения этой проблемы: можно синхронизировать физический RTC через сеть (если доступны сервера точного времени) или при помощи утилиты Arduino Serial Commander.
Для установки времени в модуле RTC достаточно… нажать на кнопку. Всё остальное сделают за вас два молодца по имени Arduino Mega Server и Arduino Serial Commander. Если вы не пользуетесь AMS, а разрабатываете свой собственный проект, можете взять код из дистрибутива Arduino Mega Server (код доступен и полностью свободен) или поискать решение этой проблемы в Интернет (там есть несколько вариантов решения).
Версия с поддержкой настоящего RTC
Arduino Mega Server, начиная с версии 0.13, поддерживает «железный» RTC. Скачать последнюю актуальную версию вы можете с официального сайта проекта, а задать возникшие вопросы можно на форуме.
И, конечно, я выражаю признательность компании CHIPSTER за сотрудничество и предоставленное для тестирования и интеграции оборудование (о модуле W5500 и о ускорении сетевой работы AMS я расскажу вам в одной из следующих статей).
Дополнение. Открыт канал на Youtube и вот промо ролик Arduino Mega Server, который демонстрирует работу с реальной системой.
Виртуальное время. Часть 1: источники времени в компьютере
Человек, имеющий одни часы, твердо знает, который час. Человек, имеющий несколько часов, ни в чём не уверен.
Закон Сегала
Требования на источники времени
Конечно же, достаточный набор свойств источника зависит от способа использования в программах. Например, одно устройство может предоставлять низкое разрешение и высокую длительность считывания, но при этом быть энергонезависимым и очень стабильным, а другое позволять измерять очень короткие промежутки времени, но при этом быстро переполняться, да ещё и не быть синхронизированным ни с чем больше.
Обзор таймеров в архитектуре PC
Источников времени в системе может быть несколько. Прикладные программы редко обращаются к каким-либо из них напрямую. Вместо этого используются всевозможные API, предлагаемые использованным языком программирования (например, C++11 < chrono >), средой исполнения (например, gettimeofday из POSIX или QueryPerformanceCounter на MS Windows), или даже системными вызовами используемой операционной системы.
Самой ОС также необходимо знать время и уметь отмерять его отрезки для планирования работы пользовательских потоков, учёта потреблённых ими ресурсов, профилировки производительности, управления энергопотреблением и т.п. При этом сама ОС работает напрямую с интерфейсами, предоставляемыми аппаратурой. Так как таймеров присутствует много, современные ОС умеют выбирать один «центрально» используемый в начале загрузки, исходя из своих представлений о «качестве» обнаруженных устройств (например, на некоторых системах часть таймеров может быть занесена в «чёрный список» из-за известных проблем в работе) или же настроек пользователя (параметр clocksource у ядра Linux и опции useplatformclock, tscsyncpolicy, disabledynamictick у BCDEDIT в Windows).
Опишу наиболее часто встречаемые устройства, являющиеся часами и таймерами в PC.
Общераспространённые
Часы реального времени (Real Time Clock, RTC) — источник текущей даты и времени для нужд ОС. Типичное разрешение этого таймера — одна секунда. Все системы, удовлетворяющие стандарту ACPI, имеют чип RTC, совместимый с Motorola MC146818, присутствовавшем в оригинальном IBM PC/AT с 1984 года. В современных системах RTC обычно интегрирован в набор системной логики южного моста на материнской плате (что означает довольно большую задержку при чтении). Энергонезависимость этого таймера обеспечивается специальной батарейкой. Принципы программирования RTC вызывают ностальгию по BCD-числам и проблеме Y2K.
Programmable Interval Timer (PIT) 8253 или 8254 от Intel — стандартный счётчик и таймер, имеющийся в PC с самого начала существования этой платформы (1981 год). Как и RTC, изначально был отдельной микросхемой, а ныне является частью системной логики. Довольно интересное устройство, содержащее три таймера (хотя последние два всегда были зарезервированы под задачи обновления ОЗУ и работу PC-спикера соответственно) и позволяющее запрограммировать их в различные режимы: периодические прерывания, однократное (one-shot) прерывание по таймауту, меандр и т.д.
Первый канал PIT до сих пор может использоваться ОС как источник прерываний для работы вытесняющего планировщика задач. Однако по современным меркам он не очень удобен в работе: низкая частота осциллятора 1193181,8 Гц (странное значение — это историческое наследие от частоты развёртки NTSC), ширина счётчика всего 16 бит (частое переполнение) при ширине регистров статуса и команд всего в восемь бит (т.е. приходится передавать или читать значение по частям), да и доступ к регистрам через медленный и негибкий механизм PIO (команды IN/OUT процессора).
Local APIC (advanced programmable interrupt controller), встроенный во все современные процессоры Intel (начиная с архитектуры P54C) и который в своём составе имеет ещё и таймер. Более того, каждый логический процессор имеет свой собственный LAPIC, что может быть удобно для выполнения работы, локальной для текущего ядра, без необходимости управления ресурсами. Однако, данное устройство не имеет фиксированной известной частоты; последняя скорее привязана к частоте ядра. Поэтому перед использованием программе необходимо её вычислить (калибровать), а для этого нужно дополнительное референсное устройство. Режимы, поддерживаемые LAPIC: однократное прерывание, периодические прерывание, и период, определяемый TSC.
Таймер в составе ACPI, почему-то называемый Performance Monitoring Timer (PMTIMER) — ещё одно устройство, которое поддерживается всеми системами, реализующими стандарт ACPI, с 1996 года. Данный таймер имеет частоту 3.579545 МГц, ширина регистра-счётчика может быть 24 или 32 бита. Сам таймер всегда активен при включенном питании системы и не зависит от режима работы центрального процессора.
High Precision Event Timer (HPET) — устройство, созданное как замена устаревшему PIT. Согласно стандарту, HPET должен содержать осциллятор, работающий с фиксированной частотой по крайней мере в 10 МГц, величину которой можно программно прочитать из его статусного регистра, и монотонно увеличивающий значение счётчик шириной в 64 бита. Также он должен содержать минимум три компаратора шириной в 32 или 64 бита, которые и используются для генерации прерываний по истечении запрограммированных периодов времени. Как и PIT, он способен работать в периодическом режиме или в режиме однократного прерывания. При этом метод его программирования (MMIO вместо PIO) удобнее и быстрее, чем у PIT, что вместе с повышенным разрешением, позволяет задавать интервалы более точно и с меньшей задержкой. Требуемая стабильность генератора равна 0,05% для интервалов длиннее 1 мс и 0,2% для промежутков короче 100 мкс; много это или мало — зависит от приложений.
Несмотря на то, что HPET уже давно присутствует в PC (с 2005 года), операционные системы не торопятся начать его использовать. Частично это вызвано не самым удобным способом задания интервалов с помощью возрастающего счётчика вместо убывающего — из-за немгновенности операций существует риск «не успеть» и задать событие в прошлом. Зачастую ОС используют таймер из APIC или PMTIMER, или же функциональность TSC, использующую такты процессора в качестве источника времени.
Трудная судьба инструкции RDTSC
История TSC достаточно интересна и поучительна, чтобы остановиться на ней подольше.
Сама идея очень прозрачная — использовать в качестве источника времени сам процессор, а точнее его тактовый генератор. Текущий номер такта сохраняется в регистре TSC (timestamp counter).
С помощью TSC можно как узнавать время от начала работы, так и замерять интервалы времени с помощью двух чтений. TSC также работает как будильник в связке с APIC в режиме TSC deadline.
Что ж, TSC — вполне естественная штука с простой логикой и простым сценарием использования, которая должна обладать многими полезными свойствами: высокое разрешение (один такт ЦПУ), низкая задержка при чтении (десятки тактов), редкие переполнения (64-битного счётчика должно хватать минимум на 10 лет), монотонность чтений (ведь счётчик всегда увеличивает своё значение), равномерность (процессор всегда работает), согласованность с другими таймерами (при старте системы можно выставить нужное значение записью в MSR).
Разве что-то могло пойти не так? На пути к успешному использованию TSC в качестве основного средства измерения времени в PC встала последующая эволюция процессоров. Новые возможности, появившиеся в процессорах после Pentium, «испортили» RDTSC и много лет мешали использовать её как основной таймер в популярных ОС. Так, в 2006 году один из Linux-разработчиков Ingo Molnar писал:
Мы наблюдали, что в течение 10 лет ни одной реализации gettimeofday, основанной на TSC и работающей в общем случае, не было написано (а я написал первую версию для Pentium, так что и я в этом повинен), и что лучше мы обойдёмся без неё.
We just observed that in the past 10 years no generally working TSC-based gettimeofday was written (and i wrote the first version of it for the Pentium, so the blame is on me too), and that we might be better off without it.
Отмечу, что со временем в архитектуру IA-32 вносились коррективы, устранявшие проявившиеся недостатки, и в настоящий момент TSC может (пока опять не сломали) быть использован в том качестве, в котором он задумывался.
Прочие устройства
Выше я описал наиболее часто распространённые и используемые устройства по определению времени. Конечно же, конкретные системы могут иметь дополнительные устройства, уникальные для процессора, интегрированной логики или даже в форме специализированных периферийных устройств (например, сверхточные атомные часы). Степень их доступности из программ зависит от того, существует ли драйвер для конкретного устройства в выбранной ОС. Так, пробежавшись по исходникам Linux, я нашёл как минимум ещё два поддерживаемых источника времени для сборок x86: устройство NatSemi SCx200 в системах AMD Geode, и Cyclone для систем IBM x440. К сожалению, в Интернете не очень много документации по ним.
Заключение
Я надеюсь, что из этой заметки стало понятно, что работа со временем внутри компьютера на системном уровне на самом деле далека от тривиальной. Требования к устройствам, поставляющим время, зависят от решаемой задачи, и не всегда легко найти полностью подходящий вариант. При этом сами устройства зачастую содержат «архитектурные особенности», способные сломать голову несчастному программисту.
Однако это всё архитектурная присказка к симуляционной сказке. На самом деле мне хотелось рассказать о том, как можно моделировать весь этот зоопарк устройств. В следующей статье я опишу, как проявляется капризная природа времени при создании виртуальных окружений — симуляторов и мониторов виртуальных машин. Спасибо за внимание!