ARDUINO BÁSICO Lição V

Propaganda
ARDUINO BÁSICO
Lição V
Frederico José Dias Möller
15/08/14
Frederico Möller
1
Sumário
●
Memória Volátil
●
EEPROM.write()
●
Memória Permanente
●
Exemplo X
●
EEPROM
●
●
EEPROM.read()
●
●
Armazenamento de
valores int e float
Exemplo IX
●
Função itoa()
Exercício IX
●
Função atoi()
15/08/14
Frederico Möller
2
Sumário
●
●
●
●
Conversão de valores
float
Exercício Final
Problemas dessa
forma de conversão
Operações de bitshift
15/08/14
Frederico Möller
3
Memória volátil
●
●
●
Uma variável nada mais é que um endereço de
memória que armazena um dado.
Em todos os programas que fizemos até agora,
neste curso, o Arduíno foi capaz de armazenar
e acessar dados armazenados em sua
memória.
Tais dados no entanto, eram perdidos assim
que o Arduíno era desligado, ou seja, estavam
armazenados na memória volátil.
15/08/14
Frederico Möller
4
Memória volátil
●
Muitos dos projetos feitos em arduíno não
precisam de que o mesmo guarde os dados
toda vez que é desligado. Podemos dizer que
pelo propósito do Arduino, a maioria de seus
projetos está bem servida com a memória
volátil, ou seja aquela que morre junto com o
fornecimento de energia da plataforma de
prototipagem.
15/08/14
Frederico Möller
5
Memória permanente
●
●
A memória permanente começa a se mostrar
necessária no Arduino, principalmente quando
temos que guardar configurações ,
mapeamentos de robôs , rotas e protocolos de
comunicações.
E é obvio que a plataforma possui uma
memória permanente. Os programas
carregados no Arduino são gravados em uma
memória flash.
15/08/14
Frederico Möller
6
EEPROM
●
●
Existe no entanto a memória EEPROM, no
Arduino. E nela o usuário pode comandar a
leitura e armazenamento de dados de forma
permanente.
Os dados gravados na memória EEPROM não
são perdidos nem quando se grava um novo
programa no Arduino. Uma vez gravado dado
só é perdido (normalmente), quando um
programa grava um novo dado em cima do
antigo
15/08/14
Frederico Möller
7
EEPROM
●
●
●
A memória EEPROM do Arduino é própria do
seu microcontrolador e portanto sua
capacidade vai depender do ATmega utilizado.
No nosso caso, temos um ATmega328 sua
memória EEPROM tem 1kB (kilobyte dividos
em 1024 endereços de 1 byte.
Mais informações sobre a memória EEPROM
em geral se encontram na seção de leitura
complementar.
15/08/14
Frederico Möller
8
EEPROM
●
●
Para gravar e ler dados na memória EEPROM,
o programa precisa ter a biblioteca EEPROM.h
inclusa.
Essa biblioteca é canônica da IDE do Arduino
e portanto não precisa ser instalada
separadamente. A IDE vem inclusivecom 3
exemplos prontos.
15/08/14
Frederico Möller
9
EEPROM.read()
●
●
●
Normalmente os endereços da EEPROM de
um Arduino vem com o valor 255 em cada. O
endereço 0 pode vir com um valor diferente.
A leitura de um endereço da memória
EEPROM é feito atravéz do comando
EEPROM.read(add)
Add é o endereço de memória, no nosso caso
um valor de 0 a 1023.
15/08/14
Frederico Möller
10
EEPROM.read()
●
●
●
Normalmente os endereços da EEPROM de
um Arduino vem com o valor 255 em cada. O
endereço 0 pode vir com um valor diferente.
A leitura de um endereço da memória
EEPROM é feito atravéz do comando
EEPROM.read(add)
Add é o endereço de memória, no nosso caso
um valor de 0 a 1023.
15/08/14
Frederico Möller
11
Exemplo IX
●
●
Vamos a um exemplo
bem simples. Vamos
ler o que tem gravado
no endereço 12 dos
nossos arduinos.
Uma vez que
desejamos obter
apenas uma leitura, a
função loop não será
utilizada
15/08/14
Frederico Möller
12
Exemplo IX
●
Notem a inclusão da
biblioteca EEPROM,
da construção do
programa na função
setup, na função
EEPROM.read() e na
função loop sem nada
em seu escopo.
15/08/14
Frederico Möller
13
Exemplo IX
●
A variável a poderia
ser do tipo char, no
entanto isso resultaria
na impressão de um
valor da tabela ASCII
ao invés de um
numérico quando a
função Serial.print(a)
fosse chamada.
15/08/14
Frederico Möller
14
Exercício IX
●
●
●
Durante a construção deste curso pelo menos
um arduíno foi utilizado para testar as funções
da biblioteca EEPROM.
Usando o que foi aprendido até agora,
descubram se o seu arduino é o que foi usado
como cobaia, ou pelo menos se alguém deixou
algo gravado na memória dele.
Para facilitar, apenas os endereços iniciais
foram usados nos testes.
15/08/14
Frederico Möller
15
Exercício IX
●
●
●
Durante a construção deste curso pelo menos
um arduíno foi utilizado para testar as funções
da biblioteca EEPROM.
Usando o que foi aprendido até agora,
descubram se o seu arduino é o que foi usado
como cobaia, ou pelo menos se alguém deixou
algo gravado na memória dele.
Para facilitar, apenas os endereços iniciais (0 a
32) foram usados nos testes.
15/08/14
Frederico Möller
16
Exercício IX
●
O problema proposto
pode até ter parecido
complicado, no
entanto se eu tivesse
pedido "varra a
memória em busca
de alterações" estaria
na cara que o que foi
proposto pouco difere
do exemplo passado
15/08/14
Frederico Möller
17
Exercício IX
●
●
A diferença é que dessa
vez temos uma variável
de endereço, esta varia
de valor dentro de um
ciclo for.
Coloquei aqui de 0 a 32,
mas ela poderia variar
dentro de qualquer
intervalo de 0 a 1023
15/08/14
Frederico Möller
18
Exercício IX
●
●
Dentro do ciclo for, o
endereço é lido, o valor
é armazenado na
variável "a" e então é
impresso na saída serial.
Temos um delay para
que seja possível
acompanhar visualmente
o valor armazenado em
cada endereço.
15/08/14
Frederico Möller
19
EEPROM.write(add,val)
●
●
●
A função EEPROM.write serve para
armazenarmos um valor na memória EEPROM
do arduino.
Em add, colocamos o endereço de memória,
que no nosso caso é um valor inteiro de 0 a
1023.
Em val, vai o valor a ser armazenado, que é um
byte, ou seja, um valor de 0 a 255.
15/08/14
Frederico Möller
20
Exemplo X
●
●
●
Vamos fazer agora, um programa que leia o 3º
endereço de memória (2), se este for maior que
100, ou então igual a 0, ele vai alterar esse
valor para 1, se não, ele vai multiplicar esse
valor por 2.
Essa operação deve ser executada apenas
uma vez, quando o arduino for ligado.
O valor armazenado na memória deve ser
exibido após a atualização.
15/08/14
Frederico Möller
21
Exemplo X
●
Uma vez que as
ações devem ocorrer
apenas quando o
arduino for ligado,
nenhuma linha de
código deve ser
escrita dentro da
função loop.
15/08/14
Frederico Möller
22
Exemplo X
●
●
●
Inicialmente
chamamos a
biblioteca EEPROM.
No setup,
começamos por ligar
a comunicação serial.
Em seguida lemos o
3º endereço de
memória.
15/08/14
Frederico Möller
23
Exemplo X
●
O principal desse
programa não é nem
a leitura e gravação
da memória
EEPROM, mas sim o
teste condicional.
15/08/14
Frederico Möller
24
Exemplo X
●
●
Na excessão, ou
seja, se val for
diferente de zero e
menor que 100 o
valor de val vai ser
multiplicado por 2.
Atualizado, val é
gravado no endereço
2 e depois é exibido
via serial.
15/08/14
Frederico Möller
25
Armazenando valoes int e float
●
●
O que aconteceria se tentassemos armazenar
um valor inteiro em um endereço ROM?
De acordo com o exemplo anterior, vimos que o
fato da variável ser inteira não impede com que
ela seja armazenada. Se o tamanho dela for o
mesmo de um char, ou seja, um byte (0 a 255)
ela vai ser armazenada sem problemas. Mas o
que aconteceria se tentássemos armazenar um
int maior que 255? Ou um valor float?
15/08/14
Frederico Möller
26
Armazenando valoes int e float
●
●
O que aconteceria se tentassemos armazenar
um valor inteiro em um endereço ROM?
De acordo com o exemplo anterior, vimos que o
fato da variável ser inteira não impede com que
ela seja armazenada. Se o tamanho dela for o
mesmo de um char, ou seja, um byte (0 a 255)
ela vai ser armazenada sem problemas. Mas o
que aconteceria se tentássemos armazenar um
int maior que 255? Ou um valor float?
15/08/14
Frederico Möller
27
Armazenando valoes int e float
15/08/14
Frederico Möller
28
Armazenando valoes int e float
●
●
●
Se observarem, ao invés de 1996, o arduino lê
o valor de 204... Ao invés de 15.56, ele lê o
valor 15.
Por que isso acontece?
Variaveis int e float em geral, reservam 4 bytes
na memória para armazenar valores, no caso
do arduino uno e similares, 2 bytes. Como os
endereços de memória tem apenas um byte,
apenas o byte menos significativo é
armazenado.
15/08/14
Frederico Möller
29
Armazenando valores int e float
1996
Em
binário
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
0
0
1
1
0
0
Apenas o byte menos
Significativo é
armazenado
196 em binário
15/08/14
Frederico Möller
30
Armazenando valores int e float
●
●
Infelizmente, na maioria das vezes, nos
interessa armazenar valores int ou float, estes
com mais de um byte de tamanho.
Felizmente exitem diversas formas de
contornar esse problema.
15/08/14
Frederico Möller
31
Armazenando valores int e float
●
●
Infelizmente, na maioria das vezes, nos
interessa armazenar valores int ou float, estes
com mais de um byte de tamanho.
Felizmente exitem diversas formas de
contornar esse problema.
15/08/14
Frederico Möller
32
A função itoa(valor int,vetor
char*,base)
●
Essa é uma função da biblioteca stdio.h.
●
Ela converte um valor inteiro em uma string.
●
●
●
Ela faz essa conversão transformando cada dígito em
um caracter, portanto 1996 tem 4 dígitos e precisará
de uma string de 4 posições.
Com essa string, basta chamar a função itoa, colocar
o valor inteiro e a string dentro dos parenteses e a
função fará a conversão.
É preciso colocar também a base desejada, 10 para
decimal, 16 para hexadecimal...
15/08/14
Frederico Möller
33
A função atoi(vetor char*)
●
Essa é outra função da biblioteca stdio.h.
●
Ela converte uma string em um valor inteiro.
●
●
Diferente da itoa, nessa função, devemos
associar uma variável ao seu resultado.
Portanto a sitaxe correta será:
–
15/08/14
Variável inteira=atoi(string)
Frederico Möller
34
Conversão de valores float
●
●
A conversão de valores float já não é tão
simples. Apesar de existir a função atof, a
função ftoa não é canônica do C.
Alguns programadores recomendam o uso da
função sprintf, que imprime a saída de uma
função printf em uma string, no entanto ela não
costuma funcionar corretamente, pelo menos
no arduino, para valores float.
15/08/14
Frederico Möller
35
Conversão de valores float
●
●
●
Existe uma função chamada to_string, mas seu
uso exige a importação de mais bibliotecas e
por isso nós não veremos ela aqui.
É interessante notar que qualquer valor
numérico armazenado no arduino é racional.
Sabendo disso é perfeitamente possível
armazenar valores de ponto flutuante usando
as funções atoi e itoa...
15/08/14
Frederico Möller
36
Exercício Final
●
●
●
Este vai ser nosso último exercício nesse
curso. Implementar uma função que armazene
e uma que leia números de ponto flutuante na
memória EEPROM do arduino.
Não é uma tarefa fácil, mas ela pode ser
executada de diversas formas.
Lembrem-se que a função deve permitir que
outros floats sejam armazenados sem apagar
os que já foram armazenados.
15/08/14
Frederico Möller
37
Exercício Final
●
●
Para facilitar, vamos nos limitar duas casas
decimais e números de no máximo ordem 2 (ou
seja, nada maior que 99.99, ou menor que
-99.99)
Como foi dito, é necessário poder armazenar
vários números, pelo menos 10 floats devem
ser armazenados sem que algum deles tenha
que ser deletado.
15/08/14
Frederico Möller
38
Exercício Final
●
●
Vamos ter que incluir,
além da biblioteca
EEPROM a biblioteca
stdlib, para podermos
usar as funções itoa e
atoi.
A primeira função
implementada vai ser a
responsável por
armazenar o valor float
na memória EEPROM.
15/08/14
Frederico Möller
39
Exercício Final
●
●
Essa função deverá ser
do tipo void, uma vez
que ela não retorna valor
algum, apenas
armazena o valor na
memória.
Ela recebe um inteiro
como endereço de
memória e um float
como valor a ser
armazenado
15/08/14
Frederico Möller
40
Exercício Final
●
É interessante observar
que o endereço aqui não
corresponde ao
endereço real de
memória. Como o valor
vai ser convertido em
uma string, vários
endereços serão usados
para armazenar um float.
15/08/14
Frederico Möller
41
Exercício Final
●
Para evitar que se tenha
que calcular o primeiro
endereço, ou que a
função tenha 6 entradas,
reservamos a faixa de
endereços do 512 ao
571 para
armazenamento de
floats.
15/08/14
Frederico Möller
42
Exercício Final
●
Cada seqüência de 6
endereços equivale a um
endereço "float". Por
exemplo, os endereços
do 512 ao 517
equivalem ao endereço
0 dessa função. Do 518
ao 513 temos o
endereço 1 e assim por
diante.
15/08/14
Frederico Möller
43
Exercício Final
●
●
Inicialmente temos duas
variáveis int declaradas,
a i, que servirá como
contadora de um ciclo
for, e a valor_conv que é
o valor float multiplicado
por 100.
Temos um vetor char de
6 posições chamado
buffer declarado logo em
seguida.
15/08/14
Frederico Möller
44
Exercício Final
●
●
Quando
multiplicamos o valor
float por 100 obtemos
um inteiro com no
máximo 4 dígitos e
um sinal.
Esse inteiro é
convertido para um
vetor de char pela
função itoa.
15/08/14
Frederico Möller
45
Exercício Final
●
●
Em seguida cada
posição do vetor é
armazenada em um
endereço de
memória.
Aqui é que entra a
conversão do
endereço da função
para o endereço real.
15/08/14
Frederico Möller
46
Exercício Final
●
Em cada passagem
no ciclo for o
endereço real de
memória é atualizado
dentro da função
EEPROM.write pela
expressão 512 +
6*add +i
15/08/14
Frederico Möller
47
Exercício Final
●
●
●
512 é onde
começamos nossa
"faixa reservada"
6*add é o endereço
da função. É
multiplicado por 6 por
causa do tamanho da
variável buffer [6]
O i é cada posição do
buffer.
15/08/14
Frederico Möller
48
Exercício Final
●
●
A função seguinte é a
ler_float, como o
nome diz, ela lê um
valor float
armazenado na
memória.
É uma função tipo
float, pois vai retornar
um valor desse tipo.
15/08/14
Frederico Möller
49
Exercício Final
●
●
A função recebe um
valor inteiro, é o
endereço de memória
onde ela vai buscar o
float.
O endereço dado à
função não corresponde
aos endereços reais
onde o valor float está
armazenado.
15/08/14
Frederico Möller
50
Exercício Final
●
Dentro da função são
declarados um vetor
de char, que vai
futuramente receber
cada byte do float
armazenado na
memória EEPROM.
15/08/14
Frederico Möller
51
Exercício Final
●
●
Um inteiro i, que será
usado como contador
em um ciclo for.
Um inteiro val_int,
que vai receber a
conversão string para
inteiro realizado pela
função atoi.
15/08/14
Frederico Möller
52
Exercício Final
●
●
Uma variável float
val_float que vai
receber a conversão
do valor inteiro para o
valor flutuante.
Essa variável poderia
ser suprimida. Ela só
está aqui para facilitar
a visualização da
lógica do exercício.
15/08/14
Frederico Möller
53
Exercício Final
●
Após a declaração de
variáveis, temos um
ciclo for onde cada
posição do buffer
recebe o valor
armazenado em um
endereço de memória
EEPROM.
15/08/14
Frederico Möller
54
Exercício Final
●
A regra de conversão
também foi usada
aqui. Se o endereço
na função for 3, o
ciclo for fará com que
os endereços
530,531,532,533,534
e 535 sejam lidos.
15/08/14
Frederico Möller
55
Exercício Final
●
Ao término do ciclo, o
buffer, já contendo
todos os caracteres
armazenados em
uma determinada
faixa da memória
EEPROM é
convertido em um
inteiro pela função
atoi.
15/08/14
Frederico Möller
56
Exercício Final
●
●
A variável val_int é
convertida em float e
dividida por 100 (uma
vez que na função de
armazenamento,
multiplicamos por
100)
O valor convertido é a
saída desta função.
15/08/14
Frederico Möller
57
Exercício Final
●
●
Por fim um pequeno
programa para
demonstrar o
funcionamento das
funções
Temos as variáveis
float a e b, valendo
15.56 e 99.99
respectivamente.
15/08/14
Frederico Möller
58
Exercício Final
●
●
As variáveis float c e
d são declaradas,
mas não tem valor
definido.
Usando a função
armazena_float,
armazenamos a no
endereço 0 e b no
endereço 1.
15/08/14
Frederico Möller
59
Exercício Final
●
Ou seja, "a" está
disposta nos
endereços
512,513,514,515,516,
517... e b do 518 ao
525.
15/08/14
Frederico Möller
60
Exercício Final
●
●
Com a função ler_float
recuperamos o valor
desses endereços e os
atribuimos às variáveis c
e d.
Por fim os valores são
impressos. Os
resultados podem ser
facilmente conferidos no
serial monitor.
15/08/14
Frederico Möller
61
Problemas dessa forma de
conversão
●
●
Já de cara podemos ver alguns problemas com
esse tipo de método.
Ao multiplicarmos o float por uma constante e
salvar o resultado em um int, restringimos
muito os valores float a serem lidos, se
tivessemos que salvar o número 999.87, não
conseguiríamos, pois 99987 está fora dos
limites int do arduino uno.
15/08/14
Frederico Möller
62
Problemas dessa forma de
conversão
●
●
Já de cara podemos ver alguns problemas com
esse tipo de método.
Ao multiplicarmos o float por uma constante e
salvar o resultado em um int, restringimos
muito os valores float a serem lidos, se
tivessemos que salvar o número 999.87, não
conseguiríamos, pois 99987 está fora dos
limites int do arduino uno.
15/08/14
Frederico Möller
63
Problemas dessa forma de
conversão
●
●
Podemos, claro, aprimorar esse método.
Um método válido seria, por meio de if-elses,
descobrir qual o tamanho da parte inteira do
float, converter cada dígito dela em char e
armazenar e depois multiplicar por 10 a parte
fracionária, converter a parte inteira do novo
número para caracter, subtrair o novo valor de
sua parte inteira, multiplicar por 10 de novo...
15/08/14
Frederico Möller
64
Problemas dessa forma de
conversão
●
●
No entanto, isso iria requerer, além do espaço
de memória para cada caracter do valor float,
um para endereçar (dizer onde começa e onde
termina o valor armazenado) e outro para dizer
quantas casas decimais tinha a parte
fracionária.
As operações bit a bit no entanto resolvem
muito bem esse problema.
15/08/14
Frederico Möller
65
Operações de bitshift
●
●
Os operadores << e >> são os operadores de
bitshift e são capazes de empurrar os bits de
variáveis byte, int, ou long int (desde que o
tamanho seja menor ou igual a 32 bits) para a
esquerda ou para a direita.
Por exemplo, o int 19800... ele pode ser escrito
em binário como:
0
15/08/14
1
0
0
1
1
0
1
0
1
Frederico Möller
0
1
1
0
0
0
66
Operações de bitshift
●
Realizando o bitshift de 4 casas para a
esquerda, obtemos:
1
●
●
0
1
0
1
0
1
1
0
0
0
0
0
0
Que equivale ao int 54656
Enquanto o bitshift de 4 casas para a direita
nos dá:
0
●
1
0
0
0
1
0
0
1
1
0
1
0
1
0
1
Que equivale ao int 1237
15/08/14
Frederico Möller
67
Operadores bitshift
●
●
O código ao lado
exemplifica a utilidade
do uso de bitshift para a
conversão de dados.
Podemor ir "shiftando"
uma variável int de 8 em
8 bits de forma a
pegarmos cada byte
dela.
15/08/14
Frederico Möller
68
Operadores bitshift
●
●
Esses bytes podem ser
armazenados em
posições de uma string,
ou em endereços da
memórita EEPROM.
Depois, fazendo o
bitshifting no sentido
reverso, podemos reaver
o valor decomposto.
15/08/14
Frederico Möller
69
Bitshift para floats
●
●
Operadores de bitshift não funcionam com
valores float e double, para armazenar esses
valores é necessário fazer primeiro uma
conversão para int (usando multiplicadores
para eliminar a parte fracionária).
A vantagem é que conseguimos usar, em geral,
menos bytes para converter floats em um vetor
de char, usando bitshift do que converter cada
dígito em um caracter.
15/08/14
Frederico Möller
70
A melhor alternativa para floats
●
●
●
Mesmo usando operadores bitshift, passar
floats para um vetor de chars, principalmente
se estivermos focando no armazenamento em
endereços da EEPROM é um processo chato.
A melhor alternativa para lidar com valores
float, nesse caso é: EVITA-LOS.
As entradas analógicas do arduino recebem
valores inteiros, as digitais, booleanos.
15/08/14
Frederico Möller
71
A melhor alternativa para floats
●
●
É perfeitamente possível manter os valores
nesses estados, ou converter desses para char
e armazenar na memória.
Um bom programa deve deixar para fazer as
conversões matemáticas que resultarão em
valores float somente quando estes forem
imprenscindíveis, como por exemplo, na hora
de exibi-los para o usuário.
15/08/14
Frederico Möller
72
O Fim deste curso
●
●
Esse curso é uma mera introdução da
ferramenta Arduino. Muita coisa, como o uso de
servo-motores, displays lcd, a biblioteca wire, o
interfaceamento do Arduino com outros
dispositivos, não foi explorada aqui.
Alguns desses itens poderão ser vistos em
cursos futuros, mas dificilmente vocês
encontrarão um curso que aborde tudo o que o
Arduino pode oferecer.
15/08/14
Frederico Möller
73
O Fim deste curso
●
●
O domínio sobre essa plataforma só pode ser
conseguido com seu constante uso.
Uma das grandes vantagens do Arduino é o
seu número de tutoriais na internet, façam bom
uso deles, assim como os de fóruns de
discussões, apostilas e livros sobre a
plataforma.
15/08/14
Frederico Möller
74
Revisão
●
Nessa lição, aprendemos sobre:
–
Memória volátil e memória permanente
–
A biblioteca EEPROM do Arduino.
–
–
15/08/14
Gravar e ler dados na memória EEPROM do
Arduino
Converter variaveis maiores para vetores de
variáveis menores
Frederico Möller
75
Leitura complementar
●
Todos os operadores em c++
–
●
História das memórias ROM
–
●
http://www.cplusplus.com/doc/tutorial/operators/
http://etcbinaria.wordpress.com/2011/01/21/evolucao-m
Duas formas de converter int para vetor char:
–
15/08/14
http://sapiensmechatronicus.blogspot.com.br/2013/
05/comunicacao-serial-do-msp430-via-xbee.html
Frederico Möller
76
Referências bibliográficas
●
MCROBERTS Micheal, Beginning Arduino,
1ªED
●
SCHILDT Hebert, C completo e total, 3ªED
●
http://www.arduino.cc/
15/08/14
Frederico Möller
77
Informações adicionais
●
●
●
Esses slides são a 1ª versão deste curso e
sofrerão alterações
Uma apostila está sendo feita e será
disponibilizada em breve.
Novas versões desses slides, a apostila, os
códigos dos exercícios e dos exemplos feitos
aqui poderão ser baixados nos seguintes links:
15/08/14
Frederico Möller
78
Informações adicionais
●
Site do PET Mecatrônica/BSI:
–
●
Blog Homo Sapiens-Mechatronicus:
–
●
http://sites2.jf.ifsudestemg.edu.br/?q=downloads
http://sapiensmechatronicus.blogspot.com.br/
Blog projetos de jogos:
–
15/08/14
http://fredericomollerped.blogspot.com.br/
Frederico Möller
79
Dúvidas???
15/08/14
Frederico Möller
80
Obrigado e até o próximo curso!!!
15/08/14
Frederico Möller
81
Download