Вся правда об ОСРВ. Статья #28. Программные таймеры

в 9:15, , рубрики: api, rtos, микроконтроллеры, осрв, программирование микроконтроллеров, программные таймеры, системное программирование, служебные вызовы

Идея программных таймеров была введена в одной из предыдущих статей. Они являются объектами ядра, предоставляющими задачам простой способ запуска событий по времени, или, чаще всего, способ выполнять действия на регулярной основе. Все детали функционала, связанного со временем (точность, обработка прерываний и т.д.) в Nucleus SE были рассмотрены в предыдущей статье.

Вся правда об ОСРВ. Статья #28. Программные таймеры - 1

Предыдущие статьи серии:

Вся правда об ОСРВ. Статья #27. Системное время
Статья #26. Каналы: вспомогательные службы и структуры данных
Статья #25. Каналы передачи данных: введение и основные службы
Статья #24. Очереди: вспомогательные службы и структуры данных
Статья #23. Очереди: введение и базовые службы
Статья #22. Почтовые ящики: вспомогательные службы и структуры данных
Статья #21. Почтовые ящики: введение и базовые службы
Статья #20. Семафоры: вспомогательные службы и структуры данных
Статья #19. Семафоры: введение и базовые службы
Статья #18. Группы флагов событий: вспомогательные службы и структуры данных
Статья #17. Группы флагов событий: введение и базовые службы
Статья #16. Сигналы
Статья #15. Разделы памяти: службы и структуры данных
Статья #14. Разделы памяти: введение и базовые службы
Статья #13. Структуры данных задач и неподдерживаемые вызовы API
Статья #12. Службы для работы с задачами
Статья #11. Задачи: конфигурация и введение в API
Статья #10. Планировщик: дополнительные возможности и сохранение контекста
Статья #9. Планировщик: реализация
Статья #8. Nucleus SE: внутреннее устройство и развертывание
Статья #7. Nucleus SE: введение
Статья #6. Другие сервисы ОСРВ
Статья #5. Взаимодействие между задачами и синхронизация
Статья #4. Задачи, переключение контекста и прерывания
Статья #3. Задачи и планирование
Статья #2. ОСРВ: Структура и режим реального времени
Статья #1. ОСРВ: введение.

Использование таймеров

Программные таймеры могут быть настроены на однократное срабатывание, то есть они запускаются, а затем, после указанного периода времени, просто завершают цикл. Либо таймер может быть настроен на перезапуск: после завершения счета таймер автоматически перезапускается. Время работы после перезапуска может отличаться от первичного времени работы. Кроме того, таймер можно опционально настроить на выполнение специальной функции завершения, которая выполняется когда (или каждый раз когда) таймер завершает рабочий цикл.

Настройка таймеров

Количество таймеров

Как и для большинства аспектов Nucleus SE, настройка таймеров управляется директивами #define в nuse_config.h. Основным параметром является NUSE_TIMER_NUMBER, который определяет сконфигурированные в приложении таймеров. По умолчанию это значение равно нулю (то есть в приложении таймеры не используются), и может принимать значения вплоть до 16. Некорректное значение приведет к ошибке компиляции, которая будет сгенерирована проверкой в файле nuse_config_check.h (этот файл входит в nuse_config.c и компилируется вместе с ним), что приведет к срабатыванию директивы #error.

Выбор ненулевого значения является главным активатором таймеров. Этот параметр используется при определении структур данных, и от его значения зависит их размер. Кроме того, ненулевое значение активирует настройки API.

Активация функции завершения

В Nucleus SE я пытался найти возможность сделать функционал опциональным, там, где это позволит сэкономить память. Хорошим примером является поддержка функций завершения таймеров. Помимо того, что эта возможность опциональна для каждого таймера, механизм может быть активирован (или нет) для всего приложения при помощи параметра NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT в nuse_config.h. Присвоение этому параметру значения FALSE блокирует определение двух структур данных в ПЗУ, которые будут подробно описаны в этой статье.

Активация API

