Многопоточность в Ардуино

mabanza
Offline
Зарегистрирован: 30.01.2016

Привет.

Я в Ардуине новичок, но сразу что мне не нравится - код с задержками delay в основном потоке. Делать серьезные проекты, мне кажется, с этим не реально.

Пример. Банальная проблема с сервой. После подачи команды на поворот, нужно делать задержку, чтоб ротор успел повернуться. В моем проекте в это время с клиента приходит асинхронный запрос по сокетному соединению. Основной поток тупо остановлен делэем для сервы. Как результат, TCP-запрос возвращает тайм-аут.

Есть ли способ использовать многопоточность?

Если нет, то. У меня используется еще шилд часов (RTC), нельзя ли как-то тики часов навесить на прерывания Ардуины, написать колбачную функцию для этих прерываний и получить что-то близкое к многопоточности? Кто такое делал, расскажите как?

Спасибо.

 

 

Valera19701
Valera19701 аватар
Offline
Зарегистрирован: 18.10.2015

Blink without delay из примеров вам в помощь

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

mabanza пишет:

Есть ли способ использовать многопоточность?

Чистой многопоточности в ардуине нет, прерывания - это тоже не многопоточность. Программными методами вполне можно добиться того, что вам нужно - надо всего лишь забыть о delay как о страшном сне и использовать его только там, где это жизненно необходимо. Остальной код пишется так, чтобы каждому куску, выполняющему свою задачу - постоянно выделялся квант времени. Не забывается и функция yield - кусок кода, который может выполнять длительную по времени задачу, должен дёргать эту функцию периодически (определив эту функцию, внутри неё уже можно делать критические по времени вещи) - кстати сказать, я именно по наличию вызовов функции yield в сторонних времяжрущих библиотеках определяю - годна на рассмотрение или сразу в топку.

В общем, совсем простом случае, "многопоточность" может выглядеть как-то так:

typedef void (*ModuleWork)(void);
typedef bool (*ModuleUpdate)(uint16_t delta);

typedef struct
{
	ModuleWork OnWork;
	ModuleUpdate OnUpdate;
	bool IsThisModuleTimeCritical;
	
} ModuleDefinition;

ModuleDefinition Modules[NUM_MODULES];
unsigned long prevMillis = 0;

void setup()
{
	// настраиваем модули, назначая функции-обработчики
}
void loop()
{
	unsigned long curMillis = millis();
	uint16_t delta = curMillis - prevMillis;
	prevMillis = curMillis;
	
	for(uint8_t i=0;i<NUM_MODULES;i++)
	{
		if(Modules[i].OnUpdate(delta))
			Modules[i].OnWork();
	}
	
}

void yield()
{
	for(uint8_t i=0;i<NUM_MODULES;i++)
	{
		if(Modules[i].IsThisModuleTimeCritical)
			Modules[i].OnWork();
	}
}

Понятное дело, что это навскидку, тупо, без нюансов, но представление даёт - каждому "модулю" передаётся дельта времени с момента последнего вызова функции OnUpdate, модуль может внутри себя накапливать этот счётчик и по достижении нужного временного интервала что-то там делать, не тормозя в своём ожидании работу других "модулей".

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

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

Всё имхо, как обычно. Каждый др...чит, как хочет - тоже, как обычно :)

mabanza
Offline
Зарегистрирован: 30.01.2016

Спасибо, но это не то. Там работает ОДИН основной поток. Loop происходит в ЕДИНСТВЕННОМ основном потоке. Если код проверки, не нужно ли включать LED, занимает достаточно долгое время, то и этот примитивный пример не будет работать.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

mabanza пишет:

Спасибо, но это не то. Там работает ОДИН основной поток. Loop происходит в ЕДИНСТВЕННОМ основном потоке. Если код проверки, не нужно ли включать LED, занимает достаточно долгое время, то и этот примитивный пример не будет работать.

Ещё раз - в ардуино нет истинной многопоточности.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

DIYMan пишет:

Ещё раз - в ардуино нет истинной многопоточности.

в одной дуино нет, а в двух есть - два потока. :D

mabanza
Offline
Зарегистрирован: 30.01.2016

Хороший код. Может быть я его и попытаюсь использовать.

Пока что у меня в проекте есть RTC шилд, это реально независимый счет времени. Я запрашиваю его тики, но! Это происходит в основном потоке ардуины. Это минус. Пока запрос тика происходит быстро - это будет работать. Это плюс.

Но и тут возможен тормоз. Как в еще одной хрени - шилде RN-171 WiFi, который опрашивается быстро, пока его не заколодит. Вот тогда он вешает основной поток.

Что касается прерываний - в 8086 они используются и для обычных и для необычных методов. Это идеальная архитектура и по привычке я бы приторочил тут прерывания. Если только их выполнение не занимает основной поток (а вы пишите что таки занимает), как это сделано в 8086.

Спасибо.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

mabanza пишет:

Если только их выполнение не занимает основной поток

прерывания позволяют тебе аппаратно выскакивать из одного потока в другой - один поток полюбому.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

mabanza пишет:

Хороший код. Может быть я его и попытаюсь использовать.

Код плохой, не используй его :) Это просто наброски для демонстрации.

mabanza пишет:

Но и тут возможен тормоз. Как в еще одной хрени - шилде RN-171 WiFi, который опрашивается быстро, пока его не заколодит. Вот тогда он вешает основной поток.

Для этого есть вотчдог ;)

mabanza пишет:

Что касается прерываний - в 8086 они используются и для обычных и для необычных методов. Это идеальная архитектура и по привычке я бы приторочил тут прерывания. 

Дёргая которые почём зря - можно свести на нет все пляски ;)

mabanza пишет:

Спасибо.

Незчт.