Mostrando postagens com marcador microcontroladores PIC - XC8. Mostrar todas as postagens
Mostrando postagens com marcador microcontroladores PIC - XC8. Mostrar todas as postagens

quinta-feira, 4 de janeiro de 2024

Frequencímetro com 2 canais usando o PIC16F887 e mostrando via serial RS232 (frequencímetro duplo)

     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:


    Note que os leds são pra indicar que esta lendo pulsos nos pinos dos ccp's , como foi configurado para cada borda de subida logo terá a o valor de frequência menor. Para este foi colocado o mesmo sinal nos 2 canais para simular.

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.

terça-feira, 14 de fevereiro de 2023

Acionando Motor de passo a 4 fios (topologia 2 bobinas) com o PIC16F88

         Olá tudo bem! 

     Depois de um longo período sem postar nada trago um exemplo de acionamento de motor de passo que opera no modo 2 bobinas (apenas 4 fios).

    Neste exemplo foi utilizado um motor japonês STH36C1078 retirado de uma câmera de vigilância, mas pode-se utilizar qualquer outro motor de passo de 4 fios, pic 16F88, dois CI 74HC00, 8 transistores BC548, 8 diodos 1N4148, duas microswitch(tipo push botton) , um led sendo o restante composto por resistores.

     Para este exemplo pensei em algo simples como um programa que ao pressionar o botão 1 o motor gira em sentido horário e ao pressionar outro botão 2 gira em sentido anti-horário.

    Observe que a vantagem deste drive montado é que jamais ocorrerá a condição de curto, supondo que o CI esteja bom, pois o CI74HC00 faz o trabalho de sempre alternar os estado lógicos que chegam a base do transistor assim a tabela de cada drive é ‘01’- aciona a bobina num sentido (por exemplo RB0 = 0 e RB1 = 1) ,’10’ – aciona em sentido inverso, sendo que os estado ‘00’ e ‘11’ deixam as bobinas desligados, para este motor são montados 2 circuitos deste tipo(um para cada bobina).

    Deve se estar atento a sequência de ligação dos fios do motor para que ele não fique apenas “tremendo” em vez de girar. Neste projeto coloquei um resistor de baixo valor(27R) em serie com a alimentação dos transistores drives caso ocorra alguma falha este resistor funciona com fusível, porem pode se utiliza um fusível.

    Este drive pode funcionar com 5 a 12V(teoricamente pode até 30V porém não testei isso) enquanto o resto do circuito pode funciona com 5V, isso é bom pois assim não sobrecarrega o regulador de tensão que alimenta o microcontrolador.

O esquema elétrico simulado :

O código fonte:

/* Empresa cliente: BLOG MICROCONTROLADORES
 * Compilador : MPlabXC8
 * Microcontroladores compativeis : 16F88A
 * Autor: Aguivone Moretti Fógia
 * Versão: 1.0
 * Data :  14/02/2023
 * descrição : acionando motor de passo com 4 fios 
 */

#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 8000000
//#define _XTAL_FREQ 20000000
#include <xc.h>

#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

///////////////defines utilizados///////////////////////////////////////////////////////////////////////////////////////////

/*************************** configuração dos pinos **************************************************/
////////PINOS DO MOTOR ///////////////
//a bobina 1 é o fio amarelo e vermelho --->>  bobina 2 é o fio laranja e marrom
//no motor fisico a sequencia  para o modo 1 passo é 

/*
 Passo/cor
 * 1   amarelo(+)    vermelho(-)     laranja(desl)   marrom(desl)
 * 2   amarelo(desl) vermelho(desl)  laranja(+)      marrom(-)
 * 3   amarelo(-)    vermelho(+)     laranja(desl)   marrom(desl)
 * 4   amarelo(desl) vermelho(desl)  laranja(-)      marrom(+)
 * para motor desligado = todos os pinos em zero ou em 1
 
 
 */
 #define A_0              RB0   //bobina A  //deve ser colocado corretamente para girar
 #define A_1              RB1               

 #define B_0              RB2  //bobina B   
 #define B_1              RB3                 

 #define BOT_H            RA0  
 #define BOT_ANTI         RA1  
///////// PINO DE SINALIZAÇÃO //////////////
#define  LED_STATUS       RB4

void Desligar_motor(void)
{
   A_0 = 0;
   A_1 = 0;
   B_0 = 0;
   B_1 = 0;
}
void tempo_pulso(long tempo)
{//pra pode usar tempos maiores
    while(tempo>0)
    {
     __delay_ms(1);
     tempo--;
    }
}

void Girar_motor_horario(long tempo,int num_giros)
{//o tempo ira definir a velocidade de giro
    while(num_giros>0)
    {
        A_0 = 1;
        A_1 = 0;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 0;
        B_0 = 1;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 1;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 0;
        B_0 = 0;
        B_1 = 1;
        tempo_pulso(tempo);
        num_giros--;
    }
}

}
void Girar_motor_anti_hor(long tempo,int num_giros)
{//o tempo ira definir a velocidade de giro
    while(num_giros>0)
    {
        A_0 = 0;
        A_1 = 0;
        B_0 = 0;
        B_1 = 1;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 1;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 0;
        A_1 = 0;
        B_0 = 1;
        B_1 = 0;
        tempo_pulso(tempo);
        A_0 = 1;
        A_1 = 0;
        B_0 = 0;
        B_1 = 0;
        tempo_pulso(tempo);
        num_giros--;
    }
}

void main(void)
{
    TRISB = 0XC0;//configura portB
    TRISA = 0XFF;//configura portA
    PORTA = 0;
    PORTB = 0;//deixa pino tx em nivel alto
    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
    ANSEL = 0X00; //portas digitais desligado. 
    GIE = 0;   
   for(;;)
     { 
       
       if(BOT_H == 0)
       {
          Girar_motor_horario(5,10);// tempo de acionamento da bobina é de 1ms e dara 10 giros 
          LED_STATUS = 1;
       }
       else
       {
            if(BOT_ANTI == 0)
            {
               Girar_motor_anti_hor(5,10);
               LED_STATUS = 1;
            }
            else
            {
               Desligar_motor();
               LED_STATUS = 0;
            }
     }
     }//loop infinito
}

Formatado no site: http://hilite.me/   14/02/2023.

Vídeo de demonstração: 



sexta-feira, 23 de setembro de 2022

Voltímetro para corrente alternada - Voltimetro AC com PIC

 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.


sexta-feira, 21 de julho de 2017

Sistema de irrigação com pic 16F628

Olá, já algum tempo comprei umas válvulas para controlar um sistema de irrigação, dai coloquei três válvulas para decidir qual mangueira acionar (com pequenos pivôs(ou aspersores de irrigação)), o primeiro problema a ser resolvido era tentar umidecer ao máximo todo o jardim com esses aspersores, porém como eu não posso controlar a pressão da água que vem da rua, resolvi montar um circuito que   pudesse acionar fazendo combinações de modo a ter pelo menos 3 níveis de alcance(o alcance depende da pressão da água e esta depende do numero de micropivô).veja abaixo o esquemático do projeto,observe que terá um botão para ligar e outro para desligar(se necessário pois quando o programa for finalizado ele já desliga toda energia).

        Com a combinação consegui um resultado satisfatório; então depois de fazer toda a parte hidráulica era hora de colocar em pratica este circuito que montei, apesar de ter 2 reles para garantir que não existiria energia nos contatos das válvulas(para eliminar risco de choques quando o sistema não estiver acionado, ou caso um vazamento molhasse os contatos elétricos) resolvi separar a parte elétrica da hidráulica e acabei tendo que usar uma caixa de alarme(de uma sucata que eu tinha aqui em casa) para colocar todo o circuito, veja abaixo as fotos da placa e da montagem final:


Vamos agora ver como ficou o código fonte, ele é separado em 2 partes,o modo configuração usado pra gravar os tempos em que o sistema ficará em cada estagio(são 7)  e o modo de funcionamento onde rodará o programa uma vez e logo em seguida desliga tudo.Veja que será usado a comunicação serial pra configurar e guardar os dados na memoria.

O código fonte:

  /* projeto irrigador simples XC8
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F628A
 * Autor: aguivone
 * Versão: 1
 * Data :  06 de julho de 2017
 * 
 * intruções:
 *  a programação deve ser feita usado #SXX* ; onde S=o ciclo que se quer alterar(1 a 7) e XX é um valor de 00 a 99
 *  note que a programação será por multiplos de 10s
 * 
 * 
 * mapa de endereços
 * posição de memoria - descrição.
 * 
 * 0 - posição de memoria rom que indica se houve gravação
 * 1 a 7 - relativo ao modo de funcionamento 1 a 7.
 * 
 */

#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 4000000    // vou usar o oscilador interno
#include <xc.h>

/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////

//#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config FOSC = HS  ///se for de 20mhz deve ser HS
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // 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 input, 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 (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#define BOT_CONFIG   RA4

#define RL_GERAL RB7

#define RL_1 RB4
#define RL_2 RB5
#define RL_3 RB6

#define LED_STATUS RB0