Каждая функция API (служебный вызов) в Nucleus SE имеет активирующую директиву #define в nuse_config.h. Для таймеров к таким символам относятся:
NUSE_TIMER_CONTROL
NUSE_TIMER_GET_REMAINING
NUSE_TIMER_RESET
NUSE_TIMER_INFORMATION
NUSE_TIMER_COUNT

По умолчанию, всем активаторам присвоено значение FALSE, таким образом, все служебные вызовы отключены, блокируя включение реализующего их кода. Для настройки таймеров в приложении нужно выбрать необходимые служебные вызовы API и присвоить им значение TRUE.

Ниже приведен фрагмент кода из файла nuse_config.h по умолчанию.

#define NUSE_TIMER_NUMBER 0/* количество таймеров приложения в системе 0-16 */
                                 /* Активаторы служебного вызова */
   #define NUSE_TIMER_CONTROL          FALSE
   #define NUSE_TIMER_GET_REMAINING    FALSE
   #define NUSE_TIMER_RESET            FALSE
   #define NUSE_TIMER_INFORMATION      FALSE
   #define NUSE_TIMER_COUNT            FALSE

Если функция API, связанная с таймерами, активирована, а в приложении нет сконфигурированных таймеров (кроме функции NUSE_Timer_Count(), которая разрешена всегда), возникнет ошибка компиляции. Если ваш код использует вызов API, который не был активирован, произойдет ошибка компоновки, так как код реализации не был включен в приложение.

Служебные вызовы таймеров

Nucleus RTOS поддерживает восемь служебных вызовов, связанных с таймерами, которые предоставляют следующий функционал:

  • Управление (запуск/остановка) таймером. В Nucleus SE реализовано в функции NUSE_Timer_Control().
  • Получение оставшегося времени таймера. В Nucleus SE реализовано в NUSE_Timer_Get_Remaining().
  • Восстановление таймера в исходное состояние (сброс). В Nucleus SE реализовано в NUSE_Timer_Reset().
  • Предоставление информации о конкретном таймере. В Nucleus SE реализовано в NUSE_Timer_Information().
  • Возврат количества сконфигурированных (на данный момент) таймеров в приложении. В Nucleus SE реализовано в NUSE_Timer_Count().
  • Добавление нового таймера в приложение (создание). В Nucleus SE не реализовано.
  • Удаление таймера из приложения. В Nucleus SE не реализовано.
  • Возвращение указателей на все таймеры в приложении. В Nucleus SE не реализовано.

Реализация каждого служебного вызова будет подробно рассмотрена далее.

Службы таймеров

Фундаментальные операции, которые можно выполнять с таймером, это управление (запуск и остановка) и считывание текущего значения. Nucleus RTOS и Nucleus SE предоставляют два базовых служебных вызова API для этих операций.

Управление таймером

Служебный вызов Nucleus RTOS API для управления таймером позволяет активировать и деактивировать таймер (запускать и останавливать). Nucleus SE предоставляет аналогичный функционал.

Вызов для управления таймером в Nucleus RTOS
Прототип служебного вызова:

STATUS NU_Control_Timer (NU_TIMER *timer, OPTION enable);

Параметры:
timer – указатель на предоставляемый пользователем блок управления таймером;
enable – требуемая функция, может принимать значения NU_ENABLE_TIMER или NU_DISABLE_TIMER.

Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INAVLID_TIMER – некорректный указатель на таймер;
NU_INAVLID_ENABLE – некорректная функция.

Вызов для управления таймером в Nucleus SE
Этот вызов API поддерживает полный функционал Nucleus RTOS API.

Прототип служебного вызова:
STATUS NUSE_Timer_Control (NUSE_TIMER timer, OPTION enable);

Параметры:
timer – индекс (ID) используемого таймера;
enable – требуемая функция, может принимать значения NUSE_ENABLE_TIMER или NUSE_DISABLE_TIMER.

Возвращаемое значение:
NUSE_SUCCESS – вызов был успешно завершен;
NUSE_INCALID_TIMER – некорректный индекс таймера;
NUSE_INVALID_ENABLE – некорректная функция.

Реализация управления таймеров в Nucleus SE
Код функции API NUSE_Timer_Control() (после проверки параметров), достаточно прост:

NUSE_CS_Enter();
if (enable == NUSE_ENABLE_TIMER)
{
   NUSE_Timer_Status[timer] = TRUE;
   if (NUSE_Timer_Expirations_Counter[timer] == 0)
   {
      NUSE_Timer_Value[timer] = NUSE_Timer_Initial_Time[timer];
   }
   else
   {
      NUSE_Timer_Value[timer] = NUSE_Timer_Reschedule_Time[timer];
   }
}
else  /* enable == NUSE_DISABLE_TIMER */
{
   NUSE_Timer_Status[timer] = FALSE;
}
NUSE_CS_Exit();

Если была указана функция NUSE_DISABLE_TIMER, статусу таймера (параметр NUSE_Timer_Status[]) присваивается значение FALSE, что приводит к игнорированию таймера обработчиком прерываний.

При выборе функции NUSE_ENABLE_TIMER счетчик таймера (NUSE_Timer_Value[]) устанавливается в значение NUSE_Timer_initial_Time[], при условии что таймер ни разу не останавливался с момента последнего сброса. В противном случае ему присваивается значение NUSE_Timer_Reschedule_Time[]. Затем статусу таймера (параметр NUSE_Timer_Status[]) присваивается значение TRUE, что приводит к тому, что таймер обрабатывается обработчиком прерываний.

Считывание таймера

Для получения оставшегося времени таймера служебный вызов Nucleus RTOS API возвращает количество тактов до истечения срока его действия. Nucleus SE предоставляет аналогичный функционал.

Вызов для получения оставшегося времени в Nucleus RTOS

Прототип служебного вызова:
STATUS NU_Get_Remaining_Time (NU_TIMER *timer, UNSIGNED *remaining_time);

Параметры:
timer – указатель на предоставленный пользователем блок управления таймером;
remaining_time – указатель на хранилище значения оставшегося времени, которое является переменной типа UNSIGNED.

Возвращаемое значение
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_TIMER – некорректный указатель на таймер.

Вызов для получения оставшегося времени в Nucleus SE
Этот вызов API поддерживает полный функционал Nucleus RTOS API.

Прототип служебного вызова:
STATUS NUSE_Timer_Get_Remaining(NUSE_TIMER timer, U16 *remaining_time);

Параметры:
timer – индекс (ID) используемого таймера;
remaining_time – указатель на хранилище значения оставшегося времени, которое является переменной типа U16.

Возвращаемое значение:
NUSE_SUCCESS – вызов был успешно завершен;
NUSE_INVALID_TIMER – некорректный индекс таймера;
NUSE_INVALID_POINTER – нулевой указатель на оставшееся время (NULL).

Реализация считывания таймера в Nucleus SE
Вариант кода функции API NUSE_Timer_Get_Remaining() (после проверки параметров) тривиально прост. Значение NUSE_Timer_Value[] получается, а затем возвращается в критической секции.

Вспомогательные службы таймеров

Nucleus RTOS имеет четыре вызова API, которые предоставляют вспомогательные функции, связанные с таймерами: сброс таймера, получение информации о таймере, получение количества таймеров в приложении и получение указателей на все таймеры в приложении. Первые три функции реализованы в Nucleus SE.

Сброс таймера

Этот вызов API сбрасывает таймер в исходное, неиспользуемое состояние. Таймер может быть активирован или деактивирован после завершения этого вызова. Его можно использовать только после того, как таймер был отключен (при помощи NUSE_Timer_Control()). В следующий раз когда таймер будет активирован, он будет инициализирован с параметром NUSE_Timer_Initial_Time[]. Nucleus RTOS позволяет предоставить новое исходное состояние и провести перепланирование времени, а также указать функцию завершения при сбросе таймера. В Nucleus SE эти значения устанавливаются во время настройки и не могут быть изменены, поскольку они хранятся в ПЗУ.

Вызов для сброса таймера в Nucleus RTOS

