Есть ли способ, которым я могу запустить несколько частей программы без выполнения нескольких операций в одном блоке кода?
Один поток ожидает внешнего устройства, а в другом потоке мигает светодиод.
Есть ли способ, которым я могу запустить несколько частей программы без выполнения нескольких операций в одном блоке кода?
Один поток ожидает внешнего устройства, а в другом потоке мигает светодиод.
Ответы:
На Arduino нет многопроцессорной и многопоточной поддержки. Вы можете сделать что-то похожее на несколько потоков с помощью некоторого программного обеспечения.
Вы хотите посмотреть на Protothreads :
Конечно, есть Arduino пример здесь с примерами кода . Этот ТАК вопрос тоже может быть полезен.
ArduinoThread тоже хороший.
источник
Arduino на базе AVR не поддерживает (аппаратную) многопоточность, я не знаком с Arduino на основе ARM. Одним из способов обойти это ограничение является использование прерываний, особенно прерываний по времени. Вы можете запрограммировать таймер так, чтобы он прерывал основную процедуру каждые много микросекунд, чтобы запустить определенную другую процедуру.
http://arduino.cc/en/Reference/Interrupts
источник
На Uno можно выполнять многопоточность на стороне программного обеспечения. Аппаратный уровень потоков не поддерживается.
Для достижения многопоточности потребуется реализация базового планировщика и ведение списка процессов или задач для отслеживания различных задач, которые необходимо выполнить.
Структура очень простого планировщика без вытеснения будет выглядеть так:
Здесь
tasklist
может быть массив указателей на функции.С каждой функцией формы:
Каждая функция может выполнять отдельную задачу, такую как
function1
выполнение манипуляций со светодиодами иfunction2
выполнение вычислений с плавающей запятой. Каждое задание (функция) будет нести ответственность за соблюдение выделенного ему времени.Надеюсь, этого будет достаточно, чтобы вы начали.
источник
Согласно описанию ваших требований:
Кажется, вы могли бы использовать одно прерывание Arduino для первого «потока» (на самом деле я бы скорее назвал его «заданием»).
Прерывания Arduino могут вызывать одну функцию (ваш код) на основе внешнего события (уровня напряжения или изменения уровня на выводе цифрового входа), которое немедленно активирует вашу функцию.
Тем не менее, один важный момент, который следует иметь в виду при обработке прерываний, заключается в том, что вызываемая функция должна быть максимально быстрой (обычно не должно быть ни
delay()
вызова, ни какого-либо другого API, от которого зависитdelay()
).Если у вас есть длинная задача для активации по внешнему триггеру, вы можете использовать совместный планировщик и добавить в него новую задачу из функции прерывания.
Вторым важным моментом в отношении прерываний является то, что их количество ограничено (например, только 2 в UNO). Поэтому, если вы начнете иметь больше внешних событий, вам нужно будет реализовать своего рода мультиплексирование всех входов в один, и ваша функция прерывания определит, какой мультиплексированный вход был фактическим триггером.
источник
Простое решение - использовать планировщик . Есть несколько реализаций. Это вкратце описывает тот, который доступен для плат на базе AVR и SAM. По сути, один вызов запускает задачу; "эскиз внутри эскиза".
Scheduler.start () добавит новую задачу, которая будет запускать taskSetup один раз, а затем повторно вызывать taskLoop точно так же, как работает эскиз Arduino. Задача имеет свой собственный стек. Размер стека является необязательным параметром. Размер стека по умолчанию составляет 128 байт.
Чтобы разрешить переключение контекста, необходимо вызвать yield () или delay () . Существует также макрос поддержки для ожидания условия.
Макрос является синтаксическим сахаром для следующего:
Await также может быть использован для синхронизации задач. Ниже приведен пример фрагмента:
Для дальнейших деталей смотрите примеры . Есть примеры от многократного мигания светодиода до кнопки отмены и простой оболочки с неблокирующим чтением командной строки. Шаблоны и пространства имен могут использоваться, чтобы помочь структурировать и уменьшить исходный код. Ниже эскиз показывает , как использовать шаблон функцию для мульти-мерцания. Для стека достаточно 64 байта.
Существует также эталон, который дает некоторое представление о производительности, то есть время запуска задачи, переключение контекста и т. Д.
Наконец, есть несколько классов поддержки для синхронизации и связи на уровне задач; Очередь и семафор .
источник
Из предыдущего заклинания этого форума следующий вопрос / ответ был перенесен в электротехнику. Он имеет пример кода Arduino для мигания светодиода с использованием прерывания по таймеру при использовании основного цикла для выполнения последовательного ввода-вывода.
https://electronics.stackexchange.com/questions/67089/how-can-i-control-things-without-using-delay/67091#67091
Repost:
Прерывания - это обычный способ сделать что-то, пока что-то происходит. В приведенном ниже примере светодиод мигает без использования
delay()
. Всякий разTimer1
, когда срабатывает,isrBlinker()
вызывается подпрограмма обработки прерывания (ISR) . Включает / выключает светодиод.Чтобы показать, что одновременно могут происходить другие вещи,
loop()
несколько раз записывает foo / bar в последовательный порт независимо от мигания светодиода.Это очень простая демонстрация. ISR могут быть намного более сложными и могут быть вызваны таймерами и внешними событиями (булавками). Многие из общих библиотек реализованы с использованием ISR.
источник
Я также пришел к этой теме при реализации матричного светодиодного дисплея.
Одним словом, вы можете построить планировщик опроса, используя функцию millis () и прерывание по таймеру в Arduino.
Я предлагаю следующие статьи от Билла Эрла:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-2/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
источник
Вы также можете попробовать мою библиотеку ThreadHandler
https://bitbucket.org/adamb3_14/threadhandler/src/master/
Он использует планировщик прерываний, чтобы разрешить переключение контекста без ретрансляции на yield () или delay ().
Я создал библиотеку, потому что мне нужно было три потока, и мне нужно было два из них для запуска в точное время независимо от того, что делали другие. Первый поток обрабатывал последовательную связь. Вторым был запуск фильтра Калмана с использованием умножения матрицы с плавающей точкой на библиотеку Eigen. И третьим был поток с быстрым токовым контуром управления, который должен был прервать вычисления матрицы.
Как это устроено
Каждый циклический поток имеет приоритет и период. Если поток с более высоким приоритетом, чем текущий исполняющий поток, достигает следующего времени выполнения, планировщик приостанавливает текущий поток и переключается на более высокий приоритет. Как только поток с высоким приоритетом завершает свое выполнение, планировщик переключается обратно на предыдущий поток.
Правила планирования
Схема планирования библиотеки ThreadHandler выглядит следующим образом:
Как пользоваться
Потоки могут быть созданы с помощью наследования c ++
Или через createThread и лямбда-функцию
Объекты потоков автоматически подключаются к ThreadHandler при их создании.
Чтобы начать выполнение созданных объектов потока, вызовите:
источник
И вот еще одна микропроцессорная кооперативная многозадачная библиотека - PQRST: приоритетная очередь для выполнения простых задач.
В этой модели поток реализован как подкласс a
Task
, который запланирован на некоторое время в будущем (и, возможно, перенесен на регулярные промежутки времени, если, как это обычно бывает,LoopTask
вместо него подклассы ).run()
Метод объекта вызывается , когда задача становится из - за.run()
Метод делает некоторые должную работу, а затем возвращает (это кооперативное бит); обычно он поддерживает какой-то конечный автомат для управления своими действиями при последовательных вызовах (тривиальный пример - этоlight_on_p_
переменная в примере ниже). Это требует небольшого переосмысления того, как вы организуете свой код, но оказалось очень гибким и надежным при довольно интенсивном использовании.Он не зависит от единиц времени, поэтому он так же рад работать в единицах,
millis()
какmicros()
и любой другой удобный тик.Вот программа blink, реализованная с использованием этой библиотеки. Это показывает, что выполняется только одна задача: обычно создаются и запускаются другие задачи
setup()
.источник
run()
метод вызван, он не прерывается, поэтому он обязан быстро завершить работу. Как правило, однако, он выполнит свою работу, а затем перенесет себя (возможно, автоматически, в случае подклассаLoopTask
) на некоторое время в будущем. Обычным шаблоном для задачи является поддержание некоторого внутреннего конечного автомата (тривиальным примером являетсяlight_on_p_
состояние выше), чтобы он вел себя соответствующим образом, когда наступит следующий срок.run()
. Это отличается от кооперативных потоков, которые могут выдавать ЦП, например, путем вызоваyield()
илиdelay()
. Или приоритетные потоки, которые могут быть запланированы в любое время. Я чувствую, что различие важно, так как я видел, что многие люди, которые приходят сюда в поисках потоков, делают это, потому что они предпочитают писать блокирующий код, а не конечные автоматы. Блокировка реальных потоков, которые дают процессор, в порядке. Блокировки задач RtC нет.