Projeto Controlduino: Conversores Analógico-Digitais

Vou começar a escrever uma série de artigos que podem ser de grande interesse para meu amigo e co-autor do Bit Is Myth, MaRZ… Um outro grande amigo, que atende pela alcunha de kl0nEz, me emprestou um clone do Arduino Mega 2560 porque estou interessado a voltar a mexer com eletrônica (há 15 anos que não encosto a mão sequer num ferro de solda!)… Como projetos pequenininhos e banais são coisas que não me chamam a atenção (eita!! “fodão” eu, né?), resolvi, como primeira experiência, criar um controlador PID, me aproveitando das 8 entradas do DAC, de 10 bits, do ATmega2560. Chamarei o projeto de ControlDuino (estou com uma baita preguiça de pensar num nome mais engraçadinho!)Só que quero fazer isso da maneira mais didática: Quero tentar explicar o projeto todo… Pra isso, é necessário que o leitor tenha alguma experiência com Cálculo (com “C” maiúsculo mesmo!) e Eletrônica… Ambos os requisitos são “complicados” e requerem uma boa dose de estudo, para os não iniciados.

Em relação à eletrônica, não vou me debulhar em explicações, mas vou tentar simplificar um bocado de Cálculo, tentando explicar conceitos de derivação e integração (de forma simplificada) e, à medida que for progredindo, mostrar alguns insights que matemáticos do passado tiveram (Fourier, Laplace, Euler, …) e que são usados até nos dias de hoje.

O objetivo é criar o controlador… Neste primeiro passo, tentarei explicar o que diabos é um ADC (Analog to Digital Converter – ou, Conversor Analógico para Digital), mas preciso, antes, explicar o que é um DAC (Digital to Analog Converter). Preste atenção às siglas… Uma leitura superficial pode ficar confusa se você achar que um ADC é a mesma coisa que um DAC.

Dois universos diferentes: O digital e o analógico

Existem esses dois universos… Aquele dentro do seu computador, onde tudo são 1’s e 0’s; e existe o universo real, fora da Matrix, onde vivemos, onde tudo que existe pode descrito de uma maneira bem mais abrangente do que uma combinação de bits. Geralmente esses dois universos não se falam muito bem. É necessário o uso de algum tipo de interface entre eles.

Duas dessas interfaces são circuitos que convertem valores digitais em analógicos e vice-versa. O DAC converte um valor digital em um valor de tensão, tipicamente entre 0 e 5 V (a faixa de alimentação típica de circuitos digitais). É possível amplificar ou atenuar a faixa, no entanto, usarei a faixa típica aqui… Exemplos de faixas e domínios diferentes podem ser obtidos nas interfaces de áudio e vídeo de seu computador, por exemplo… No caso do circuito de áudio, um valor digital de 16 bits será convertido em tensões na faixa de -0.5 até +0.5 V na saída da placa de som… No caso da sua placa de vídeo, os valores de 8 bits de cada um dos componentes de cor (RGB) são convertidos em frequências diferentes no sinal de vídeo (se você usar um conector de vídeo composto, por exemplo).

Estou interessado no tipo simples de DAC aqui. Aquele que transforma um valor binário em tensão…

Taxas de amostragem e o DAC

Lembram-se que falei sobre sampling rate neste artigo? A taxa de amostragem é a quantidade de vezes, em um segundo, que uma amostra (um valor) é enviada para a saída (ou colhida, numa entrada) e é medida em ciclos por segundo, ou amostras por segundo (samples per second ou sps), medida em Hertz (Hz). Quanto maior a taxa de amostragem, mais parecida a forma de onda na saída do dispositivo será em relação à original, certo? Isso quer dizer que quanto mais rápido for o DAC, melhor!