Прототип служебного вызова:
STATUS NU_Reset_Timer (NU_TIMER *timer, VOID (*expiration_routine) (UNSIGNED), UNSIGNED initial_time, UNSIGNED reschedule_time, OPTION enable);

Параметры:
timer – указатель на сбрасываемый таймер;
expiration_routine – указывает функцию, которая будет выполнена при завершении цикла;
initial_time – исходное количество тактов таймера до завершения цикла;
reschedule_time – количество тактов таймера до завершения второго и последующих циклов;
enable – требуемое состояние таймера после сброса, может принимать значения NU_ENABLE_TIMER или NU_DISABLE_TIMER.

Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_TIMER – некорректный указатель на блок управления таймера;
NU_INVALID_FUNCTION – нулевой указатель на функцию завершения (NULL);
NU_INVALID_ENABLE – указанное состояние некорректно;
NU_NOT_DISABLED – таймер уже запущен (его следует остановить перед вызовом этой функции).

Вызов для сброса таймера в Nucleus SE
Этот служебный вызов API поддерживает упрощенную версию ключевого функционала Nucleus RTOS API.

Прототип служебного вызова:
STATUS NUSE_Timer_Reset (NUSE_TIMER timer, OPTION enable);

Параметры:
timer – индекс (ID) сбрасываемого таймера;
enable – требуемое состояние после сброса, может принимать значения NUSE_ENABLE_TIMER или NUSE_DISABLE_TIMER.

Возвращаемое значение:
NUSE_SUCCESS – вызов был успешно завершен;
NUSE_INVALID_TIMER – некорректный индекс таймера;
NUSE_INVALID_ENABLE – указанное состояние некорректно;
NUSE_NOT_DISABLED – таймер уже запущен (его следует остановить перед вызовом этой функции).

Реализация сброса таймера в Nucleus SE
Вариант кода функции API NUSE_Timer_Reset() (после проверки параметров и текущего состояния) довольно прост:

NUSE_CS_Enter();
NUSE_Init_Timer(timer);
if (enable == NUSE_ENABLE_TIMER)
{
   NUSE_Timer_Status[timer] = TRUE;
}
/* иначе enable == NUSE_DISABLE_TIMER а статус остается FALSE */
NUSE_CS_Exit();

Вызов NUSE_Init_Timer() инициализирует значение времени и очищает счетчик завершения. После этого при необходимости проверятся значение требуемого состояния и включен ли таймер.

Информация о таймере

Этот служебный вызов позволяет получить набор информации о таймере. Реализация Nucleus SE отличается от Nucleus RTOS тем, что она возвращает меньше информации, так как именование объектов не поддерживается.

Вызов для получения информации о таймере в Nucleus RTOS

Прототип служебного вызова:
STATUS NU_Timer_Information (NU_TIMER *timer, CHAR *name, OPTION *enable, UNSIGNED *expirations, UNSIGNED *id, UNSIGNED *initial_time, UNSIGNED *reschedule_time);

Параметры:
timer – указатель на таймер, о котором запрашивается информация;
name – указатель на 8-символьную область для имени таймера;
enable – указатель на переменную, принимающую текущее состояние активатора таймера: NU_ENABLE_TIMER или NU_DISABLE_TIMER;
expirations – указатель на переменную, принимающую счетчик количества завершений цикла таймера с момента его последнего сброса;
id – указатель на переменную, принимающую значение параметра, переданного в функцию завершения цикла таймера;
initial_time – указатель на переменную, принимающую значение, в которое будет инициализирован таймер после сброса;
reschedule_time – указатель на переменную, принимающую значение, в которое будет инициализирован таймер после завершения.

Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_TIMER – некорректный указатель на таймер.

Вызов для получения информации о таймере в Nucleus SE
Этот вызов API поддерживает основной функционал Nucleus RTOS API.

Прототип служебного вызова:
STATUS NUSE_Timer_Information (NUSE_TIMER timer, OPTION *enable, U8 *expirations, U8 *id, U16 *initial_time, U16 *reschedule_time);

