quarta-feira, 1 de setembro de 2021

Voltímetro pela porta serial com 8051F382 - Simplicity Studio

 Olá, pessoal !

    Passando pra deixar mas um exemplo de código, aqui demonstrando como usar o modulo analógico digital do microcontrolador  C8051F382, e imprimir isto na porta serial RS232.

O código fonte:   

// Target:         C8051F382
// Tool chain:     Simplicity Studio / Keil C51 9.51
// Command Line:   None
//
//
//     Release 1.0
//     12_05_2021
//     autor:Aguivone M. Fógia
//     exemplo de como usar o ADC( e comunicação serial
//     usando o port 2.7
//

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------

#include <SI_C8051F380_Register_Enums.h>
#include <stdio.h>

//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------

#define SYSCLK        12000000         // SYSCLK frequency in Hz
#define BAUDRATE      115200           // Baud rate of UART in bps
#define INT_DEC       256              // Integrate and decimate ratio

#define TIMER0_RELOAD_HIGH  0          // Timer0 High register
#define TIMER0_RELOAD_LOW 255          // Timer0 Low register

//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------
SI_INTERRUPT_PROTO(ADC0_ISR, ADC0EOC_IRQn);
SI_INTERRUPT_PROTO(Timer2_ISR, TIMER2_IRQn);

void Oscillator_Init (void);
void Port_Init (void);
void Timer2_Init(void);
void ADC0_Init(void);
void UART0_Init (void);
unsigned char porta4 = 0XFF;


void delay_ms(int16_t ms);

//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------

int32_t RESULT;            // ADC0 decimated value, one for each
                                       // analog input

//-----------------------------------------------------------------------------
// SiLabs_Startup() Routine
// ----------------------------------------------------------------------------

void SiLabs_Startup (void)
{
   PCA0MD &= ~0x40;                    // WDTE = 0 (clear watchdog timer
}

//-----------------------------------------------------------------------------
// MAIN Routine
//-----------------------------------------------------------------------------
void main (void)
{
   uint32_t measurement;
   bool   flag;
                                       // enable)

   Oscillator_Init ();                 // Initialize system clock to
                                       // 12MHz
   Port_Init ();                       // Initialize crossbar and GPIO
   Timer2_Init();                      // Init Timer2 to generate
                                       // overflows to trigger ADC
   UART0_Init();                       // Initialize UART0 for printf's
   ADC0_Init();                        // Initialize ADC0

   IE_EA = 1;                             // Enable global interrupts
   while (1)
   {
      IE_EA = 0;                          // Disable interrupts
      printf("\f");
         // The 10-bit ADC value is averaged across INT_DEC measurements.
         // The result is then stored in RESULT, and is right-justified
         // The measured voltage applied to the port pins is then:
         //
         //                           Vref (mV)
         //   measurement (mV) =   --------------- * Result (bits)
         //                        (2^10)-1 (bits)

      measurement =  RESULT * 4.887;
      printf("Tensao lida: %4ld mV\n",measurement);
      IE_EA = 1;                          // Re-enable interrupts
      delay_ms(10);
      if(flag==1)
        {
         P4 = 0;
         flag = 0;
        }
      else
        {
          P4 = 255;
          flag = 1;
        }
   }
}

//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// SYSCLK_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This routine initializes the system clock to use the internal 12MHz
// oscillator as its clock source.  Also enables missing clock detector reset.
//
void Oscillator_Init (void)
{
   OSCICN = 0x83;                      // Configure internal oscillator for
                                       // its highest frequency
   RSTSRC = 0x04;                      // Enable missing clock detector
}

//-----------------------------------------------------------------------------
// Port_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure the Crossbar and GPIO ports.
//
// P0.4 - UART TX (push-pull)
// P0.5 - UART RX
// P2.7 - analog input (ADC0)
//
//-----------------------------------------------------------------------------
void Port_Init (void)
{
   P2SKIP = 0x80;                      // pula a entrada analogica

   XBR0   = 0x01;                     // CP1AE,CP1E,CP0AE,CP0E,SYSCKE,SMB0E,SPI0E,(UART0E)
   XBR2   = 0x00;                     // RESRV,RSRV,RESRV,RESRV,RESRV,RESRV,SMB1E, UART1E
   XBR1   = 0xC2;                     // WEAKPUD,(XBARE),T1E,T0E,ECIE,(PCA0ME):000:Nenhum,001:CEX0,010:CEX0eCEX1,...

   VDM0CN = 0x80;                     // Colocado porque senão da problema na gravacao da flash(já estava isso)
   RSTSRC = 0x02;                     // ativa reset de falha de oscilação.

   P0MDOUT |= 0x10;                    // Enable TX0 as a push-pull output
   P1MDOUT = 0x00;                     // enable Switches as open-drain
   P2MDOUT = 0x00;                     // enable Switches as open-drain
   P3MDOUT = 0x00;                     // enable Switches as open-drain
   P4MDOUT = 0x04;                     // enable Switches as open-drain

   //configura portas
   P0MDIN  = 0xFF;                     //  are digital
   P1MDIN  = 0xFF;                     //  are digital
   P2MDIN  = 0x7F;                      //seta pino P2.7 como entrada analogica
   P3MDIN  = 0xFF;                     //  are digital
   P4MDIN  = 0xFF;                     //  are digital


}

//-----------------------------------------------------------------------------
// Timer2_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure Timer2 to 16-bit auto-reload and generate an interrupt at 10 us
// intervals.  Timer2 overflows automatically triggers ADC0 conversion.
//
//-----------------------------------------------------------------------------
void Timer2_Init (void)
{
   TMR2CN = 0x00;                      // Stop Timer2; Clear TF2;
                                       // use SYSCLK as timebase, 16-bit
                                       // auto-reload
   CKCON |= 0x10;                      // Select SYSCLK for timer 2 source
   TMR2RL = 65535 - (SYSCLK / 10000);  // Init reload value for 10 us
   TMR2 = 0xffff;                      // Set to reload immediately
   IE_ET2 = 1;                            // Enable Timer2 interrupts
   TMR2CN_TR2 = 1;                            // Start Timer2
}

//-----------------------------------------------------------------------------
// ADC0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configures ADC0 to make single-ended analog measurements on Port 2 according
// to the values of <ANALOG_INPUTS> and <PIN_TABLE>.
//
//-----------------------------------------------------------------------------
void ADC0_Init (void)
{
   ADC0CN = 0x02;                      // ADC0 disabled, normal tracking,
                                       // conversion triggered on TMR2 overflow

   REF0CN = 0x03;                      // Enable internal VREF
   AMX0P = 0X1A;                       // ADC0 input = P2.7 (para microcontrolador de 48 pinos)
   AMX0N = 0x1F;                       // ADC0 negative input = GND
                                       // i.e., single ended mode

   ADC0CF = ((SYSCLK/3000000)-1)<<3;   // Set SAR clock to 3MHz
   ADC0CF |= 0x00;                     // Right-justify results
   EIE1 |= 0x08;                       // Enable ADC0 EOC interrupt
   ADC0CN_ADEN = 1;                          // Enable ADC0
}

