Existe uma maneira de ter várias partes do programa funcionando juntas sem fazer várias coisas no mesmo bloco de código?
Um segmento aguardando um dispositivo externo e também piscando um LED em outro segmento.
arduino-uno
threads
Bja
fonte
fonte
Respostas:
Não há suporte para multiprocessos nem multithreading no Arduino. Você pode fazer algo próximo a vários threads com algum software.
Você quer olhar para Protothreads :
Obviamente, há um exemplo do Arduino aqui com código de exemplo . Esta questão SO também pode ser útil.
O ArduinoThread também é bom.
fonte
O Arduino baseado em AVR não suporta segmentação (hardware), não estou familiarizado com o Arduino baseado em ARM. Uma maneira de contornar essa limitação é o uso de interrupções, especialmente interrupções cronometradas. Você pode programar um timer para interromper a rotina principal a cada tantos microssegundos, para executar uma outra rotina específica.
http://arduino.cc/en/Reference/Interrupts
fonte
É possível fazer multi-threading do lado do software no Uno. A segmentação no nível do hardware não é suportada.
Para obter multithreading, será necessária a implementação de um planejador básico e a manutenção de um processo ou lista de tarefas para rastrear as diferentes tarefas que precisam ser executadas.
A estrutura de um planejador não preventivo muito simples seria como:
Aqui,
tasklist
pode haver uma matriz de ponteiros de função.Com cada função do formulário:
Cada função pode executar uma tarefa separada, como
function1
manipulações de LED efunction2
cálculos de flutuação. Será responsabilidade de cada tarefa (função) aderir ao tempo alocado a ela.Felizmente, isso deve ser suficiente para você começar.
fonte
Conforme a descrição de seus requisitos:
Parece que você pode usar uma interrupção do Arduino para o primeiro "encadeamento" (prefiro chamá-lo de "tarefa", de fato).
As interrupções do Arduino podem chamar uma função (seu código) com base em um evento externo (nível de tensão ou alteração de nível em um pino de entrada digital), que acionará sua função imediatamente.
No entanto, um ponto importante a ser lembrado com as interrupções é que a função chamada deve ser o mais rápida possível (normalmente, não deve haver
delay()
chamada ou qualquer outra API que dependadelay()
).Se você tiver uma tarefa longa para ativar no acionador de evento externo, poderá usar um agendador cooperativo e adicionar uma nova tarefa a partir da sua função de interrupção.
Um segundo ponto importante sobre interrupções é que seu número é limitado (por exemplo, apenas 2 na ONU). Portanto, se você começar a ter mais eventos externos, precisará implementar algum tipo de multiplexação de todas as entradas em uma, e sua função de interrupção determinará qual entrada de multiplexação foi o acionador real.
fonte
Uma solução simples é usar um agendador . Existem várias implementações. Isso descreve em breve um que está disponível para placas baseadas em AVR e SAM. Basicamente, uma única chamada iniciará uma tarefa; "esboço dentro de um esboço".
Scheduler.start () adicionará uma nova tarefa que executará o taskSetup uma vez e depois chamará repetidamente taskLoop, assim como o esboço do Arduino funciona. A tarefa tem sua própria pilha. O tamanho da pilha é um parâmetro opcional. O tamanho da pilha padrão é 128 bytes.
Para permitir a alternância de contexto, as tarefas precisam chamar yield () ou delay () . Há também uma macro de suporte para aguardar uma condição.
A macro é açúcar sintático para o seguinte:
Await também pode ser usada para sincronizar tarefas. Abaixo está um exemplo de trecho:
Para mais detalhes, veja os exemplos . Existem exemplos de vários LEDs piscando para botão de retorno e um shell simples com leitura de linha de comando sem bloqueio. Modelos e espaços para nome podem ser usados para ajudar a estruturar e reduzir o código fonte. O esboço abaixo mostra como usar as funções de modelo para multi-piscar. É suficiente com 64 bytes para a pilha.
Há também uma referência para dar uma idéia do desempenho, ou seja, tempo para iniciar a tarefa, alternar o contexto etc.
Por último, existem algumas classes de suporte para sincronização e comunicação em nível de tarefa; Fila e semáforo .
fonte
De um encantamento anterior deste fórum, a seguinte pergunta / resposta foi movida para Engenharia Elétrica. Ele possui um código de arduino de amostra para piscar um LED usando uma interrupção do timer enquanto usa o loop principal para executar E / S serial.
https://electronics.stackexchange.com/questions/67089/how-can-i-control-things-without-using-delay/67091#67091
Repost:
As interrupções são uma maneira comum de fazer as coisas enquanto outra coisa está acontecendo. No exemplo abaixo, o LED está piscando sem usar
delay()
. Sempre que éTimer1
acionado, a rotina de serviço de interrupção (ISR)isrBlinker()
é chamada. Liga / desliga o LED.Para mostrar que outras coisas podem acontecer simultaneamente,
loop()
grava foo / bar repetidamente na porta serial, independentemente do LED piscar.Esta é uma demonstração muito simples. Os ISRs podem ser muito mais complexos e podem ser acionados por temporizadores e eventos externos (pinos). Muitas das bibliotecas comuns são implementadas usando ISRs.
fonte
Eu também vim para esse tópico ao implementar um display LED de matriz.
Em uma palavra, você pode criar um agendador de polling usando a função millis () e a interrupção do timer no Arduino.
Sugiro os seguintes artigos de Bill Earl:
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
fonte
Você também pode tentar minha biblioteca ThreadHandler
https://bitbucket.org/adamb3_14/threadhandler/src/master/
Ele usa um agendador de interrupção para permitir a alternância de contexto sem retransmitir yield () ou delay ().
Criei a biblioteca porque precisava de três threads e dois deles para rodar em um momento preciso, independentemente do que os outros estavam fazendo. O primeiro thread tratou da comunicação serial. O segundo estava executando um filtro Kalman usando multiplicação de matriz flutuante com a biblioteca Eigen. E o terceiro era um segmento de loop de controle de corrente rápido que precisava ser capaz de interromper os cálculos da matriz.
Como funciona
Cada encadeamento cíclico tem uma prioridade e um período. Se um encadeamento, com prioridade mais alta que o encadeamento em execução atual, atingir o próximo tempo de execução, o planejador pausará o encadeamento atual e passará para o de maior prioridade. Depois que o encadeamento de alta prioridade conclui sua execução, o planejador volta para o encadeamento anterior.
Regras de agendamento
O esquema de agendamento da biblioteca ThreadHandler é o seguinte:
Como usar
Threads podem ser criados via herança c ++
Ou via createThread e uma função lambda
Os objetos de thread se conectam automaticamente ao ThreadHandler quando são criados.
Para iniciar a execução dos objetos de encadeamento criados, chame:
fonte
E aqui está mais uma biblioteca multitarefa cooperativa de microprocessadores - PQRST: uma fila de prioridade para executar tarefas simples.
Nesse modelo, um encadeamento é implementado como uma subclasse de a
Task
, programada para algum tempo futuro (e possivelmente remarcada em intervalos regulares, se, como é comum, ela subclassesLoopTask
). Orun()
método do objeto é chamado quando a tarefa vence. Orun()
método faz algum trabalho devido e, em seguida, retorna (este é o bit cooperativo); normalmente manterá algum tipo de máquina de estado para gerenciar suas ações em invocações sucessivas (um exemplo trivial é alight_on_p_
variável no exemplo abaixo). Isso requer uma pequena reflexão sobre como você organiza seu código, mas provou ser muito flexível e robusto em uso bastante intensivo.É agnóstico quanto às unidades de tempo, por isso é tão bom correr em unidades de
millis()
quantomicros()
ou em qualquer outro tick que seja conveniente.Aqui está o programa 'piscar' implementado usando esta biblioteca. Isso mostra apenas uma única tarefa em execução: outras tarefas normalmente seriam criadas e iniciadas dentro
setup()
.fonte
run()
método é chamado, ele não é interrompido; portanto, ele tem a responsabilidade de concluir razoavelmente prontamente. Normalmente, no entanto, ele faz o seu trabalho e se remarca (possivelmente automaticamente, no caso de uma subclasse deLoopTask
) por algum tempo futuro. Um padrão comum é que a tarefa mantenha alguma máquina de estado interna (um exemplo trivial é olight_on_p_
estado acima), para que ela se comporte adequadamente no próximo dia de vencimento.run()
. Isso contrasta com os threads cooperativos, que podem render a CPU, por exemplo, chamandoyield()
oudelay()
. Ou encadeamentos preventivos, que podem ser agendados a qualquer momento. Eu sinto que a distinção é importante, pois vi que muitas pessoas que andam por aqui procurando por threads o fazem porque preferem escrever código de bloqueio em vez de máquinas de estado. Bloquear threads reais que produzem a CPU é bom. Bloquear tarefas RtC não é.