Параметры:
timer – индекс таймера, о котором запрашивается информация;
enable – указатель на переменную, принимающую значение TRUE или FALSE в зависимости от того, активирован таймер или нет;
expirations – указатель на переменную типа U8, принимающую значение количества завершений таймера с момента его последнего сброса;
id – указатель на переменную типа U8, принимающую значение параметра, переданного в функцию завершения таймера (вернет пустое значение, если функции завершения отключены);
initial_time – указатель на переменную типа U16, принимающую значение, которым будет инициализирован таймер после сброса;
reschedule_time – указатель на переменную типа U16, принимающую значение, которым будет инициализирован таймер после завершения.

Возвращаемое значение:
NUSE_SUCCESS – вызов был успешно завершен;
NUSE_INVALID_TIMER – некорректный индекс таймера;
NUSE_INVALID_POINTER – один или несколько параметров-указателей некорректны.

Реализация получения информации о таймере в Nucleus SE
Реализация этого вызова API довольно проста:

NUSE_CS_Enter();
if (NUSE_Timer_Status[timer])
{
   *enable = NUSE_ENABLE_TIMER;
}
else
{
   *enable = NUSE_DISABLE_TIMER;
}
*expirations = NUSE_Timer_Expirations_Counter[timer];
#if NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT
   *id = NUSE_Timer_Expiration_Routine_Parameter[timer];
#endif
*initial_time = NUSE_Timer_Initial_Time[timer];
*reschedule_time = NUSE_Timer_Reschedule_Time[timer];
NUSE_CS_Exit();

Функция возвращает статус таймера. Значение параметра функции завершения возвращается, только если их поддержка была активирована в приложение.

Получение количества таймеров

Этот служебный вызов возвращает количество таймеров, сконфигурированных в приложении. В Nucleus RTOS это значение может меняться со временем, и возвращаемое значение будет отображать текущее количество таймеров. В Nucleus SE возвращаемое значение задается на этапе сборки и не может изменяться.

Вызов для счетчика таймеров в Nucleus RTOS

Прототип служебного вызова:
UNSIGNED NU_Established_Timers(VOID);

Параметры: отсутствуют

Возвращаемое значение: количество созданных таймеров в системе.

Вызов для счетчика таймеров в Nucleus SE
Этот вызов API поддерживает ключевой функционал Nucleus RTOS API.

Прототип служебного вызова:
U8 NUSE_Timer_Count(void);

Параметры: отсутствуют

Возвращаемое значение:
количество сконфигурированных таймеров в приложении

Реализация счетчика таймеров

Реализация этого вызова API достаточно проста: возвращается значение символа #define NUSE_TIMER_NUMBER.

Структуры данных

Таймеры используют пять или семь структур данных (находящихся в ОЗУ или в ПЗУ) которые (как и другие объекты Nucleus SE) являются набором таблиц, размер и количество которых соответствует количеству сконфигурированных таймеров и выбранных параметров.

Настоятельно рекомендую, чтобы код приложения не использовал прямой доступ к этим структурам данных, а обращался к ним через предоставляемые функции API. Это позволит избежать несовместимости с будущими версиями Nucleus SE и нежелательных побочных эффектов, а также упростит портирование приложений на Nucleus RTOS. Ниже приведен подробный обзор структур, чтобы упростить понимание работы кода служебных вызовов и для отладки.

Данные ОЗУ

Эти данные имеют следующую структуру:
NUSE_Timer_Status[] – массив типа U8, имеющий одну запись для каждого сконфигурированного таймера и хранящий статус таймера (работает или остановлен: TRUE или FALSE).
NUSE_Timer_Value[] – массив типа U16, имеющий одну запись для каждого сконфигурированного таймера и хранящий текущее значение счетчика таймера.
NUSE_Timer_Expirations_Counter[] – массив типа U8, содержащий счетчик количества случаев, когда таймеры достигали завершения цикла с момента их последнего сброса.

Все эти структуры данных инициализируются функцией NUSE_Init_Timer() при запуске Nucleus SE. Одна из следующих статей будет содержать полное описание процедур запуска Nucleus SE.

