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