////////////////////////////////////////////////variaveis globais///////////////////////////////////////////////////////////
int modo=0;//modo de funcionamento
int tempo[7];//para guardar os tempos
int pos_prog=6;//posição inicial

//usadas na serial
char rs232[6];//guarda pacote recebido
char caracter;
int pos_serial=0;
bit flag_interrupcao = 0;

/////////////////////////////////funçoes usadas pela uart //////////////////////////////////////////////////////
//void inicializa_RS232(long velocidade,int modo)
void inicializa_RS232()
{////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.  
    TXSTA = 0X24;//modo assincrono,trasmissao 8 bits./alta velocidade
    SPBRG = 25;//9600
    RCIE = 1;//habilita interrupção de recepçã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 escreve(char valor)
{
    TXIF = 0;//limpa flag que sinaliza envio completo.
    TXREG = valor;
    while(TRMT==0);//resolvi usar este flag em vez de TXIF
   // while(TXIF ==0);//espera enviar caracter//alterei para 1 para testar 18/07/17
   // __delay_us(20); 
}
void imprime(const char frase[])
{
     char indice = 0;
     char tamanho = strlen(frase);
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve(frase[indice]);
           indice++;
       }
}
void int_to_char(int quant)
{//converte para inteiro e imprime
     unsigned char ucDez='0', ucUn='0';
     while(quant>=10)
    {
      quant -= 10;  
      ucDez++;  
    }
    while(quant>=1)
    {
       quant -= 1; 
       ucUn++;  
    }
     escreve(ucDez);
     escreve(ucUn);
}
void delay_ms(long valor)//resolve um erro do conmpilador
{
    while(valor>0)
    {
     __delay_ms(1);   
     valor--;
    }        
}
void delay_seg(long valor)//rotina de 1s
{
    while(valor>0)
    {
     __delay_ms(1000); 
     //__delay_ms(100); //pra simular
     LED_STATUS = ~LED_STATUS;
     valor--;
    } 
    //para simular pode reduzir o tempo
}
void ler_dados()
{
    imprime(" \n\r tempos=");
    int var_auxi=0;
    while(var_auxi<7)
    {              
      tempo[var_auxi] = eeprom_read(var_auxi+1);
      int_to_char(tempo[var_auxi]); 
      escreve('0');
      escreve('|');
      var_auxi++;
    }
}


///////////////////////////////////////////////////interrupção//////////////////////////////////////////////////////////////
void interrupt RS232(void)//vetor de interrupção
 {
    if(RCIF)
    {//se pacote for de tamanho diferente ele descarta o pacote. formato #XXX*
     caracter = RCREG;
     if(flag_interrupcao == 0)
     {//verifica se está liberado para ler dados
     if(pos_serial>0)
     {               
            if(caracter == '*')
            {//pacote aceito
                if(pos_serial == 4)
                {
                 flag_interrupcao = 1;
                 rs232[pos_serial]=caracter; 
                 pos_serial = 0;//pra evitar que escreva antes de ler
                }
                else
                {//descarta pois é menor que o esperado
                    pos_serial = 10;//faz zerar tudo
                }
                 
            }
            else
            {
               rs232[pos_serial]=caracter; 
               pos_serial++;
            }            
        }
     if(pos_serial>4)
        {
            //erro no tamanho zera tudo
             rs232[0]='0';
             rs232[1]='0';
             rs232[2]='0';
             rs232[3]='0';
             rs232[4]='0';
             pos_serial = 0; 
        }
     if(caracter == '#')
     {
        pos_serial = 1;
        rs232[0]='#';
     }  
    }
     RCIF = 0;//  limpa flag de interrupção de recepção
    }    

 }


//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void)
{
    TRISB = 0X02;//configura portB como saida, B1 (pino RX) como entrada
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    TRISA = 0XFF;//configura portA   como entrada
    inicializa_RS232();//modo de alta velocidade
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    __delay_ms(200);
    imprime("Sistema de irrigação V.1.0.0 \n\r"); 
    //pega valores da memoria
    if(eeprom_read(0) != 'G')
    {//nunca foi gravada, então coloca valor padrão
        eeprom_write(0X00,0);
        eeprom_write(0X01,1);//endereço/valor
        eeprom_write(0X02,1);
        eeprom_write(0X03,1);
        eeprom_write(0X04,1);
        eeprom_write(0X05,1);
        eeprom_write(0X06,1);
        eeprom_write(0X07,1);
    }
    ////leitura 
    modo=1;
    if(BOT_CONFIG == 0)
    {   delay_ms(1000);
        if(BOT_CONFIG == 0)
        {
         imprime("Modo configuração \n\r");        
         modo=0;
       // eeprom_write(0X00,'G');//indica que houve gravação na memoria
         LED_STATUS =1;
        }        
    } 
    if(modo == 1)
        {
          RCIE = 0;//desabilita interrupção de recepção
          TXIE = 0;//desabilita interrupção de transmissao 
          ler_dados(); 
          LED_STATUS =0;
        }
   
    
//////////////
    for(;;)
    {   
        if(modo==0)
        {
            if(flag_interrupcao ==  1)
            {//tem dados para ler
             LED_STATUS =0;
             escreve(rs232[0]);
             escreve(rs232[1]);
             escreve(rs232[2]);//retorna o valor lido
             escreve(rs232[3]);
             escreve(rs232[4]);
             //////processa dados/////
             if((rs232[2]>0X2F)&&(rs232[2]<0X40)&&(rs232[3]>0X2F)&&(rs232[3]<0X40))//indica que foi recebido um numero valido
             {//trata pacote                 
                 if((rs232[1]>0X30)&&(rs232[1]<0X38))//indica que foi recebido um modo valido(entre 1 e 7)
                 {//verifica se o modo de programação tem caracter valido
                    int valor = (((rs232[2] - 0X30)*10)+(rs232[3] - 0X30));//converte pra int
                    int posicao = (rs232[1] - 0X30);//converte pra int 
                    eeprom_write(posicao,valor);//altera o valor na de memoria;                    
                    imprime("\n\r Alterado:\n\r");
                    for(int i=1;i<8;i++)
                    {//imprime valores da memoria
                     int_to_char(eeprom_read(i));
                     escreve('|');
                    }
                    eeprom_write(0X00,'G');//indica que houve gravação na memoria;
                 }
             }
             pos_serial = 0; 
             flag_interrupcao = 0;//libera pra pegar outro pacote 
             LED_STATUS =1;
            }  
            
        }
        else
        {   //a sequencia foi pensada de forma que cada valvula tenha pelo menos 1 ciclo de descanso a cada 2 ciclos(execto se o tempo for zero para uma das posicoes)          
              switch(pos_prog)
              {
                  case 0:
                  { //fim de programa
                     RL_1=0;
                     RL_2=0;
                     RL_3=0;
                     delay_ms(500);
                     RL_GERAL=0;
                  }break;
                  case 1:
                  {
                     if(tempo[0]>0)
                     {
                        RL_1=1;
                        RL_2=0;
                        RL_3=0;
                     } 
                     pos_prog=3;//vai para proxima posição
                     delay_seg(tempo[0]*10);
                  }break;
                  case 2:
                  {
                     if(tempo[1]>0)
                     {
                        RL_1=0;
                        RL_2=1;
                        RL_3=0;
                     } 
                     pos_prog=7;//vai para proxima posição
                     delay_seg(tempo[1]*10);
                  }break;
                  case 3:
                  {
                     if(tempo[2]>0)
                     {
                        RL_1=1;
                        RL_2=1;
                        RL_3=0;
                     } 
                     pos_prog=4;//vai para proxima posição
                     delay_seg(tempo[2]*10);
                  }break;
                  case 4:
                  {
                     if(tempo[3]>0)
                     {
                        RL_1=0;
                        RL_2=0;
                        RL_3=1;
                     } 
                     pos_prog=5;//vai para proxima posição
                     delay_seg(tempo[3]*10);
                  }break;
                  case 5:
                  {
                     if(tempo[4]>0)
                     {
                        RL_1=1;
                        RL_2=0;
                        RL_3=1;
                     } 
                     pos_prog=2;//vai para proxima posição
                     delay_seg(tempo[4]*10);
                  }break;
                  case 6:
                  {//como o programa inicia aqui é preciso ter uma atenção diferente neste ponto
                     RL_GERAL=1; //rele principal, mantem a placa energizada 
                     if(tempo[5]>0)
                     {
                        RL_1=0;
                        RL_2=1;
                        RL_3=1;
                     }
                     else
                     {
                        RL_1=0;
                        RL_2=0;
                        RL_3=0;
                     } 
                     pos_prog=1;//vai para proxima posição
                     delay_seg(tempo[5]*10);
                  }break;                  
                  case 7:
                  {
                     if(tempo[6]>0)
                     {
                        RL_1=1;
                        RL_2=1;
                        RL_3=1;
                     } 
                     pos_prog=0;//vai para proxima posição
                     delay_seg(tempo[6]*10);
                  }break;
              }
        }             
     }//loop infinito 

}

O esquemático de simulação no proteus ficou:


O vídeo de demonstração :



Até a próxima pessoal!