Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

6
voti

Timer h-m-s con PIC [2] - Il Programma

Eccoci alla seconda puntata. Adesso voglio parlare un po' del programma che fa funzionare il timer che ho progettato, ma prima è meglio se introduco l'hardware finale, parlo un po' dell'allocazione dei PIN così da capire meglio anche il programma.

Scheda finale

Ecco la scheda finale; a essere sinceri è anche il primo PCB che sbroglio e produco, quindi ne vado fiero.

PCB Fronte

PCB Fronte

PCB Retro

PCB Retro

Ecco l'allocazione dei PIN di I/O

Nome Pin Funzione
RA0 Segm. B
RA1 Segm. A
RA2 Segm. F
RA3 Segm. G
RA4 Punto
RA5 Segm. C
RA6 Segm. E
RA7 Segm. D
RC2 Led Rosso ore
RC3 Led Rosso min
RC4 Led Giallo
RC5 CC Disp. SX
RC6 CC Disp. DX
RC7 Relè
RB0 Tasto Start
RB1 Tasto +
RB2 Tasto -
RB3 Tasto Mode

Oltre ai PIN usati per la programmazione e per il quarzo del TIMER, rimangono due pin liberi (RC0 e RC1) per usi futuri. In questa versione il relè è alimentato con la tensione prelevata prima dello stabilizzatore, quindi non a 5V, ma nel mio caso a 12V.

Il Programma

Ecco il listato del programma, l'ho commentato molto e suddiviso in molte funzioni così da comprenderlo meglio, penso che si spieghi da solo.

/*
Ver 1.0

Author: Davide Bagnoli, 2013

MCU: Microchip PIC18F2550

Clock: 2 MHz (internal clock)

Compiler: XC8 - Version 1.12

 Progetto di un timer ore-min-sec con display.
*/

/**************************************************************************** 
Librerie
****************************************************************************/ 

#include <xc.h>

/**************************************************************************** 
Dichiarazioni Funzioni
****************************************************************************/ 

void setup_micro(void);          //impostazioni del microcontrollore
void setup_prog(void);           //inizializzazione programma
void interrupt tmr_int(void);    //gestore degli interrupt
void Disp_Num(char n);           //visualizza il numero n sul display attivo
void Disp_Multiplex(char index); //attiva il display indicato da n (0:dx 1:sx)
void Disp_Clear(void);           //pulisce il display attivo
void Rele_On(void);              //accende il relè e il led giallo
void Rele_Off(void);             //spenge il relè e il led giallo
void DecrementaTempo(void);      //decrementa di un secondo il tempo impostato
char CountDown(void);            //torna 0 se il countdown è terminato
void Led_Mode(char mode);        //cambia i led accesi a seconda della modalità scelta
void Count_Start(void);          //avvia il countdown
void Count_End(void);            //termina il countdown

/**************************************************************************** 
Definizione MACRO
****************************************************************************/ 

#define RELE LATCbits.LATC7     	//Pin del Relè
#define LED_GIALLO LATCbits.LATC4	//Pin Led giallo segnale timer attivo
#define LED_ALTO LATCbits.LATC2         //Pin Led in alto per segnale mode
#define LED_BASSO LATCbits.LATC3	//Pin Led in basso per segnale mode
#define CAT_DISP_SX LATCbits.LATC5	//Pin Transistor Catodo Comune display SX
#define CAT_DISP_DX LATCbits.LATC6      //Pin Transistor Catodo Comune display DX
#define BTN_MODE PORTBbits.RB3          //Pin Tasto Mode
#define BTN_UP PORTBbits.RB1            //Pin Tasto +
#define BTN_DOWN PORTBbits.RB2          //Pin Tasto -
#define BTN_START PORTBbits.RB0         //Pin Tasto Start/Stop
#define NUM_0 0b11100111                //Configurazioni LATA per numeri display
#define NUM_1 0b00100001
#define NUM_2 0b11001011
#define NUM_3 0b10101011
#define NUM_4 0b00101101
#define NUM_5 0b10101110
#define NUM_6 0b11101110
#define NUM_7 0b00100011
#define NUM_8 0b11101111
#define NUM_9 0b10101111
#define NUM_PIN_PUNTO 4                 //Numero Pin punto display [0-7]
#define PUNTO LATAbits.LATA4            //Pin Punto Display

/**************************************************************************** 
Dichiarazioni Variabili
****************************************************************************/ 

unsigned char i;
unsigned char cont;
unsigned char ore;
unsigned char min;
unsigned char sec;
unsigned char start;
unsigned char mode;// 0=secondi 1=minuti 2=ore
unsigned char end; // 0= bottone rilasciato 1=comando eseguito
unsigned char rilascio;

/**************************************************************************** 
Direttive Programmazione
****************************************************************************/ 

#pragma config OSC = INTIO67 //oscillatore interno, RA6 e RA7 pin di I/O
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#pragma config WDT = OFF //watchdog disabilitato
#pragma config PBADEN = OFF //AD disabilitato
#pragma config LVP = OFF //programmazione a bassa tensione disabilitata

/**************************************************************************** 
MAIN
****************************************************************************/ 

void main(void)
{
    setup_micro();
    setup_prog();

    while(1)
    {
	if(i==0)//aggiorno display sx
   	{

            Disp_Clear();

	    Disp_Multiplex(0);//display sx
	    switch(mode)
	    {
                case 0: Disp_Num((char)sec/10);
                    break;
	        case 1: Disp_Num((char)min/10);
                    break;
	        case 2: Disp_Num((char)ore/10);
                    break;
	    } 
     	}   
        
        if(i==5)//aggiorno display dx
        {      
	    Disp_Clear();
	    Disp_Multiplex(1);//display dx
	    switch(mode)
	    {
                case 0: Disp_Num((char)sec%10);
                    break;
	        case 1: Disp_Num((char)min%10);
                    break;
	        case 2: Disp_Num((char)ore%10);
                    break;
	    } 
	} 
	    
	if(BTN_MODE && BTN_UP && BTN_DOWN && BTN_START)//se tutti i bottoni NON sono premuti
	{
            cont = 0;
	    rilascio = 1;
	}	
	else
	    cont++;//se un tasto è premuto, faccio il debouncing.
	    
	if(cont > 80)//almeno un tasto premuto
	{    
	    cont = 0;
	    if(!BTN_MODE && rilascio)//premuto bottone delle modalità, non commuta se tenuto premuto
	    {
	    	mode = (mode+1)%3;
	    	Led_Mode(mode);
	    	rilascio = 0;
	    }	
	    	
	    if(!BTN_UP && !start)//premuto bottone +
	    { 
	   	switch(mode)
	    	{
	    		case 0: sec = (sec+1)%60;
                            break;
	    		case 1: min = (min+1)%60;
                            break;
		    	case 2: ore = (ore+1)%100;
                            break;
		}	
	    }	
		    
	    if(!BTN_DOWN && !start)//premuto bottone -
	    {
	    	switch(mode)
	    	{
       		case 0:	if(sec==0)
                            sec = 59;
                        else
                            sec --;
                    break;
		case 1: if(min==0)
                            min = 59;
                        else
                            min --;
                    break;
		case 2: if(ore==0)
                            ore = 99;
        		else
                            ore --;
                    break;
                }
            }
		    
            if(!BTN_START && rilascio)//avvia/ferma timer, non commuta se tenuto premuto
            {
                if(!CountDown())
		{
                    if(!start)//se il tempo non è zero e non è avviato
		    	Count_Start();
		    else
		    	Count_End();
		}	
          	rilascio = 0;
            }
	} 
        
        i = (i+1)%10;//incremento contatore
    }
}

/**************************************************************************** 
Gestione Interruzioni (Interrupt Handler)
****************************************************************************/ 

void interrupt tmr_int(void)
{
	if(PIR1bits.TMR1IF)//se l'interruzione è data dal TIMER1	
        {
            TMR1H = 0x80;//precarico il timer
            TMR1L = 0x00;
            PIR1bits.TMR1IF = 0;//azzero il flag di overflow del TIMER1
            PUNTO = ~PUNTO;//lampeggio punto
            DecrementaTempo();//Diminuisco di 1sec il tempo
            if(CountDown())//se il tempo è finito
                Count_End();//spegne il timer e il relè conteggio
	}
}	

/**************************************************************************** 
Setup Micro
****************************************************************************/ 

void setup_micro(void)
{
    OSCCONbits.IRCF = 0b101;//Clock a 2Mhz
    OSCCONbits.IOFS = 0;//Clock Stabile
    OSCCONbits.SCS = 0b10;//Clock di sistema da oscillatore interno

    LATA = 0xFF;
    TRISA = 0x00;//tutti output

    LATB = 0x00;
    TRISB = 0xFF;//tutti input

    LATC = 0x00;
    TRISC = 0b00000011;//tutti input trane RC0 e RC1
	
    INTCON2bits.RBPU = 0;//abilito resistori di pullup su PORTB
	
    T1CONbits.RD16 = 1;//modalità a 16 bit
    T1CONbits.T1RUN = 0;//uso clock diverso dalla cpu
    T1CONbits.T1CKPS0 = 0;//prescaler 1:1
    T1CONbits.T1CKPS1 = 0;//prescaler 1:1
    T1CONbits.TMR1CS = 1;//clock esterno
    T1CONbits.T1SYNC = 1;//non sincronizza clock esterno
    T1CONbits.T1OSCEN = 1;//abilito oscillatore

    TMR1H = 0x80;
    TMR1L = 0x00;//precarico il timer per avere un interrupt al secondo

    PIE1bits.TMR1IE = 1;//abilito interrupt su overflow timer1
    IPR1bits.TMR1IP = 1;//interrupt alta priorità su timer1
	
    T1CONbits.TMR1ON = 0;//spengo timer1
	
    INTCONbits.GIEH = 1;//abilito interrupt globali
    INTCONbits.GIEL = 1;//abilito interrupt sulle periferiche
    RCONbits.IPEN = 1;//sabilito interrupt a due livelli
}

/**************************************************************************** 
Setup Programma
****************************************************************************/ 

void setup_prog(void)
{
	i = 0;
	cont = 0;
	rilascio = 1;
	mode = 0;
	end = 0;
	start = 0;
	sec = 0;
	min = 0;
	ore = 0;
	Led_Mode(mode);
	Rele_Off();
	Disp_Clear();
	PUNTO = 0;
}

/**************************************************************************** 
Scrittura su display
****************************************************************************/ 

void Disp_Num(char n)//modifico solo i 7 bit dei segmenti
{
	char tmp = 0b00000001 << NUM_PIN_PUNTO; //Creo la maschera per il punto
	tmp = tmp & LATA;//azzero tutti i bit tranne quello dello punto
	switch(n)
	{
		case 0: LATA = tmp | NUM_0;
			break;
		case 1: LATA = tmp | NUM_1;
			break;
		case 2: LATA = tmp | NUM_2;
			break;
		case 3: LATA = tmp | NUM_3;
			break;
		case 4: LATA = tmp | NUM_4;
			break;
		case 5: LATA = tmp | NUM_5;
			break;
		case 6: LATA = tmp | NUM_6;
			break;
		case 7: LATA = tmp | NUM_7;
			break;
		case 8: LATA = tmp | NUM_8;
			break;
		case 9: LATA = tmp | NUM_9;
			break;
		default:LATA = tmp | 0x00;//se non conosco il numero spengo il display
	}	
}

void Disp_Multiplex(char index)//se index == 0 allora display sx altrimenti dx
{
	if(index == 0)
	{
		CAT_DISP_SX = 1;
		CAT_DISP_DX = 0;
	}
	else
	{
		CAT_DISP_SX = 0;
		CAT_DISP_DX = 1;
	}		
}		

void Disp_Clear(void)//cancello il display
{
	LATA = LATA & (0b00000001 << NUM_PIN_PUNTO);
}	

/**************************************************************************** 
On/Off Relè
****************************************************************************/

void Rele_Off(void)
{
	RELE = 0;
	LED_GIALLO = 0;
}	

void Rele_On(void)
{
	RELE = 1;
	LED_GIALLO = 1;
}

/**************************************************************************** 
Gestione Tempo
****************************************************************************/

char CountDown(void)//ritorna 1 se finito il conto alla rovescia
{
	if(sec == 0 && min == 0 && ore == 0)
		return 1;
	return 0;
}	
void DecrementaTempo(void)
{
	if(!CountDown())
	{
		if(sec == 0)
		{
			sec = 59;
			if(min == 0)
			{
				min = 59;
				ore--;
			}	
			else
				min--;
		}	
		else
			sec--;	
	}		
}	

/**************************************************************************** 
Gestione Led Modalità
****************************************************************************/

void Led_Mode(char mode)
{
	switch(mode)
	{
		case 0 : LED_ALTO = 1;
                         LED_BASSO = 1;
			break;
		case 1 : LED_ALTO = 0;
			 LED_BASSO = 1;
			break;
		case 2 : LED_ALTO = 1;
			 LED_BASSO = 0;
			break;
		default: LED_ALTO = 0;
			 LED_BASSO = 0;
	}	
}
	
/**************************************************************************** 
Partenza/Fine Conteggio
****************************************************************************/

void Count_Start(void)
{
	TMR1H = 0x80;
	TMR1L = 0x00;//precarico il timer per avere un interrupt al secondo
	T1CONbits.TMR1ON = 1;//accendo timer1
	PUNTO = 1;
	start = 1;
	Rele_On();
}

void Count_End(void)
{
	T1CONbits.TMR1ON = 0;//spengo timer1
	PUNTO = 0;
	start = 0;
	Rele_Off();
}	

La parte più interessante è forse il main, dove è presente un ciclo infinito che si occupa di due cose, il multiplexing dei display e la gestione della pressione dei pulsanti con conseguente debouncing software.

Per il debouncing uso un contatore che viene incrementato se un pulsante dei quattro è premuto, altrimenti viene azzerato. Se questo contatore supera un certo valore, allora c'è almeno un tasto premuto da un certo tempo e quindi ho effettuato un semplice debouncing. Questo tipo di gestione mi permette anche di avere un incremento o decremento rapido se il pulsante viene tenuto premuto. Nel caso dei pulsanti MODE e START, c'è un'ulteriore variabile rilascio, che serve ad attivare una certa reazione al pulsante solo ad ogni pressione inibendo il funzionamento continuo se il tasto viene tenuto premuto.

Indice

  1. Timer h-m-s con PIC [1] - Il Progetto
  2. Timer h-m-s con PIC [2] - Il Programma
2

Commenti e note

Inserisci un commento

di ,

Grazie Paolino, un complimento da te su questa materia mi fa molto piacere!

Rispondi

di ,

Beh, direi che hai fatto un buon lavoro!

Rispondi

Inserisci un commento

Per inserire commenti è necessario iscriversi ad ElectroYou. Se sei già iscritto, effettua il login.