Difference between revisions of "PIC16F1459 SMT µClock"
| Line 1: | Line 1: | ||
| − | ''' | + | Si tratta di un progettino estremamente semplice, nato da un display a 7 segmenti a 4 cifre multiplexato e dalla voglia di fare qualcosa di utile e carino in SMT. Il risultato è veramente gradvole e funzionale, unica pecca il consumo un po' altino ( circa 30mA ), ma ho intenzione di migliorare il firmware per abbassarlo un po'. |
| + | ==Hardware== | ||
| + | Sull'hardware non c'è molto da dire, è grossomodo identico a centinaia di altri progetti simili che si trovano in rete. Utilizza il comodissimo quarzo da 32,768 kHz ( connesso tra SOSCI e SOSCO ) come clock per un timer a 16bit ( timer 1 ), che tramite precaricamenti e prescaler vari finisce per generare un interrupt ogni secondo. Per regolare le ore e i minuti sono presenti due pulsanti che per semplificare il software hanno un anti-bounce hardware fatto con due resistenze da 33kΩ e 22kΩ e un condensatore da 100nF, questo sistema è una variazione di quanto ho trovato in giro per la rete, i valori dei componenti li ho determinati per aggiustamenti successivi e devo dire che ne sono molto soddisfatto, dato che non capita praticamente mai che faccia salti di due unità anzichè una. [[File:microClock.jpg|thumb|300px|L'orologio appena finito.]] | ||
| + | Dato che i GPIO del PIC sono in grado di assorbire/fornire 25mA l'uno gli anodi dei segmenti sono collegati direttamente a PORTC ( così come l'anodo dei due punti al centro ), mentre per l'attivazione di ciascuna cifra è necessario usare un piccolo transistor NPN ( BC817 in SOT23 ) controllato dal microprocessore, dato che deve mandare a massa fino a 8 volte la corrente di un singolo segmento; naturalmente servono delle resistenze da 10kΩ sulle basi dei transistor. Il display utilizzato ha la sigla LTC2727 e [[file:LTC2727.pdf qui]] trovate il datasheet. | ||
| + | Il curcuito sbrogliato è abbastanza piccolo, circa 41mm x 50mm, a singola faccia, ha dei fori di montaggio da 3mm ai quattro angoli ed è stato realizzato con la tecnica del toner transfert senza alcun problema ( le piste sono quasi tutte da 24mil ). Di seguito trovate i [[file:microclock_eagle.zip file eagle]] per realizzare il circuito. | ||
| + | ==Firmware== | ||
| + | Anche sul firmware c'è poco da dire, gira su un PIC16F1459 per questione di simpatia e disponibilità. Usa l'interrupt su timer1 per aggiornare l'orario ogni secondo e gli interrupt-on-change su RA0 e RA1 per la regolazione dell'orario. È molto poco ottimizzato per il consumo, ma ci si può lavorare. Dato che è abbondantemente commentato metto semplicemente il codice scritto e testato con MPLABX e XC8 v 1.3, chiunque abbia un minimo di esperienza coi microprocessori Microchip lo può capire ( e migliorare ) in pochi minuti. | ||
| + | [[code type = "C"]] | ||
| + | #include <xc.h> | ||
| − | [[ | + | #pragma config BOREN = OFF, IESO = OFF, FOSC = INTOSC, FCMEN = OFF, MCLRE = OFF |
| + | #pragma config WDTE = OFF, CP = OFF, PWRTE = ON, CLKOUTEN = OFF | ||
| + | #pragma config USBLSCLK = 48MHz, LPBOR = OFF, CPUDIV = NOCLKDIV, PLLEN = DISABLED | ||
| + | #pragma config WRT = OFF, STVREN = OFF, PLLMULT = 3x, BORV = HI, LVP = OFF | ||
| + | |||
| + | #define _XTAL_FREQ 4000000 //Quarzo esterno da 32.768kHz, oscillatore interno a 4MHz | ||
| + | |||
| + | //Numeri in uscita su PORTC con configurazione 16Mar14 | ||
| + | int NUMBERS[] = { 0b0111111, 0b0100010, 0b1011011, 0b1101011, 0b1100110, 0b1101101, | ||
| + | 0b1111101, 0b0100111, 0b1111111, 0b1101111 }; | ||
| + | |||
| + | //Contatore su timer 0 | ||
| + | int sec = 0, min = 0, h = 0; | ||
| + | bit dots = 0; | ||
| + | |||
| + | int main( void ){ | ||
| + | OSCCONbits.IRCF = 0b1101; // Internal oscillator @ 4MHz | ||
| + | TRISA = 0b00001011; | ||
| + | TRISC = 0b00000000; | ||
| + | TRISB = 0x00; | ||
| + | |||
| + | //Setting per gli interrupt | ||
| + | INTCONbits.GIE = 1;//Abilita gli interrupt | ||
| + | INTCONbits.PEIE = 1;//Abilita gli interrupt sulle periferiche esterne ( TMR1) | ||
| + | INTCONbits.IOCIE = 1;//Abilita gli interrupt-on-change | ||
| + | INTCONbits.IOCIF = 0; | ||
| + | |||
| + | TMR1IE = 1;//Abilita gli interrupt su timer1 | ||
| + | TMR1IF = 0; | ||
| + | |||
| + | //Setting di Timer 1 | ||
| + | T1CON = 0b10011101; //Timer 1 attivato, utilizza come source un oscillatore | ||
| + | //al quarzo tra T1OSO e T1OSI, prescaler 1:2 e non sincro | ||
| + | TMR1GE = 0; //Le funzionalità del Gate di Timer 1 sono disattivate | ||
| + | |||
| + | //Setting degli interrupt-on-change | ||
| + | IOCAP = 0x00;//Spenti sul rising edge di tutte le PORTA | ||
| + | IOCAN = 0b00000011;//Interrupt sul falling edge di RA1 e RA0 | ||
| + | IOCBP = 0x00;//Spenti sia sul falling che sul rising di PORTB | ||
| + | IOCBN = 0x00; | ||
| + | //Ciclo di riscrittura del numero parametri di multiplex funzionanti il | ||
| + | //17Feb14 5ms x cifra, no resistenze. BC337 a catodo comune 10K sulla base. | ||
| + | //Vdd = 3.3V | ||
| + | |||
| + | while( 1 ){ | ||
| + | RB4 = 1; | ||
| + | PORTC = NUMBERS[h/10] + dots * 128; | ||
| + | __delay_ms(1); | ||
| + | PORTC = 0x00; | ||
| + | RB4 = 0; | ||
| + | RB6 = 1; | ||
| + | PORTC = NUMBERS[h%10] + dots * 128; | ||
| + | __delay_ms(1); | ||
| + | PORTC = 0x00; | ||
| + | RB6 = 0; | ||
| + | RB5 = 1; | ||
| + | PORTC = NUMBERS[min/10] + dots * 128; | ||
| + | __delay_ms(1); | ||
| + | PORTC = 0x00; | ||
| + | RB5 = 0; | ||
| + | RB7 = 1; | ||
| + | PORTC = NUMBERS[min%10] + dots * 128; | ||
| + | __delay_ms(1); | ||
| + | PORTC = 0x00; | ||
| + | RB7 = 0; | ||
| + | } | ||
| + | |||
| + | } | ||
| + | |||
| + | void interrupt gestione(){ | ||
| + | if( TMR1IF ){ | ||
| + | //Ricarica l'interrupt. | ||
| + | TMR1H = 0xC0; | ||
| + | TMR1L = 0x00; | ||
| + | dots = !dots; | ||
| + | sec++; | ||
| + | if( sec == 60){ | ||
| + | min++; | ||
| + | sec = 0; | ||
| + | } | ||
| + | if( min == 60 ){ | ||
| + | min = 0; | ||
| + | h++; | ||
| + | } | ||
| + | if( h == 24 ) h = 0; | ||
| + | TMR1IF = 0; | ||
| + | } | ||
| + | if( IOCAF1 ){ | ||
| + | h++; | ||
| + | if( h == 24 ) h = 0; | ||
| + | sec = 0; | ||
| + | TMR1H = 0xc0; | ||
| + | TMR1L = 0x00; | ||
| + | IOCAF1 = 0; | ||
| + | IOCIF = 0; | ||
| + | } | ||
| + | if( IOCAF0 ){ | ||
| + | min++; | ||
| + | sec = 0; | ||
| + | if( min==60) min = 0; | ||
| + | TMR1H = 0xc0; | ||
| + | TMR1L = 0x00; | ||
| + | IOCAF0 = 0; | ||
| + | IOCIF = 0; | ||
| + | } | ||
| + | } | ||
| + | [[/code]] | ||
Revision as of 11:19, 16 March 2014
Si tratta di un progettino estremamente semplice, nato da un display a 7 segmenti a 4 cifre multiplexato e dalla voglia di fare qualcosa di utile e carino in SMT. Il risultato è veramente gradvole e funzionale, unica pecca il consumo un po' altino ( circa 30mA ), ma ho intenzione di migliorare il firmware per abbassarlo un po'.
Hardware
Sull'hardware non c'è molto da dire, è grossomodo identico a centinaia di altri progetti simili che si trovano in rete. Utilizza il comodissimo quarzo da 32,768 kHz ( connesso tra SOSCI e SOSCO ) come clock per un timer a 16bit ( timer 1 ), che tramite precaricamenti e prescaler vari finisce per generare un interrupt ogni secondo. Per regolare le ore e i minuti sono presenti due pulsanti che per semplificare il software hanno un anti-bounce hardware fatto con due resistenze da 33kΩ e 22kΩ e un condensatore da 100nF, questo sistema è una variazione di quanto ho trovato in giro per la rete, i valori dei componenti li ho determinati per aggiustamenti successivi e devo dire che ne sono molto soddisfatto, dato che non capita praticamente mai che faccia salti di due unità anzichè una.Dato che i GPIO del PIC sono in grado di assorbire/fornire 25mA l'uno gli anodi dei segmenti sono collegati direttamente a PORTC ( così come l'anodo dei due punti al centro ), mentre per l'attivazione di ciascuna cifra è necessario usare un piccolo transistor NPN ( BC817 in SOT23 ) controllato dal microprocessore, dato che deve mandare a massa fino a 8 volte la corrente di un singolo segmento; naturalmente servono delle resistenze da 10kΩ sulle basi dei transistor. Il display utilizzato ha la sigla LTC2727 e File:LTC2727.pdf qui trovate il datasheet. Il curcuito sbrogliato è abbastanza piccolo, circa 41mm x 50mm, a singola faccia, ha dei fori di montaggio da 3mm ai quattro angoli ed è stato realizzato con la tecnica del toner transfert senza alcun problema ( le piste sono quasi tutte da 24mil ). Di seguito trovate i File:Microclock eagle.zip file eagle per realizzare il circuito.
Firmware
Anche sul firmware c'è poco da dire, gira su un PIC16F1459 per questione di simpatia e disponibilità. Usa l'interrupt su timer1 per aggiornare l'orario ogni secondo e gli interrupt-on-change su RA0 e RA1 per la regolazione dell'orario. È molto poco ottimizzato per il consumo, ma ci si può lavorare. Dato che è abbondantemente commentato metto semplicemente il codice scritto e testato con MPLABX e XC8 v 1.3, chiunque abbia un minimo di esperienza coi microprocessori Microchip lo può capire ( e migliorare ) in pochi minuti. code type = "C"
- include <xc.h>
- pragma config BOREN = OFF, IESO = OFF, FOSC = INTOSC, FCMEN = OFF, MCLRE = OFF
- pragma config WDTE = OFF, CP = OFF, PWRTE = ON, CLKOUTEN = OFF
- pragma config USBLSCLK = 48MHz, LPBOR = OFF, CPUDIV = NOCLKDIV, PLLEN = DISABLED
- pragma config WRT = OFF, STVREN = OFF, PLLMULT = 3x, BORV = HI, LVP = OFF
- define _XTAL_FREQ 4000000 //Quarzo esterno da 32.768kHz, oscillatore interno a 4MHz
//Numeri in uscita su PORTC con configurazione 16Mar14 int NUMBERS[] = { 0b0111111, 0b0100010, 0b1011011, 0b1101011, 0b1100110, 0b1101101,
0b1111101, 0b0100111, 0b1111111, 0b1101111 };
//Contatore su timer 0 int sec = 0, min = 0, h = 0; bit dots = 0;
int main( void ){
OSCCONbits.IRCF = 0b1101; // Internal oscillator @ 4MHz TRISA = 0b00001011; TRISC = 0b00000000; TRISB = 0x00;
//Setting per gli interrupt INTCONbits.GIE = 1;//Abilita gli interrupt INTCONbits.PEIE = 1;//Abilita gli interrupt sulle periferiche esterne ( TMR1) INTCONbits.IOCIE = 1;//Abilita gli interrupt-on-change INTCONbits.IOCIF = 0;
TMR1IE = 1;//Abilita gli interrupt su timer1 TMR1IF = 0;
//Setting di Timer 1
T1CON = 0b10011101; //Timer 1 attivato, utilizza come source un oscillatore
//al quarzo tra T1OSO e T1OSI, prescaler 1:2 e non sincro
TMR1GE = 0; //Le funzionalità del Gate di Timer 1 sono disattivate
//Setting degli interrupt-on-change IOCAP = 0x00;//Spenti sul rising edge di tutte le PORTA IOCAN = 0b00000011;//Interrupt sul falling edge di RA1 e RA0 IOCBP = 0x00;//Spenti sia sul falling che sul rising di PORTB IOCBN = 0x00; //Ciclo di riscrittura del numero parametri di multiplex funzionanti il //17Feb14 5ms x cifra, no resistenze. BC337 a catodo comune 10K sulla base. //Vdd = 3.3V
while( 1 ){
RB4 = 1;
PORTC = NUMBERS[h/10] + dots * 128;
__delay_ms(1);
PORTC = 0x00;
RB4 = 0;
RB6 = 1;
PORTC = NUMBERS[h%10] + dots * 128;
__delay_ms(1);
PORTC = 0x00;
RB6 = 0;
RB5 = 1;
PORTC = NUMBERS[min/10] + dots * 128;
__delay_ms(1);
PORTC = 0x00;
RB5 = 0;
RB7 = 1;
PORTC = NUMBERS[min%10] + dots * 128;
__delay_ms(1);
PORTC = 0x00;
RB7 = 0;
}
}
void interrupt gestione(){
if( TMR1IF ){
//Ricarica l'interrupt.
TMR1H = 0xC0;
TMR1L = 0x00;
dots = !dots;
sec++;
if( sec == 60){
min++;
sec = 0;
}
if( min == 60 ){
min = 0;
h++;
}
if( h == 24 ) h = 0;
TMR1IF = 0;
}
if( IOCAF1 ){
h++;
if( h == 24 ) h = 0;
sec = 0;
TMR1H = 0xc0;
TMR1L = 0x00;
IOCAF1 = 0;
IOCIF = 0;
}
if( IOCAF0 ){
min++;
sec = 0;
if( min==60) min = 0;
TMR1H = 0xc0;
TMR1L = 0x00;
IOCAF0 = 0;
IOCIF = 0;
}
} /code