Esse é um dos critérios que você deveria usar para comparar performance e qualidade de placas de vídeo, por exemplo. De nada adianta se sua placa de vídeo tiver uma GPU que funcione a 2 GHz e o DAC, que converte os valores RGB contidos na RAM da placa e os transforma em sinal analógico, tenha um sampling rate máximo de 1 GHz. Felizmente, para você, as GPUs modernas contém seus próprios DACs e tudo o que você tem que fazer é ficar de olho na velocidade da GPU (mas, nem sempre isso é verdadeiro e seria muito bom se o fabricante disponibilizasse informações de sampling rate do RAM-DAC).

O mesmo raciocínio deveria ser aplicado aos circuitos de áudio… Lá também tem um DAC que, na maioria dos circuitos de áudio on board, não passa de 56 kHz. Alguns só suportam 44.1 kHz. Placas de som profissionais chegam a ter sampling rate de 96 kHz ou mais!

DACs são rápidos! ADCs, nem tanto…

Cito dos DACs porque eles são circuitos bem mais simples que os seus irmãos ADCs (Analog to Digital Converters) e esses últimos usam um DAC para funcionarem. Pelo menos a maioria dos DACs encapsulados em chipsets.

Num DAC, basicamente, cada bit do valor de entrada têm um peso. O bit de mais alta ordem (MSB) tem peso \frac{1}{2}, enquanto o bit LSB tem peso 2^{-(n+1)}, para um DAC que lide com n bits.

Isso parece complicado… Até você revisar a disposição dos bits em um valor… Os sistemas numéricos que usamos são chamados sistemas posicionais nao é à toa… Cada posição tem um “peso” igual a B^n (onde B é a base numérica e n é a posição, começada em 0). O que os DACs fazem é deslocar a posição do MSB para a posição -1 e os demais bits para posições anteriores a essa. Esse sistema de “pesos” deve-se a um fato matemático:

\displaystyle\sum_{n=1}^{\infty}\left(\frac{1}{2}\right)^n=1

Se todos os bits do valor estiverem setados uma variação da fórmula acima causará uma saída, no DAC, igual (ou aproximado) ao valor máximo de referência (5V). Modificando a formula para o nosso uso, levando-se em conta que podemos ter bits desligados (0) e ligados (1) e a ordem dos bits, a tensão de saída do DAC pode ser calculada com a seguinte formula:

V_{out} = V_{in} \displaystyle\sum_{i=0}^{n}\frac{1}{2^{n-i+1}}b_i

Onde, V_{out} é a tensão na saída do DAC, V_{ref} é a tensão máxima de referência (5 V), n é o número de bits (onde o LSB é o bit 0) e b_i é o valor do bit i. Assim, se tivermos um DAC de 10 bits e colocarmos o valor 0x400 na entrada, obteremos extamente 0.5 vezes o valor de referência máximo. Cada um dos bits inferiores adicionará metade dessa grandeza cada.

Um circuito simples que pode funcionar dessa maneira é o mostrado abaixo:

Nalha resistiva associada aos bits a serem convertidos.
Nalha resistiva associada aos bits a serem convertidos.

Dependendo da tensão aplicada nos “bits” (de acordo com a posição das chaves), o arranjo de resistores muda, criando diferentes divisores de tensão resistivos, modificando a tensão aplicada ao pino “+” do circuito seguidor de tensão… Ponha as aulas de física do ensino médio em prática, ignore o amplificador operacional, e calcule os circuiros equivalentes de acordo com o estado de cada chave e você mesmo poderá verificar a afirmação acima.

Num DAC real as chaves são substituídas por pares de transistores (a saída de uma porta lógica CMOS, por exemplo)… mas isso é só um detalhe que você nem precisa se preocupar… Acima está o funcionamento de um DAC: É direto é rápido. A velocidade do circuito depende exclusivamente de quão rápido o processador consegue colocar os bits na entrada e da velocidada do amplificador operacional. Ou seja, ele pode ser muito rápido.

Conversor Analógico para Digital (ADC):

A descrição do funcionamento de um ADC, aqui, é baseada em um tipo específico, usado em microcontroladores. Existem circuitos dedicados que funcionam de maneira diferente e são mais rápidos.

