Skip to content
 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 

Repository files navigation

класс TTimerList.

Просматривая форумы хоббистов и фанатов Arduino я удивился, что 50% вопросов новичков - о том как избавиться от функции delay() в своих программах. По некоторому размышлению, у меня возникла идея создать список асинхронных таймеров, которые можно настроить на разные длительности (от 1 миллисекунды до почти 50 суток). Timer0, 8-разрядный аппаратный таймер настраивается на срабатывание каждую миллисекунду и вызывает метод статического класса TTimerList. Правда, для точности в 1 мс, пришлось пожертвовать точностью millis(), она теперь врёть процентов на 3. Для Atmega8, у которого Timer0 на совпадение не работает, пришлось задействовать Timer1.

Так вот, класс содержит список Items типа

struct TCallStruct				// внутренняя структура для хранения КАЖДОГО таймера
{
 PVoidFunc	CallingFunc;			// функция, которую нужно вызвать при срабатывании
 long		InitCounter;			// заданное кол-во миллисекунд
 long		WorkingCounter;			// рабочий счетчик. после начала счёта сюда копируется значение из InitCounter
 bool           Active;	  			// и каждую миллисекунду уменьшается на 1. При достиженнии 0 вызывается функция 
					        // CallingFunc, и снова записывается значение из InitCounter. Счёт начинается сначала. :)
};

Пробегая по списку, каждую миллисекунду класс уменьшает на 1 рабочий счетчик каждого добавленного таймера (у которого CallingFunc != NULL) и который запущен (Active==true). Если рабочий счетчик достиг нуля, вызывается функция CallingFunc описанная как

using PVoidFunc = void (*)(void);  указатель на функцию без параметров ничего не возвращающую.

После этого в рабочий счетчик загружается значение из InitCounter и счет начинается сначала. Прерывания запрещаются на время перебора таймеров в списке.

Использование:

скопировать два файла TTimerList cpp и h в директорию с рабочим проектом, подключить через #include "TTimerList.h". Обязательно объявить глобально

extern TTimerList TimerList; 

и можно добавлять, удалять, останавливать и снова запускать таймеры асинхронно. Cписок может содержать 16 таймеров. Если таймеров нужно больше или меньше, надо исправить строчку

#define     MAXTIMERSCOUNT		16		

в файле uTimerList.h на нужное количество таймеров.

	THandle      Add(PVoidFunc AFunc, long timeMS);	        // функция AFunc, которую надо вызвать через timeMS миллисекунд

функция Add добавляет таймер в список и сразу же запускает его, принимает адрес функции, которую нужно вызвать. вызываемая функция описана как void (*)(void). При добавлении таймера, функцию в Add подставляем просто по имени, без скобок. Если подставить со скобками - компилятор сделает вызов функции, если без скобок - передаст адрес функции как параметр, что и нужно по смыслу.

Add(...) отдает так называемый хэндл, идентификатор таймера, по которому его можно удалить, остановить и запустить вновь. По смыслу это обычный номер в списке, но так как добавление идёт не подряд, а на свободное место, то он может быть каким угодно, если планируется останавливать и удалять таймер, лучше этот номер запоминать.

Полностью рабочий блинк без delay() и millis() (да и без loop()) :)

#include "uTimerList.h"

extern TTimerList TimerList;

THandle hnd_blink;          // хэндл для светодиода на ноге 13
THandle hnd_error;          // хэндл для светодиода на ноге 10

byte    normalLedPin = 13;   // сюда подключим зеленый светодиод, который будет просто моргать
byte    errorLedPin = 10;   // сюда подключим красный светодиод, сигнализирующий, например, об ошибке

void setup() 
{
 pinMode(errorLedPin,OUTPUT);
 pinMode(normalLedPin,OUTPUT);
 
 hnd_blink = TimerList.AddSeconds(OKBlink,1); // добавляем таймер, задержка между морганиями 1 секунда, вызывается OKBlink
 hnd_error = TimerList.Add(ErrorBlink,250);   // добавляем таймер, моргаем 2 раза в секунду, вызывается ErrorBlink

}

