• Объявления

    • admin

      Размещайте материалы своей компании БЕСПЛАТНО!   18.04.2018

      Редакционная политика портала позволяет размещать на бесплатной основе различные типы материалов: интересную информацию, наработки, технические решения, аналитические статьи и т.д. Пример такого блога. Взамен мы рекламируем ваш блог в наших группах в соц. сетях, ну и плюс естественная самореклама от пользователей форума и блогов, которые будут читать ваш блог. К примеру охват одного поста только в нашей группе VK составляет более 10 тыс. человек. Т.е. мы предлагаем бартер - вы ведете у нас блог и публикуете какую-то полезную и интересную информацию связанную с вашим производством, а мы рекламируем ваш блог в наших соц. сетях. Блоги можно полностью кастомизировать: поставить изображение шапки, сделать меню или оглавление, также в своем блоге вы будете модератором - сможете удалять комментарии и т.д. Ведение своего блога требует времени и навыков, но рекламный эффект колоссальный, т.к. это живое общение и отклик. Посты не должны быть рекламой, а также должны соответствовать правилам форума. Для тех компаний, которые будут публиковать интересный контент, права в дальнейшем будут расширяться - сможете публиковать больше ссылок, пресс-релизы, новости компании, анонсы и т.д. Ну а если вы хотите размещать платную рекламу: условия и прайс размещения на сайте и форуме, коммерческая тема на форуме, реклама в группе VK.

Гнездо кукушки

  • записи
    3
  • комментария
    2
  • просмотров
    159

Минималистическая RTOS

ARV

87 просмотров

В моём гнезде прибавление.

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

Преамбула.

Что мы понимаем под понятием "таймер"? Ну, не в смысле задатчика времени варки яиц всмятку, а в программировании? Это некая функция, которая "сама по себе" выполняется через заданные интервалы времени. Или же чуть иначе: функция выполнится через заданный интервал времени однократно. Наконец, и третья интерпретация тоже имеет место быть: таймер - это некий счетчик, который сам по себе считает, а мы можем время от времени поглядывать на его значение и принимать какие-то решения.

Амбула.

Как обычно реализуются таймеры в микроконтроллерном программировании? Безусловно, наиболее удобно - с задействованием аппаратных таймеров-счетчиков и прерываний от них. Существует вариант реализации и без этого, но сие удовольствие надо оставить пациентам более строгого режима лечения. С прерыванием от аппаратного таймера все понятно, но их количество (аппаратных счетчиков я имею ввиду) ограничено. И поэтому в общем случае используется модель "программных" таймеров на основе одного прерывания от аппаратного. Вот как, например, выглядит один из простейших вариантов:

#define TIMER_CNT	5
static volatile uint8_t timer[TIMER_CNT];

// обработчик прерывания от таймера, вызывается каждую миллисекунду
ISR(TIMER_vect){
  for(uint8_t i=0; i<TIMER_CNT; i++){
    if(timer[i]) timer[i]--;
  }
}

// вот так можно ограничить длительность цикла интервалом времени в 100 мс
timer[0] = 100;
while(timer[0]){
  // что-то длительное
  if(какое-то-условие-неизвестно-когда-возникающее) break;
}
if(!timer[0]){
  // из цикла вышли по таймауту
} else {
  // из цикла вышли по условию
}

Вроде бы, все просто и понятно. И даже удобно. Я сам 100 раз так делал!

Но есть и неприятности. Во-первых, надо постоянно следить за тем, какой "номер" таймера задействован в том или ином участке кода. Когда таймеров два или три - проблемы нет, а когда в разных функциях в разных модлях их по нескольку штук, можно и запутаться. Во-вторых, массив timer должен быть глобальным, что само по себе не страшно, но как-то не комильфо... В-третьих, сделать таймер не однобайтным, а двухбайтным, чтобы иметь возможность отсчитывать большие интервалы времени, уже так красиво не выйдет - следует обеспечивать атомарный доступ к значению счетчика... И главное: этот подход реализует только последний вариант таймера из числа рассмотренных в преамбуле, т.е. гибкость его ограничена.

Путем нехитрых манипуляций можно заметно улучшить ситуацию. Хотя и несколько усложнив код:

#include <util/atomic.h>
  
#define TIMER_CNT	5

typedef uint8_t (*tmr_func)(void);  
typedef struct{
  uint16_t	counter;
  uint16_t	duration;
  tmr_func	func;
} timer_t;

static volatile timer_t timer[TIMER_CNT];

ISR(TIMER_vect){
  for(uint8_t i=0; i<TIMER_CNT; i++){
    if(timer[i].counter){
      timer[i].counter--;
      if((timer[i].counter == 0) && (timer[i].func != NULL))
        if(timer[i].func()) timer[i].counter = timer[i].duration;
    }
  }
}

void timer_start(uint8_t t, uint16_t duration, tmr_func f){
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
    timer[t].counter = duration;
    timer[t].duration = duration;
    timer[t].func = f;
  }
}

uint8_t timer_out(uint8_t t){
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
    return timer[t].counter == 0;
  }
}

// вот так можно заставить светодиоды мигать с разной частотой
static uint8_t blink_led1(void){
  PORTB ^= 1<<0; // светодиод на нулевой линии порта
  return 1; // для перезапуска функции
}

static uint8_t blink_led2(void){
  PORTB ^= 1<<1; // светодиод на первой линии порта
  return 1; // для перезапуска функции
}

timer_start(0, 500, blink_led1);
timer_start(0, 300, blink_led2);

while(1){
  // тут что-то делаем, а светодиоды тем временем мигают каждый по-своему
}

Разумеется, здесь уже и атомарность доступа к значению счетчика реализована (ценой вызова отдельной функции), и все вариаты из преамбулы тоже. Надеюсь, очевидно, что если переданная в таймер функция вернет 0, она больше не будет вызываться после того, как таймер истечет? Чем вам не RTOS в минимальном виде? Главное условие в применимости такого подхода - предельно быстрое исполнение таймерной функции. Но при использовании автоматов состояний этим способом можно решать большой спектр практических задач.

Но проблема с "учетом" таймеров осталась. Да и если вы вдруг станете нуждаться в бОльшем количестве таймеров, чем TIMER_CNT, вам придется эту константу менять. И в случае, если вы модифицируете старый проект, и старое количество таймеров вам не нужно, то тоже надо это вручную менять. Мелочь, а неприятно.

Хорошо было бы, если бы в любом месте кода описал свой отдельный static (т.е. невидимый другим) таймер, и пользуешься им. Не нужен -удалил его описание, и не пользуешься. А "система" сама заботится о том, чтобы таймер "тикал" или не "тикал", если не нужен.

И обычно для этого используют возможности RTOS.

Хотя... Хотя максимальное количество выделяемых RTOS таймеров позапросу пользователя тоже ограничено значением какой-то константы... Но и из этого исхода есть выход! Только об этом в следующий раз. Т.е. о самом главном я и не сказал...

  • Одобряю 1


2 комментария


Рекомендуемые комментарии

Удачное решение. Я 10 лет использую такой метод при написАнии программ на Ассме и никогда он не подводил. Время выполнения той или иной функции можно гибко изменять, не влияя на выполнение полного цикла программ.

Поделиться комментарием


Ссылка на комментарий
2 часа назад, Геннадий сказал:

Удачное решение

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

Хотел написать о том, как можно вообще забыть про количество таймеров при написании кода, а вышло, что об этом ни слова... Ничего, передохну немного и напишу.

Поделиться комментарием


Ссылка на комментарий

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас