Olá neste primeiro projeto de 2024 vou colocar um exemplo de frequencímetro duplo para medir 2 frequências através dos CCP'S e enviar via seria, além de funções prontas para calculo da frequência e configuração do prescaler para cada faixa de frequência desejada.
Nos testes do simulador e no projeto físico funcionou muito bem e com muita precisão, um exemplo de simulação é visto na figura a seguir:
O código fonte :
/* * * Compilador : MPlabXC8 * Microcontroladores compativeis : 16F886 e 887 * Autor: Aguivone Moretti Fógia * Versão: 1.1.2 * Data : 02/01/2024 - inicio de testes com oscilador interno (de 8MHZ) - 100 a 8000hz no simulador * Data : 02/01/2024 - inicio de testes com oscilador interno (de 20MHZ) - * descrição : Frequencimetro * */ #include <stdio.h> #include <string.h> //para usar funçoes de string deve se adicionar este header #include <stdlib.h> // se igual a 8MHZ: /* #define _XTAL_FREQ 8000000 #pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN) */ // se igual a 20MHZ: #define _XTAL_FREQ 20000000 #pragma config FOSC = HS // cristal de 20mhz #include <xc.h> /////////////////////////////////////////////////////////configuraçôes////////////////////////////////////////////////////// #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) ///////////////defines utilizados/////////////////////////////////////////////////////////////////////////////////////////// //o pino R/W é conectado ao terra pois não é feita nenhuma leitura no display //configuração: // // pino da porta selecionada / pino display // // C4 = pino de D4 do display // C5 = pino de D5 do display // C6 = pino de D6 do display // C7 = pino de D7 do display // // C1 = pino de RS do display // C2 = pino de EN do display // /*************************** configuração dos pinos **************************************************/ ////////PINOS DO DISPLAY DE LCD /////////////// //16F88 /* #define BIT_0 RB0 #define BIT_1 RB1 #define BIT_2 RB3 #define BIT_3 RB4 #define PIN_RS485 RA1 //pino de controle da rs485 #define EN RA2 #define RS RA3 ///////// PINO DE SINALIZAÇÃO ////////////// #define LED_STATUS RA4 //////////PINOS DE ENTRADA /////////////// #define BOT_FUNCAO RB7 #define BOT_SELECAO RB6 #define BOT_OK RA6 */ //16F887 #define LED_STATUS RB0 #define LED_freq1 RB1 #define LED_freq2 RB2 long periodo_anterior_1=0; long periodo_anterior_2=0; long periodo1=0; long periodo2=0; char CCP1_mult,CCP2_mult; float aux = 0; float fator_de_div = 0; void CCP_div_por(char CCP1_V,char CCP2_V) {//valor da divisão do CCPs CCP1CON = 0X05;//captura a cada borda CCP2CON = 0X05;//por padrao CCP1_mult = CCP1_V; if(CCP1_mult == 4) { CCP1CON = 0X06; } if(CCP1_mult == 16) { CCP1CON = 0X07; } CCP2_mult = CCP2_V; if(CCP2_mult == 4) { CCP2CON = 0X06; } if(CCP2_mult == 16) { CCP2CON = 0X07; } } void prescaler_por(char prescaler) { switch(prescaler) { case 1:///por 1 fator_de_div = _XTAL_FREQ/4; T1CON = 0X01; break; case 2: /// por 2 fator_de_div = _XTAL_FREQ/8; T1CON = 0X11; break; case 4: /// por 4 fator_de_div = _XTAL_FREQ/16; T1CON = 0X21; break; case 8:/// por 8 fator_de_div = _XTAL_FREQ/32; T1CON = 0X31; break; } } void interrupt interrupcoes(void)//vetor de interrupção { long periodo_atual_1 = CCPR1;//pega valores depois vê se é pra tratar long periodo_atual_2 = CCPR2; /*if(TMR1IF) { //não será usado aqui TMR1IF = 0; }*/ if(CCP1IF) { CCP1IF = 0;//limpa flag de interrupção if(periodo_atual_1> periodo_anterior_1) { periodo1 = periodo_atual_1 - periodo_anterior_1; } else { periodo1 = (65535 - periodo_anterior_1) + periodo_atual_1; } LED_freq1 = ~LED_freq1; periodo_anterior_1 = periodo_atual_1; } if(CCP2IF) { CCP2IF = 0;//limpa flag de interrupção if(periodo_atual_2> periodo_anterior_2) { periodo2 = periodo_atual_2 - periodo_anterior_2; } else { periodo2 = (65535 - periodo_anterior_2) + periodo_atual_2; } LED_freq2 = ~LED_freq2; periodo_anterior_2 = periodo_atual_2; } if(RCIF == 1) {//interrupção de recepção de dados char aux; //lembrar de por um timeout pra não ficar travado esperando dados RCIF = 0;// limpa flag de interrupção de recepção aux = RCREG; //deve ler para não trava o chip esperando tratar a interrupçao // não será utilizado aqui } } void imprime_rs232(char frase[],int tamanho) { //para conseguir imprimir em hexa sem problemas é melhor passar o tamanho do pacote int indice = 0; __delay_us(1); for(indice = 0;indice < tamanho;indice++) { while(TRMT==0);//espera enviar caracter, esvaziando o shift register , usando o trmt é mais garantido a entrega TXREG = frase[indice];//no datasheet diz que ao carregar o TXREG limpa o bit TXIF while(TRMT==0); } } 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) } void long_to_char(long valor) { char Buffer_serial_out[5]; //delimitador pra facilitar jogar no excel Buffer_serial_out[0]='0'; Buffer_serial_out[1]='0'; Buffer_serial_out[2]='0'; Buffer_serial_out[3]='0'; Buffer_serial_out[4]='0'; if(valor < 65535) { while(valor>=10000) { valor = valor - 10000; Buffer_serial_out[0]++; } while(valor>=1000) { valor = valor - 1000; Buffer_serial_out[1]++; } while(valor>=100) { valor = valor - 100; Buffer_serial_out[2]++; } while(valor>=10) { valor = valor - 10; Buffer_serial_out[3]++; } while(valor>=1) { valor = valor - 1; Buffer_serial_out[4]++; } imprime_rs232(Buffer_serial_out ,5); } } //*******************************Rotina principal*********************************************/ void main(void) { TRISA = 0XF7;//configura portA pino habilita rs TRISB = 0X00;//configura portB TRISC = 0X06;//configura portC //tx é saida TRISD = 0X00;//configura portD // somente no 16F887 TRISE = 0XFF;//configura portD PORTA = 0; PORTB = 0;//deixa pino tx em nivel alto PORTC = 0; PORTD = 0;// somente no 16F887 GIE = 0;//já desliga as interrrupções para terminar as configurações OPTION_REG = 0X80; // pull up desabilitados e prescaler para timer 0 - desabilita watchdog INTCON = 0;//desabilita todas as interrupções //CMCON = 0X07;//desliga comparadores no 16F887 é outro //--------------------------------------------------------- // CCP1CON = 0X05; //Capture mode, every rising edge // CCP2CON = 0X05; CCP_div_por(1,1);// modo do ccp dividido por 1,4 ou 16. //--------------------------------------------------------- ANSEL = 0X00; //portas digitais e AN0 e AN1. ANSELH = 0X00; //portas digitais e AN8 e AN13.// OSCTUNE = 0X1F; // oscilador vel maxima(para que o baud rate tambem dê certo) // ---------------------------------------------------------------------------------------------------------- // para oscilador interno de 8 mhz use : // OSCCON = 0XFE ; // oscilador interno com frequencia de 8mhz ->16F88 // OSCCON = 0XF5 ; // oscilador interno com frequencia de 8mhz ->16F887 //---------------------------------------------------------------------------------------------------------- // para cristal de 20 Mhz use: OSCCON = 0XFE; // oscilador de 20mhz //---------------------------------------------------------------------------------------------------------- inicializa_RS232(19200,1,0); CCP1IE = 1;//habilita interrupçao de ccp CCP2IE = 1;//habilita interrupçao de ccp // T1CON=1; //Timer on(para fazer as contagem)) qunado uas a afunção abaixo não precisa por este prescaler_por(1); // TMR1IE = 1;//habilita interrupção de estouro de timer para verificar quantos estouros houve não será usado PEIE = 1;//habilita interrupção de hardware GIE = 1; imprime_rs232("Frequencimetro duplo com pic \n\r",30); for(;;) { __delay_ms(10); GIE = 0; //imprime o valor da frequencia atual a cada 200ms imprime_rs232("\n\r FREQUENCIAS : ",17); aux = fator_de_div / periodo1; aux = aux*CCP1_mult; if(aux > 99999) { aux/1000; periodo1 = (long)aux; long_to_char(periodo1);//valor de frequencia calculada imprime_rs232("KHz ",3); } else { periodo1 = (long)aux; long_to_char(periodo1);//valor de frequencia calculada imprime_rs232("Hz - freq.2 = ",3); } aux = fator_de_div / periodo2; aux = aux*CCP2_mult; if(aux > 99999) { aux/1000; periodo2 = (long)aux; long_to_char(periodo2);//valor de frequencia calculada imprime_rs232("KHz ",3); } else { periodo2 = (long)aux; long_to_char(periodo2);//valor de frequencia calculada imprime_rs232("Hz ",3); } LED_STATUS = ~LED_STATUS; GIE = 1; }//loop infinito } // Notas: ***************** /* estando com o CCP1 e CCP2 sem divisão ( div por 1) e com cristal de 20mhz * a faixa de range confiavel é aprox : * prescaler para 1 : a partir de 80hz a 100Khz(estimado) * prescaler para 2 : a partir de 40hz a 70khz(etimado) * prescaler para 4 : a partir de 10hz a 30khz(simulado) * prescaler para 8 : a partir de 10hz a 25khz(simulado) * * para faixa maxima o simulador proteus é limitado o ideal é usar um frequencimetro * isso se deve ao estouro do timer para frequencia baixa(se for operar nessas frequencia use clock menor) */
formatado por : http://hilite.me/ acessando em 04/01/2024.