De maneira geral, os ADCs são bem mais lentos que os DACs e eis o porquê: Um ADC faz sua conversão por “tentativa e erro”, por assim dizer. O circuito colhe a amostra que deseja converter numa etapa chamada sampling (o que mais?), provavelmente carregando um capacitor dentro do circuito para manter a amostra estável em todo o processo de conversão. Então ele entra num ciclo de conversão onde ele assume um valor inicial para o valor binário, geralmente no meio da faixa. Segue a coversão desse valor para um sinal analógico e a comparação com a amostra. Se a comparação indicar que o valor binário, transformado em analógico por um DAC interno, é maior que a amostra, então o valor binário é decrementado e a comparação é feita novamente… O loop termina quando o algorítmo do ADC decide que encontrou um valor binário que, transformado em analógico, seja igual ou bem aproximado à amostra… Mais ou menos assim:

// 'compare_sample()' compara a amostra contra
// um valor informado.
//
// Retornar 0 significa que a saída do DAC e o sinal de
// entrada são "iguais", de acordo com os critérios de
// precisão do ADC. Retornar +1 significa que o sinal de
// entrada é maior que o do DAC e -1, menor.
extern int compare_sample(int value); 

// Coleta a amostra, para estabilizá-la, em algum
// circuito discreto do DAC.
extern void collect_sample(void);

int adc_convert(void)
{
  int signal;
  int latch = 0x1ff; // meio da faixa de 10 bits.

  collect_sample();

  while ((signal = compare_sample(latch)) != 0)
  {
    if (signal < 0) { 
      // Se chegou no limite inferior, sai. 
      if (latch == 0) 
        break; 
      else 
        latch--; 
    } 

    if (signal > 0)
    {
      // Se chegou no limite superior, sai.
      if (latch == 0x3ff)
        break;
      else
        latch++;
    }
  }

  return latch;
}

De acordo com o pseudo-código simplificado acima, você pode esperar que cada ciclo de conversão gaste uma quantidade arbitrária de ciclos de clock. Se cada comparação acontecer em um único ciclo de clock, dependendo de quantos incrementos ou decrementos o ADC terá que fazer no valor estimado (o valor de latch), o ADC gastaria até 2^{n-1}-1 clocks para a conversão, onde n é o número de bits do conversor. Na prática, os ADCs desse tipo usam o algorítmo de busca binária e reduzem o tempo em escala logarítmica (num conversor de 10 bits, log_2 1024 = 10, assim ao invés de 511 iterações, teríamos, no máximo 10).

O ATmega2560 gasta 1,5 ciclos de clock para a etapa de sampling e 13,5 ciclos para o ciclo de conversão. Assim, toda conversão do ADC toma 15 ciclos de clock sempre. Quinze ciclos para uma simples conversão, num microcontrolador funcionando com um clock de 16 MHz, nos dá um sampling rate aproximado de 1 MHz (uma conversão a cada 1 μs, mais ou menos), juntando isso com os ciclos gastos por nossas próprias rotinas poderíamos ter um sampling rate de (olha o chute!) uns 100 kHz, talvez… Felizmente não precisamos de intervalos de amostragem tão pequenos no projeto do ControlDuino… Acho que é suficiente termos uma leitura a cada 10 ms ou mais (sampling rate de 100 Hz ou menos). Mesmo que precisemos de um sampling rate de 1 kHz, isso não é problema para o Arduino…

Precisão e erros

A maioria dos ADCs que conheço têm cerca de 0.5 LSB de precisão.Essa medida de precisão é feita em relação ao valor convertido porque a faixa da amostra pode ser variável, basta alterar o valor de referência. 1 LSB é exatamente a diferença entre valores consecutivos. Essa precisão de \frac{1}{2} LSB faz sentido do ponto de vista do arredondamento. Quando você quer arredondar um número real (no domínio \mathbb{R}) para inteiro (no domínio \mathbb{N}) faz:

\displaystyle I = \left \lfloor R+\frac{1}{2} \right \rfloor

