quarta-feira, 1 de junho de 2016

AnalogRead do Arduino no AVR Libc


Veremos nesse artigo como utilizar a conhecida função analogRead() do Arduino no AVR Libc.

No post anterior dessa série, eu falei sobre a Serial usando o AVR Libc, que utilizaremos também nesse post, então se você ainda não estudou esse assunto, leia o outro post primeiro antes de ler esse aqui.

Mas porque e quando devemos utilizar essa técnica? Vejamos, o Arduino nada mais é senão um conjunto de bibliotecas que implementa de "forma amigável" todas as funcionalidades do microcontrolador ATMega da Atmel.

O Arduino utiliza a toolchain da Atmel como base e organiza suas próprias funções em bibliotecas customizadas. A função que veremos nesse artigo é a analogRead() que pode ser encontrada no arquivo wiring_analog.c.

Como sabemos, ao compilar um código do Arduino, acabamos por agregar todas as funções implementadas, inclusive as não utilizadas. Ou seja, ao carregar um programa simples do Arduino que faz piscar um LED, carregamos todas as funções de suas bibliotecas, incluindo de ADC, Serial, Interrupção, Timers etc. Quando queremos um código mais enxuto, utilizamos o AVR Libc e compilamos apenas as bibliotecas que vamos usar, gerando um executável bem menor!

Você pode estudar todos os arquivos utilizados pelo Arduino navegando na pasta Cores que é uma subpasta encontrada no diretório de instalação do Arduino. No Windows 7, devemos encontrar essa pasta no seguinte caminho:

C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino

Lá teremos cabeçalhos como o Arduino.h, wiring.h etc. Nos arquivos de implementação (que nos interessam nesse post), podemos encontrar os comandos de iniciação da parte analógica do Arduino no arquivo wiring.c e a função analogRead() em si, no arquivo wiring_analog.c.

O que é ADC?

ADC é uma sigla em inglês para Conversor Analógico Digital. Quando utilizamos funções como a analogRead() para fazer a leitura de uma porta analógica, convertemos tensão analógica em um valor digital, baseado em limites de referência. Os chips ATMega oferecem algumas soluções de níveis de referência para converter os valores analógicos em digitais.

Para verificar essas opções que temos nos microcontroladores da Atmel, precisamos verificar os registradores que configuram esses circuitos internos de ADC.

Manipularemos dois registradores para configurar o ADC de um ATMega, sendo eles o ADCSRA e o ADMUX. O primeiro é o registrador de controle e status, já o último é o registrador responsável pela configuração do demuxer.

Primeiro precisamos configurar o ADCSRA para ligar o circuito que faz a amostragem da leitura e também ativar esse circuito. Para configurar um registrador, atuamos sobre os bits, ligando ou desligando eles. Vejamos o registrador ADCSRA na imagem abaixo:


Os bits desse registradores são:

  • Bit 7 - ADEN: Ativação do ADC
    Escrevendo o valor 1 (ativando) nesse bit, ativamos o circuito do ADC do ATMega. Escrevendo o valor 0 (desativando), desligamos.
  • Bit 6 - ADSC: Inicia Conversão ADC
    No modo simples de conversão, ativamos esse bit para iniciar cada conversão. No modo Free Running, ativamos esse bit para iniciar a primeira conversão.
  • Bit 5 - ADFR: Seletor do Modo Free Running
    Quando ativado esse bit, ligamos o modo Free Running, em que o circuito de ADC amostra e atualiza os registradores de dados constantemente.
  • Bit 4 - ADIF: Flag de Interrupção
    Quando o circuito ADC completa um ciclo de amostragem e os registradores de dados são atualizados, esse bit é ativado, indicando que o processo terminou. Se o registrador ADIE (e também o bit I do SREG) estiver ativo, o ADIF é desativado automaticamente e a função de interrupção de hardware executada.
  • Bit 3 - ADIE: Ativação da Interrupção do ADC
    Ativando este bit e também o bit I do SREG, ligamos a interrupção do ADC.
  • Bit 2,1,0 - ADPS2, ADPS1, ADPS0: Seletor do ADC Prescaler
    Estes bits determinam o fator de divisão do cristal e o clock do ADC, conforme tabela abaixo:

Para configurar o ADC, vamos utilizar os bits 7, 2, 1 e 0. Vamos utilizar o bit 7 ADEN para ativar o ADC do microcontrolador, mas antes temos que usar os bits ADPS0ADPS1 e ADPS2, conforme tabela acima para configurar a taxa do prescaler, dependendo do cristal que utilizaremos no ATMega.

Precisamos de uma frequência de amostragem entre 50Khz a 200Khz, então pegamos o valor do cristal e dividimos pelo prescaler. O valor do resultado tem que ficar dentro dessa faixa, conforme equação a seguir:

