Leitura analógica com PIC12F675 e MPLAB X

Posted by

Hoje, configuraremos e usaremos a entrada analógica com PIC12F675 e MPLAB X, mostrando o valor analógico (tensão) em dois LEDs que piscarão de acordo. Ao fazer minha pesquisa para este artigo (que usarei como parte de um projeto de chaveiro de termômetro), percebi que quase não existem exemplos de leitura de entrada analógica com PIC12F675 na internet. Hoje estamos mudando isso.

De acordo com o datasheet do 12F675, ele apresenta quatro canais analógicos (pinos 3, 5, 6 e 7) com resolução de 10 bits, fornecendo leituras de 0-5 V (quando o chip é alimentado com 5 V) a 0-1023 inteiros. As leituras são fornecidas em formato binário em ADRESH (2 bits mais altos) e ADRESL (8 bits mais baixos) – isso é selecionável (você pode ter ADRESH como os bits mais altos ou mais baixos).

O circuito

Para fins de teste, implementarei as leituras analógicas em dois canais: termistor NTC em AN2 e divisor de resistor em AN3, conforme visto abaixo. Isso se deve ao fato de que estou usando minha placa de circuito “FritzenLab keychain” para teste e desenvolvimento. Este keychain apresentará um termômetro com o dito NTC e dois LEDs.

Diagrama esquemático da leitura analógica com PIC12F675
Diagrama esquemático da leitura analógica com PIC12F675


Observe o circuito termistor conectado ao GP2, o divisor de resistor conectado ao GP4 e dois LEDs conectados ao GP0 e GP1.

Chaveiro FritzenLab com termômetro
Chaveiro FritzenLab com termômetro
Chaveiro FritzenLab com gravador PICkit3

O código

Neste tutorial, estou usando o MPLAB X como IDE, o XC8 como compilador e o PICkit3 como programador, conforme configurado aqui e aqui. O código foi adaptado destas fontes: aqui e aqui.

A primeira coisa que temos que fazer é configurar todos os registradores necessários para entradas analógicas e saídas digitais. Entre os mais importantes está o TRISIO para definir GP2 e GP4 como entradas, outros pinos como saídas:

TRISIO = 0b010100;// GP2 and GP4 analog inputs

Então comunicamos à ANSEL que AN2 e AN3 serão usados ​​como analógicos:

ANSEL = 0b00011100;// AN2 and AN3 as analog inputs

Também ADCON0 para configurações analógicas gerais (neste caso para AN3):

ADCON0=0b10001101; // AN3, right justified, VDD as ref

Agora para AN2 (comentado por enquanto, não o estou usando):

//ADCON0=0b00001001; // AN2, right justified, VDD as ref

Finalmente habilitando o módulo ADC:

ADON=1; // Enable ADC

Agora temos que executar leituras analógicas. Para isso, decido fazê-lo periodicamente, usando uma interrupção do Timer0: ele é configurado para entrar na função de interrupção a cada ~65ms (milissegundos) (oscilador interno de 4MHz, sendo dividido por 4 e com um prescaler de 256). Apesar de entrar na interrupção a cada ~65ms, eu só entro com meu código a cada 4x isso (então em torno de 260ms).

Então, tenho um IF com dois ELSEs: IF “readAndDecide” é verdadeiro (se for hora de fazer uma leitura analógica), ele faz exatamente isso e prossegue (no próximo ciclo) para o primeiro ELSE, que controla o LED de dezenas. O LED de dezenas piscará o número de vezes do dígito mais significativo: se, por exemplo, a leitura de tensão analógica for 3,7V, o LED de dezenas piscará 3 vezes.

Então, o primeiro ELSE controla o segundo ELSE, que pisca o número de vezes que a leitura analógica de unidades foi feita. No exemplo acima, se a leitura de voltagem for 3,7 V, o LED das unidades piscará 7 vezes. Então, por exemplo, se a leitura de voltagem no IF for 1,2 V, o LED das dezenas piscará uma vez e o LED das unidades piscará duas vezes, e assim por diante.

