Este projeto foi inspirado em um kit de capacímetro da Sparkfun (https://www.sparkfun.com/products/9485). Na época que eu montei, não estava disponível o fonte do firmware. Isto me levou a criar uma versão minha (usando hardware próprio ao invés do Arduino), e uma oficina de montagem em protoboard. Invertendo o processo usual, adaptei o projeto para uso com o Arduino.
Objetivos
O primeiro objetivo deste projeto é ilustrar a versatilidade dos microcontroladores, mostrando como os seus recursos podem ser usados para construir um capacímetro digital.
Não menos importante é passar alguns conceitos básicos de eletrônica e microcontroladores.
Eletrônica Básica
Como ponto de partida vamos ver a tensão elétrica. A tensão é um potencial. Assim como podemos falar no potencial gravitacional de um objeto suspenso a uma certa altura, é uma característica que indica a possibilidade de algo acontecer. Ao soltar um objeto, o potencial gera movimento. Ao fecharmos um circuito elétrico, a tensão gera a corrente elétrica.
A forma mais simples de se fechar um circuito elétrico é com uma resistência. Neste caso a lei de Ohm define a relação entre tensão, resistência e corrente:
O capacitor é um componente mais complexo. Ele é construído por duas placas condutoras separadas por um isolante, o que sugere que não existirá corrente elétrica fluindo por ele. Entretanto, ao conectar um capacitor a uma fonte de tensão, temos uma corrente à medida que elétrons se movem para deixar as placas do capacitor na mesma tensão que a fonte. Esta corrente é inicialmente alta e diminui à medida que a tensão no capacitor aumenta (dizemos que o capacitor estará sendo carregado). Paradoxalmente, um capacitor descarregado se comporta inicialmente como um curto quanto ligado a uma fonte. Se a fonte for retirada, o capacitor tende a manter a tensão obtida (os capacitores reais irão perder aos poucos esta carga). Se no lugar da fonte colocarmos um curto, a tensão existente no capacitor irá gerar uma corrente elétrica alta, que irá diminuindo à medida que o capacitor descarregar.
O comportamento de carga e descarga dos capacitores é determinado por uma fórmula matemática:
Um outro componente que vamos usar no nosso projeto é o LED – diodo emissor de luz. Antes de mais nada, o LED é diodo, o que significa que se comporta como uma mão única de corrente. Se a tensão é ligada em um sentido, a corrente flui. No sentido inverso, não flui. Na direção correta, a tensão positiva é conectada ao anodo e a negativa ao catodo. A intensidade do LED é determinada pela corrente que passa por ele.
A tensão sobre o LED varia pouco com a corrente. Em uma montagem típica (como a acima) a corrente que passa no LED é determinada por um resistor: I = (V-Vled)/R. Normalmente escolhemos a corrente que nos fornece a luminosidade desejada e calculamos o valor de R para obtê-la.
Neste projeto vamos usar um display LED de 7 segmentos. Este display possui sete LEDs (oito, contando o ponto decimal), possibilitando apresentar um dígito numérico (e certas letras, com um pouco de boa vontade de quem lê).
Vamos precisar de quatro dígitos, o que gera um problema de montagem. Se tivermos os dois terminais de de cada LED, teremos um total de 4 x 8 x 2 = 64 conexões. Uma primeira simplificação é conectar internamente ao display todos os terminais de um lado dos LEDs, criando uma configuração de anodo (ou catodo) comum. Ainda assim, teríamos 4 x 9 = 39 conexões. Para reduzir mais, usamos a ideia da multiplexação: conectamos juntos os segmentos dos dígitos e, usando o terminal comum, acendemos os dígitos um a um. Fazendo este processo rapidamente, a vista tem a impressão que todos os dígitos estão acesos simultaneamente.
Microcontroladores
Um microcontrolador é um computador em um chip: contém a CPU, memória e periféricos integrados. O seu uso típico é colocar inteligência em produtos.
Em um microcontrolador é comum termos três tipos de memória:
- Flash: memória não volátil (mantém conteúdo quando desligada), usada para o programa. A escrita na Flash requer procedimentos especiais e demorados.
- Ram: memória volátil (perde o conteúdo quando desligada), usada para as variáveis do programa
- EEProm: memória não volátil mas com escrita mais simples e rápida que a Flash, usada para configurações e dados que precisam ser mantidos quando o microcontrolador for desligado.
O Arduino Nano usa o microcontrolador ATmega328.
Um periférico que existe em praticamente todo microcontrolador é a Entrada e Saída Digital
- Entrada digital: tensão no pino é lida como 0 ou 1
- Saída digital: tensão no pino é controlada pelo software entre os níveis “alto” e “baixo”
O ATmega328 tem 20 pinos de E/S digital (os pinos do Arduino marcados como Entradas Analógicas podem ser usados também par E/S digital).
Como todo computador, os microcontroladores possuem um clock, que é a base de tempo p/ execução do programa. Este clock é também usado pelos timers – contadores que podem:
- Interromper periodicamente o programa
- Gerar formas de onda (PWM)
- Medir tempo entre eventos
O ATmega328 possui 3 timers:
- Timers 0 e 2 de 8 bits (contam de 0 a 255)
- Timer 1 de 16 bits (contam de 0 a 65535), com suporte a medição de tempo entre eventos
As bibliotecas do Arduino usam o Timer 0 para as funções delay() e milis(). O timer2 é usado apenas da função tone(). Os três timers podem ser usados na geração de PWM (função analogWrite()). No meu projeto vou usar os timers 1 e 2; para isto não vou utilizar PWM nem tone().
Um último recurso necessário para o projeto do capacímetro é a observação de uma tensão analógica. Quando falamos disso, a primeira coisa que vem em mente é o ADC – conversor analógico digital – usado no Arduino através do analogRead(). O ADC fornece um número proporcional à tensão no pino, algo extremamente útil em muitas aplicações. Entretanto, o ADC requer um certo tempo para fazer a conversão e o seu resultado precisa ser analisado pelo programa.
Um recurso mais simples é o comparador analógico, que fornece uma saída em 0 ou 1 conforme a tensão em um pino for maior ou menor que uma referência interna ou externa. Melhor ainda, a saída do comparador do ATmega328 pode ser conectada internamente ao Timer1 para encerrar uma contagem de tempo.
Projeto do Capacímetro
A ideia básica é medir o tempo necessário para o capacitor carregar até uma certa tensão. Conhecidas as tensões e a resistência em série com o capacitor, podemos usar a fórmula que vimos antes para determinar o valor do capacitor.
Uma saída digital será usada para descarregar o capacitor e liberar a sua carga. A tensão do capacitor será monitorada pelo comparador, que para o Timer1 quando ela atingir a tensão desejada. Desta forma o tempo de carga será medido diretamente pelo hardware, sem necessidade de controle direto pelo software.
O Timer2 será utilizado para controlar a multiplexação do display e para dar um tempo entre as medições.
O Arduino trabalha com um clock de 16MHz, tanto o Timer1 como o Timer2 podem trabalhar com uma fração deste clock. Os seguintes valores serão usados:
- Timer2
- Usa clock/64, conta de 0 até 255
- Tempo até “dar a volta” (overflow) = 256*64/16000000 = 1,024ms
- Para obter uma medição a cada 2 segundos: contar 1953 interrupções
- Timer1
- Usa clock/64, conta de 0 até 65535
- Permite medir de 4 µs a 262 ms
- Cálculo do Capacitor
- Referência = 2,5V, R = 330K
- T µs = 0,2287*C pF ou C = (contagem*40000)/2287 pF
- Permite medir C de 17pF a 1.15uF
Projeto do Hardware
As figuras a seguir se referem ao hardware próprio, com anotações sobre o uso do Arduino.
Alimentação:
Regulador para baixar tensão de bateria de 9V para os 5V usados no circuito. Arduino possui circuito semelhante:
Alimentação, reset e clock do microcontrolador
Estes circuitos estão na placa do Arduino
Ligação do Capacitor e Referência de Tensão:
No circuito abaixo os resistores de 47K e 330K são resistores de 1% de precisão.
Quando o pino 12 estiver em nível baixo, o capacitor será descarregado através do resistor de 120R. Para carregar o capacitor o pino será configurado como entrada, passando a apenas monitorar a tensão.
Ligação do display
No Arduino os sinais DIG1 e DIG2 são ligados às saídas digitais 4 e 5 para não interferir com a comunicação serial do bootloader
Montagem em Protoboard usando o Arduino Nano
Listagem do Software
#define FALSE 0
#define TRUE 1
// Variaveis
static volatile char fAmostra; // TRUE quando é hora de amostrar
static volatile char fAmostrado; // TRUE quando é amostra foi feita
static volatile unsigned int tempo; // tempo para carga do capacitor até 2,5V
// zero indica que ultrapassou o tempo máximo
// Um byte para cada segmento
// Bit 7 = segto G, Bit 6 = segto F, .. Bit 1 = segto A, Bit 0 = ponto
static unsigned char segto [4];
static unsigned char newsegto [4];
// Controle dos segmentos do display
// Segmentos a serem acesos para cada dígito
// 0 1 2 3 4 5 6 7 8 9 - P n u
static const unsigned char segDigito[14] =
{
0b01111110, 0b00001100, 0b10110110, 0b10011110, 0b11001100,
0b11011010, 0b11111010, 0b00001110, 0b11111110, 0b11011110,
0b10000000, 0b11100110, 0b10101000, 0b00111000
};
#define TRACO 10
#define C_PICO 11
#define C_NANO 12
#define C_MICRO 13
// Iniciação
void setup() {
// Pinos ligados ao display são de saída
pinMode (A0, OUTPUT); digitalWrite(A0, LOW);
pinMode (A1, OUTPUT); digitalWrite(A1, LOW);
pinMode (A2, OUTPUT); digitalWrite(A2, LOW);
pinMode (A3, OUTPUT); digitalWrite(A3, LOW);
pinMode (A4, OUTPUT); digitalWrite(A4, LOW);
pinMode (A5, OUTPUT); digitalWrite(A5, LOW);
pinMode (9, OUTPUT); digitalWrite(9, LOW);
pinMode (10, OUTPUT); digitalWrite(10, LOW);
// Pinos de seleção do dígito (catodos) em 1
pinMode (2, OUTPUT); digitalWrite(2, HIGH);
pinMode (3, OUTPUT); digitalWrite(3, HIGH);
pinMode (4, OUTPUT); digitalWrite(4, HIGH);
pinMode (5, OUTPUT); digitalWrite(5, HIGH);
// Configura o comparador
ADCSRB &= ~_BV(ACME);
ACSR = _BV(ACIC) | _BV(ACIS1) | _BV(ACIS0);
DIDR1 = _BV(AIN1D) | _BV(AIN0D);
// Configura o timer 2
TCCR2A = 0; // Modo normal
TCCR2B = _BV(CS22); // Usar clkIO/64
TIMSK2 = _BV(TOIE2); // Interromper no overflow
// Configura o timer 1
TCCR1A = 0; // Operação normal
TCCR1B = 0; // Contador parado
TCCR1C = 0; // Não usado
// Descarrega o capacitor
DescarregaCap ();
// LED de debug
pinMode (LED_BUILTIN, OUTPUT);
}
void loop() {
if (fAmostra) // Quando for hora de amostrar
{
digitalWrite (LED_BUILTIN, HIGH);
Amostra (); // Amostra
fAmostra = FALSE;
CalculaCap (); // Calcula
AtlDisp (); // Mostra no display
digitalWrite (LED_BUILTIN, LOW);
}
}
// Calcula o valor do capacitor e coloca em newsegto
static void CalculaCap (void)
{
if (tempo < 3)
{
// Ccapacitância muito alta ou ausente
newsegto[0] = newsegto[1] = newsegto[2] = newsegto[3] = segDigito[TRACO];
}
else
{
// Capacitor está entre 17pF e 1.146.218 pF (1.15uF)
unsigned long cap = (tempo * 40000UL) / 2287UL; // em pF
unsigned int valor;
unsigned int ponto;
if (cap < 1000UL)
{
valor = (unsigned int) cap;
newsegto [3] = segDigito[C_PICO];
ponto = 2;
}
else if (cap < 9995UL)
{
cap += 5UL; // arredonda
valor = ((unsigned int) cap) / 10;
newsegto [3] = segDigito[C_NANO];
ponto = 0;
}
else if (cap < 99950UL)
{
cap += 50UL; // arredonda
valor = (unsigned int) (cap / 100UL);
newsegto [3] = segDigito[C_NANO];
ponto = 1;
}
else if (cap < 999500UL)
{
cap += 500UL; // arredonda
valor = (unsigned int) (cap / 1000UL);
newsegto [3] = segDigito[C_NANO];
ponto = 2;
}
else
{
cap += 5000UL; // arredonda
valor = (unsigned int) (cap / 10000UL);
newsegto [3] = segDigito[C_MICRO];
ponto = 0;
}
newsegto [0] = segDigito [valor / 100];
newsegto [1] = segDigito [(valor / 10) % 10];
newsegto [2] = segDigito [valor % 10];
newsegto [ponto] |= 1; // coloca o ponto
}
}
// Tratamento da interrupção do timer 0, usada para varrer o display
// e disparar a amostragem
#define DLY_AMOSTRA 1953 // 1 amostra a cada 2 segundos
ISR (TIMER2_OVF_vect)
{
static unsigned int cntAmostra = DLY_AMOSTRA;
VarreDisp ();
if (--cntAmostra == 0)
{
cntAmostra = DLY_AMOSTRA;
fAmostra = TRUE;
}
}
// Amostragem do valor do capacitor
static void Amostra (void)
{
fAmostrado = FALSE;
TCNT1H = 0;
TCNT1L = 0;
CarregaCap ();
TIMSK1 = _BV(ICIE1) | _BV(TOIE1);
TCCR1B = _BV(ICES1) | _BV(CS11) | _BV(CS10);
while (!fAmostrado)
;
DescarregaCap ();
}
// Atualiza o display para mostrar o conteudo de newsegto
static void AtlDisp (void)
{
segto[0] = newsegto[0];
segto[1] = newsegto[1];
segto[2] = newsegto[2];
segto[3] = newsegto[3];
}
// Faz a varredura (apresentação do display)
// Esta rotina está amarrada à conexão do display ao ATmega
static void VarreDisp (void)
{
static unsigned char disp = 0;
register unsigned char segtos;
// apaga o digito anterior
if (disp == 0)
digitalWrite(3, HIGH);
else if (disp == 1)
digitalWrite(4, HIGH);
else if (disp == 2)
digitalWrite(5, HIGH);
else
digitalWrite(2, HIGH);
// programa os segmentos
segtos = segto[disp];
if (segtos & 0x80) // G - PB2
PORTB |= 0x04;
else
PORTB &= ~0x04;
if (segtos & 0x40) // F - PC0
PORTC |= 0x01;
else
PORTC &= ~0x01;
if (segtos & 0x20) // E - PC1
PORTC |= 0x02;
else
PORTC &= ~0x02;
if (segtos & 0x10) // D - PC2
PORTC |= 0x04;
else
PORTC &= ~0x04;
if (segtos & 0x08) // C - PC3
PORTC |= 0x08;
else
PORTC &= ~0x08;
if (segtos & 0x04) // B - PC4
PORTC |= 0x10;
else
PORTC &= ~0x10;
if (segtos & 0x02) // A - PC5
PORTC |= 0x20;
else
PORTC &= ~0x20;
if (segtos & 0x01) // PONTO - PB1
PORTB |= 0x02;
else
PORTB &= ~0x02;
// acende o dígito atual
if (disp == 0)
digitalWrite(4, LOW);
else if (disp == 1)
digitalWrite(5, LOW);
else if (disp == 2)
digitalWrite(2, LOW);
else
digitalWrite(3, LOW);
// passa para o dígito seguinte
disp = (disp + 1) & 3;
}
// Descarrega o capacitor
static void DescarregaCap (void)
{
// Configura pino AIN0/D6 como saída digital
// em nível 0
DDRD |= _BV(PD6);
PORTD &= ~_BV(PD6);
pinMode (6, OUTPUT);
digitalWrite (6, LOW);
}
// Dispara a carga do capacitor
static void CarregaCap (void)
{
// Configura o pino AIN0/D6 como entrada digital
// (sem pullup) para não interferir
pinMode (6, INPUT);
digitalWrite (6, LOW);
}
// Tratamento da interrupção de captura do timer1,
ISR (TIMER1_CAPT_vect)
{
unsigned char cntLo, cntHi;
cntLo = ICR1L; // ler primeiro Low
cntHi = ICR1H;
tempo = (cntHi << 8) + cntLo;
TCCR1B = 0; // para o timer
TIMSK1 = 0; // não interromper
fAmostrado = TRUE;
}
// Tratamento da interrupção de overflow do timer1
ISR (TIMER1_OVF_vect)
{
tempo = 0; // indica overflow
TCCR1B = 0; // para o timer
TIMSK1 = 0; // não interromper
fAmostrado = TRUE;
}
O que achou do projeto? Deixe seu comentário abaixo.
Saiba Mais
Introdução ao Arduino – Primeiros passos na plataforma
Medindo distância com Arduino UNO utilizando sensor ultrassônico e LCD
Medindo temperatura e umidade com Arduino UNO utilizando Si7021 e LCD




















Pergunta: Nõ precisa considerar também a capacitância parasita dos fios caso os valores de capacitância fiquem bem baixo? Como faria isto? conseguiria justamente medir capacitâncias na ordem de 1pF?
Sim, no caso de capacitâncias baixas a capacitância da montagem vai afetar o resultado. Um problema maior é que a resolução da medida é 17pF, portanto ao medir 170pF nas melhores condições já temos um erro de mais ou menos 10%. Para medir com precisão capacitâncias da ordem de 1pF você vai precisar de um capacímetro bem sofisticado.
Projeto bem interessante! Eu cheguei a fazer um na faculdade sobre leitor de resistor.
Apenas um adendo, na parte que você começa assim: “Vamos precisar de quatro dígitos…”, acredito que teve uma infelicidade de deixar 4×9 = 39 e na verdade o resultado desta multiplicação é 36.
Posso colocar a culpa na auto-correção?
Artigo muito bom, tem uma abordagem didática, e apresenta o microcontrolador na plataforma. O código explora as funcionalidades do arduíno, mas sem ficar restrito às bibliotecas prontas. Parabéns. Muito bom. Vou indicar a leitura para os meus alunos.
Muito bom!
Obrigado por compartilhar seu conhecimento.
Como não entendo quase nada de eletrônica, oq mais gostei foi a parte de eletrônica básica! 😉
Muito legal o projeto é muito bem explicado! Obrigado pelo projeto!