Frequência do Comparador ADC = Frequencia do Cristal / Prescaler

Se estamos usando um cristal de 16Mhz, por exemplo, podemos usar o prescaler de 128 para ter uma frequência de 125Khz, que está dentro da faixa de 50~200Khz. Vamos ver o código como fica:


Criei uma função chamada adc_init() para facilitar, mas ela é bem simples, configurando os bits ADPS2, ADPS1 e ADPS0 com valor de 1 e atribuindo no registrador ADCSRA

A linha depois desliga o modo Free Running, mas essa configuração não é necessária pois esse modo vem desabilitado por padrão.

No código acima utilizamos a Macro _BV() para deslocar os bits do registrador e atribuir apenas os bits a serem configurados. Essa Macro é o equivalente a escrever a instrução (1<<bit).

Portanto na última linha ligamos o bit ADEN para ativar o circuito do ADC. Essa linha poderia ter sido escrita assim:

ADCSRA = ADCSRA | (1<<ADEN)

Usar o operador OR (|) é uma forma segura de atuar apenas no bit que está sendo configurado, pois todos os outros bits do registrador ficarão com suas configurações anteriores. Essa instrução funciona para quando vamos ligar um bit do registrador.

Quando vamos desligar um bit do registrador, utilizamos o operador AND (&) e invertemos todos os bits para desligar apenas aquele bit específico. No caso de desligar o modo Free Running, poderia ser escrita assim a instrução:

ADCSRA = ADCSRA & ~(1<<ADFR)

Veja que usamos o operador TIL (~) para inverter todos os bits.

Depois de configurar o circuito de ADC, podemos criar a função analogRead() configurando o registrador ADMUX e ligando o processo de conversão. 

O registrador ADMUX pode ser visto na tabela abaixo:


Os bits desse registrador são:
  • Bit 7, 6 - REFS1, REFS0 - Seletor da Voltagem de Referência
    Estes bits configuram a referência de voltagem para o cálculo do ADC, conforme mostrado na tabela abaixo:
  • Bit 5 - ADLAR - Resultado Alinhado a Esquerda
    O bit ADLAR afeta na apresentação do resultado. Se este bit for ativado, o resultado será armazenado no registrador alto de dados, ou seja apenas no ADCH. Mas se for desligado o resultado é armazenado alinhado a direita.
  • Bits 3, 2, 1, 0 - MUX3, MUX2, MUX1, MUX0 - Seletor do Canal Analógico
    Estes bits configuram qual será a porta analógica usada para obter a conversão ADC, conforme tabela abaixo:

Precisamos então configurar o valor de referência e a porta analógica a ser utilizada. Vejamos o código e analisaremos em seguida:

Nas primeiras linhas declaramos três variáveis, duas para receber o resultado do comparador ADC, sendo low para o byte menos significativo e high para o byte mais significativo.

A variável bToChannel é uma forma exagerada para garantir que utilizaremos apenas os 3 últimos bits para configurar a porta analógica que vamos utilizar, de ADC0 até ADC7.

Primeiro atribuímos diretamente o valor da variável bToChannel para o registrador ADMUX. Isso vai afetar os bits MUX2, MUX1 e MUX0.

Depois ativamos o bit REFS0, que conforme a tabela acima, configura o ADC para usar a tensão que existe no pino AVCC. Sabemos que esse pino do ATMega é ligado nos 5V, então esse será nosso valor de referência.

A próxima instrução desliga o bit ADLAR para que o alinhamento seja feito a direita e possamos pegar o resultado dos registradores ADCL e ADCH, utilizando todos os 10 bits de resolução, de 0 a 1023. Se ligássemos esse bit ADLAR, o resultado viria apenas no registrador de dados ADCH, com definição de apenas 8 bits, ou seja, de 0 a 255.

Finalmente ativamos o comparador do ADC para iniciar as amostragens, ligando o bit ADSC do registrador ADCSRA. Isso inicia o processo de leitura e amostragem da porta analógica configurada. Quando o circuito ADC termina esse processo ele configura o bit ADIF, portanto usamos a função loop_until_bit_is_set() para ficar em loop até que esse bit seja ativado.

Repare que não utilizamos a configuração de interrupção pelo bit ADIE, pois não existe necessidade. Aqui apenas esperamos terminar o processo e fazemos a leitura dos registradores de controle.

A utilização dessa função pode ser vista no código abaixo:


Repare também que utilizamos a Serial, que aprendemos no outro post, e apenas imprimimos o valor de todas as portas analógicas.

O resultado da execução desse código pode ser visto no Monitor da Serial, conforme imagem abaixo:


O código completo pode ser baixado clicando aqui!

Até o próximo artigo pessoal!

Nenhum comentário:

Postar um comentário