Ir ao conteúdo
  • Cadastre-se

Dúvidas sobre cálculos para resolução do adc


Rodrigo_moraes

Posts recomendados

Olá pessoal, estou fazendo um voltimetro com leds de 7 segmentos e a principio meu circuito esta no anexo.

como veem a tensão de entrada é 12V e a tensão que representa esse valor é 3.75v, e na vref 5v do pic, e aquele 9 é uns testes que andei fazendo por aqui, para ver se eu chego a um numero igual aquele da entrada

e meu codigo fonte feito no ccs é esse:

#include "I:\Documents and Settings\Rodrigo\Desktop\teste volt\main.h"
#include <math.h>

char display[10]=
{0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7C, 0x07, 0x7F, 0x67};

long int digito1;
long int medida;
int32 medida1;
long int valor;

void main(){
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(2);

while(true){

medida = (int32)read_adc();
medida1 = (((int32)medida*5000)/1023);

digito1 = (medida1/100);

output_bit(pin_b0,1);
output_b(display[digito1]);
delay_ms(1);
output_bit(pin_b0, 0);
}

}

Sei que esta incompleto, pois falta os outros 2 digitos e colocar interrupçoes , mas é so pra ver se eu consigo printar um valor que conhicide no voltimetro virtual. Gostaria de saber se é esta formula que usa para pegar o valor do read_adc() e se esta correta a forma de printar o valor nos led 7 segmentos, lembrando que meu pic é o 16f716 com resolução de 8bits.

Sera que estou no caminho certo???

Obrigado!

post-273784-13884961486123_thumb.jpg

Link para o comentário
Compartilhar em outros sites

Sim estás só tem um problema é acredite é do compilador:


long int medida;

medida = (int32)read_adc();
medida1 = (((int32)medida*5000)/1023);

Vai dar caca usar modelagem cast "int32" na variável medida, pois não se sabe porque o CCS entende long int como variável de 16bits, sugiro que declare medida como int32 também.

Peripécias como essa so poderia ser coisa do CCS rs.

Dúvidas estamos ai.

Abs.

Link para o comentário
Compartilhar em outros sites

Sim estás só tem um problema é acredite é do compilador:


long int medida;

medida = (int32)read_adc();
medida1 = (((int32)medida*5000)/1023);

Vai dar caca usar modelagem cast "int32" na variável medida, pois não se sabe porque o CCS entende long int como variável de 16bits, sugiro que declare medida como int32 também.

Peripécias como essa so poderia ser coisa do CCS rs.

Dúvidas estamos ai.

Abs.

Aow amigo Felipe fiz o que me sugeriu, retirei o "int32" e adicionei nas declarações:


int32 digito1;
int32 medida;
int32 medida1;

e mudei o divisor do digito de 100 para 500:


digito1 = (medida1/500);

e deu certinho!:lol:

apareceu o primeiro dígito da tensão, de entrada que no caso seria 12v e aparece o numero 1, coloquei uma tensão de 22v a aparece o numero 2,

volt22.png

agora preciso de mais uma ajuda e essa vai ser crucial.... hehe:

como estou usando 3 displays de 7 semgmentos, o que exatamante eu devo fazer para dar o refresh nos displays?

devo usar interrupçoes ou usar um delay_ms()?

acredito que interrupçao seria a mais usual, porém nao faço ideia de como faço isso no ccs....

alguma ideia?

Obrigado!!!!!

Link para o comentário
Compartilhar em outros sites

É agora começa a diversão com display de 7 segs.

Basicamente o que você vai fazer é uma espécia de varredura dos displays onde cada display é aceso uma vez com uma taxa de atualização de aproximadamente 100HZ.

Um modo legal seria o uso da interrupção por timer e uma variável de controle, a cada entrada na interrupção você verifica qual display deve ser acionado.

sugestão:


// rotina de interrupção exemplo

#int_timer1
void t1int (void)
{
static int controle;

controle++;
if (controle > 2) controle = 0; //controla overflow

switch (controle)
{
case 0 : /*inclua aqui a rotina para acionar o display 1 apenas*/
break;

case 1: /*inclua aqui sua rotina para acionar display 2 apenas*/
break;

case 2: /*inclua aqui sua rotina pra acionar o display 3 apenas*/
break;
}

set_timer1(get_timer1() + PRELOAD); //prepara para proxima int

}

void main()
{
/*aqui você configura o timer1*/
/*depois faz isso :*/

enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
}

agora e com você! abs

Link para o comentário
Compartilhar em outros sites

Amigo felipe, implementei o codigo qu você me passou, porém nao entendi alguns trechos dele:


case 0 : /*inclua aqui a rotina para acionar o display 1 apenas*/
break;

eu coloquei assim no meu:


case 0 :

output_high(pin_a4);
delay_ms(100);
break;

e nada aconteceu. É isso mesmo que se coloca no case do ccs?

e esses trechos:

set_timer1(get_timer1() + PRELOAD);

o que seria esse preload?

e esse aqui tem que configurar algo como o tmr0 t0if?

 /*aqui você configura o timer1*/

Me desculpe pela falta de conhecimentos tecnicos..... hehehe

Link para o comentário
Compartilhar em outros sites

Amigo, em cada case você precisa ligar o display e mandar a informaçao para ele e em seguida desligar ele. Preolad é um valor q você ira carregar teu timer ,para poder gerar teu tempo.A configuração do timer1 seria algo como :

setup_timer_1 (T1_INTERNAL | T1_DIV_BY_8);   //Configurao do Timer1 para clock interno = 1E6 dividido por 8
set_timer1 (3036);

Pode tentar usar o modo q usei num relogio,segue o codigo abaixo:

dezm = (m/10); //sacamos las decenas
unim = (m-(dezm*10)); // y las unidades

dezh = (h/10); //sacamos las decenas
unih = (h-(dezh*10)); // y las unidades

output_b (digito[unih]); // Apresenta Digito[unidade_do_contador]
output_high (Display1); // Habilita display 1
delay_ms (1); // Aguarda 50ms para dar uma pausa no PIC
output_low (Display1); // Desabilita display 1
output_b (digito[dezh]); // Apresenta Digito[dezena_do_contador]
output_high (Display2); // Habilita display 2
delay_ms (1); // Aguarda 50ms para dar uma pausa no PIC
output_low (Display2); // Desabilita display 2

output_b (digito[unim]); // Apresenta Digito[unidade_do_contador]
output_high (Display3); // Habilita display 1
delay_ms (1); // Aguarda 50ms para dar uma pausa no PIC
output_low (Display3); // Desabilita display 1
output_b (digito[dezm]); // Apresenta Digito[dezena_do_contador]
output_high (Display4); // Habilita display 2
delay_ms (1); // Aguarda 50ms para dar uma pausa no PIC
output_low (Display4);

Link para o comentário
Compartilhar em outros sites

pois é pessoal, desculpem a minha falta de conheciemento outra vez.....

fui tentar printar os outros dois digitos no display e nao consigo, sai um valor errado, como eu disse ao felipe, pensei que tivesse aparecido o valor correto do primeiro digito, quando eu testei 12v apareceu no primeiro digito o numero 1 e quando coloquei 22v apareceu o numero 2, só que quando eu coloquei 14.4v apareceu o numero 2....

levando-se em consideraçao que meu divisor testa tensão maxima de 16v, obtem-se 5 volts na saida.Nao preciso de muito, pois é apenas para bateria de carro....

com certeza o erro esta nos tipos de dados que eu estou colocando:

int32 digito1, digito2;
int32 medida;
int32 medida1;

e a minha formula:


medida = read_adc();
medida1 = ((medida * 5)/1023);


digito1 = (medida1);
digito2 =((digito1*100))/50;

qual seria os tipos de dados, e qual seria a formula, correta para retirar o valor real do adc?

Link para o comentário
Compartilhar em outros sites

Olha seu erro:



medida = read_adc();
medida1 = ((medida * 5)/1023);

Vai dar errado, reparou o valor máximo que essa conta vai assumir?

suponhamos que o A/D tenha recebido 5V na entrada logo o código que vai sair dele é 1023 assim:

medida1 := 1023 certo?

olha o que acontece quando aplicamos a sua formula:

(1023 * 5)/1023 = 5V ...ai você me responde:

WHAT?! :D

É isso mesmo, nunca vais conseguir a tensão de 16V se o fundo de escala da tua formula esta para 5V veja so algebricamente quem é quem na formula:

Volts = (ADClido * Fundo de escala) / ADCMAXIMO

mas resolver isso é fácil oras, se você me disse que o máximo que vai ler é 16V e que ele esta para 5V na saida do divisor então podemos assumir que sua nova formula sera:


medida = read_adc();
medida1 = ((medida * [B][I]16[/I][/B])/1023);

ai você olha e diz de novo:

WHAT?!

oras, se 5V esta para 16 concorda que se tivermos 16V no divisor teremos 5V na entrada analogica do pic logo que valor que o A/D vai retornar? 1023

se aplicarmos na formula:

medida1 = (1023 * 16 )/1023 = 16V

tendeu, como a maxima tensão que vai medir esta para o maximo valor de tensão de referencia do A/D basta mudar o fundo de escala na formula, isso vale para toda e qualquer tensão que obedeça essa regra.

observe que isso vale para uma Vref de 5V que é seu caso, existem voltimetros que usam a Vref do A/D com outros valores 2,5V por exemplo ai nesse caso para a formula funcionar o divisor tem de ser reprojetado de modo que seu valor de saida máximo seja iguall a Vref ou seja 2,5V certo?

Agora ponha a cabeça pra pensar.

Abs.

Link para o comentário
Compartilhar em outros sites

Amigos, depois de penar muito e muito para descobrir o valor, a solução estava embaixo do meu nariz.....kkkkkkk. :D

Fiz um teste com o proteus, criei um novo esquema para testes e adicionei um 16f877a, que tem resoluçao de 8 e 10 bits e com um diferencial, ele tem saidas tx e rx, e com isso liguei nele um terminal virtual e fiz as configuraçoes do adc e foi ai que descobri que cometi um erro digamos "bobo".Porque?

Bom, como o amigo felipe me disse, alguns pics usam 2.5v de vref, os meus usam 5v, ou o valor do vcc, sabendo disso o valor maximo admitido na entrada

analogica é de 5v. E foi aqui que eu errei:

Sabendo que a entrada analogica admite no maximo 5v, o read_adc() le o valor analogico e devolve um binário e nao um valor real ou seja:

Se há 5 volts na an0, o read_adc ira devolver o valor de 255 e nao o valor analogico, como eu achava que era..... :D

E portanto o tipo de dado que essa variável que ira receber é inteiro, no meu caso como no teste:

int32 tensão;

Agora, como eu sempre vi em sites a fora essa é uma legitima "receita de bolo", porque?

Bom, sabendo que a vref é de 5V, e que eu estou trabalhando com o adc em 8bits, o valor para saber quantos volts por bit, então eu fiz:

5/1023 = 0.0048875 volts por bit.

sabendo disso, os dados das variaveis que irao receber os dados dessa equaçao serao do tipo float:

float voltage, resultado;

Agora para converter o valor binario(digital) em valor analogico eu fiz assim:

voltage = (tensão * 0.0048875);

como no meu caso estou medindo 12v,apos o divisor tra 3.75v ele representa 191 em numero binario, logo:

voltage = 191*0.0048875 = 0.9335125

Agora isso aqui é particular de cada projeto, para saber o valor Real da tensão medida eu preciso obter 2 valores:

o valor da razao entre a entrada e a saida do divisor.

e quantos volts eu obtenho na saida do divisor a cada volt medido na entrada.

para o primeiro, se eu tenho 16v na entrada, eu obtenho 5 na saida do divisor, ai eu fiz:

16/5 = 3.2

já para o segundo eu fiz na tentativa e erro, pois no simulador eu obtive um valor muito acima do necessario, claro no simulador, assim que montar na proto, saberei o valor real, então eu cheguei ao valor de 0.2488 volts na saida a cada 1 volt na entrada do divisor.

Como ja tenho o valor convertido em valor analogico então para saber finalmente o valor real medido fiz:

resultado = (voltage / 0.2488) * 3.2);

sendo voltage = 0.9335125, no meu caso

resultado = (0.9335125 / 0.2488) * 3.2)

resultado = 12,00 volts.

é claro que por ser um adc de 8bits o valor nao sai estão preciso, mas ta otimo....:D

Eai o que acharam amigos? Depois que me escreveram, eu me empenhei e li muito pra achar o caminho certo, agora vou trablhar na parte de separar os digitos e enviá-los pro display 7 seg e testar os codigos que me passaram a respeito das interrupçoes, e assim que tiver resultados eu posto de novo amigos....

T+

Link para o comentário
Compartilhar em outros sites

Ola amigos, eu de novo aqui.... agora para postar as fotos do voltimetro funcionando no proteus:

voltimetrofinal.png

ele esta com uma margem de erro de 0.01v para + e para -, isso é aceitavel, pois como se sabe, adc de 8bits nao é muito preciso, agora estou esperando chegar os displays que encomendei(acabou na loja onde compro), parece que todo mundo compra na mesma hora....hehehehe.Para ai sim ver como ele se comporta na pratica, claro que algum ajuste é preciso,e por isso, sei que nao é muito o contexto, mas tem como, eu gravar o meu pic sem retirar da placa de testes? Pois gosto de uma coisa mais agil(sem ter que ficar tirando o pic do soquete do prototipo).

Agradeço pessoal, logo posto as fotos do prototipo.

T+

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

 

GRÁTIS: ebook Redes Wi-Fi – 2ª Edição

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!