Ou seja, um valor de tensão intermediário entre dois valores binários é arredondado segundo essa regra.

O valor de 1 LSB, na faixa de tensões entre 0 e 5V equivale a 4.8 mV, aproximadamente. Basta dividir a faixa por 1024 (ou 2^{10}).

Toda medição apresenta erros, imprecisões. No ADCs do ATmega2560 esse erro, teórico, de ±2 LSB, ou seja 9.7 mV para cima ou para baixo. Isso quer dizer que a medição feita pelo ADC é arredondada em relação ao ponto ideal da amostra, mas pode variar ±2 LSBs… Se compararmos toda a faixa contra esses 2 LSBs, isso nos dá uma imprecisão de ±0.2%… O que é MUITO bom!

Considerações sobre outros erros e ruídos no Arduino:

Existem outros tipos de erros além da imprecisão de ± 2 LSBs. Temos erros de offset (quando o valor zero ocorre com tensões de entrada maiores que 0 V), erros de ganho (quando o valor 0x3ff ocorre com valores inferiores à V_{ref}) e erros de não-linearidade (integral e derivativa). No nosso caso, apenas os dois primeiros são importantes… Mesmo assim, não há muito o que possamos fazer a não ser condicionar o sinal de entrada através de um circuito externo ao Arduino de forma a facilitar a calibração. Não farei isso aqui por este ser um projeto “simples”.

E só para dificultar um pouco mais as coisas, existe mais um probleminha: Lembre-se que temos um circuito digital funcionando com uma frequência de clock de 16 MHz. Enquanto existe algum programa sendo executado, um porrilhão de transistores estarão abrindo e fechando que nem loucos dentro do microcontrolador. Isso pode adicionar ruídos no circuito que podem influenciar a conversão do ADC… Não temos como isolar o conversor, já que ele é embutido no chip, mas a Atmel nos dá uma solução interessante: Colocar o microcontrolador pra dormir e só acordá-lo quando o ADC completar uma conversão.

Acordar o microprocessador significa interrompê-lo (requisitar uma interrupção). O ADC do ATmega2560 tem esse recurso: Quando uma conversão é completada, ele pode sinalizar uma interrupção para o microcontrolador. É um recurso interessante, mas ele também nos causa dores de cabeça… Para manter uma temporização constante entre medidas, pretendo usar um dos timers do microcontrolador para gerar interrupções em intervalos regulares controlados. Na rotina de tratamento da interrupção do timer a conversão será disparada. Isso me permite evitar a preocupação de temporizar todas as rotinas cuidadosamente… Só que, se durante o atendimento da interrupção do timer eu tiver que colocar o microcontrolador para dormir e esperar por outra interrupção terei que lidar com duas interrupções aninhadas (se é que isso é possível!). Tá certo que é pouco provável que outra interrupção de timer ocorra durante a conversão, mas pode ocorrer, em alguma circunstância que não previ… Isso requer estudos.

No momento, vou esquecer a compensação de ruídos e depois preocupo-me com isso…

Como ficamos?

Espero que você tenha entendi um pouco do funcionamento e complexidades em se lidar com a conversão analógico-digital. A coisa não é, simplesmente, ler um registrador e esperar que o valor binário, convertido do analógico, seja exatamente proporcional ao valor analógico de entrada.

O próximo passo é entender como os ADCs são inicializados e o valor convertido recuperado, no Arduino, bem como entender como configurar um timer e assinalar um tratador de interrupção para ele. Isso vai ficar para um próximo capítulo da série, mes amis

Anúncios

2 comentários sobre “Projeto Controlduino: Conversores Analógico-Digitais

  1. Oi gostei muito do seu texto e achei muito explicativo.
    Você poderia colocar algumas referências no final do texto para podermos explorar melhor o assunto?

    Obrigado pelo texto.

    1. Vou tentar… estou redigindo esses textos com pouquíssimas consultas, rememorando velhos conhecimentos sobre eletrônica e controle de processos… Mas, acho que posso conseguir um bom material.. ;)

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s