Prévia do material em texto
AULA 5
LÓGICA E
MICROCONTROLADORES
Prof. Charles Way Hun Fung
2
TEMA 1 – VETORES OU ARRAY
Durante o desenvolvimento de um projeto com o Arduino e sensores, é
comum armazenar muitos dados em variáveis de mesmo tipo para um mesmo
propósito. Um exemplo é fazer a leitura de um sensor de temperatura: imagine
que esse sensor será lido uma vez a cada hora, então no final de um dia seriam
necessárias 24 variáveis para armazenar todas as leituras. Além disso, seria
necessário fazer o gerenciamento de todas essas variáveis com nomes diferentes.
Soluções para o problema apresentado são as novas estruturas para
armazenamento de dados. Nesta aula vamos introduzir o uso de vetores e
matrizes que consistem em variáveis unidimensionais ou multidimensionais,
respectivamente.
Segundo Oliveira (2017), vetor ou array é uma estrutura unidimensional que
armazena diversos valores de mesmo tipo. A figura a seguir ilustra um vetor.
Figura 1 – Estrutura de um vetor
Perceba que, na Figura 1, dentro de cada quadrado há o conteúdo de uma
variável que está contida no vetor. O número que se encontra sobre esses
quadrados são os índices, que indicam a posição de cada variável. Pode-se dizer
que a posição zero do vetor possui conteúdo ou valor igual a 5. Da mesma forma,
pode-se dizer que a posição 1 possui o valor 8, e assim por diante.
O vetor da Figura 1 possui um tipo; no caso, o que melhor se enquadra na
figura é o tipo inteiro, mas da mesma forma que as variáveis, os vetores possuem
um tipo dentre os possíveis na linguagem C/C++. A declaração de um vetor segue
o formato
<tipo> <nome vetor>[<tamanho do vetor>].
Por exemplo:
int numeros[10];
Esse exemplo declara um vetor com o nome de “numeros” do tipo inteiro
de 10 posições.
3
Para atribuir um valor para uma posição do vetor, é necessário usar o nome
do vetor, a posição entre colchetes – “[ ]” – seguido do operador de atribuição e o
valor, como mostrado no exemplo a seguir.
numeros[2] = 5;
Além disso, é possível inicializar um vetor com valores específicos fazendo
uma atribuição direta da seguinte forma.
int vet[] = {1, 5, 10, 7, 4, 2, 2, 9, 20, 15};
Nessa atribuição, não é necessário adicionar o tamanho do vetor, apenas
o conteúdo entre chaves e cada elemento separado por vírgulas.
Quando o vetor é do tipo caractere, a atribuição no momento da declaração
é um pouco diferente:
char frase[] = “Ola mundo”;
Perceba que, nessa declaração, o vetor recebe o texto entre aspas duplas.
Cada caractere vai ocupar uma posição específica no vetor, como na figura a
seguir.
Figura 2 – Vetor de caracteres
Um vetor de caracteres é conhecido como Strings; vamos abordá-lo em um
tema adiante sobre esse tipo de vetor. Como se pode ver na Figura 2, a frase da
declaração é colocada no vetor caractere por caractere até um finalizador ‘\0’, que
indica o termo nulo dessa linguagem. Esse caractere nulo é importante como
finalizador de String, já que variáveis caracteres podem assumir qualquer valor.
TEMA 2 – USANDO VETORES NO ARDUINO
Para exemplificar o uso de vetores com o Arduino, serão realizadas
diversas leituras de uma distância utilizando um sensor de ultrassom. Para isso,
será feito o uso de um sensor ultrassônico HC-SR04, que possui alcance de
2 cm a 4 m. Esse sensor é apresentado na figura a seguir.
4
Figura 3 – Sensor de ultrassom
Crédito: Pozdeyev Vitaly/Shutterstock.
O sensor de ultrassom HC-SR04 possui quatro pinos: VCC, Trigger, Echo
e GND. O primeiro pino corresponde aos 5 V e GND representa o terra. O pino
Trigger indica o som deve ser enviado, enquanto o Echo é usado para receber o
dado quando retorna. O cálculo da distância se baseia no tempo que a onda
sonora demora para ir e voltar ao objeto da medição da distância.
A Figura 4 apresenta o sensor de ultrassom com o Arduino. No Tinkercad,
o sensor ultrassônico possui apenas um pino de sinal, que, no nosso exemplo,
está conectado ao pino digital 7. Nesse exemplo, é realizada uma medição da
distância a cada 10 s; após um minuto, apresenta-se a média dos valores no
terminal serial.
Figura 4 – Circuito com ultrassom
5
Figura 5 – Código para leitura do ultrassom
6
No código apresentado na Figura 5 (linha 8), é feito uso da função
“distanciaUltrassom”, que calcula o tempo que o sinal ultrassônico levou para ir
do sensor até o objeto. Essa configuração, que estabelece o pino de Trigger como
saída na linha 11, serve para tornar flexível o uso desse pino; na prática, podemos
colocar qualquer pino digital como Trigger. Da mesma forma, o pino de Echo que
é como entrada (INPUT), na linha 19. As linhas 12, 13, 15, 16 e 17 fazem a
transmissão de um pulso de 10 µs pelo Trigger, o qual é representado pelas linhas
15 e 16, que deixam o pino de Trigger em HIGH.
O retorno desse pulso é registrado na linha 22, que possui a função
“pulseIn”, que retorna o tempo para receber esse sinal em Echo. Esse tempo é
retornado por essa função e será usado para determinar a distância até o objeto
por meio de uma equação de conversão para centímetros.
Na função “loop()”, na linha 33, temos a conversão do valor do tempo para
centímetros. Essa equação foi retirada da documentação fornecida pelo fabricante
do sensor de ultrassom.
Na linha 34, o valor da distância em centímetros é colocado no vetor, na
posição determinada por “pos”. Essa variável serve para controlar em qual
posição do vetor será armazenado o valor da medição. As linhas de 36 a 41
mostram essa medição no terminal serial. Após o armazenamento dos dados, a
variável “pos” é incrementada na linha 42, que indica que a próxima posição do
vetor está pronta para receber um novo valor.
Na linha 43, há uma verificação quanto ao “pos” ter chegado ao valor 6.
Como o vetor tem apenas 6 posições e as posições no vetor vão de 0 a 5, não
seria possível armazenar dados na sexta posição, logo, deve-se fazer o cálculo
da média desses valores. Para realizar esse cálculo, foi usado um “loop for” na
linha 44, que usa uma variável de controle i, que começa em 0 e vai até o valor 5;
quando chega ao valor 6, encerra o “loop”. Nesse laço, é realizada a soma dos
valores das medições e colocado na variável “media”, na linha 45. Perceba que a
variável “media” acumula os valores das medições:
media = media + medidas[i];
Essa equação diz que “media vai receber o valor de media mais o valor da
posição do vetor determinada por i”. A variável “media” começa com o valor zero
e, com o passar das iterações, vai acumulando o valor de medidas.
Quando o “loop” termina, o valor de “media” é dividido por 6 e mostrado no
terminal nas linhas 49 a 51, como mostrado na figura a seguir.
7
Figura 6 – Resultado apresentado pelo terminal serial
Após a apresentação desse resultado, o valor da variável “media” é zerado
na linha 52. Para que a leitura seja feita a cada 1s, é usado um “delay” na linha
55.
TEMA 3 – VETOR DE CARACTERES OU STRINGS
Os vetores de caracteres precisam de atenção especial por possuir um tipo
próprio, o tipo String. Toda vez que se desejar escrever uma frase ou palavra na
linguagem wiring, deve-se fazer uso de formas específicas dos comandos.
Primeiramente, a forma que é escrita uma sequência de caracteres nessa
linguagem é entre aspas duplas:
“<Texto>”
Exemplo:
char nome[] = “José”;
char frase[] = “Ola mundo!”;
Um detalhe importante na linguagem é a diferença entre um caractere e
uma String; o primeiro é representado entre aspas simples, por exemplo, ‘A’, ‘c’,
‘3’, indicando o caractere A maiúsculo, c minúsculo e o caractere 3,
respectivamente. No caso da String, é representada entre aspas duplas, como
mencionado anteriormente, por exemplo: “Teste”, “Ola mundo” e “A”. Uma dúvida
comum do estudante dessa linguagem é diferenciar ‘A’de “A”, porque
aparentemente parecem ser a mesma coisa, mas não são. O primeiro é o
caractere A maiúsculo, o segundo é uma String que contém o A maiúsculo, mas
não é só isso, a String é composta pelo seu conteúdo seguido pelo caractere nulo
(‘\0’), como mostrado na figura a seguir.
8
Figura 7 – Exemplo de String com o nulo para finalização
O formato do caractere nulo pode parecer estranho, mas é exatamente
esse formato, com um caractere barra seguido do caractere zero. O nulo tem uma
representação na linguagem que não deve ser confundida com o zero. Ambos
possuem significados diferentes.
No Arduino, as Strings são utilizadas principalmente na comunicação serial,
na qual se pode mostrar mensagens e valores de variáveis. No decorrer do curso,
já usamos diversas vezes a comunicação serial, mas fundamentalmente essa
comunicação funciona da seguinte forma.
1. Configuração da taxa de comunicação.
2. Envio de mensagens.
A configuração é realizada na função “setup()”, na qual é chamada a função
“Serial.begin”, como mostrado no exemplo a seguir.
Serial.begin(9600);
Entre parênteses se encontra a taxa de comunicação, esta deve ser igual
tanto no Arduino como no receptor, no caso, o monitor serial.
Nas mensagens enviadas na serial, há duas funções básicas:
• Serial.print: escreve a String que está determinada entre parênteses. Por
exemplo:
Serial.print(“Ola mundo”);
Essa função não pula para a próxima linha do monitor serial assim que
finaliza sua execução;
• Serial.println: escreve a String que está determinada entre parênteses,
pulando de linha no monitor serial após sua execução. Por exemplo:
Serial.println(“Teste”);
Além disso, as Strings podem ser usadas para converter diversos tipos de
dados:
9
1. Converter um caractere para uma String:
String teste = String(‘a’);
2. Converter um constante inteiro:
String teste = String(12);
3. Converter um número inteiro para uma base numérica:
a. Base decimal: String teste = String(x, DEC); em que x é uma variável;
b. Base hexadecimal: String teste = String(32, HEX);
c. Base binária: String teste = String(255, BIN);
4. Converter um float com casas decimais:
String teste = String(1.523, 3);
5. Concatenar duas Strings:
String s = “ola”;
String teste = String(s+” mundo”);
TEMA 4 – MATRIZES
Da mesma forma que vetores, as matrizes são um conjunto de dados de
mesmo tipo, porém possuem duas dimensões: linhas e colunas. Elas são formas
bidimensionais de armazenar dados. A figura a seguir ilustra o formato de uma
matriz 4x5 (4 linhas e 5 colunas).
Figura 8 – Exemplo de matriz
A matriz da Figura 8 mostra como os dados se distribuem na matriz.
Perceba que as linhas e colunas são numeradas: linhas vão de 0 a 3 e colunas
de 0 a 4, formando a matriz 4x5.
10
A forma de declaração de uma matriz segue um modelo parecido ao vetor,
porém com duas dimensões:
<tipo> <nome matriz>[<linhas>][<colunas>];
Exemplo:
int leituras[4][5];
Nesse exemplo, é declarada uma matriz inteira com o nome de leituras com
quatro linhas e cinco colunas. As matrizes aceitam todos os tipos de dados: inteiro
(int), float (real), char (caractere), double (real com maior precisão).
Também é possível declarar os valores de uma matriz diretamente na
declaração, por exemplo:
int leituras[][] = {{1,2,3},{4,5,6},{7,8,9}};
Para atribuir um valor para uma determinada posição de uma matriz, deve-
se especificar a linha e a coluna dessa posição e usar o operador de atribuição,
como no exemplo a seguir.
leituras[2][3] = 5;
Essa linha significa que a variável inteira que se encontra no cruzamento
entre a linha 2 e a coluna 3 receberá o valor de 5. Isso pode ser ilustrado mudando
o valor da posição de linha 2 e coluna 3 para 5, na Figura 8.
Figura 9 – Matriz da Figura 8 modificada com novo valor da posição 2x3
Em uma matriz, é comum fazer uma varredura por todos os componentes.
Isso é útil tanto para atribuir valores para todas as posições da matriz como para
procurar algum valor específico. Por exemplo:
Contar quantas posições na matriz da figura 9 têm valor superior a 10.
11
Para resolver esse problema, devemos criar duas variáveis para contar a
quantidade de linhas e colunas.
int linha;
int coluna;
Nesse caso, essa matriz tem quatro linhas e cinco colunas, então, para
cada coordenada, devemos fazer um “loop for” da seguinte forma.
for(linha=0;linha<4;linha++){
for(coluna=0;coluna<5;coluna++){
}
}
O funcionamento desse código é baseado na mecânica dos “loops”. A ideia
é bem simples, fixa um valor da linha e preenche os valores na respectiva linha.
Em seguida, incrementa o valor da linha e preenche os valores da linha, processo
que se repete até finalizar as linhas.
No código, a variável “linha” começa com um valor zero, e verifica-se a
condição de linha<4; esta é verdadeira. Sendo assim, vai executar os comandos
dentro do primeiro “for”. Nos comandos, há um novo “for”, nesse caso a variável
de controle é coluna, que começa com zero e também tem uma condição
verdadeira – em breve comentaremos sobre os códigos no interior desse “for” que
vão ser executados. Assim que esse “for” interno terminar, a variável “linha” será
incrementada em uma unidade; nesse caso, vai para o valor 1. A condição do “for”
externo ainda continua sendo verdadeira, reiniciando o “for” interno. Essa
repetição desse código vai continuar até que todas as linhas sejam verificadas.
Perceba que cada posição da matriz é explorada com esse código, na seguinte
ordem.
[0][0], [0][1], [0][2], [0][3], [0][4], [1][0], [1][1], [1][2], [1][3], [1][4], [2][0], [2][1], [2][2],
[2][3], [2][4], [3][0], [3][1], [3][2], [3][3], [3][4].
Perceba que, como mencionado anteriormente, o valor da linha é fixado
para, em seguida, suas posições de coluna serem verificadas.
12
No problema proposto, deve-se contar a quantidade de posições da matriz
que possui valor superior a 10. Para isso, devemos criar uma variável para realizar
essa contagem; chamaremos essa variável de contador, que deverá começar
zerada para que não ocorra nenhum erro na contagem das posições da matriz.
int contador = 0;
O resto do código é apresentado a seguir.
for(linha=0;linha<4;linha++){
for(coluna=0;coluna<5;coluna++){
if(leituras[linha][coluna]>10){
contador++;
}
}
}
Serial.println(“A quantidade de valores é ”+contador);
No “loop” interno, temos um comando de comparação que verifica se a
posição analisada possui um conteúdo com valor superior a 10. Caso isso seja
verdade, o comando “contador++”, que incrementa o contador em uma unidade,
é executado. No final, o valor de contador é mostrado na serial.
TEMA 5 – EXEMPLO DO USO DE MATRIZ COM O ARDUINO
Nesse exemplo, faremos a leitura de três sensores de temperatura e vamos
armazenar as leituras em uma matriz. Nesse caso, serão feitas
10 leituras e serão mostradas as médias das leituras na serial.
O esquemático do circuito para realizar a leitura das temperaturas é
apresentado a seguir.
13
Figura 10 – Leitura de três sensores de temperatura
O sensor de temperatura utilizado é o TMP36, que possui três pinos; o pino
da direita é a alimentação de 5 V, na esquerda é o GND (Terra), o qual está
conectado ao GND do Arduino. O pino do meio é uma saída analógica que está
conectada às entradas analógicas do Arduino. Nesse exemplo, utilizaremos as
entradas A0, A1 e A2.
O código para resolver esse problema será separado em partes, para
melhorar a compreensão.
Figura 11 – Declaração e variáveis e função setup
Na Figura 11, as declarações de variáveis são apresentadas. As variáveis
declaradas na linha 1, temperatura1, temperatura2 e temperatura3 serão usadas
para receber diretamente a medição de temperatura dos sensores, porém será
realizada uma conversão para que na variável esteja o valorem graus Celsius.
14
Temp_media1, temp_media2, temp_media3 são as médias das
temperaturas dos sensores que serão mostradas na serial.
A variável “num_medida” será usada para indicar quantas leituras foram
realizadas; caso esse número chegue a 10, deverá ser realiza a média dos
valores.
A matriz medidas[3][10] será onde os dados das medições serão
armazenados. Cada linha dessa matriz registra os dados de um único sensor,
sendo a linha zero o primeiro sensor, a linha 1 o segundo sensor e a linha 2 o
terceiro sensor. Como o total de medidas é dez, foram criadas dez colunas de
dados. Em uma matriz dessas dimensões, não é possível armazenar mais valores
que dez em cada linha; caso seja necessário armazenar mais dados, deve-se
declarar a matriz com dimensões maiores.
A função “setup” é usada apenas para configurar a comunicação serial com
uma taxa de comunicação de 9.600 bps (bits por segundo).
Figura 12 – Aquisição e armazenamento dos dados
A Figura 12 mostra o começo da função “loop”; nela são apresentadas as
linhas 14 a 16, em que há aquisição dos dados pelas entradas analógicas. A
função “map” converte os valores recebidos, que seriam de 0 a 1.023, para -40 a
125, que é o intervalo de temperatura que o sensor TMP36 opera.
Após a aquisição, o valor de temperatura é colocado nas variáveis
temperatura1, temperatura2 ou temperatura3, dependendo da entrada analógica
que o sensor está conectado. Nas linhas 18 a 20, os dados de temperatura são
armazenados, sendo a linha 0 para o primeiro sensor, linha 1 para o segundo
sensor e linha 3 para o terceiro sensor, organizando, dessa forma, uma linha para
15
cada sensor de temperatura. Após o armazenamento desses dados, a variável
“num_medida” é incrementada para que seja possível armazenar a próxima leitura
dos sensores que ocorrerá após um segundo, devido ao “delay(1000)” na linha
24.
Figura 13 – Cálculo da média das medições, apresentação no terminal serial e
zeramento das variáveis
Quando a variável “num_medida” atingir o valor 10, indicará que já foram
realizadas dez medições; nesse caso, o cálculo da média deverá ser realizado. A
média é calculada por meio do somatório das medições dividido pela quantidade
de medidas. Por isso o somatório das medidas dos sensores é realizado nas
linhas 30 a 34. Na linha 30, tem um “loop for” que faz a variável i variar de 0 a 9;
nas linhas seguintes, temos as variáveis “temp_media1”, “temp_media2” e
“temp_media3”, tendo seu valor acrescido de uma nova medição – essas linhas à
linha 31 serão explicadas com mais detalhes para facilitar o entendimento.
Na linha 31, a variável “temp_media1” está na sentença:
temp_media1 +=medidas[0][i];
16
O operador “+=” pode ser traduzido na sentença a seguir.
temp_media1 = temp_media1+medidas[0][i];
Essa sentença indica que a variável “temp_media1” receberá o valor atual
dela mais o valor de medidas[0][i]. O valor de medidas[0][i] dependerá do valor de
i; isso indica que o valor dessa variável será na matriz na linha 0 e coluna i. Esse
valor será adicionado ao valor atual de “temp_media1” e substituirá o valor atual
da variável.
Após esse somatório, ocorrerá a divisão por 10 em cada uma das variáveis
que guarda a média nas linhas de 36 a 38. Em seguida, os valores de média são
mostrados no terminal serial usando as funções “print” e “println” nas linhas de 41
a 46. Esse resultado pode ser visto na figura a seguir.
Figura 14 – Resultado no terminal serial
Para finalizar, as variáveis de média “temp_media1”, “temp_media2” e
“temp_media3”, em conjunto com a variável “num_medida”, foram zeradas para
que o processo de medição recomece.
17
REFERÊNCIAS
ALVES, W. P. Linguagem e lógica de programação. São Paulo: Érica, 2014.
ASCENCIO, A. F. G. Fundamentos da programação de computadores. São
Paulo: Prentice Hall, 2002.
FORBELLONE, A. L. V. Lógica de programação: a construção de algoritmos e
estrutura de dados. 3. ed. São Paulo: Prentice Hall, 2005.
MCROBERTS, M. Arduino básico. São Paulo: Novatec, 2011.
OLIVEIRA, C. L. V. Arduino descomplicado: aprenda com projetos de eletrônica
e programação. São Paulo: Érica, 2017.