Ниже приведены определения этих структур данных в файле nuse_init.c:
RAM U8 Timer_Status[NUSE_TIMER_NUMBER];
RAM U16 NUSE_Timer_Value[NUSE_TIMER_NUMBER];
RAM U8 NUSE_Timer_Expirations_Counter[NUSE_TIMER_NUMBER];

Данные ПЗУ

Структура этих данных:
NUSE_Timer_Initial_Time[] – массив типа U16, имеющий одну запись для каждого сконфигурированного таймера и хранящий значение каждого таймера.
NUSE_Timer_Reschedule_Time[] – массив типа U16, имеющий одну запись для каждого сконфигурированного таймера и хранящий значение, в которое таймер будет установлен после завершения. Нулевое значение говорит о том, что таймер «одноразовый» и не должен перезапускаться автоматически.
NUSE_Timer_Expiration_Routine_Address[] – массив типа ADDR, содержащий адрес процедур истечения таймеров. Этот массив существует, только если поддержка процедуры истечения таймера была активирована.
NUSE_Timer_Expiration_Routine_Parameter[] – массив типа U8, содержащий значения параметра, который передается в функции завершения таймеров. Этот массив существует, только если поддержка функций завершения была активирована.

Эти структуры данных объявляются и инициализируются (статически) в файле nuse_config.c, таким образом:

ROM U16 NUSE_Timer_Initial_Time[NUSE_TIMER_NUMBER] =
{
   /* исходное время таймера------ */
};
ROM U16 NUSE_Timer_Reschedule_Time[NUSE_TIMER_NUMBER] =
{
   /* время таймеров после первого завершения ------ */
};
#if NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT || NUSE_INCLUDE_EVERYTHING
   /* прототипы функций завершения */
   ROM ADDR
      NUSE_Timer_Expiration_Routine_Address[NUSE_TIMER_NUMBER] =
   {
      /* адреса функций завершения таймеров ------ */
      /* может быть NULL */
   };
   ROM U8
      NUSE_Timer_Expiration_Routine_Parameter[NUSE_TIMER_NUMBER] =
   {
      /* параметры функции завершения таймера ------ */
   };
#endif

Объём памяти для таймера

Как и у всех других объектов Nucleus SE, объем данных, необходимый для таймеров, предсказуем.

Объём данных в ОЗУ (в байтах) для всех таймеров в приложении может быть вычислен следующим образом:
NUSE_TIMER_NUMBER * 4

Объём данных в ПЗУ (в байтах) для всех таймеров в приложении, если поддержка функций завершения отключена, может быть вычислен следующим образом:
NUSE_TIMER_NUMBER * 4

В противном случае он равен:
NUSE_TIMER_NUMBER * (sizeof(ADDR) + 5)

Нереализованные вызовы API

В Nucleus SE не реализованы три вызова API, которые можно найти в RTOS.

Создание таймера

Этот вызов API создает таймер. В Nucleus SE в нем нет необходимости, так как таймеры создаются статически.

Прототип служебного вызова:
STATUS NU_Create_Timer(NU_TIMER *timer, CHAR *name, VOID (*expiration_routine)(UNSIGNED), UNSIGNED id, UNSIGNED initial_time, UNSIGNED reschedule_time, OPTION enable);

Параметры:
timer – указатель на предоставляемый пользователем блок управления таймером; он будет использоваться для управления таймерами в других вызовах API;
name – указатель на 7-символьное имя таймера с терминирующим нулем;
expiration_routine – указывает функцию, которая должна выполняться после завершения таймера;
id – элемент данных типа UNSIGNED, передаваемый на функцию завершения: этот параметр может использоваться для идентификации таймеров с одинаковой функцией завершения;
initial_time – указывает исходное количество тактов таймера до завершения таймера;
reschedule_time – указывает количество тактов таймера до завершения второго и последующих циклов; если этот параметр равен нулю, таймер останавливается только один раз;
enable – этот параметр может принимать значения NU_ENABLE_TIMER и NU_DISABLE_TIMER; NU_ENABLE_TIMER активирует таймер после его создания; NU_DISABLE_TIMER оставляет таймер отключенным; таймеры, созданные с параметром NU_DISABLE_TIMER, должны быть активированы при помощи вызова NU_Control_Timer.

Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_TIMER – нулевой указатель на блок управления таймером (NULL), или блок управления уже используется;
NU_INVALID_FUNCTION – нулевой указатель на программу завершения (NULL);
NU_INVALID_ENABLE – некорректный параметр enable;
NU_INVALID_OPERATION – параметр initial_time был равен нулю.

