/***************************************************************************//**
  @defgroup ltimer Таймер
  Модуль представляет собой набор функций для реализации таймера на основе
  системных часов процессора (контроллера) или ОС.

  Работа таймера основана на системном счетчике, который должен равномерно
  увеличиваться с течением времени.
  Реализация функций для инициализации системного счетчика и получения его значения
  реализуются для каждой платформы своим портом. Также порт определяет количество
  тиков системного счетчика за секунду с помощью константы #LCLOCK_TICKS_PER_SECOND
  и тип #t_lclock_ticks для представления текущего значения системного счетчика.
  Эти функции могут использоваться как напрямую, так и через общее API для работы
  с таймером из ltimer.h, который может использоваться для отсчета интервалов
  времени.

  Все функции принимают значения в отсчетах системного счетчика, поэтому необходимо
  использовать константу #LCLOCK_TICKS_PER_SECOND для перевода времени в количество отсчетов.
  Также можно использовать макросы LTIMER_MS_TO_CLOCK_TICKS() и LTIMER_CLOCK_TICKS_TO_MS()
  для перевода интервалов в миллисекундах в количество отсчетов и наоборот.

  Для работы с модулем в проекте нужно добавить в стандартные пути
  для включения заголовочных файлов пути к файлу ltimer.h и к директории с портом
  для используемой платформы, а также путь к файлу lcspec.h, в котором
  описаны специфические для компилятора макросы (в данном случае используется
  #LINLINE для определения inline-функций). Также необходимо в исходники
  добавить файл lclock.c из нужного порта.

  При работе с системным счетчиком контроллера перед вызовом функций данного модуля
  необходимо вызвать функцию lclock_init() (или lclock_init_val(), если нужно задать
  начальное значение системного счетчика). При работе с системными часами ОС
  обычно эта функция не требуется, так как таймер ОС уже инициализирован
  самой ОС (однако ее вызов можно оставить, так как каждый порт реализует хотя бы
  "пустую" версию этой функции).

  Для работы с системным счетчиком напрямую используется функция lclock_get_ticks(),
  возвращающая значение системного счетчика.

  Для работы с API таймера, необходимо объявить переменную типа #t_ltimer, которая
  будет хранить состояние таймра. Работать с полями структуры напрямую не рекомендуется.
  Вместо этого следует использовать функции из ltimer.h, которые принимают эту
  переменную в качестве первого аргумента.

  При стандартной работе, сперва таймер устанавливается для отсчета заданного
  интервала с помощью ltimer_set(). Проверить, истек ли заданный интервал,
  можно с помощью ltimer_expired(). Количество отсчетов системного счетчика
  до истечения интервала можно получить с помощью ltimer_expiration().
  Перезапустить таймер на установленный в предыдущем ltimer_set() интервал можно
  с помощью ltimer_reset() или ltimer_restart() (относительно времени последнего
  истечения таймера или текущего времени соответственно).

  Например, если какие-то действия нужно выполнять в течении заданного интервала,
  то это можно сделать следующим образом:

  @code
  //время интервала в мс
  #define TIMEOUT_MS   200

  void main(void) {
    t_ltimer tmr;
    //инициализируем системный счетчик
    lclock_init();
    // устанавливаем таймер на заданное кол-во мс
    ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(TIMEOUT_MS));
    while (!ltimer_expired(&tmr)) {
        //выполняем требуемые действия

    }

    //интервал завершился
  }
  @endcode


  В качестве основной идеи была взята реализация таймера для стека uIP от
  Adam Dunkels <adam@sics.se>
  *****************************************************************************/

/***************************************************************************//**
  @addtogroup ltimer
  @{
  @file ltimer.h
  Файл содержит описание функций для работы с таймером. Все функции реализованы
  как inline-функции, так что файл содержит и реализацию функций
  @author Borisov Alexey <borisov@lcard.ru>
  *****************************************************************************/
#ifndef __LTIMER_H__
#define __LTIMER_H__

#include "lclock.h"
#include "lcspec.h"

/** Структура для описания состояния таймера */
typedef struct ltimer {
  t_lclock_ticks start; /**< Значение системного таймера, с которого ведется отсчет */
  t_lclock_ticks interval; /**< Интервал времени, после которого таймер считается истекшим */
} t_ltimer;


/***************************************************************************//**
  @brief Получить время запуска таймера

  @param[in] t          Указатель на состояние таймера
  @return               Значение времени запуска таймера в системных клоках
 ******************************************************************************/
static LINLINE t_lclock_ticks ltimer_start_time(t_ltimer *t) {
    return t->start;
}

/***************************************************************************//**
  @brief Получить время срабатывания таймера

  @param[in] t          Указатель на состояние таймера
  @return               Значение времени срабатывания таймера в системных клоках
 ******************************************************************************/
static LINLINE t_lclock_ticks ltimer_expiration_time(t_ltimer *t) {
    return t->start + t->interval;
}

/***************************************************************************//**
  @brief Получить установленный интервал для таймера

  @param[in] t          Указатель на состояние таймера
  @return               Значение установленного интервала в системных клоках
 ******************************************************************************/
static LINLINE t_lclock_ticks ltimer_interval(t_ltimer *t) {
    return t->interval;
}



/***************************************************************************//**
  @brief Установить временной интервал относительно заданного времени

  Функция устанавливает таймер на заданный интервал, аналогично ltimer_set(),
  но относительно заданного времени (значения системного счетчика), а не
  относительно текущего.
  С помощью функции ltimer_expired() можно в дальнейшем проверить, истек ли
  заданный интервал.
  @param[in] t           Указатель на состояние таймера
  @param[in] interval    Интервал, через который таймер истечет в системных клоках.
  @param[in] start_time  Значение системного счетчика, относительно которого
                         устанавливается временной интервал
 ******************************************************************************/