//-----------------------------------------------------------------------------
// UART0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure the UART0 using Timer1, for <BAUDRATE> and 8-N-1.
//
//-----------------------------------------------------------------------------
void UART0_Init (void)
{
   SCON0 = 0x10;                       // SCON0: 8-bit variable bit rate
                                       //        level of STOP bit is ignored
                                       //        RX enabled
                                       //        ninth bits are zeros
                                       //        clear SCON0_RI and SCON0_TI bits
   // seta automaticamente o valor a ser colocado no TH1
   if (SYSCLK/BAUDRATE/2/256 < 1) {
      TH1 = -(SYSCLK/BAUDRATE/2);
      CKCON |=  0x08;                  // T1M = 1; SCA1:0 = xx
   } else if (SYSCLK/BAUDRATE/2/256 < 4) {
      TH1 = -(SYSCLK/BAUDRATE/2/4);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 01
      CKCON |=  0x01;
   } else if (SYSCLK/BAUDRATE/2/256 < 12) {
      TH1 = -(SYSCLK/BAUDRATE/2/12);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 00
   } else if (SYSCLK/BAUDRATE/2/256 < 48) {
      TH1 = -(SYSCLK/BAUDRATE/2/48);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 10
      CKCON |=  0x02;
   } else {
      while (1);                       // Error.  Unsupported baud rate
   }

   TL1 = TH1;                          // Init Timer1
   TMOD &= ~0xF0;                      // TMOD: timer 1 in 8-bit autoreload
   TMOD |=  0x20;
   TCON_TR1 = 1;                            // START Timer1
   SCON0_TI = 1;                            // Indicate TX0 ready
}

//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Timer2_ISR
//-----------------------------------------------------------------------------
//usa overflow para verificar o ADC
//
//incrementa o proximo ADC a ser lido quando for ler 2 ou mais ADCS
SI_INTERRUPT(Timer2_ISR, TIMER2_IRQn)
{
   TMR2CN_TF2H = 0;                           //limpa interrupção do timer 2
   //para 1 adc somente pode até desligar esse timer
}

//-----------------------------------------------------------------------------
// ADC0_ISR
//-----------------------------------------------------------------------------
//
// This ISR averages <INT_DEC> samples for each analog MUX input then prints
// the results to the terminal.  The ISR is called after each ADC conversion,
// which is triggered by Timer2.
//
//-----------------------------------------------------------------------------
SI_INTERRUPT(ADC0_ISR, ADC0EOC_IRQn)
{

   ADC0CN_ADINT = 0;                         // Clear ADC conversion complete
   RESULT = ADC0;    // Read the ADC value and add it to the
                                       // running total
}

//-----------------------------------------------------------------------------
// Support Subroutines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Timer0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   :
//   1) int16_t ms - number of milliseconds to wait
//                        range is positive range of an int: 0 to 32767
//
// This function configures the Timer0 as a 16-bit timer, interrupt enabled.
// Using the internal osc. at 12MHz with a prescaler of 1:8 and reloading the
// T0 registers with TIMER0_RELOAD_HIGH/LOW, it will wait for <ms>
// milliseconds.
// Note: The Timer0 uses a 1:12 prescaler
//-----------------------------------------------------------------------------
void delay_ms(int16_t ms)
{
   TH0 = TIMER0_RELOAD_HIGH;           // Init Timer0 High register
   TL0 = TIMER0_RELOAD_LOW ;           // Init Timer0 Low register
   TMOD |= 0x01;                       // Timer0 in 16-bit mode
   CKCON &= 0xFC;                      // Timer0 uses a 1:12 prescaler
   TCON_TR0  = 1;                           // Timer0 ON

   while(ms)
   {
      TCON_TF0 = 0;                         // Clear flag to initialize
      while(!TCON_TF0);                     // Wait until timer overflows
      ms--;                            // Decrement ms
   }

   TCON_TR0 = 0;                            // Timer0 OFF
}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------

formatado  por : http://hilite.me/  01/09/2021.


Acessando a porta serial RS232 com o 8051F382 - Simplicity studio

    Olá, pessoal!

    Neste exemplo tudo enviado ao microcontrolador é repetido de volta pela porta serial, nos exemplos não utilizo cristal externo pois o chip já possui um interno.

O código fonte :

// Target:         C8051F382
// Tool chain:     Simplicity Studio / Keil C51 9.51
// Autor: Aguivone M. Fogia
// Data: 01/09/2021
//

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------

#include <SI_C8051F380_Register_Enums.h>
#include <stdio.h>

//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------

#define SYSCLK      12000000           // SYSCLK frequency in Hz
#define BAUDRATE        9600           // Baud rate of UART in bps

//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------
SI_INTERRUPT_PROTO(UART0_Interrupt, UART0_IRQn);

void SYSCLK_Init (void);
void UART0_Init (void);
void PORT_Init (void);
void  escreve_frase(uint8_t dados[] ,uint8_t tam);
void  escreve_recebido(uint8_t tam);

//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------

#define UART_BUFFERSIZE 10
uint8_t UART_Buffer[UART_BUFFERSIZE];
uint8_t UART_Buffer_tx[UART_BUFFERSIZE];//para transmissão
uint8_t UART_Buffer_Size = 0;
uint8_t UART_Input_First = 0;
uint8_t UART_Output_First = 0;
uint8_t pos_buffer=0;
uint8_t tam_buffer_tx=0;
uint8_t pos_buffer_tx=0;
static char RX_232;
static char TX_232;
bool tem_recp = 0;
bool rec_232 = 0;
bool trans_232 = 0;


//entradas

SI_SBIT(SW1, SFR_P0, 6);                  // SW1 ='0' means switch pressed
SI_SBIT(SW2, SFR_P0, 7);                  // SW2 ='0' means switch pressed

unsigned char porta4 = 0XFF;



//-----------------------------------------------------------------------------
// SiLabs_Startup() Routine
// ----------------------------------------------------------------------------
// This function is called immediately after reset, before the initialization
// code is run in SILABS_STARTUP.A51 (which runs before main() ). This is a
// useful place to disable the watchdog timer, which is enable by default
// and may trigger before main() in some instances.
//-----------------------------------------------------------------------------
void SiLabs_Startup (void)
{
   PCA0MD &= ~0x40;                    // WDTE = 0 (clear watchdog timer
}


//-----------------------------------------------------------------------------
// MAIN Routine
//-----------------------------------------------------------------------------