Удаление таймера

Этот вызов API удаляет ранее созданный таймер. В Nucleus SE в нем нет необходимости, так как таймеры создаются статически и не могут быть удалены.

Прототип служебного вызова:
STATUS NU_Delete_Timer(NU_TIMER *timer);

Параметры:
timer – указатель на блок управления таймером.

Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_TIMER – некорректный указатель на таймер;
NU_NOT_DISABLED – указанный таймер не отключен.

Указатели на таймеры

Этот вызов API формирует последовательный список указателей на все таймеры в системе. В Nucleus SE в нем нет необходимости, так как таймеры определяются простым индексом, а не указателем.

Прототип служебного вызова:
UNSIGNED NU_Timer_Pointers(NU_TIMER **pointer_list, UNSIGNED maximum_pointers);

Параметры:
pointer_list – указатель на массив указателей NU_TIMER; он будет заполнен указателями на сконфигурированные в системе таймеры;
maximum_pointers – максимальное количество указателей в массиве.

Возвращаемое значение:
Количество указателей NU_TIMER, помещенных в массив.

Совместимость с Nucleus RTOS

Как и в случае со всеми другими объектами Nucleus SE, моей целью было обеспечение максимальной совместимости кода приложений с Nucleus RTOS. Таймеры не являются исключением и, с точки зрения пользователя, они реализованы так же, как и в Nucleus RTOS. Есть и определенная несовместимость, которую я посчитал допустимой, с учетом того, что в результате код станет более понятным и более эффективным с точки зрения объема требуемой памяти. В остальном, вызовы API Nucleus RTOS могут быть практически напрямую перенесены на Nucleus SE.

Идентификаторы объектов

В Nucleus RTOS, все объекты описываются структурой данных — управляющим блоком, который имеет определенный тип данных. Указатель на этот блок управления служит идентификатором таймера. Я решил, что в Nucleus SE для эффективного использования памяти необходим другой подход: все объекты ядра описываются набором таблиц в ОЗУ и/или ПЗУ. Размер этих таблиц определяется количеством сконфигурированных объектов каждого типа. Идентификатор конкретного объекта – индекс в этой таблице. Таким образом, я определил NUSE_TIMER в качестве эквивалента U8, переменная (а не указатель) этого типа служит идентификатором таймера. С этой небольшой несовместимостью легко справиться, если код портируется с Nucleus SE на Nucleus RTOS и наоборот. Обычно над идентификаторами объектов не выполняются никакие операции, кроме перемещения и хранения.
Nucleus RTOS также поддерживает присвоение имен таймерам. Эти имена используются только при отладке. Я исключил их из Nucleus SE, чтобы сэкономить память.

Размер таймера

В Nucleus RTOS таймеры реализованы при помощи 32-битных счетчиков. Я решил уменьшить это значение до 16 бит в Nucleus SE. Это привело к значительному улучшению эффективного использования памяти и времени выполнения. Nucleus SE может быть модифицирована, если приложению требуется более длительное время выполнения.

Функции завершения

Nucleus SE реализует функции завершения похожим с Nucleus RTOS образом, только они могут быть отключены полностью (что позволяет сохранить память), а также они определяются статически. Функцию завершения нельзя изменить, когда таймер сброшен.

Нереализованные вызовы API

Nucleus RTOS поддерживает восемь служебных вызовов для работы с таймерами. Из них три не реализованы в Nucleus SE. Подробное описание этих вызовов, а также причины такого решения можно найти выше в этой статье, в разделе «Нереализованные вызовы API».

В следующей статье будут рассматриваться прерывания.

Автор: EasyLy

Источник

* - обязательные к заполнению поля