static LINLINE void ltimer_set_at(t_ltimer *t, t_lclock_ticks interval, t_lclock_ticks start_time) {
    t->interval = interval;
    t->start = start_time;
}

/***************************************************************************//**
  @brief Установить временной интервал

  Функция устанавливает таймер на заданный интервал относительно текущего времени.
  С помощью функции ltimer_expired() можно в дальнейшем проверить, истек ли
  заданный интервал.
  @param[in] t        Указатель на состояние таймера
  @param[in] interval Интервал, через который таймер истечет в системных клоках.
 ******************************************************************************/
static LINLINE void ltimer_set(t_ltimer *t, t_lclock_ticks interval) {
    ltimer_set_at(t, interval, lclock_get_ticks());
}

/***************************************************************************//**
  @brief Установить временной интервал в мс

  Функция устанавливает таймер на заданный интервал в милисекундах относительно
  текущего времени.
  С помощью функции ltimer_expired() можно в дальнейшем проверить, истек ли
  заданный интервал.
  @param[in] t        Указатель на состояние таймера
  @param[in] ms       Интервал, через который таймер истечет в милисекундах.
 ******************************************************************************/
static LINLINE void ltimer_set_ms(t_ltimer *t, unsigned ms) {
    ltimer_set(t, LTIMER_MS_TO_CLOCK_TICKS(ms));
}



/***************************************************************************//**
  @brief Сброс таймера.

  Данная функции перезапускает таймер на интервал, заданный в последнем ltimer_set()
  относительно времени последнего истечения таймера (а не текущего времени, как
  ltimer_restart()). По сути функция продлевает время истечения таймера на тот
  же интервал, что был задан. Интервалы между срабатыванием таймеров
  сохраняется во времени, в отличие от использования ltimer_restart().
  @param[in] t        Указатель на состояние таймера
  *****************************************************************************/
static LINLINE void ltimer_reset(t_ltimer *t) {
    t->start += t->interval;
}



/***************************************************************************//**
  @brief Перезапуск таймера с заданного времени.

  Данная функции перезапускает таймер на интервал, заданный в последнем ltimer_set(),
  относительно текущего времени, начиная с указанного времени.
  @param[in] t          Указатель на состояние таймера
  @param[in] start_time Значение системного счетчика, соответствующего времени,
                         с которого перезапускается таймер
  *****************************************************************************/
static LINLINE void ltimer_restart_at(t_ltimer *t, t_lclock_ticks time) {
    t->start = time;
}


/***************************************************************************//**
  @brief Перезапуск таймера.

  Данная функции перезапускает таймер на интервал, заданный в последнем ltimer_set(),
  относительно текущего времени.
  @note  Время срабатывания периодического таймер будет "плыть" при использовании
  данной функции (из-за случайной разницы между текущим временем и временем
  завершения предыдущего интервала). Для периодического таймера можно использовать
  ltimer_reset()
  @param[in] t        Указатель на состояние таймера  
  *****************************************************************************/
static LINLINE void ltimer_restart(t_ltimer *t) {
    ltimer_restart_at(t, lclock_get_ticks());
}

/***************************************************************************//**
  @brief Проверка, истечет ли заданный интервал в определенное время

  Функция проверяет, истечет ли установленный с помощью ltimer_set() интервал
  в моент времени, определяемый заданным значением системного счетчика.
    @param[in] t        Указатель на состояние таймера
    @param[in] exp_time Значение системного счетчика, для которого проверяется,
                        истечет ли таймер в данный момент
    @return             Не ноль, если интервал истечет, иначе --- ноль
 ******************************************************************************/
static LINLINE int ltimer_expired_at(const t_ltimer *t, t_lclock_ticks exp_time) {
    return (t_lclock_ticks)(exp_time - t->start) > (t_lclock_ticks)t->interval;
}

/***************************************************************************//**
  @brief Проверка, истек ли заданный интервал

  Функция проверяет, истек ли заданный с помощью ltimer_set() интервал.
    @param[in] t        Указатель на состояние таймера
    @return             Не ноль, если интервал истек, иначе --- ноль
 ******************************************************************************/
static LINLINE int ltimer_expired(const t_ltimer *t) {
    return ltimer_expired_at(t, lclock_get_ticks());
}

/***************************************************************************//**
  @brief Получить время до истечения интервала от заданного момента

  Функция возвращает значение интервала времени (в системных клоках) от заданного
  момента до времени истечения установленного с помощью ltimer_set() интервала.
    @param[in] t        Указатель на состояние таймера
    @param[in] cur_time Значение системного счетчика, от которого считается время
                        до момента истечения интервала
    @return             Время (в клоках системного счетчика) до истечения интервала
*******************************************************************************/
static LINLINE t_lclock_ticks ltimer_expiration_from(const t_ltimer *t, t_lclock_ticks cur_time) {
    t_lclock_ticks expired = (cur_time-t->start);
    return (expired > t->interval) ? 0 : t->interval - expired;
}

/***************************************************************************//**
  @brief Получить время до истечения интервала

  Функция возвращает значение интервала времени (в системных клоках) от текущего
  момента до времени истечения установленного с помощью ltimer_set() интервала.
    @param[in] t        Указатель на состояние таймера
    @return             Время (в клоках системного счетчика) до истечения интервала
*******************************************************************************/
static LINLINE t_lclock_ticks ltimer_expiration(const t_ltimer *t) {
    return ltimer_expiration_from(t, lclock_get_ticks());
}

#endif /* __LTIMER_H__ */

/** @} */