void main (void)
{

                                       // enable)
   PORT_Init();                        // Initialize Port I/O
   SYSCLK_Init ();                     // Initialize Oscillator
   UART0_Init();
   IE_EA = 1;
   escreve_frase("Iniciando!",10);
   while(1)
   {
      // If the complete word has been entered via the terminal followed by
      // carriage return or newline
       if(tem_recp)//recebeu dados
          {
             escreve_recebido(pos_buffer);
             tem_recp = 0;//sinaliza que já leu
          }
  }
}


void  escreve_frase(char dados[] ,uint8_t tam)
{
    tam_buffer_tx = 0;
    while(tam > tam_buffer_tx)
      {
         UART_Buffer_tx[tam_buffer_tx] = dados[tam_buffer_tx];//transfere dados
         tam_buffer_tx++;
      }
    trans_232 = 1;//sinaliza que deve transmitir
    SCON0_TI = 1; //para dar inicio a transmissão
}
void  escreve_recebido(uint8_t tam)
{
    tam_buffer_tx = 0;
    while(tam > tam_buffer_tx)
      {
         UART_Buffer_tx[tam_buffer_tx] = UART_Buffer[tam_buffer_tx];//transfere dados
         tam_buffer_tx++;
      }
    trans_232 = 1;//sinaliza que deve transmitir
    SCON0_TI = 1; //para dar inicio a transmissão
}

//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure the Crossbar and GPIO ports.
//
// P0.4   digital   push-pull    UART TX
// P0.5   digital   open-drain   UART RX
//
//-----------------------------------------------------------------------------

void PORT_Init (void)
{
   P0MDOUT |= 0x10;                    // Enable UTX as push-pull output
   XBR0     = 0x01;                    // Enable UART on P0.4(TX) and P0.5(RX)
   XBR1     = 0x40;                    // Enable crossbar and weak pull-ups
}

//-----------------------------------------------------------------------------
// SYSCLK_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This routine initializes the system clock to use the internal oscillator
// at its maximum frequency.
// Also enables the Missing Clock Detector.
//-----------------------------------------------------------------------------

void SYSCLK_Init (void)
{
   OSCICN |= 0x03;                     // Configure internal oscillator for
                                       // its maximum frequency
   RSTSRC  = 0x04;                     // Enable missing clock detector
}

//-----------------------------------------------------------------------------
// UART0_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// Configure the UART0 using Timer1, for <BAUDRATE> and 8-N-1.
//-----------------------------------------------------------------------------

void UART0_Init (void)
{
   SCON0 = 0x10;                       // SCON0: 8-bit variable bit rate
                                       //        level of STOP bit is ignored
                                       //        RX enabled
                                       //        ninth bits are zeros
                                       //        clear SCON0_RI and SCON0_TI bits
   if (SYSCLK/BAUDRATE/2/256 < 1) {
      TH1 = -(SYSCLK/BAUDRATE/2);
      CKCON &= ~0x0B;                  // T1M = 1; SCA1:0 = xx
      CKCON |=  0x08;
   } else if (SYSCLK/BAUDRATE/2/256 < 4) {
      TH1 = -(SYSCLK/BAUDRATE/2/4);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 01
      CKCON |=  0x01;
   } else if (SYSCLK/BAUDRATE/2/256 < 12) {
      TH1 = -(SYSCLK/BAUDRATE/2/12);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 00
   } else {
      TH1 = -(SYSCLK/BAUDRATE/2/48);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 10
      CKCON |=  0x02;
   }

   TL1 = TH1;                          // init Timer1
   TMOD &= ~0xf0;                      // TMOD: timer 1 in 8-bit autoreload
   TMOD |=  0x20;
   TCON_TR1 = 1;                            // START Timer1
   IP |= 0x10;                         // Make UART high priority
   IE_ES0 = 1;                            // Enable UART0 interrupts

}

//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// UART0_Interrupt
//-----------------------------------------------------------------------------
//
// This routine is invoked whenever a character is entered or displayed on the
// Hyperterminal.
//
//-----------------------------------------------------------------------------

SI_INTERRUPT(UART0_Interrupt, UART0_IRQn)
{
   if (SCON0_RI == 1)
   {
       SCON0_RI = 0;
       RX_232 = SBUF0;
       if(pos_buffer > UART_BUFFERSIZE)  // tem um bug no compilador ou chip que faz reseta
                {//erro descarta
                  rec_232=0; //não recebendo
                  pos_buffer=0;//limpa buffer
                  tem_recp = 0;//sinaliza fim de pacote
                }
       if(rec_232 && RX_232 == '*')
                {//esta recebendo e chegou delimitador de fim
                  rec_232=0; //não recebendo
                  tem_recp = 1;//sinaliza fim de pacote
                }
       if(rec_232)
         {//esta recebendo
           UART_Buffer[pos_buffer] = RX_232;
           pos_buffer++;
         }
       if(RX_232 == '#')
         {
           pos_buffer=0;//sinaliza recepção
           rec_232=1; //recebendo
         }
   }
   if (SCON0_TI)                 // Check if transmit flag is set
     {
        SCON0_TI = 0;                          // Clear interrupt flag
        if(trans_232 == 1)
          {

           if (pos_buffer_tx <  tam_buffer_tx)
            {
             // Store a character in the variable byte
             TX_232 = UART_Buffer_tx[pos_buffer_tx];
             pos_buffer_tx++;             // Decrease array size
             SBUF0 = TX_232;                   // Transmit to Hyperterminal

            }
            else
              {
                trans_232 = 0; //fim de transmissao
                pos_buffer_tx =0;
              }
        }
     }

}

//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------

Formatado por : http://hilite.me/  01/09/2021



sexta-feira, 27 de agosto de 2021

Acionando os pinos de I/O do C8051F382 em C

Olá pessoal!        
        Marcando meu retorno aos exemplos de chips e tecnologias utilizadas na área de pesquisa e desenvolvimento vou colocar alguns exemplos de como utilizar os microcontroladores da Silicon Labs, em especifico os da serie C8051F38XX que apresentam um alto desempenho porém tem como empecilho a falta de uma memória rom interna como os PIC´s e AVR´s, outro ponto negativo é o gravador desses microcontroladores que são mais caros e mais difíceis de encontrar.
        O software de programação utilizado será o Simplicity Studio e o toochain  "Keil C51 9.51", quanto a este software gostei muito pois apresenta poucos bugs e as funções em C são bem tranquilas de se utilizar.
como programa inicial vou postar um exemplo de como acionar os pinos deste microcontrolador, uma observação é o PORT4 que apresenta um comportamento diferenciado em relação ao outros pinos.
        Um complicador em relação a outros modelos de microcontroladores é o "Digital
Crossbar" que deve ser setado corretamente pois conforme se configura os pinos com funções especiais ele acaba por mudar também a função dos outros pinos com funções especiais, ou seja se vc configura um pino como interrupção ele poderá te impedir de usar um outro pino que tem uma outra função, te obrigando a escolher outro pino, porem uma vantagem é poder "chavear funções de hardware para mais pinos. A figura a seguir mostra como é essa arquitetura(retirado do datasheet do fabricante).Esse recurso pode ser desabilitado por meio dos registradores PXSKIP (onde em X é colocado o PORT em questão).
        
       Segue abaixo um exemplo de código que usa o PORT0 e o PORT4, para ler 2 botões e acionar leds através destes pinos.

O Código fonte :

// Target:         C8051F382
// Tool chain:     Simplicity Studio / Keil C51 9.51
// Autor: Aguivone Moretti Fogia
// Data: 27/08/2021
//
//exemplo de como usar botoes e leds
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <SI_C8051F380_Register_Enums.h>

//-----------------------------------------------------------------------------
// Global Constants
//-----------------------------------------------------------------------------

#define SYSCLK       12000000          // Internal oscillator frequency in Hz
//entradas porta 0
SI_SBIT(BOT1, SFR_P0, 6);                  // BOT1 ='0' botão pressionado
SI_SBIT(BOT2, SFR_P0, 7);                  // BOT2 ='0' botao pressionado

unsigned char porta4 = 0XFF;

//Saidas porta 0
SI_SBIT(LED1, SFR_P0, 0);                      // LED='0' Ligado   //pois ligado ao VCC
SI_SBIT(LED2, SFR_P0, 1);                      // LED='1' Desligado
// A porta 4 tem um funcionamento diferente em relação as outras
//ledes ligado ao port 4
#define LED3  0x7F
#define LED4  0xBF


//-----------------------------------------------------------------------------
// Function Prototypes
//-----------------------------------------------------------------------------
SI_INTERRUPT_PROTO(PCA0_ISR, PCA0_IRQn);

void OSCILLATOR_Init (void);
void PORT_Init (void);
void Liga_Port4(const char pino);
void Desliga_Port4(const char pino);
bool flag_teste;
//-----------------------------------------------------------------------------
// SiLabs_Startup() Routine
// ----------------------------------------------------------------------------
// This function is called immediately after reset, before the initialization
// code is run in SILABS_STARTUP.A51 (which runs before main() ). This is a
// useful place to disable the watchdog timer, which is enable by default
// and may trigger before main() in some instances.
//-----------------------------------------------------------------------------
void SiLabs_Startup (void)
{
   PCA0MD = 0x00;                      // Disable watchdog timer
}

//-----------------------------------------------------------------------------
// main() Routine
//-----------------------------------------------------------------------------

void main (void)
{
   uint16_t LOOP;
   uint16_t LOOP_max =65000;
   PORT_Init ();                       // Initialize crossbar and GPIO
   OSCILLATOR_Init ();                 // Initialize oscillator
   LOOP =0;
   IE_EA = 0;
   flag_teste = 0;
   while (1)
   {
     LOOP++;
         if(LOOP>LOOP_max)
         {
              Liga_Port4(LED3);
              LED1=0; //liga led
              LOOP=0;
         }
         if(LOOP ==(LOOP_max/2))
             {
              Desliga_Port4(LED3);
              LED1=1; //desliga led
             }
         if(BOT1)
           {
             Liga_Port4(LED4);
           }
           else
             {
               Desliga_Port4(LED4);
             }
        if(BOT2)
           {
              LED2=0; //liga led
           }
           else
             {
               LED2=1;//desliga led
             }
   }
}

//-----------------------------------------------------------------------------
// tratamento especial para port 4
//-----------------------------------------------------------------------------
void Desliga_Port4(const char pino)
{//desliga em nivel alto
   porta4 = porta4 | (~pino);
   P4 = porta4;
}
void Liga_Port4(const char pino)
{
  porta4 = porta4 & pino;
  P4 = porta4;
}
//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// OSCILLATOR_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function initializes the system clock to use the internal oscillator
// at 12 MHz.
//
//-----------------------------------------------------------------------------
void OSCILLATOR_Init (void)
{
   OSCICN = 0x83;                      // Set internal oscillator to run  (desabilitou osc interno)
                                       // at its maximum frequency(pois setou os 2 ultimos bit)
   CLKSEL = 0x00;                      // Select the internal osc. as (SYSCLK/4)
                                       // the SYSCLK source
}

//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters   : None
//
// This function configures the crossbar and GPIO ports.
//
// P0.0   digital   open-drain     PCA0 CEX0
// P0.1   digital   push-pull
//
//-----------------------------------------------------------------------------
void PORT_Init (void)
{
      OSCICN  = 0X81;                     // IHFOsc Enable, OpcDiv:3MHz
      CLKMUL  = 0XC0;                     // talvez nem precise neste microcontrolador -> habilita  e inicializa pll
      CLKSEL  = 0X03;                     // SYSCLK derived from the Internal High-Frequency Oscillator.
      PFE0CN  = 0X20;                     //(para High Speed) Prefetch Engine Control,otimiza tempo de execução lendo 2 bytes de execução por vez
      /* FLSCL.FOSE(7) = 0 for < 10MHz and 1 for SYSCLK > 10MHz
      FLSCL.FLRT(4) = 0 for < 25MHz and 1 for SYSCLK > 25MHz */
      FLSCL   = 0x90;
      CKCON   = 0x30;
      SPI0CN  = 0x02;                     //Select Mode 3-Wire pra liberar pino NSS

      P0MDIN  = 0xFF;                     //  are digital
      P1MDIN  = 0xFF;                     //  are digital
      P2MDIN  = 0xFF;                     //  are digital
      P3MDIN  = 0xFF;                     //  are digital
      P4MDIN  = 0xFF;                     //  are digital

      P0MDOUT = 0x10;                     // enable Switches as open-drain
      P1MDOUT = 0x00;                     // enable Switches as open-drain
      P2MDOUT = 0x00;                     // enable Switches as open-drain
      P3MDOUT = 0x00;                     // enable Switches as open-drain
      P4MDOUT = 0x04;                     // enable Switches as open-drain

      P0SKIP = 0xCF;                      // TXeRX precisa do Skip pra funcionar
      P1SKIP = 0xFF;
      P2SKIP = 0xFF;
      P3SKIP = 0xCF;

      XBR0   = 0x01;                     // CP1AE,CP1E,CP0AE,CP0E,SYSCKE,SMB0E,SPI0E,(UART0E)
      XBR2   = 0x00;                     // RESRV,RSRV,RESRV,RESRV,RESRV,RESRV,SMB1E, UART1E
      XBR1   = 0xC2;                     // WEAKPUD,(XBARE),T1E,T0E,ECIE,(PCA0ME):000:Nenhum,001:CEX0,010:CEX0eCEX1,...
      // VDM0CN -> monitor de tensão de reset
      VDM0CN = 0x80;                    //Colocado porque senão da problema na gravacao da flash(já estava isso)

      P4 = 0xD8        ;  //portas de p4 desligadas
     // P4 = 0        ;  //portas de p4 ligadas
}

Formatado por : http://hilite.me/  27/08/2021
Formatado por : http://hilite.me/ 27/08/2021

domingo, 8 de dezembro de 2019

Medidor do fator de potência com o PIC 18F4550 - Wattímetro e frequêncímetro.

Olá pessoal!

    Neste exemplo os conceitos abordados serão:
  • Uso do conversor analógico digital
  • Implementação da comunicação em 4 bits utilizando LCD(funciona para 16X2 ou 4X20).
  • Leitura de tensão AC e DC .
  • Medir o fator de potência.
     A intenção é criar um voltímetro,amperímetro,wattímetro,frequencímetro e medidor do fato de potência que por consequência deve ler tensão e corrente DC E AC.O programa se torna extenso mas se for entendido por partes ficará mais fácil o que cada bloco de código faz.
      Para o amperímetro é usado um resistor de 1 R ligado a uma carga(neste caso foi usado uma associação indutor e resistor para ter defasagem das senoides, para o voltímetro foi usado um divisor de tensão que mede até 30V porém não fica tão preciso devido ao numero de bits do conversor AD interno(10 bits).Para o Wattímetro é usado apenas os dados de tensão e corrente para fornecer a potencia instantânea, já para o caso de tensão DC a potencia media é feita ao longo do tempo(pois pode haver variações de tensão e corrente).O circuito montado para testes e simulação é mostrado na figura a seguir:
    Figura 1 - Esquemático geral(elaboração própria)

        Para medir o fator de potencia poderia ter sido feito internamente, com o microcontrolador, porém montar um simples circuito detector de zero com uma porta XOR é uma solução interessante, pois então só é preciso pegar a forma de onda que é proporcional a defasagem do sinal na saida da porta XOR. Porém durante os testes foi observado que esta porta XOR gera dois pulsos, e que o segundo é do momento que as ondas descem, o que gera imprecisão devido ao tempo de decaimento da tensão na saída da porta, na figura 2,em amarelo sinal de saída de XOR, em azul a tensão em vermelho a corrente do circuito.Com este circuito interno foi utilizado apenas uma porta do microcontrolador para medir a largura do sinal(período) e do pulso(fator de potência), por meio das interrupções do ccp1.

                                   Figura 2 - Sinais lidos no circuito de detecção de zero(elaboração própria)
   
      Outra observação é que para simular no proteus, ora se usa a fonte DC, ora se usa a fonte AC em simulações separadas, para alternar entre o modo AC e DC basta apertar o botão de seleção,na figura a seguir é mostrado as simulações  AC e DC.No modo AC a tensão e corrente medida são as de pico, para converter para RMS(que é a mostrada nos multímetros), basta dividir por raiz de 2, conforme figura AC ,figura 4.
          Figura 3 - Modo DC(elaboração própria)

Figura 4 - Modo AC (elaboração própria)

O circuito funcionando é visto na figura 5, veja que ele tem 2 botões sendo um para reset do microcontrolador e outro para alternar os modo de funcionamento. 
                                         Figura 5 - foto do protótipo implementado(elaboração própria)

O código fonte:

/* 
 * Author: Aguivone Moretti Fógia
 * descrição: Multimetro que mede fator de potencia
 * freq.de clock: 20MHZ
 * data:08/12/2019 
 * testado fisicamente.
 */
#include "MCC18_18F4550.h"
#include "tempo_20mhz.h"
#include "LCD_4bits.h"
unsigned char Buffer_lcd[16]; // variavel usada pelo LCD
//variaveia globais para cpp1////////////////////////////////
unsigned int tempo_pulso=0;
unsigned int periodo=0;
unsigned char estado_amostras=0;
unsigned int tempo_ccp1=0;
//variaveis globais para ADC
unsigned char modo_tensao=0;//padrão CC
unsigned char descarta_FP=0;
float PM[5];//pega os ultimos 5 valores para potencia media DC
//defines usados
#define sin1_FP PORTCbits.RC4
#define sin2_FP PORTCbits.RC5
#define BOT_SEL PORTEbits.RE2
#define LED_STATUS PORTEbits.RE1
#define LED_MODO   PORTEbits.RE0 //sinaliza modo AC e pisca a cada pulso de clock
//equações usadas
#define EQ_TENSAO (float)((lido_adc*35.2)/1023) //para divisor de 10K - 1K - Máx: 33V
#define EQ_CORRENTE (float)((lido_adc*5.0)/1023)
#define EQ_FP (float)((90.0*tempo_pulso)/periodo) //fator de potencia
#define EQ_FREQ  (float)((1/(0.0000002*periodo*2.0))/8)//para 20Mhz - divide por 8 pois o T1CON está assim//frequencia
unsigned char sinal_FP= ' ';
////prototipos de funções ///////////////////////
void Inic_Regs (void);
void Read_ADC_DC(void);
void Read_ADC_CA(void);
void high_isr (void);
void Tela_inicial(unsigned char modo);
void verifica_botao(void);
//void low_isr (void);
//vetor de interrupção de alta prioridade
#pragma code high_vector=0x08
void interrupt_at_high_vector(void)
{
    _asm GOTO high_isr _endasm        //desvia programa para rotina de tratamento da interrupção
}
#pragma code 
//*****************************
//Rotina de tratamento de interrupção (ISR)
#pragma interrupt high_isr 
void high_isr (void)
{
   if(PIR1bits.CCP1IF)
    {
        if(estado_amostras==0)
        {//prepara contagem
           TMR1L =0;//Zera timer 1
           TMR1H =0;//Zera timer 1           
           if((sin2_FP)&&(sin1_FP))//se os dois estiver em um indica que está na descida do sinal e isso gera imprecisão
           {//descarta
               estado_amostras=0;               
           }
           else
           {//sinal subindo               
               if(sin1_FP)
                   {sinal_FP= '-';}
               else{sinal_FP= '+';}
               CCP1CON = 0X04;//captura a cada borda de descida
               estado_amostras=1;
           }            
           
        }
        else
        {
            if(estado_amostras==1)
            {//borda de descida - pega valor do pulso
                tempo_pulso =((((unsigned int)CCPR1H)<<8)|CCPR1L);
                estado_amostras = 2;
                CCP1CON = 0X05;//captura a cada borda de subida 
                _delay_ms(1);//
                if((sin2_FP==0)||(sin1_FP==0))//se os dois estiver em zero indica que está na descida do sinal e isso gera imprecisão
                           {//sinal invalido  
                             estado_amostras = 0;
                           }
            }
            else
            {//fim de coleta
                if(estado_amostras == 2)
                {   
                 periodo =((((unsigned int)CCPR1H)<<8)|CCPR1L);
                 estado_amostras=3;//pra sinalizar fim de amostragem  
                 PIE1bits.CCP1IE = 0;//não precisa mais das interrupçõe do CCP1
                 PORTDbits.RD3 = 0;
                }
            }
        }        
        PIR1bits.CCP1IF = 0;//limpa flag   
    }
    
}
/////rotina principal/////////////////////////////
void main() 
{
  Inic_Regs();
  inicializa_LCD();
  Tela_inicial('D');
  modo_tensao=0;
  while(1)
   { 
     verifica_botao();
     if(modo_tensao)//modo AC
     {
        Read_ADC_CA();
        tempo_ccp1=0; 
        estado_amostras=0;//libera nova coleta     
        PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
        while(tempo_ccp1<100)//demora em media 100ms
         { 
           if(estado_amostras==3)
               {//tem coleta mostra o valor 
                   float temp;
                   unsigned int parte_inteira;                //declaração de variável local
                   unsigned int parte_decimal;                //declaração de variável local 
                   if(descarta_FP == 0)
                   {
                    temp=EQ_FP;//pois divide por (2*semicilo)
                   }
                   else
                   {
                    temp=0;//tensão ou corrente sem leitura   
                   }
                    parte_inteira = (int)temp;            //pega parte inteira do valor
                    parte_decimal =(unsigned int)((temp - parte_inteira) * 10);    //resgata parte fracionária do valor ,2 casa decimais
                    sprintf(Buffer_lcd,"%c%u.%u",sinal_FP,parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
                    impri_lcd(4,'3');
                    if(descarta_FP == 0)
                   {
                     temp=(EQ_FREQ);//para 20Mhz - divide por 8 pois o T1CON está assim
                     
                   }
                   else
                   {
                    temp=(EQ_FREQ*2);//tensão ou corrente sem leitura -> onda fica igual a corrente ou a tensão  
                   }                   
                   parte_inteira = (int)temp;            //pega parte inteira do valor
                   parte_decimal =(unsigned int)((temp - parte_inteira) * 10);    //resgata parte fracionária do valor ,2 casa decimais
                   sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
                   impri_lcd(3,'4'); //frequencia
                   _delay_ms(250);//lê a cada 0.5s
                   PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
                   _delay_us(10);//espera tempo para CCP1 se preparar
                   estado_amostras=0;//libera nova coleta
               }
               if(estado_amostras==2)
               {
                    if((sin2_FP==0)||(sin1_FP==0))//se os dois estiver em um indica que está na descida do sinal
                        //(do segundo sinal) e isso gera imprecisão 
                           {//sinal invalido  
                             estado_amostras = 0;
                           }

               }
           _delay_ms(1);
           tempo_ccp1++;
       }//fim do laçõ ccp1
       PIE1bits.CCP1IE = 0;//desliga interrupcão do ccp1 pra garantir
     }
     else//modo DC
     {
         Read_ADC_DC(); 
     }     
    _delay_ms(500);
    LED_STATUS=~LED_STATUS;
  }//fim do while 
  }//fim do laço main

/////////////////////////////////////////////////////////////////////////////////

void verifica_botao(void)
{
     if(!BOT_SEL)//testa botão de seleção -> AC/DC
     {
            _delay_ms(200);//debouce
            if(!BOT_SEL)
            {
               if(modo_tensao)
               {//modo DC
                   Tela_inicial('D');
                   modo_tensao=0;
                   LED_MODO =0;   
                   PIE1bits.CCP1IE = 0;//desliga interrupcão do ccp1
               }
               else
               {//modo AC
                   Tela_inicial('A');
                   LED_MODO =1;
                   modo_tensao=1;
                   estado_amostras=0;//libera nova coleta     
                   PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
               }
               _delay_ms(200);//debouce
            }
     }
}
 void Inic_Regs(void)
{
    TRISA = 0x03;            //PORTA saida, exceto RA0(tensão) e RA1(corrente) será usado para entradas analogicas
    TRISB = 0x00;            //PORTB saida
    TRISC = 0x34;            //PORTC saida e pino c2 entrada ccp1,pino C4 e C5 informa sinal do fator de potencia 
    TRISD = 0x00;            //PORTD saida
    TRISE = 0x04;            //PORTE saida,botão no RE2
    PORTA = 0x00;            //limpa PORTA
    PORTB = 0x00;            //limpa PORTB
    PORTC = 0x00;            //limpa PORTC
    PORTD = 0x00;            //limpa PORTD:
    PORTE = 0x00;            //limpa PORTE
//********************************************************************
    ADCON0 = 0X01;          //liga modulo analogico digital e configura entrada de sinal para o pino RA0(AN0)
    ADCON1=0b00001011;          // Vref- seta ao Vss e  Vref+ ao Vdd , somente Ra0 ate Ra4 analogico
    //ADCON2 = 0b101101010;    // justificado a direita     // clk Fosc/32
       ADCON2=0b10101001;
    // tempo de aquisição 12TAD(assim funciona pra qualquer cristal, porem fica mais lento, se precisar deve ser ajustado)
    INTCON=0b11000000;//liga interrupção de periferico e geral
    T3CON = 0;//mantem o ccp1 e ccp2 ligado ao timer1
    T1CON = 0b10110001;//modo de 16bits - fosc/dividido por 8 - timer ligado (lé no maximo500khz para clock de 8MHZ))
    CCP1CON = 0b00000101;//captura a cada borda de subida
    IPR1= 0X05;//coloca estouro de timer  e ccp1 como alta prioridade
    PIR1bits.CCP1IF = 0;//limpa flag do ccp1
    PIE1bits.CCP1IE = 1;//liga interrupcão do ccp1
    PIE1bits.TMR1IE = 0;//deixa interrupção de timer desligado(não vai prescisar))
 }
 //configura tela inicial
 void Tela_inicial(unsigned char modo)
{    
    if(modo == 'A') 
    {
        sprintf(Buffer_lcd,"Vrms=      V  -> AC ");
        impri_lcd(0,'1');
        sprintf(Buffer_lcd,"Vp=     V  Ip=     A");
    }else
    {
        sprintf(Buffer_lcd,"Multimetro    -> DC ");
        impri_lcd(0,'1');
        sprintf(Buffer_lcd,"Vdc=     V Idc=    A");  
    }
  impri_lcd(0,'2'); 
  sprintf(Buffer_lcd,"Fp=        Pi=     W");
  impri_lcd(0,'3');
  sprintf(Buffer_lcd,"F=      hz Pm=     W");
  impri_lcd(0,'4');  
} 
 //faz leitura de amostras em corrente continua
void Read_ADC_CA(void)
{       
       unsigned int lido_adc;
       float temp,tensao;
       unsigned int parte_inteira;                //declaração de variável local
       unsigned int parte_decimal;                //declaração de variável local
       unsigned int hist_leitura[5]={0,0,0,0,0};//pega 5 amostras para não estourar a variavel
       unsigned char fim_captura=245;//esse valor pede pra encontrar quando a onda está subindo
       unsigned int num_de_tentativas = 0;
       descarta_FP=0;//libera leitura do fator de potencia
       //////////Lê tensão////////////////////
        ADCON0 = 0X01; //RA0
        _delay_us(40);//espera tensão estabilizar 
        hist_leitura[0] = 0XFFFF;//coloca valor maximo para garantir 
        while((fim_captura == 245)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA1 habilita conversão         
             while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[1] =  (((int)ADRESH)<<8)|(ADRESL);
             if((hist_leitura[1]>hist_leitura[0])&&(hist_leitura[1]<10))//pra garantir que está no começo da onda 
                {
                   fim_captura = 0;//finalizou processo onda está subindo
                }
            hist_leitura[0]= hist_leitura[1];//recebe valor atual
            verifica_botao();
            num_de_tentativas++;
            if(num_de_tentativas>1000)//anti travamento
            {
                fim_captura = 253;//indica que não leu tensão
            }
        }
        while((fim_captura < 250)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA1 habilita conversão         
            while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[fim_captura] =  (((int)ADRESH)<<8)|(ADRESL);
            if(fim_captura == 0)
            {
                if(hist_leitura[0]<hist_leitura[4])
                {
                   fim_captura = 252;//finalizou processo 
                   lido_adc = ((hist_leitura[4]+hist_leitura[3]+hist_leitura[2])/3);//tira uma média das 3 ultimas amostras
                }
            }
            else
            {
                if(hist_leitura[fim_captura]<hist_leitura[fim_captura-1])
                {
                    unsigned char conta = 0;
                     lido_adc =0;
                    while(conta<5)//0 a 4
                    {
                        if(conta != fim_captura)//ignora valor atual
                        {
                            lido_adc =  lido_adc + hist_leitura[conta];
                        }
                        conta++;
                    }
                     lido_adc = lido_adc/4;//tira media
                    fim_captura = 252;//finalizou processo 
                }
            }            
            if(fim_captura<250)
            {
                fim_captura++;
                if(fim_captura > 4)
                {
                    fim_captura = 0; 
                }
            }
           verifica_botao(); 
        }
        if(fim_captura == 253)
        {
          temp = 0;
          descarta_FP=1;
        }else
        {
         temp = EQ_TENSAO ;         
        }
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor
        tensao =temp;
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        Tela_inicial('A');//limpa campos
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(3,'2'); 
        temp=(tensao/1.4142);
        parte_inteira = (int)temp;            //pega parte inteira do valor        
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(5,'1'); 
        
        //////////Lê corrente////////////////////        
        hist_leitura[0]=0;
        hist_leitura[1]=0;
        hist_leitura[2]=0;
        hist_leitura[3]=0;
        hist_leitura[4]=0;
        ADCON0 = 0X05; ///liga RA1  
        _delay_us(40);//espera tensão estabilizar 
        fim_captura = 245;
        hist_leitura[0] = 0XFFFF;//coloca valor maximo para garantir 
        num_de_tentativas=0;
         while((fim_captura == 245)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA1 habilita conversão         
            while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[1] =  (((unsigned int)ADRESH)<<8)|(ADRESL);
             if((hist_leitura[1]>hist_leitura[0])&&(hist_leitura[1]<10))//pra garantir que está no começo da onda
                {
                   fim_captura = 0;//finalizou processo onda está subindo
                }
            hist_leitura[0]= hist_leitura[1];//recebe valor atual
            verifica_botao();
            num_de_tentativas++;
            if(num_de_tentativas>1000)//anti travamento
            {
                fim_captura = 253;//indica que não leu tensão
            }            
        }
        while((fim_captura < 250)&&(modo_tensao==1))
        {
            ADCON0bits.GO = 1; //RA0 habilita conversão         
            while (ADCON0bits.DONE == 1); //espera finalizar leitura
            hist_leitura[fim_captura] =  (((int)ADRESH)<<8)|(ADRESL);
            if(fim_captura == 0)
            {
                if(hist_leitura[0]<hist_leitura[4])
                {
                   fim_captura = 252;//finalizou processo 
                   lido_adc = ((hist_leitura[4]+hist_leitura[3]+hist_leitura[2])/3);//tira uma média das 3 ultimas amostras
                }
            }
            else
            {
                if(hist_leitura[fim_captura]<hist_leitura[fim_captura-1])
                {
                    unsigned char conta = 0;
                     lido_adc =0;
                    while(conta<5)//0 a 4
                    {
                        if(conta != fim_captura)//ignora valor atual
                        {
                            lido_adc =  lido_adc + hist_leitura[conta];
                        }
                        conta++;
                    }
                     lido_adc = lido_adc/4;//tira media
                    fim_captura = 252;//finalizou processo 
                }
            }            
            if(fim_captura<250)
            {
                fim_captura++;
                if(fim_captura > 4)
                {
                    fim_captura = 0; 
                }
            }
            verifica_botao();
        }
        if(fim_captura == 253)
        {
          temp = 0;
          descarta_FP=1;
        }else
        {
         temp = EQ_CORRENTE;  
        }               
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor        
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'2');
        temp = tensao*temp;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'3'); 
        //potencia media ac
        temp = temp/2;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'4');

}
//faz leitura de amostras em corrente continua
void Read_ADC_DC(void)
{
       unsigned int lido_adc;
       float temp,tensao;
       unsigned int parte_inteira;                //declaração de variável local
       unsigned int parte_decimal;                //declaração de variável local
       unsigned int amostras[5];
       //////////Lê tensão////////////////////
        ADCON0 = 0X01;//RA0 
        _delay_us(40);//espera tensão estabilizar
        lido_adc = 0;
        while(lido_adc<5)
        {
        ADCON0bits.GO = 1; //RA0
        while (ADCON0bits.DONE == 1); //espera finalizar leitura
        amostras[lido_adc] =  (((unsigned int)ADRESH)<<8)|(ADRESL);
        lido_adc++;
        }
        temp = amostras[0] +amostras[1] +amostras[2] +amostras[3] +amostras[4];
        lido_adc =(unsigned int)temp/5;
        temp = EQ_TENSAO;  
        Tela_inicial('D');//limpa campos
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor
        tensao =temp;
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(4,'2');
        
        //////////Lê corrente ////////////////////
       ADCON0 = 0X05; ///liga RA1        
        _delay_us(40);//espera tensão estabilizar
       lido_adc = 0;
        while(lido_adc<5)
        {
        ADCON0bits.GO = 1; //RA0
        while (ADCON0bits.DONE == 1); //espera finalizar leitura
        amostras[lido_adc] =  (((unsigned int)ADRESH)<<8)|(ADRESL);
        lido_adc++;
        }
        temp = amostras[0] +amostras[1] +amostras[2] +amostras[3] +amostras[4];
        lido_adc =(unsigned int)temp/5;
        temp = EQ_CORRENTE;       
    //*******************************************************************
        if(temp < 0)                    //valor é menor que 0?
        { temp = temp * (-1);        //inverte o sinal de float
        }
    //*******************************************************************
        parte_inteira = (int)temp;            //pega parte inteira do valor        
    //*******************************************************************
        parte_decimal =(unsigned int)((temp - parte_inteira) * 100);    //resgata parte fracionária do valor ,2 casa decimais
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(15,'2');
        temp = tensao*temp;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'3');        
        PM[3]= PM[4];
        PM[2]= PM[3];
        PM[1]= PM[2];
        PM[0]= PM[1];
        PM[4]= temp+ PM[0]+PM[1]+ PM[2]+ PM[3];
        tensao =temp;
        temp= PM[4]/5;//potencia media reaproveitando variavel
        PM[4]=tensao;
        parte_inteira = (int)temp;            //pega parte inteira do valor 
        parte_decimal =(unsigned int)((temp - parte_inteira) * 10);//somente uma casa decimal
        sprintf(Buffer_lcd,"%u.%u",parte_inteira,parte_decimal);        //converte valor em string e armazena no vetor buf    
        impri_lcd(14,'4');
}

Arquivo MCC18_18F4550_H.h


/* 
 * File:   main_lcd.c
 * Author: aguivone
 * descrição: como usar display lcd no modo 4 bits
 * freq.de clock: 20MHZ
 * data:31/10/2019 
 * não esquecer de coloca o arquivo MCC18_18F4550.h
 */
#include <stdio.h>
#include <p18f4550.h>    
#include <string.h>
#include <delays.h>

#ifndef __MCC18_18F4550___H
#define    __MCC18_18F4550___H

// CONFIG1L
#pragma config PLLDIV = 1       // PLL Prescaler Selection bits (No prescale (4 MHz oscillator input drives PLL directly))
#pragma config CPUDIV = OSC1_PLL2// System Clock Postscaler Selection bits ([Primary Oscillator Src: /1][96 MHz PLL Src: /2])
#pragma config USBDIV = 1       // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)

// CONFIG1H
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator (HS))
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOR = OFF        // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 3         // Brown-out Reset Voltage bits (Minimum setting 2.05V)
#pragma config VREGEN = OFF     // USB Voltage Regulator Enable bit (USB voltage regulator disabled)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = ON      // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = ON      // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = ON       // MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config ICPRT = OFF      // Dedicated In-Circuit Debug/Programming Port (ICPORT) Enable bit (ICPORT disabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000800-001FFFh) is not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) is not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM is not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) are not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block (000000-0007FFh) is not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM is not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) is not protected from table reads executed in other blocks)


#endif    /* __MCC18_18F4550___H */

Arquivo tempo.h
/* 
 * File:   tempo.h
 * Author: aguiv
 *
 * Created on 8 de Novembro de 2019, 15:54
 */
#include <delays.h>
#ifndef TEMPO_H
#define    TEMPO_H

 void _delay_us(int valor);
 void _delay_ms(int valor);
 #include "tempo_20MHz.c"

#endif    /* TEMPO_H */

Aquivo tempo_20MHz.c
/* 
 * File:   tempo.c
 * Author: aguivone
 *
 * Created on 8 de Novembro de 2019, 16:02
 */
// usado para criar funções de tempo

 void _delay_us(int valor)
 {
     while(valor>0)
     {
      Nop();Nop();Nop();Nop();Nop();//Nop();Nop();Nop();      
      //para 32-> 8 ciclos ,20MHZ -> 5 ciclos dão 1us , para 8mhz -> 2ciclo, para 4mhz ->>1ciclo 
      valor--;
     }
 }
 void _delay_ms(int valor)
 {
     while(valor>0)
     {
      Delay1KTCYx(5);
      //para 32MHZ -> coloque 8
      //para 20MHZ -> 5000 ciclos dão 1ms -> coloque 5
      // 8mhz -> 2000 -> coloque 2
      // 4mhz -> 1000 -> coloque 1
      valor--;
     }
 }
 //delay de segundos
 void _delay_s(int valor)
 {
     while(valor>0)
     {
      _delay_ms(1000); 
      valor--;
     }
 }



