Curso Arduino – #2 – O Básico da Programação e as Portas I/O
Na segunda parte do Curso Arduino, vamos começar a programar! De início, vamos, obviamente, abordar o básico dos básicos.
O Arduino, tal como já foi referido, opera na linguagem C. Este artigo vai introduzir os fundamentos da programação em C e apresentar o seu uso prático no exemplo de portas I/O.
O Básico da Programação do Arduino
Na linguagem C, todas as instruções que queremos dar, colocamos na função principal (mais sobre funções abaixo):
int main() { //Conteúdo do Programa }
O símbolo “//” indica um comentário. É uma informação, de uma linha, que ajuda as pessoas a entender o programa. Durante a compilação, todos os comentários são omitidos. Se quiser escrever um comentário mais longo, deve colocá-lo entre “*/“.
No Arduino, há aspetos que estão simplificados. Existem duas funções: uma delas executa a instrução uma vez, a outra executa a instrução definida em loop. Vejamos:
void setup() { //Instruções que são executadas apenas uma vez } void loop() { //Instruções que são executadas em loop }
Na prática, a primeira função geralmente contém as configurações. Por exemplo: a definição dos pinos como entradas ou saídas. Nesta função, depois de ligar a placa, serão realizadas ações que é suposto só acontecerem uma vez.
Na segunda função, coloca-se o código que pretende executar o tempo todo (em loop). Vai perceber isto muito melhor nos exemplos práticos mais abaixo.
Funções – o que é que significam?
Os códigos podem ser escritos por si, ou pode usar um pré-feito, fornecido por programadores ou geeks que compartilham o seu próprio código. No início do curso, vamos concentrar-nos nas funções fornecidas pelas livrarias, juntamente com o compilador Arduino.
Existe um conceito de função na linguagem C. Uma função, nas linguagens de programação, é um bloco (lista) de certos comandos extraídos do código principal, cujo desempenho fornece um resultado.
Cada função pode integrar vários argumentos e enviar um resultado. O programador pode determinar quais valores serão o resultado e os dados de entrada. Cada função tem o seu próprio tipo de resultado enviado (prefixos ex.: int, string, etc.) – pode ser um número, um sinal ou outra coisa. Há também um tipo específico de função – não envia qualquer valor (prefixo void).
Vamos concentrar-nos nas principais funções dos programas Arduino.
void setup() { }
O procedimento setup, como já vimos, é indicado para executar as instruções definidas uma só vez. Como o nome sugere, é destinado principalmente a configurações gerais. Neste, o processador é iniciado, os periféricos são configurados, etc.
void loop() { }
A função (procedimento) loop é, como o nome indica, um loop infinito. É indicado para instruções que devem ser executadas o tempo todo.
Vamos passar aos exemplos práticos!
Arduino UNO – pinagem
Como foi dito no artigo anterior, através de certos conectores, pode ligar elementos externos ao Arduino, como: LEDs e botões. No entanto, antes de chegarmos a essa parte, precisamos saber a descrição dos pinos/conectores existentes na placa. Abaixo pode ver um esquema com os principais pinos do Arduino UNO:
A verde escuro (#0 a #19) estão indicados os pinos digitais de entrada/saída (I/O). Quando usados como saídas, podemos definir como sendo 0V (nível lógico 0, low) ou 5V (nível lógico 1, high). Quando são configurados como entradas, são capazes de detetar se o pino em questão possui tensão de 0V ou 5V.
As entradas analógicas (A0-A5) estão destacadas a verde claro. Estes são pinos únicos que permitem medir a tensão (0-5V). Como pode ver, a numeração destes pinos coincide com os pinos universais (#14 a #19). Trabalhar no modo analógico é uma função adicional deles.
Em azul, foram destacados pinos com funções alternativas. Isto significa que, além de serem pinos I/O normais, podem executar funções mais complexas. Vamos trabalhar com eles posteriormente, mas para já só precisamos de uma explicação básica:
- SDA, SCL – barramentos I2C usados, por exemplo, para comunicação com sensores mais avançados. Existem dois pinos SDA e dois pinos SCL, no canto inferior esquerdo e superior direito da placa;
- TX, RX – interface UART, usados principalmente para comunicação com o computador;
- PWM – saídas nas quais é possível gerar um sinal retangular (variável). É função muito útil, por exemplo, no controlo de servos;
- LED – LED permanentemente instalado no Arduino, que está diretamente ligado ao pino #13.
A cor-de-laranja estão saídas que não são programáveis. Estas são responsáveis, principalmente, pela alimentação do sistema. Serão discutidas mais detalhadamente quando chegar a hora de as usar.
Os conectores indicados na imagem como ICSP são usados para programação direta de dois microcontroladores, que estão localizados na placa Arduino UNO. Esses conectores são usados em casos muito específicos e, não há necessidade de falarmos sobre eles para já.
Exercício Prático de Saídas – LED
Vamos começar a prática com algo muito simples: ligar um LED. De acordo com a descrição anterior, qualquer pino de I/O pode ser usado nessa ligação. Para já, escolha a saída digital #8. Saída digital é a saída que pode ser definida num de dois estados (high ou low). No caso do Arduino, será 5V ou 0V.
Material necessário:
- 1x Placa Arduino UNO e cabo USB;
- 1x Breadboard;
- 1x LED;
- 1x Resistência 330Ω;
- 2x Cabos jumper macho-macho.
O sistema deve ser ligado de acordo com esquema apresentado abaixo. O LED é ligado em série com uma resistência (330RΩ). De seguida, ligue o pino mais longo do LED (ânodo) na mesma coluna do jumper ligado ao pino #8. O segundo pino, deverá estar na mesma coluna de uma das pernas da resistência. A outra perna deve estar na mesma coluna que o jumper ligado ao pino terra (GND). Existem 3 pinos GND na placa, pode escolher qualquer um.
A programação da inclusão do LED é muito simples. Ligue o Arduino ao computador com o cabo USB. Abra o Arduino IDE e escreva o código abaixo. De seguida, faça o upload para a placa.
void setup() { pinMode(8, OUTPUT); digitalWrite(8, HIGH); } void loop() { }
A função pinMode (Pin, Mode) permite selecionar se o pino é uma entrada ou uma saída. O pino é um número inteiro entre 0 e 13 e o modo pode ser:
- INPUT
- OUTPUT
- INPUT_PULLUP.
Se queremos controlar uma saída, usamos o modo OUTPUT. Os outros modos serão discutidos mais tarde.
Graças a esta configuração, pode definir o estado lógico na saída e, assim, ativar o LED. A função digitalWrite (Pin, Status) é usada para este propósito. O estado é um estado lógico que pode ser HIGH ou LOW (alto ou baixo).
Neste exemplo, o LED já foi ligado ao pino terra, então, o Arduino deve atingir um estado alto: digitalWrite (8, HIGH).
Depois de definir o pino num único estado, o seu valor não será alterado até definir um valor diferente. Portanto, o programa acima fará com que o LED permaneça ligado o tempo todo.
Exercício Prático de Delays – LED a Piscar
Neste exercício, queremos colocar o LED a piscar. Para isso, é necessária uma nova função para inserir o delay. O esquema de ligação é exatamente igual ao anterior. O código deverá ficar assim:
void setup() { pinMode(8, OUTPUT); //Definir o pino 8 como saída } void loop() { digitalWrite(8, HIGH); //Ligar o LED delay(1000); //Esperar 1 segundo digitalWrite(8, LOW); //Desligar o LED delay(1000); //Esperar 1 segundo }
Na função loop, o estado é constantemente alternado. Foram adicionados atrasos ao programa através da função delay. Esta função assume um determinado número de milissegundos a atrasar.
Se não introduzisse os atrasos, o sistema mudaria de estado tão rapidamente que seria impossível ver a alternância a olho nu. Pode realizar esta experiência ao colocar 0ms como delay.
Trabalho de Casa nº1
Qual o menor valor de delay que pode colocar para conseguir ver o LED a piscar? O que é que acontece quando o LED pisca demasiado rápido? Partilhe as suas respostas nos comentários abaixo!
Trabalho de Casa nº2
Escolha um pino I/O e ligue um segundo LED. Escreva um programa que ligue os dois LEDs. De seguida, escreva um programa que faça os dois LEDs piscar alternadamente.
Exercício Prático de Entradas – Instrução Condicional (if)
Muitas vezes é pretendido que o sistema reaja a sinais externos. Desta vez vamos ligar o botão de pressão ao Arduino, para além do LED.
Material necessário:
- 1x Arduino UNO e cabo USB;
- 1x Breadboard;
- 1x LED;
- 1x Botão de Pressão;
- 1x Resistência de 330Ω;
- 5x Cabos jumper.
A ligação deverá ser realizada de acordo com o esquema abaixo. De um lado, o botão foi ligado ao terra (menos) e do outro lado ao pino 7.
O objetivo é criar um programa que ligue o LED quando o botão é pressionado. A tarefa é muito simples, mas vamos inserir algo novo – instruções condicionais.
Queremos que o programa esteja permanentemente num dos dois estados – o LED está ligado ou desligado. De início, é necessário ler o estado lógico do pino de entrada do botão.
Lembre-se do modo INPUT_PULLUP mencionado anteriormente. A primeira parte do nome (input) obviamente significa entrada, enquanto o segundo (pullup) sugere a inclusão de uma resistência interna que verifica o estado do interruptor. Vamos usá-lo sempre que ligarmos um botão ao Arduino.
Como já foi referido, é preciso ler o estado do pino de entrada. Para isso, é necessária a função digitalRead (pin), que envia a informação HIGH ou LOW, dependendo do estado. No entanto, apenas ler o estado da entrada não é o suficiente, devemos fazer com que o programa trabalhe em função dessa informação. Daí a instrução condicional (if). Graças a esta, pode executar uma determinada parte do código se houver o cumprimento de determinados requerimentos ou se se verificarem certas condições. Por exemplo, se pressionar um botão.
[...] void loop() { if (condição) { /* A instrução é executada em loop somente quando a condição é cumprida*/ } }
Esta função pode ser facilmente ampliada com uma parte do código que será executada somente se a condição não for verificada. A instrução else é usada para isso.
[...] void loop() { if ( condição) { /* A instrução é executada em loop somente quando a condição é cumprida */ } else { /* A instrução é executada somente quando a condição não é cumprida*/ } }
Ao combinar o conhecimento adquirido, pode criar um programa que realize a tarefa proposta. Analise o código e faça o upload para o Arduino.
void setup() { pinMode(8, OUTPUT); //LED como saída pinMode(7, INPUT_PULLUP); //Botão como entrada digitalWrite(8, LOW); //Desliga o LED } void loop() { if (digitalRead(7) == LOW) { //Se o botão for pressionado digitalWrite(8, HIGH); //Ligar o LED } else { //Se o botão não for pressionado digitalWrite(8, LOW); //Desligar LED } }
Todavia, este programa é de pouca utilidade. Por que é que precisamos de um botão que só funciona quando o estamos a pressionar? Não seria melhor se o LED estivesse aceso por um determinado período de tempo após o botão ser pressionado?
Exemplo – Interruptor de Luz com “Temporizador”
Neste exemplo queremos que, tendo em conta o exemplo anterior, o LED esteja ligado por 10 segundos após pressionar o botão.
É capaz de escrever um programa adequado? Esperamos que sim! Se tiver dificuldades, pode sempre dar uma vista de olhos no nosso código:
void setup() { pinMode(8, OUTPUT); //LED como saída pinMode(7, INPUT_PULLUP); //Botão como entrada digitalWrite(8, LOW); //Desliga o LED } void loop() { if (digitalRead(7) == LOW) { //Se o botão for pressionado digitalWrite(8, HIGH); //Ligar o LED delay(10000); //Esperar 10 segundos digitalWrite(8, LOW); //Desligar LED } }
Exemplo – Semáforo
O próximo exemplo é um sistema de semáforo sequencial. O principal objetivo é escrever um programa que, depois do botão ser pressionado, mostre uma sequência correta de luzes. Vamos assumir o seguinte ciclo:
[…] Verde -> Amarelo -> Vermelho -> Amarelo+Vermelho […]
Material necessário:
- 1x Arduino UNO e cabo USB;
- 1x Breadboard;
- 1x Botão de pressão;
- 1x LED vermelho;
- 1x LED amarelo;
- 1x LED verde;
- 3x Resistências de 330Ω;
- 6x Cabos jumper.
Quando pressionar o botão, o sistema deve iniciar a sequência. Vamos fazê-lo por etapas. Primeiro, ligue os 3 LEDs conforme o esquema abaixo:
Vamos preparar um programa que serve apenas para configurar as entradas e saídas.
void setup() { pinMode(10, OUTPUT); //LED vermelho pinMode(9, OUTPUT); //LED amarelo pinMode(8, OUTPUT); //LED verde pinMode(7, INPUT_PULLUP); //Botão digitalWrite(10, LOW); //Desligar os LEDs digitalWrite(9, LOW); digitalWrite(8, LOW); }
Agora que estão os LEDs e o botão configurados, vamos escrever um programa que mude as luzes automaticamente, a cada 1 segundo. O sketch completo deverá ficar assim:
void setup() { pinMode(10, OUTPUT); //LED vermelho pinMode(9, OUTPUT); //LED amarelo pinMode(8, OUTPUT); //LED verde pinMode(7, INPUT_PULLUP); //Botão digitalWrite(10, LOW); //Desligar os LEDs digitalWrite(9, LOW); digitalWrite(8, LOW); } void loop() { digitalWrite(10, LOW); //Vermelho digitalWrite(9, LOW); //Amarelo digitalWrite(8, HIGH); //Verde delay(1000); //Esperar 1 segundo digitalWrite(10, LOW); //Vermelho digitalWrite(9, HIGH); //Amarelo digitalWrite(8, LOW); //Verde delay(1000); //Esperar 1 segundo digitalWrite(10, HIGH); //Vermelho digitalWrite(9, LOW); //Amarelho digitalWrite(8, LOW); //Verde delay(1000); //Esperar 1 segundo digitalWrite(10, HIGH); //Vermelho digitalWrite(9, HIGH); //Amarelo digitalWrite(8, LOW); //Verde delay(1000); //Esperar 1 segundo }
Faça o upload do programa no Arduino e verifique se está tudo a funcionar na perfeição.
Exercício Prático – Função While
Até agora, usamos apenas o loop principal e obrigatório (função void loop). Agora é hora de conhecer um loop que podemos usar dentro dos nossos programas.
Agora vamos abordar o loop while (), que funciona enquanto uma certa condição for cumprida (verdadeira). O seu funcionamento é apresentado no seguinte código:
[...] void loop() { while (condição) { /* O código é executado em loop enquanto a condição for verdadeira */ } }
Para uma maior clareza, a função while apenas executa o código que fica entre as suas chavetas {}. O restante código não é executado naquele momento.
Vamos aproveitar o sistema de semáforo que foi montado anteriormente, para escrever um programa que fará piscar um LED de cada vez quando o botão for pressionado. Este exercício é uma tarefa mais difícil do que a anterior.
Confira o código que propomos:
void setup() { pinMode(10, OUTPUT); //LED vermelho pinMode(9, OUTPUT); //LED amarelo pinMode(8, OUTPUT); //LED verde pinMode(7, INPUT_PULLUP); //Botão digitalWrite(10, LOW); //Desligar LED digitalWrite(9, LOW); digitalWrite(8, LOW); } void loop() { while (digitalRead(7) == LOW) { //Quando o botão é pressionado digitalWrite(10, LOW); //Vermelho desligado delay(1000); digitalWrite(10, HIGH); //Vermelho ligado delay(1000); } }
Se entender o código acima, pode prosseguir e completar a tarefa original: comutação automática das luzes.
Desta vez, as sequências devem ser exibidas até pressionar o botão. Assumimos que o botão é pressionado e largado muito rapidamente. O programa finalizado deve ter este aspeto:
void setup() { pinMode(10, OUTPUT); //LED vermelho pinMode(9, OUTPUT); //LED amarelo pinMode(8, OUTPUT); //LED verde pinMode(7, INPUT_PULLUP); //Botão digitalWrite(10, LOW); //Desligar LED digitalWrite(9, LOW); digitalWrite(8, LOW); } void loop() { digitalWrite(10, LOW); //Vermelho digitalWrite(9, LOW); //Amarelo digitalWrite(8, HIGH); //Verde while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado digitalWrite(10, LOW); //Vermelho digitalWrite(9, HIGH); //Amarelo digitalWrite(8, LOW); //Verde while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado digitalWrite(10, HIGH); //Vermelho digitalWrite(9, LOW); //Amarelo digitalWrite(8, LOW); //Verde while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado digitalWrite(10, HIGH); //Vermelho digitalWrite(9, HIGH); //Amarelo digitalWrite(8, LOW); //Verde while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado }
Neste caso, o loop foi usado de uma maneira bastante estranha. Como pode verificar não há nada dentro das chavetas! Então, como é que o programa está a funcionar? Isso ocorre porque o programa usa loops para parar.
Como é que está a funcionar?
- Começamos a iluminar os LEDs de acordo com uma sequência;
- Entramos na função loop while (), que está imediatamente abaixo;
- As chavetas estão vazias, portanto, o programa está sempre em loop, sem fazer nada;
- Somente depois do botão ser pressionado (a condição passa a ser falsa) o programa sai do loop;
- A sequência seguinte é acionada e a situação repete-se.
Vamos agora verificar o programa na prática!
O que é que está a acontecer? Está tudo a funcionar como suposto? Claro que não! Mesmo quando o botão é pressionado por um curto período de tempo, às vezes o programa funciona corretamente, e outras vezes, salta algumas posições. Por que é que isso acontece?
Como se deve lembrar do primeiro artigo, o processador, de forma simplificada, realiza cerca de 16 milhões de operações por segundo. Portanto, ao pressionar o botão, o processador será capaz de ter acesso a todos os estados da nossa sinalização. Posto isto, depois de largar o botão poderá haver uma escolha aleatória na sequência.
Como resolver este problema? Muito simples! É suficiente alterar o programa, para que a mudança de luz não ocorra com mais frequência do que, por exemplo, a cada segundo. Para isso, podemos usar a função delay () já conhecida.
void setup() { pinMode(10, OUTPUT); //LED vermelho pinMode(9, OUTPUT); //LED amarelo pinMode(8, OUTPUT); //LED verde pinMode(7, INPUT_PULLUP); //Botão digitalWrite(10, LOW); //Desligar LED digitalWrite(9, LOW); digitalWrite(8, LOW); } void loop() { digitalWrite(10, LOW); //Vermelho digitalWrite(9, LOW); //Amarelo digitalWrite(8, HIGH); //Verde delay(1000); //Parar o programa durante 1 segundo while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado digitalWrite(10, LOW); //Vermelho digitalWrite(9, HIGH); //Amarelo digitalWrite(8, LOW); //Verde delay(1000); //Parar o programa durante 1 segundo while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado digitalWrite(10, HIGH); //Vermelho digitalWrite(9, LOW); //Amarelo digitalWrite(8, LOW); //Verde delay(1000); //Parar o programa durante 1 segundo while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado digitalWrite(10, HIGH); //Vermelho digitalWrite(9, HIGH); //Amarelo digitalWrite(8, LOW); //Verde delay(1000); //Parar o programa durante 1 segundo while (digitalRead(7) == HIGH) {} //Quando o botão é pressionado }
Agora já deve funcionar perfeitamente!
É importante ressaltar que as condições na função while () podem ser combinadas e muito mais complexas, mas voltaremos a este tópico quando conhecermos as variáveis.
Sumário
Depois de verificar e perceber os programas mostrados neste artigo, não deverá ter qualquer problema com periféricos mais importantes e complexos. Esperamos que tenha percebido que muitas vezes é preferível dividir o programa em várias partes, em vez de escrever o código integral todo de uma só vez. Se algo não estiver claro, deixe o seu comentário na secção abaixo.
No próximo artigo, vamos abordar a comunicação com o computador através da porta USB. Graças a isto, será mais fácil testar programas extensos.
___________
O que achou deste artigo? Deixe o seu comentário abaixo, e partilhe nas Redes Sociais que certamente será útil e interessante para os seus amigos!
Curso Arduino – #0 – Introdução
Curso Arduino – #1 – O Básico do Arduino e o Software de Programação
Curso Arduino – #2 – O Básico da Programação e as Portas I/O
Curso Arduino – #3 – UART e Variáveis
Curso Arduino – #4 – Conversor Analógico-Digital
Curso Arduino – #5 – PWM, Servomecanismos e Bibliotecas
Curso Arduino – #6 – UART (continuação) e Servos
Curso Arduino – #8 – Controlo de Motores DC
Curso Arduino – #9 – Sensor de Distância Ultrassónico HC-SR04 e Novas Funções
Curso Arduino – #10 – Gráficos, Números Aleatórios e Condições