- O que é multitarefa?
- Por que pular delay () no Arduino?
- Por que usar millis ()?
- Componentes necessários
- Diagrama de circuito
- Programando Arduino UNO para multitarefa
A multitarefa levou os computadores a uma revolução onde um ou mais programas podem ser executados simultaneamente o que aumenta a eficiência, flexibilidade, adaptabilidade e produtividade. Em sistemas embarcados, os microcontroladores também podem lidar com multitarefa e executar duas ou mais tarefas simultaneamente sem interromper as instruções atuais.
Aqui neste tutorial, aprenderemos como o Arduino realiza multitarefa com a função Arduino millis. Geralmente uma função delay () é usada no Arduino para uma tarefa periódica como LED piscando, mas esta função delay () interrompe o programa por algum tempo definitivo e não permite que outras operações sejam executadas. Portanto, este artigo explica como podemos evitar o uso da função delay () e substituí-la por millis () para realizar mais de uma tarefa simultaneamente e tornar o Arduino um controlador multitarefa. Antes de entrar em detalhes, vamos começar entendendo a Multitarefa.
O que é multitarefa?
Multitarefa significa simplesmente executar mais de uma tarefa ou programa ao mesmo tempo. Quase todos os sistemas operacionais oferecem multitarefa. Este tipo de sistema operacional é conhecido como MOS (sistema operacional multitarefa). O MOS pode ser um sistema operacional de PC móvel ou desktop. O bom exemplo de multitarefa em computadores é quando os usuários executam o aplicativo de e-mail, navegador de internet, reprodutor de mídia, jogos, ao mesmo tempo e se os usuários não querem usar o aplicativo, ele é executado em segundo plano se não for fechado. O usuário final usa todos esses aplicativos ao mesmo tempo, mas o sistema operacional leva esse conceito um pouco diferente. Vamos discutir como o sistema operacional gerencia multitarefa.
Como pode ser visto na figura, a CPU divide o tempo em três partes iguais e atribui cada parte a cada tarefa / aplicação. É assim que a multitarefa é feita na maioria dos sistemas. O conceito será quase o mesmo para o Arduino Multitasking, exceto que a distribuição do tempo será um pouco diferente. Como o Arduino é executado em baixa frequência e a RAM se compara ao laptop / celular / PC, o tempo dado a cada tarefa também será diferente. O Arduino também tem uma função delay () amplamente usada. Mas antes de começar vamos discutir porque não devemos usar a função delay () em nenhum projeto.
Por que pular delay () no Arduino?
Se a documentação de referência do Arduino for considerada, existem dois tipos de funções de atraso, a primeira é delay () e a segunda é delayMicroseconds (). Ambas as funções são idênticas em termos de geração de atraso. A única diferença é que, na função delay (), o número inteiro do parâmetro passado está em milissegundos, ou seja, se escrevermos delay (1000), o atraso será de 1000 milissegundos, ou seja, 1 segundo. Similarmente, na função delayMicroseconds (), o parâmetro passado está em microssegundos, ou seja, se escrevermos delayMicroseconds (1000), então o atraso será de 1000 microssegundos, ou seja, 1 milissegundo.
Aí vem o ponto, ambas as funções pausam o programa pelo período de tempo passado na função de atraso. Portanto, se estivermos dando um atraso de 1 segundo, o processador não poderá ir para a próxima instrução antes de passar 1 segundo. Da mesma forma, se o atraso for de 10 segundos, o programa irá parar por 10 segundos e o processador não permitirá que as próximas instruções sejam executadas até que os 10 segundos se passem. Isso dificulta o desempenho do microcontrolador em termos de velocidade e execução das instruções.
O melhor exemplo para explicar a desvantagem da função de atraso é usar dois botões. Considere que queremos alternar dois LEDs usando dois botões. Portanto, se um botão for pressionado, o LED correspondente deve brilhar por 2 segundos, da mesma forma, se o segundo for pressionado, o LED deve brilhar por 4 segundos. Mas quando usamos delay (), se o usuário estiver pressionando o primeiro botão, o programa irá parar por 2 segundos e se o usuário pressionar o segundo botão antes de 2 segundos de atraso, então o microcontrolador não aceitará a entrada como o programa está em estágio de parada.
A documentação oficial do Arduino menciona isso claramente em suas notas e avisos sobre a descrição da função delay (). Você pode verificar isso para tornar mais claro.
Por que usar millis ()?
Para contornar o problema causado pelo uso de delay, o desenvolvedor deve utilizar a função millis () que é fácil de usar uma vez que você se torna habitual e utilizará 100% do desempenho da CPU sem gerar nenhum atraso na execução das instruções. millis () é uma função que apenas retorna a quantidade de milissegundos que se passaram desde que a placa Arduino começou a executar o programa atual sem congelar o programa. Este número de tempo irá transbordar (ou seja, voltará a zero), após aproximadamente 50 dias.
Assim como o Arduino tem delayMicroseconds (), ele também possui a versão micro de millis () como micros (). A diferença entre micros e millis é que, micros () transbordará após aproximadamente 70 minutos, em comparação com millis () que é de 50 dias. Portanto, dependendo do aplicativo, você pode usar millis () ou micros ().
Usando millis () em vez de delay ():
Para usar o millis () para cronometragem e atraso, você precisa registrar e armazenar a hora em que a ação ocorreu para iniciar a hora e, em seguida, verificar em intervalos se a hora definida já passou. Portanto, conforme declarado, armazene a hora atual em uma variável.
currentMillis longo sem sinal = millis ();
Precisamos de mais duas variáveis para descobrir se o tempo necessário já passou. Armazenamos a hora atual na variável currentMillis , mas também precisamos saber quando o período de tempo começou e quanto tempo é o período. Portanto, o Interval e o previousMillis são declarados. O intervalo nos dirá o atraso de tempo e previosMillis armazenará a última vez que o evento ocorreu.
unsigned long previousMillis; longo período sem sinal = 1000;
Para entender isso, vamos dar um exemplo de um LED piscando simples. O período = 1000 nos dirá que o LED piscará por 1 segundo ou 1000ms.
const int ledPin = 4; // o número do pino do LED conectado int ledState = LOW; // usado para definir o estado do LED unsigned long previousMillis = 0; // armazenará a última vez que o LED piscou const long period = 1000; // período para piscar em ms void setup () { pinMode (ledPin, OUTPUT); // define ledpin como saída } void loop () { unsigned long currentMillis = millis (); // armazena a hora atual if (currentMillis - previousMillis> = period) {// verificar se 1000ms passaram previousMillis = currentMillis; // salve a última vez que você piscou o LED if (ledState == LOW) {// se o LED está desligado, ligue-o e vice-versa ledState = HIGH; } else { ledState = LOW; } digitalWrite (ledPin, ledState); // definir LED com ledState para piscar novamente } }
Aqui, a declaração
As interrupções no Arduino funcionam da mesma forma que em outros microcontroladores. A placa Arduino UNO tem dois pinos separados para conectar interrupções no pino 2 e 3 do GPIO. Nós cobrimos isso em detalhes no Tutorial de interrupções do Arduino, onde você pode aprender mais sobre interrupções e como usá-las.
Aqui, mostraremos a multitarefa do Arduino lidando com duas tarefas ao mesmo tempo. As tarefas incluirão o piscar de dois LEDs em atrasos de tempo diferentes junto com um botão de pressão que será usado para controlar o estado ON / OFF do LED. Portanto, três tarefas serão realizadas simultaneamente.
Componentes necessários
- Arduino UNO
- Três LEDs (qualquer cor)
- Resistências (470, 10k)
- Jumpers
- Tábua de pão
Diagrama de circuito
O diagrama de circuito para demonstrar o uso da função Arduino Millis () é muito fácil e não tem muitos componentes para anexar como mostrado abaixo.
Programando Arduino UNO para multitarefa
A programação do Arduino UNO para multitarefa exigirá apenas a lógica por trás do funcionamento do millis (), explicada acima. Recomenda-se praticar piscar o LED usando milis repetidamente para tornar a lógica clara e se sentir confortável com milis () antes de começar a programar o Arduino UNO para multitarefa. Neste tutorial, a interrupção também é usada com millis () simultaneamente para multitarefa. O botão será uma interrupção. Portanto, sempre que uma interrupção é gerada, ou seja, o botão é pressionado, o LED muda para o estado ON ou OFF.A programação começa com a declaração dos números dos pinos onde os LEDs e o botão de pressão estão conectados.
int led1 = 6; int led2 = 7; int toggleLed = 5; int pushButton = 2;
Em seguida, escrevemos uma variável para armazenar o status dos LEDs para uso futuro.
int ledState1 = LOW; int ledState2 = LOW;
Assim como explicado acima no exemplo de piscar, as variáveis para period e previousmillis são declaradas para comparar e gerar atraso para LEDs. O primeiro LED pisca após cada 1 segundo e outro LED pisca após 200 ms.
sem sinal long previousMillis1 = 0; const long period1 = 1000; sem sinal long previousMillis2 = 0; const long period2 = 200;
Outra função millis será usada para gerar o atraso de debounce para evitar o pressionamento múltiplo do botão. Haverá uma abordagem semelhante à anterior.
int debouncePeriod = 20; int debounceMillis = 0;
As três variáveis serão usadas para armazenar o status do botão de pressão como interrupção, LED de alternância e estado do botão de pressão.
bool buttonPushed = false; int ledChange = LOW; int lastState = HIGH;
Defina a ação do pino que irá funcionar como INPUT ou OUTPUT.
pinMode (led1, OUTPUT); pinMode (led2, OUTPUT); pinMode (toggleLed, OUTPUT); pinMode (pushButton, INPUT);
Agora defina o pino de interrupção anexando interrupção com definição de ISR e modo de interrupção. Observe que é recomendado usar digitalPinToInterrupt (pin_number) ao declarar a função attachInterrupt () para converter o pino digital real para o número de interrupção específico.
attachInterrupt (digitalPinToInterrupt (pushButton), pushButton_ISR, CHANGE);
A sub-rotina de interrupção é escrita e só mudará o sinalizador buttonPushed. Observe que a sub - rotina de interrupção deve ser a mais curta possível, então tente escrevê-la e minimizar as instruções extras.
void pushButton_ISR () { buttonPushed = true; }
O loop começa armazenando o valor em milis em uma variável currentMillis que armazenará o valor do tempo decorrido toda vez que o loop for iterado.
currentMillis longo sem sinal = millis ();
Existem no total três funções em multitarefa, piscar um LED a 1 segundo, piscar o segundo LED a 200ms e se o botão for pressionado, então desligue / ligue o LED. Portanto, escreveremos três partes para fazer essa tarefa.
O primeiro é alternar o estado do LED a cada 1 segundo, comparando os milis decorridos.
if (currentMillis - previousMillis1> = period1) { previousMillis1 = currentMillis; if (ledState1 == LOW) { ledState1 = HIGH; } else { ledState1 = LOW; } digitalWrite (led1, ledState1); }
Da mesma forma, o segundo alterna o LED a cada 200ms, comparando os milis decorridos. A explicação já foi explicada anteriormente neste artigo.
if (currentMillis - previousMillis2> = period2) { previousMillis2 = currentMillis; if (ledState2 == LOW) { ledState2 = HIGH; } else { ledState2 = LOW; } digitalWrite (led2, ledState2); }
Por último, o flag buttonPushed é monitorado e após gerar um atraso de debounce de 20ms, ele apenas alterna o estado do LED correspondente ao botão de pressão anexado como interrupção.
if (buttonPushed = true) // verificar se ISR é chamado { if ((currentMillis - debounceMillis)> debouncePeriod && buttonPushed) // gerar 20ms de atraso de debounce para evitar pressionamentos múltiplos { debounceMillis = currentMillis; // salvar o último tempo de atraso de debounce if (digitalRead (pushButton) == LOW && lastState == HIGH) // alterar o led após o botão ser pressionado { ledChange =! ledChange; digitalWrite (toggleLed, ledChange); lastState = LOW; } else if (digitalRead (pushButton) == HIGH && lastState == LOW) { lastState = HIGH; } buttonPushed = false; } }
Isso conclui o Tutorial do Arduino millis (). Observe que para se tornar habitual com millis (), apenas pratique para implementar esta lógica em algumas outras aplicações. Você também pode expandi-lo para usar motores, servo motores, sensores e outros periféricos. Em caso de dúvida, escreva para o nosso fórum ou comente abaixo.
O código completo e o vídeo para demonstrar o uso da função millis no Arduino são fornecidos abaixo.