Aquivo da biblioteca do LCD - LCD_4bits.h

/* 
 * File:   LCD_4bits.h
 * Author: aguivone
 *
 * Created on 8 de Novembro de 2019, 16:07
 */
//biblioteca para acessar o display de LCD no modo 4 bits
//o pino r/w é conectado ao terra pois não é feita nenhuma leitura no display
//configuração:
//
// pino display  / pino da porta selecionada
//
// D4 = bit 0 da porta
// D5 = bit 1 da porta
// D6 = bit 2 da porta
// D7 = bit 3 da porta
//
// RS = bit 4 da porta
// EN = bit 5 da porta
//

#include <stdio.h>
#include <p18f4550.h>    //se for necessario troque este
#include <string.h>

#ifndef LCD_4BITS_H
#define    LCD_4BITS_H
//variaveis locais
extern unsigned char Buffer_lcd[16]; //lembre de criar esta variavel no codigo principal
/////////////////////////////////////pinos do microcontrolador usado no lcd ///////////////////////////////////////////
//define qual porta vai usar neste caso port D

///acepic////////////
/*
  #define BIT_0  PORTDbits.RD4
 #define BIT_1  PORTDbits.RD5
 #define BIT_2  PORTDbits.RD6
 #define BIT_3  PORTDbits.RD7

 #define RS     PORTEbits.RE0
 #define EN     PORTEbits.RE1
*/
//prototipo montado na protoboard/////////
 #define BIT_0  PORTDbits.RD4
 #define BIT_1  PORTDbits.RD5
 #define BIT_2  PORTDbits.RD6
 #define BIT_3  PORTDbits.RD7

 #define RS     PORTDbits.RD0
 #define EN     PORTDbits.RD1


union{
    unsigned char VALOR;
    struct{
        unsigned b0:1;
        unsigned b1:1; 
        unsigned b2:1;
        unsigned b3:1;
        unsigned b4:1;
        unsigned b5:1;
        unsigned b6:1;
        unsigned b7:1;
    };    
}LCD;
void porta_lcd(unsigned char ucDado); 
void escreve_LCD(unsigned char ucDado,unsigned char ucVlr); 
void inicializa_LCD(void);
void impri_lcd(int iPos_inicial ,unsigned char ucLinha);
void impri_caracter_lcd(unsigned char ucCaracter,int iPos_inicial ,unsigned char ucLinha);
 #include "LCD_4bits.c"
#endif    /* LCD_4BITS_H */



Até a próxima! obrigado!