Ir ao conteúdo

Projeto voltímetro digital com PIC 16f877 e LCD


vitormetallica

Posts recomendados

Postado

Olá pessoal estou começando agora no mundo da eletrônica. Preciso desenvolver com certa urgência um projeto de um hardware que faça a leitura de tensão por meio de um PIC 16f877 e forneça na saída o valor em um LCD!Sei que terei que trabalhar com o conversor A/D do PIC!!

Gostaria de saber se alguém tem o circuito desse projeto no proteus e o código em C!! Ficarei muito grato se alguém se prontificar a me ajudar pois esse é meu 1º projeto com PIC!!

Desde já obrigado.

Postado

Esse projeto do voltímetro precisa medir até 12V! Eu tenho gravador de PIC disponível para o projeto!

O que eu terei que fazer é medir a tensão de algum equipamento dentro dessa faixa.

Postado

na apostila do grande mestre paulo tem um voltimetro,q usa display de sete segmento ,mais pra lcd fica mais fácil implementar,da um olhada la ,foi feito em basic,mais so mede ate 9.9v eu acho,mais creio q isso possa ser mudado.

  • Membro VIP
Postado

trabalhar com ad: exemplo do hitech-c

a2demo.c


unsigned char x;

init_a2d(); // initialise the A2D module- este não achei, mas é moleza e se vira né!
GIE=0; // we don't want interrupts
TRISB=0xF0; // the lower four bits of POTRB will be used in output mode

for(;;){
x=read_a2d(1); // sample the analog value on RA0
PORTB = (8>>(x>>6)); // Use the 2 MS Bits of the result to select the bit position of the LED on PORTB
}
}
void main(void){

adc.c


* Read the ADC on a 16C71.
*/
#include <htc.h>
#include "adc.h"
void
adc_read(unsigned char channel)
{

ADCON0 = (channel << 3) + 0xC1; // enable ADC, RC osc.
ADGO = 1;
while(ADGO)
continue; // wait for conversion complete
}
/*

adc.h


* Analog conversion stuff for 16C71 - see adc.c for more info
*/

/*
* Read the adc on the specified channel - result is in ADRES
*/

extern void adc_read(unsigned char channel);
/*

Tabalhar com LCD

lcd.c


* LCD interface example
* Uses routines from delay.c
* This code will interface to a standard LCD controller
* like the Hitachi HD44780. It uses it in 4 bit mode, with
* the hardware connected as follows (the standard 14 pin
* LCD connector is used):
*
* PORTD bits 0-3 are connected to the LCD data bits 4-7 (high nibble)
* PORTA bit 3 is connected to the LCD RS input (register select)
* PORTA bit 1 is connected to the LCD EN bit (enable)
*
* To use these routines, set up the port I/O (TRISA, TRISD) then
* call lcd_init(), then other routines as required.
*
*/

#ifndef _XTAL_FREQ
// Unless specified elsewhere, 4MHz system frequency is assumed
#define _XTAL_FREQ 4000000
#endif


#include <htc.h>
#include "lcd.h"

#define LCD_RS RA3
#define LCD_RW RA2
#define LCD_EN RA1

#define LCD_DATA PORTD

#define LCD_STROBE() ((LCD_EN = 1),(LCD_EN=0))

/* write a byte to the LCD in 4 bit mode */

void
lcd_write(unsigned char c)
{
__delay_us(40);
LCD_DATA = ( ( c >> 4 ) & 0x0F );
LCD_STROBE();
LCD_DATA = ( c & 0x0F );
LCD_STROBE();
}

/*
* Clear and home the LCD
*/

void
lcd_clear(void)
{
LCD_RS = 0;
lcd_write(0x1);
__delay_ms(2);
}

/* write a string of chars to the LCD */

void
lcd_puts(const char * s)
{
LCD_RS = 1; // write characters
while(*s)
lcd_write(*s++);
}

/* write one character to the LCD */

void
lcd_putch(char c)
{
LCD_RS = 1; // write characters
lcd_write( c );
}


/*
* Go to the specified position
*/

void
lcd_goto(unsigned char pos)
{
LCD_RS = 0;
lcd_write(0x80+pos);
}

/* initialise the LCD - put into 4 bit mode */
void
lcd_init()
{
char init_value;

ADCON1 = 0x06; // Disable analog pins on PORTA

init_value = 0x3;
TRISA=0;
TRISD=0;
LCD_RS = 0;
LCD_EN = 0;
LCD_RW = 0;

__delay_ms(15); // wait 15mSec after power applied,
LCD_DATA = init_value;
LCD_STROBE();
__delay_ms(5);
LCD_STROBE();
__delay_us(200);
LCD_STROBE();
__delay_us(200);
LCD_DATA = 2; // Four bit mode
LCD_STROBE();

lcd_write(0x28); // Set interface length
lcd_write(0xF); // Display On, Cursor On, Cursor Blink
lcd_clear(); // Clear screen
lcd_write(0x6); // Set entry Mode
}
/*

lcd.h


* LCD interface header file
* See lcd.c for more info
*/

/* write a byte to the LCD in 4 bit mode */

extern void lcd_write(unsigned char);

/* Clear and home the LCD */

extern void lcd_clear(void);

/* write a string of characters to the LCD */

extern void lcd_puts(const char * s);

/* Go to the specified position */

extern void lcd_goto(unsigned char pos);

/* intialize the LCD - call before anything else */

extern void lcd_init(void);

extern void lcd_putch(char);

/* Set the cursor position */

#define lcd_cursor(x) lcd_write(((x)&0x7F)|0x80)
/*

main.c


#include "lcd.h"

void
main(void)
{
lcd_init();
lcd_goto(0); // select first line
lcd_puts("12345678");
lcd_goto(0x40); // Select second line
lcd_puts("Hello world");

for(;;);
}
#include <htc.h>

difícil né?

sério?? achou mesmo?? bom... tentei. Leia o datasheet do pic para maiores (ou menores) detalhes. Volte ao pisca led

sucessos!

Postado

Não sei se você pegou a ideia do Josedasilva, mas é para você estudar. Esse é um típico projeto simples para que você possa compreender como se faz. SE você pegar pronto, nao vai aprender.

De toda forma, fiz aqui p você. Não vou explicar como funciona o circuito nem o códifo. Essa tarefa fica com você.

Circuito:

In9eS.png

Código:

#include <16F877A.h>
#device adc=10

#FUSES NOWDT //No Watch Dog Timer
#FUSES XT //Crystal osc <= 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BROWNOUT //Reset when brownout detected
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection

#use delay(clock=4000000)

#include <lcd.c>

int16 q1;
float p, q2;

void main()
{

lcd_init();

setup_adc_ports(AN0_AN1_AN3); //Canal 0 analógico
setup_adc(ADC_CLOCK_INTERNAL); //Fuente de reloj RC

set_adc_channel(0); //Habilitación canal0
delay_us(20);

while (TRUE)
{
q1 = read_adc(); //Lectura canal0

p = (q1 * 5)/1024.0;
q2 = p * 2.41;

printf(lcd_putc,"\fTensao:%2.2f", q2);
delay_ms (200);
}
}

Falou

Postado

OK MateusLPS você tem toda razão, estou dando uma olhada nas dicas do josedasilva, até porque tenho que saber bem a respeito desse projeto, pois terei que apresentá-lo junto a minha turma e o professor fará perguntas a respeito do código e do circuito, fazendo perguntas sobre algumas linhas do código e por aí vai, de qualquer maneira, como você disse, terei que estudar!!Estou um pouco enferrujado no que diz respeito a programação em C, pois faz alguns anos que não programo, mas baixei umas apostilas da net e um manual sobre LCD, agora é correr atrás para estudar!

Agradeço a você pela ajuda e caso seja preciso implementar mais alguma coisa nesse projeto posto aqui no clube pedindo ajuda!! valeu brigadão

Postado

você deve alterar o divisor resistivo consistido pelos resistores de 5K e 3.6K.

você refaz as contas com essa fórmula:

TwsKc.png

E no código você altera o q2 = p * 2.41;, pelo "fator de conversão" de 30V para 5V que nesse caso seria 30/5 = 6. então fica: q2 = p * 6;

Testa aí p ver se funciona!

Falou

Postado

Olá Matheus.

Veja se estou correto:

No caso se eu utilizar R2 10K, o meu R1 seria 290K é isto, para uma tensão de 30V, ficaria assim: ?

Vcc = 30V

Vsaida= Vcc x R2 / R1 + R2

Vsaida = 30 x 10K / R1 + 10K

Vsaida= 300 K / (290k) + 10K

V saida= 300K / 300K

V saida= 1

Seria isto ???

Postado

Nao nao, você nao calculou os valores do R1 e R2, você chutou? O calculo seria:

Atribui-se um valor comercial a um dos resistores. Por exemplo 5K a R1.

Vcc
Vout = ------- * R2
R1 + R2

O PIC só suporta 5V na entrada, então para uma entrada de 30V, temos que dividir
esse valor entre os dois resistores, daí o nome divisor resistivo, para que fique
5V para o PIC.

Temos então:

30V
5V = ---------- * R2
5000 + R2

Lembre-se que atribuimos 5K para R1. Multiplicando cruzado:

25000 + 5 * R2 = 30 * R2, isolando R2, fica:

25000 = 30R2 - 5R2, portanto

R2 = 1000 ou 1K ohms

Circuito:

smd5A.png

Veja que entra os 30V máximos. O potenciometro é apenas para simular uma entrada de tensão variável.

O divisor de tensão consiste em dividir a tensão por 6 para nao queimar a porta do PIC. Na programação você multiplica de volta por 6. Veja q entra 18V no exemplo mas o PIC lê 3V.

Veja também q temos valores com vírgula. Esses valores devem ser ajustados para coincidir com a entrada.

Falou

Postado

Agora entendi.

Bem explicado.

Eu tentei compilar o código com a mudança mais deu erro, por gentileza veja se está correto.

#include <16F877A.h>#device adc=10#FUSES NOWDT //No Watch Dog Timer#FUSES XT //Crystal osc <= 4mhz#FUSES PUT //Power Up Timer#FUSES NOPROTECT //Code not protected from reading#FUSES NODEBUG //No Debug mode for ICD#FUSES BROWNOUT //Reset when brownout detected#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O#FUSES NOCPD //No EE protection#use delay(clock=4000000)#include <lcd.c>int16 q1;float p, q2;void main(){lcd_init();setup_adc_ports(AN0_AN1_AN3); //Canal 0 analógicosetup_adc(ADC_CLOCK_INTERNAL); //Fuente de reloj RCset_adc_channel(0); //Habilitación canal0delay_us(20);while (TRUE){q1 = read_adc(); //Lectura canal0p = (q1 * 5)/1024.0;q2 = p * 6;printf(lcd_putc,"\fTensao:%2.2f", q2);delay_ms (200);}}

Tanks. Depois de montado farei o esquema completo no Visio e postareia aos nossos amigos e visitantes do CDH
Postado

Olá MateusLPS desculpe minha ignorância, estou fazendo o circuito que você postou do Proteus, gostaria de saber se o componente que você usou em paralelo com o potenciômetro se chama na biblioteca do proteus DC voltage source, porque não encontrei nenhum componente que tenha aquele display ou visor mostrando o valor da tensão!

valeu

Postado

Bom dia Caro MatheusLPS!!

Estou tentado fazer o mesmo circuito so que em tensão alternada:30vca.jpg

porém o valor não é o correto e ainda por cima fica varinado.

tentei colocar um diodo mas tb não adiantou:30vcacomdiodo.jpg

Queria saber como fazer o formula correta também lá em "C", pois tentei dividir por raiz de 2 mas tb não adiantou e continuou variando.

Desde já agradeço.

Obrigado

Tiago

Postado
Olá MateusLPS desculpe minha ignorância, estou fazendo o circuito que você postou do Proteus, gostaria de saber se o componente que você usou em paralelo com o potenciômetro se chama na biblioteca do proteus DC voltage source, porque não encontrei nenhum componente que tenha aquele display ou visor mostrando o valor da tensão!

valeu

Também é possível pegar o componente clicando com o botão direito do mouse na área de design do Proteus (aonde voce coloca os componentes), e vá em: Place -->Virtual Instrument --> DC Voltmeter

Queria saber como fazer o formula correta também lá em "C", pois tentei dividir por raiz de 2 mas tb não adiantou e continuou variando.

Pra achar a tensão RMS não basta dividir a tensão instantanea por raiz de 2. A tensão RMS é a tensão de pico (máxima) dividida pela raiz de 2...

Fora que a alimentação do PIC é 5 e 0V, não da pra voce comparar com valores negativos. voce teria que transformar seu sinal AC em DC, usando um circuito pra retificação, filtragem e regulagem:

http://www.feiradeciencias.com.br/sala15/15_07a.asp

Postado
Pra achar a tensão RMS não basta dividir a tensão instantanea por raiz de 2. A tensão RMS é a tensão de pico (máxima) dividida pela raiz de 2...

Fora que a alimentação do PIC é 5 e 0V, não da pra voce comparar com valores negativos. voce teria que transformar seu sinal AC em DC, usando um circuito pra retificação, filtragem e regulagem:

Sim eu Sei que o PIC só se pode colocar sinais positivos, por isso coloquei o diodo. Hoje agora vou colcar um Ret de onda completa.

O meu Problema é na parte de programação, ainda sou bem iniciante nessa parte. eu acho que eu tenho de mudar o calculo, porém não sei como fazer para ele diferenciar, o valor nele lido. pelo que notei fazendo rodar em baixa frequencia é que o PIC está sempre lendo o valor instantaneo, eu acho que o correto seria eu pegar os valor somente o valor maximo e ai sim aplicar a raiz de 2. Ou medir valor por valor dentro de um ciclo (pelo menos uns 100 pontos).

Tiago Souza.

Postado
Sim eu Sei que o PIC só se pode colocar sinais positivos, por isso coloquei o diodo. Hoje agora vou colcar um Ret de onda completa.

O meu Problema é na parte de programação, ainda sou bem iniciante nessa parte. eu acho que eu tenho de mudar o calculo, porém não sei como fazer para ele diferenciar, o valor nele lido. pelo que notei fazendo rodar em baixa frequencia é que o PIC está sempre lendo o valor instantaneo, eu acho que o correto seria eu pegar os valor somente o valor maximo e ai sim aplicar a raiz de 2. Ou medir valor por valor dentro de um ciclo (pelo menos uns 100 pontos).

voce tem alguma limitação quanto a quantidade de capacitores que voce pode usar?

No link que eu te passei, explica justamente o que fazer no hardware pra fazer o sinal se aproximar durante todo o tempo da tensão de pico, bastaria colocar capacitores com alta capacitancia em paralelo com o circuito.

Até dá pra fazer o que voce quer via firmware, voce teria que ficar amostrando os valores por um tempo, só guardar o maior valor, e depois fazer a conta de dividir por raiz de 2, mas se a amplitude do AC aumentar, ou se for adicionado um sinal DC de offset, sua conta ficará errada e seu circuito não terá como perceber o erro pra se ajustar. Outro problema é que se voce não pegar os dados rapidos o suficiente, voce poderá não conseguir pegar a tensão de pico e passar direto por ela. Além disso, voce perde muito tempo só com a amostragem via firmware pois voce só teria o resultado correto após terminar um ciclo da onda AC e olhe lá, pois voce poderia passar direto do valor (16 milisegundos, que em termos de microcontroladores é tempo pra caramba). Não bastando tudo isso, voce tem que atualizar o display pelo menos a cada 32 milisegundos, do contrário ele vai flicar.

Pra fazer isso via firmware, o melhor seria utilizar uma interrupção de um dos timers. Dentro da interrupção o código em C seria simples:

//---------------------------------------
intResultadoADC=read_adc();
if (intResultadoADC > intMaiorValor) //se o valor da leitura atual for maior que a leitura antiga
{
intMaiorValor = intResultadoADC; //guardar essa leitura como sendo a maior
}
intConta = intConta + 1 //conta quantas vezes entrou na rotina de timer
//-------------------------------------

Já no main, voce coloca:

//-------------------------------
//while(1)
{
if (intConta == suficiente)
{
intResultadoADC = (intResultadoADC * 5)/1024.0; //converte pra decimal
intResultadoADC = intResultadoADC/sqrt(2); //RMS
printf(lcd_putc,"\fTensao:%2.2f", intResultadoADC); //escreve no display
}
}
//Sendo suficiente, o calculo do numero de vezes que a rotina de interrupção precisa ser chamada com o prescaler setado e clock do seu PIC dê os 16 milisegundos.
//-------------------------------

Lembre-se de setar um prescaler que estoure o timer o máximo de vezes possível dentro desse intervalo de 16 milisegundos, pra ter uma maior chance de voce pegar a tensão de pico.

Postado
voce tem alguma limitação quanto a quantidade de capacitores que voce pode usar?.

Não!!! Porém estava simulando com esse circuito para entender melhor o funcionamento do PIC com AC.

Minha referencia de tensão vou retirar da saida de um CI no qual Também estou com duvidas:

http://forum.clubedohardware.com.br/nao-consigo-medir/923267

No link que eu te passei, explica justamente o que fazer no hardware pra fazer o sinal se aproximar durante todo o tempo da tensão de pico, bastaria colocar capacitores com alta capacitancia em paralelo com o circuito.

Isso tranquilo já fiz bastantes fontes se tiver de retificar tranquilo.

Até dá pra fazer o que voce quer via firmware, voce teria que ficar amostrando os valores por um tempo, só guardar o maior valor, e depois fazer a conta de dividir por raiz de 2, mas se a amplitude do AC aumentar, ou se for adicionado um sinal DC de offset, sua conta ficará errada e seu circuito não terá como perceber o erro pra se ajustar. Outro problema é que se voce não pegar os dados rapidos o suficiente, voce poderá não conseguir pegar a tensão de pico e passar direto por ela. Além disso, voce perde muito tempo só com a amostragem via firmware pois voce só teria o resultado correto após terminar um ciclo da onda AC e olhe lá, pois voce poderia passar direto do valor (16 milisegundos, que em termos de microcontroladores é tempo pra caramba). Não bastando tudo isso, voce tem que atualizar o display pelo menos a cada 32 milisegundos, do contrário ele vai flicar.

Pra fazer isso via firmware, o melhor seria utilizar uma interrupção de um dos timers. Dentro da interrupção o código em C seria simples:

Vou tentar fazer com seu codigo nesse fim de semana. Despois post as duvidas ou resultados.

Valeu mesmo foi de bastante ajuda.

PS.:Se der da uma olhada no meu post, :confused:sobre meu sinal de saida.

Obrigado

Tiago:)

  • 2 semanas depois...
Postado

Bom dia a todos!!

Estão fui fazer alguns teste na Prática mesmo.

Porém não deu muito certo. :(Não sei se fiz algo errado.:confused:

  • No Proteus funcionou.:)
  • Fui montando e estando.:)
  • Só que o LCD, não aparece nada.:(
  • Ligue sem nada no An0 e com Vcc, e nada.:(
  • o Pic sei que gravou pois coloquei para setar uma saida direto.:)

Segue foto do Esquema no proteus bem exatamente como está agora:

TesteLCDcomVoltimetro.jpg

E o codigo:

#include <16F877A.h>
#device adc=10

#FUSES NOWDT //No Watch Dog Timer
#FUSES XT //Crystal osc <= 4mhz
#FUSES PUT //Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BROWNOUT //Reset when brownout detected
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection

#use delay(clock=20000000)

#include <lcd.c>



void init(void)
{
// port directions: 1=input, 0 = output


}
int k; // não estou usando ainda
int16 q1;
float p, q2;


void main()
{
k =15;
output_high (pin_c0); // para verificar se gravou
output_high (pin_c1);
output_high (pin_c2);
output_high (pin_c3);

lcd_init();

setup_adc_ports(AN0_AN1_AN3); //Canal 0 analógico
setup_adc(ADC_CLOCK_INTERNAL); //Fuente de reloj RC

set_adc_channel(0); //Habilitación canal0
delay_us(20);

while (TRUE)
{
q1 = read_adc(); //Lectura canal0

p = (q1 * 5)/1022.0;
q2 = (p * 6) ;

printf(lcd_putc,"\fTensao:%2.2f", q2);
delay_ms (200);
}
}

O LCD não sei se está ligado certo. Vi como foi montado o anterior, e como no proteus deu certo, liguei do mesmo jeito.

Desde já agradeço.

Obrigado.

Tiago:)

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...

LANÇAMENTO!

eletronica2025-popup.jpg


CLIQUE AQUI E BAIXE AGORA MESMO!