Depois que ambos os LEDs piscarem (primeiro as dezenas, depois as unidades), o controle é transferido de volta para o IF principal, que fará outra leitura analógica. Continua para sempre. O código completo está abaixo e também no meu Github.

/*
 Timer based on this code: https://microcontroladores-c.blogspot.com/2014/09/timer0-com-pic12f675.html
 * AD converter based on: https://forum.microchip.com/s/topic/a5C3l000000MRbeEAG/t336178
 * AD code also from here: https://saeedsolutions.blogspot.com/2012/07/pic12f675-adc-code-proteus-simulation.html
 */
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <math.h>
#define _XTAL_FREQ 4000000

#define LED1 GP0
#define LED2 GP1
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#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      // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF         // Data Code Protection bit (Data memory code protection is enabled)


volatile uint8_t counter= 0;
volatile int reading= 0;
volatile unsigned int voltage= 0;
volatile uint8_t readAndDecide= 1;
volatile int tens= 0;
volatile int units= 0;
volatile uint8_t executeTens= 0;
volatile uint8_t executeUnits= 0;

void __interrupt() isr()//interrupt vector
{   
    counter++;
    if(counter == 4){
        counter= 0;
        
        if(readAndDecide == 1){
            readAndDecide= 0;
            executeTens= 1;
            executeUnits= 0;
            
            GO_nDONE= 1;        
            while(GO_nDONE);
            reading = ((ADRESH<<8)+ADRESL); 
            voltage = (reading * 50) / 1024; // 0-50 instead of 0-5 (v))
        
            if(voltage >= 40){
            tens= 4;
            units= voltage - 40;
            }else if(voltage >= 30){
                tens= 3;
                units= voltage - 30;
            }else if(voltage >= 20){
                tens= 2;
                units= voltage - 20;
            }else if(voltage >= 10){
                tens= 1;
                units= voltage - 10;
            }else{
                tens= 0;
                units= voltage;
            }
            tens= tens * 2;
            units= units * 2;
        }else if(executeTens == 1){
            tens--;
            LED2= 0;
            if(LED1){
                LED1= 0;
            }else{
                LED1= 1;
            }
            if(tens < 1){
                executeTens= 0;
                executeUnits= 1;
            }
            
        }else if(executeUnits == 1){
            LED1= 0;
            units--;
            if(LED2){
                LED2= 0;
            }else{
                LED2= 1;
            }
            if(units < 1){
                executeTens= 0;
                executeUnits= 0;
                readAndDecide= 1;
            }
            
        }
        
        
        
        
    }
    
    TMR0IF = 0;//  clear timer0 interrupt flag
    TMR0 = 0;// zeroes timer 0 counting, so that it couts from 256 down to 0 again
}


//////////////////////////////////////////////////////Main routine///////////////////////////////////////////////////////////////
void main(void) {
    TRISIO = 0b010100;// GP2 and GP4 analog inputs
    CMCON = 7;// disable comparators
    ANSEL = 0b00011100;// AN2 and AN3 as analog inputs  
    //ADCON0=0b00001001; // AN2, right justified, VDD as ref
    ADCON0=0b10001101; // AN3, right justified, VDD as ref
    ADON=1; // Enable ADC
    WPU = 0X00;// pull ups disabled
    TMR0 = 0;
    OSCCAL = 0b11111111;// internal oscillator to max frequency
    OPTION_REG = 0b10000111;
    INTCON = 0b11100000;
    
    
    
   
    
    while(1)
    {
        
        
    }//infinite loop

}
Nothing happens in the main loop after it reaches the while(1), all magic is happening in the interrupt handling function. Also please note that in the way I made the code, only one analog input is read at a time. If you want to change to the other you have to uncomment it, comment the other and reflash the PIC12F675.

ADCON0=0b00001001; // AN2, right justified, VDD as ref

OR 

ADCON0=0b10001101; // AN3, right justified, VDD as ref

NOT both.

Testando

A configuração de teste está ilustrada acima, o chaveiro conectado ao PICkit3 e ao MPLAB X + XC8. Fiz um vídeo para ilustrar todos os passos necessários para ler um valor analógico com o PIC12F675.

Quer conhecer um pouco mais de PIC? veja este artigo.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *