Olá, hoje postarei um voltímetro para ler sinais AC usando o microcontrolador PIC16F88, no qual analisa um sinal de corrente alternada no pino RA0 e retorna o valor lido em milivolts na porta serial, atente que na simulação e no protótipo real o valor na função de configuração da serial é diferente.
Este projeto é útil quando se precisa ler o valor de pico da tensão, com alguma pequenas modificações é possível ter uma tensão média(tipo do multímetro) ou o valor de pico (tensão RMS). Para simulação foi usado apenas um gerador de sinal, um osciloscopio e um terminal serial configurado para (19200 de baud rate), no pic a frequência de clock é de 8 mhz (pois está usando o oscilador interno). Este projeto funcionou bem na protoboard e na simulação. Favor clicar na imagem para ver melhor os detalhes da tensão.
O código fonte :
/*
* Empresa cliente: exemplo_blog
* Compilador : MPlabXC8
* Microcontroladores compativeis : 16F88A
* Autor: Aguivone Moretti Fógia
* Versão: 1
* Data : 23_09_2022 -> ler tensao AC
*/
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 8000000
#include <xc.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
//#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) -- cristal de 20mhz
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT habilitado)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (proteção da eeprom)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (proteção do codigo)
#define TAM_MAX 10//tamanho maximo do vetor de amostragem(numero de pontos)
#define RANGE_ADC 10//variação maxima permitida (filtro de ruido)
#define LED_STATUS RB0
#define TEMPO_ENVIO 5000//(aprox. 0,5s)tempo para enviar os dados(em tempo de rotina))
int vetor[TAM_MAX+1]; //armazena as amostra temporaria
char flag_libera=0;
char tempo_media = 0;
int tempo_de_envio = 0;
int tensao_pico=0;
/////////////////////////////////funçoes usadas pela uart /////////////////////////////////////////////////////////////////
/*datasheet pag 101 - baudrate para 8mhz - high speed
baud rate desejado - baud rate real - faixa de erro - SPBRG
2400 2404 0,16 207
9600 9615 0,16 51
19200 19231 0,16 25
28800 29412 2,12 16
38400 38462 0,16 12
57600 55556 3,55 8
*/
void inicializa_RS232(long velocidade,int modo,char pega_ref)
{////por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
//verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
long valor;
if(modo == 1)
{//modo = 1 ,modo alta velocidade
TXSTA = 0X24;//8bits/transmissão habilitado/modo assincrono/highspeed
valor =(((_XTAL_FREQ/velocidade)-16)/16);//calculo do valor do gerador de baud rate -> 10_06_2022
}
else
{//modo = 0 ,modo baixa velocidade
TXSTA = 0X20;////8bits/transmissão habilitado/modo assincrono/lowspeed
valor =(((_XTAL_FREQ/velocidade)-64)/64);//calculo do valor do gerador de baud rate
}
if(pega_ref == 0)
{//valor calculado
SPBRG =(int)valor; // veja a pagina 101 do datasheet já tem calculado para 8MHZ
}
else
{//valor colocado diretamente
SPBRG =(int)velocidade; //coloca valor direto baseado no data sheet
}
RCIE = 1;//habilita interrupção de recepção
PEIE =1;
TXEN = 1; // habilita o modulo de transmisão
TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
///////////conversor adc //////////////////////////
// 0 - lê sensor de luz 1 - lê sensor magnetico
int ler_adc_un(void) //(int sele)
{
int lido_adc;
GIE = 0;//para não travar com outra interrupção
ADCON0 = 0XC5;//pede nova conversão //AN0
while (ADCON0 == 0XC5); //espera finalizar leitura
GIE = 1;
lido_adc = (ADRESH << 8) | ADRESL; // XXXXXXXXLL
return(lido_adc);
}
void interrupt interrupcao(void)//vetor de interrupção
{
char teste;
if(RCIF == 1)
{//interrupção de recepção de dados
//lembrar de por um timeout pra não ficar travado esperando dados
RCIF = 0;// limpa flag de interrupção de recepção
teste = RCREG;
}
}
void long_to_char(int valor)
{
char valores[] = {'0','0','0','0'};
while(valor>=1000)
{
valor = valor - 1000;
valores[0]++;
}
while(valor>=100)
{
valor = valor - 100;
valores[1]++;
}
while(valor>=10)
{
valor = valor - 10;
valores[2]++;
}
while(valor>=1)
{
valor = valor - 1;
valores[3]++;
}
for(int indice = 0;indice < 4;indice++)
{
while(TRMT==0);//espera enviar caracter, esvaziando o shift register , usando o trmt é mais garantido a entrega
TXREG = valores[indice];//no datasheet diz que ao carregar o TXREG limpa o bit TXIF
while(TRMT==0);
}
TXREG ='\r';//retorna paar inicio de linha
while(TRMT==0);
TXREG ='\n';//pula linha
while(TRMT==0);
}
void rotaciona_vetor()
{ // apenas rotciona e descarta o valor que estava em vetor[0]
int aux=0;
while(aux<TAM_MAX)
{
vetor[aux] = vetor[aux+1];
aux++;
}
}
void ordena_vetor()
{//coloca em ordem crescente onde o maior valor estará em vetor[tam_max]
int aux=0;
int aux2=0;
int houve_troca = 1;
while(houve_troca == 1)
{ ///faz isso enquanto houver troca de posição
houve_troca = 0;
aux=0;
while(aux<TAM_MAX)
{
if(vetor[aux] > vetor[aux+1])
{
aux2 = vetor[aux+1];
vetor[aux + 1] = vetor[aux];
vetor[aux] = aux2;
houve_troca = 1;
}
aux++;
}
}
}
int analisa_sinal(void)
{ //analisa os 4 valores mais altos
int valor_pico = 1050;//valor impossivel -> só pra indicar erro na leitura
ordena_vetor();// coloca em ordem crescente
////verifica se há valores discrepantes//// erro ou ruido
//veja que qualquer coisa dessas possibilidade é interpretado como zero
if((vetor[TAM_MAX]-vetor[TAM_MAX-1])< RANGE_ADC) //se a diferença não for maior que o range
{//senão houver valor discrepante está ok
if((vetor[TAM_MAX-1]-vetor[TAM_MAX-2])< RANGE_ADC) //se a diferença não for maior que o range
{//valida os 3 ultimos
valor_pico = (vetor[TAM_MAX]+vetor[TAM_MAX-1]+vetor[TAM_MAX-2])/3;
}
}
else
{
if((vetor[TAM_MAX-1]-vetor[TAM_MAX-2])< RANGE_ADC) //se a diferença não for maior que o range
{//descarta o maior valor
if((vetor[TAM_MAX-2]-vetor[TAM_MAX-3])< RANGE_ADC) //se a diferença não for maior que o range
{//valida
valor_pico = (vetor[TAM_MAX-1]+vetor[TAM_MAX-2]+vetor[TAM_MAX-3])/3;
}
}
}
return(valor_pico);
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////çlj
void main(void)
{
TRISB = 0X04;//configura portB como saida , RB2 é entrada pois é rx 232 e B4 não será usado
TRISA = 0XFF;//configura portA como entrada .só RA4 e RA3 como saida
PORTA = 0;
PORTB = 0;
OPTION_REG = 0X80; // pull up desabilitados e prescaler para timer 0 - desabilita watchdog
INTCON = 0;//desabilita todas as interrupções
CMCON = 0X07;//desliga comparadores
OSCTUNE = 0X1F; // oscilador vel maxima
OSCCON = 0XFE ; // oscilador interno com frequencia de 8mhz
/// OSCCON = 0X7C ; // oscilador cristal externo 20mhz
/////ADC/////////////
ANSEL = 0X01; //portas digitais e AN0 .
ADCON0 = 0XC1;//ligado ao oscilador RC - AN0 selecionado e modulo de conversão ligado
ADCON1 = 0X80;//justificado a direita(assim os 2bits mais significativos fica no primeiro byte) , ref ligado ao vdd
// ADCON1 = 0XC0;//justificado a direita(assim os 2bits mais significativos fica no primeiro byte) , ref ligado ao vdd -> clock dividido por 2
/////////////////
PIE1bits.ADIE = 0;// desliga interrupção do conversor ADC
//19200 -> modo de alta velocidade -> creio que o clock não está muito bem calibrado pois não está dando muito certo
//valores validos entre spbrg 27 e 30 -> para 19200 logo valores mais adequados 27 a 29 = mas 28 ficou melhor
inicializa_RS232(29,1,1);//passa valor direto do registrador na placa fisica deve ser 28 ou 29
inicializa_RS232(25,1,1);//passa valor direto do registrador para simular no proteus deve ser 25
GIE = 1;
for(;;)
{ // atualiza os vetores
__delay_us(100); //para garantir uma leitura estavel 0,4ms
vetor[TAM_MAX] = ler_adc_un();
if((flag_libera == 1))
{
if(vetor[TAM_MAX]<vetor[0])//localizou a regiao de pico de tensão
{
if(analisa_sinal() < 1025) //busca valor de pico valido
{
tensao_pico = analisa_sinal(); //valor instantaneo
}
flag_libera=0;
}
}
else
{//flag de liberação desligado
if(vetor[TAM_MAX] > vetor[0])//localizou o inicio de subida
{
flag_libera=1;//vasculha o ponto de subida
}
}
rotaciona_vetor();
if(tempo_de_envio >= TEMPO_ENVIO)
{
int aux=0;
aux = (tensao_pico*49)/10;
long_to_char(aux); // valor medio
LED_STATUS = ~LED_STATUS;//pisca a cada envio
tempo_de_envio = 0;
}
tempo_de_envio++;
}
}
Formatado no site http://hilite.me/ no dia 23/09/2022.
Nenhum comentário :
Postar um comentário
olá,digite aqui seu comentário!