void OKBlink(void)    // функция вызывается раз в секунду
{
 static bool state=true;
 digitalWrite(normalLedPin, state);
 state = ! state;
}

void ErrorBlink(void)  // функция вызывается раз в 250 миллисекунд.
{
 static bool state=true;
 digitalWrite(errorLedPin, state);
 state = ! state;
}


void loop() 
{
	// здесь даже писать ничего не надо, всё мигает отоматически

}

Даже если нужно чтоб интервалы были неравномерные, например, светодиод горит 50 миллисекунд, а не горит - 5 секунд (быстро мигает раз в 5 секунд, для индикации нормальной работы), то и это можно легко сделать одним таймером:

Объявляем глобально переменную-хэндл:

THandle hLEDBlink;

в setup() создаем таймер

hLEDBlink=TimerList.AddSeconds(tmrLEDBlink,1); // пока ставим интервал раз в секунду, потом изменим

и в функции отклика tmrLEDBlink() пишем:

void tmrLEDBlink()
{
 static bool ledstate = false;	  // статическая переменная хранит предыдущее состояние светодиода
 ledstate = !ledstate;		  // инвертируем состояние светодиода 	
 digitalWrite(ledpin, ledstate);  // пишем новое состояние в порт (зажигаем/гасим светодиод)
 TimerList.TimerNewInterval(hLEDBlink, ledstate ? 50 : 4950); // устанавливаем новый интервал для таймера hLEDBlink
    }						              // гореть он будет 50мс, не гореть - 4950мс. 		

Список таймеров удобен, например, когда нужно опрашивать датчики асинхронно, независимо друг от друга и с разными интервалами. Например, чтоб читать датчик DHT11 раз в минуту, надо создать функцию

void readDHT(void); 

и добавить в список таймеров

THandle hnd_dht = TimerList.AddMinutes(readDHT, 1);  // 1 раз в минуту

всё, раз в минуту будет вызываться readDHT, потом его можно остановить

TimerList.TimerStop(hnd_dht)

или удалить

TimerList.Delete(hnd_dht);

тогда readDHT() вызываться не будет.

Вот все полезные функции списка таймеров:

THandle      Add(PVoidFunc AFunc, long timeMS);	        // функция AFunc, которую надо вызвать через timeMS миллисекунд
THandle      AddSeconds(PVoidFunc AFunc, word timeSec); // то же, только интервал задается в секундах. 
THandle      AddMinutes(PVoidFunc AFunc, word timeMin); // то же, только интервал задается в минутах. 
                    	                                // исключительно для удобства создания длинных интервалов 

// функции для списка в целом

	inline bool  	CanAdd() const;                    // если можно добавить таймер, вернет true

	inline bool	IsActive() const;                  // true, если хоть один таймер запущен

	void		Delete(THandle hnd);               // удалить таймер hnd

	void		Step(void);                	// раз в миллисекунду перебирает таймеры	

	void		AllStart();                        // запустить рабочий цикл перебора таймеров

	void		AllStop();                         // выключить все таймеры, TimerList остановлен

	inline byte	Count() const {return count; };    // счетчик добавленных таймеров, всего. Можно не использовать, просто проверять возможность
	 	                                            // добавления, вызывая ф-цию CanAdd();

	inline byte MaxTimersCount() const { return MAXTIMERSCOUNT; }; // максимальное число таймеров

// функции для одного конкретного таймера, заданного его номером (THandle)

	void	TimerPause(THandle hnd);	//приостановить таймер hnd

	void	TimerResume(THandle hnd);	// продолжить счёт для таймера hnd с остановленного места

	void	TimerStop(THandle hnd);		// остановить отдельный таймер 

	bool	TimerActive(THandle hnd);       // запущен ли конкретный таймер

	void    TimerStart(THandle hnd);        // запустить отдельный таймер (после остановки) 
	
	void	TimerNewInterval(THandle hnd, long newinterval); // назначить таймеру hnd новый интервал

код хорошо (надеюсь) комментирован, изучайте.

Cвязаться со мной по вопросам можно по электропочте dap68@mail.ru или по аське ICQ 394705231.

About

Много таймеров из одного

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages