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.
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.

Elettrotecnica e non solo (admin)
Un gatto tra gli elettroni (IsidoroKZ)
Esperienza e simulazioni (g.schgor)
Moleskine di un idraulico (RenzoDF)
Il Blog di ElectroYou (webmaster)
Idee microcontrollate (TardoFreak)
PICcoli grandi PICMicro (Paolino)
Il blog elettrico di carloc (carloc)
DirtEYblooog (dirtydeeds)
Di tutto... un po' (jordan20)
AK47 (lillo)
Esperienze elettroniche (marco438)
Telecomunicazioni musicali (clavicordo)
Automazione ed Elettronica (gustavo)
Direttive per la sicurezza (ErnestoCappelletti)
EYnfo dall'Alaska (mir)
Apriamo il quadro! (attilio)
H7-25 (asdf)
Passione Elettrica (massimob)
Elettroni a spasso (guidob)
Bloguerra (guerra)