Premetto che se ho sbagliato sezione di farmelo subito notare!
Ho realizzato un programmino di prova, che gira sul Pierin, per misurare la frequenza teorica del quarzo del modulo interno RTCC e magari farci quindi qualche autoaggiustamento in SW. Il mio difetto è che poi mi voglio andare ad impegolare per capire a fondo gli aspetti di quel che ho fatto. Oggi sono interessanto alla sua precisione, qualcosa di istruttivo tutto sommato.
Il programma funziona correttamente e il quarzo da misurare, di 32768Hz teorici, senza condensatori di carico il PIC mi dice che è a 32770Hz e con due condesatori da 12pF diventano 32768Hz "precisi", con risoluzione di 1Hz nel mio caso. Se li aumento, scende di un altro paio di Hz al massimo. Quindi è coerente con la misura e la teoria che sta dietro al quarzo.
Quello che vi voglio chiedere è come farsi un'idea corretta di quel "preciso". Ho semplicemente implementato il Timer3 che incrementa ad ogni fronte positivo della frequenza del RTCC, squadrata e buttata fuori da un pin del micro (RB1), avendo il timer3 come ingresso T3CKI sul pin RA5 che riceve i 32768Hz. Quindi collegati con un filo esterno.
La misura dura un secondo "preciso"(?) contato da un altro timer, Timer0.
Ragionando con il codice sottomano, capisco che può dipendere dal momento in cui accendo e spengo la base tempi del timer0 e la precisione del quarzo. Quindi qualcosa come (1/TCY * n_istruzioni + ppm_12MHz) dove le n_istruzioni sono tutte quelle istruzioni che sono in eccesso rispetto all'effettivo spegnimento del Timer0 e TCY è la frequenza di una istruzione; per ridurle al minimo ho spento i timer nella ISR. I ppm_12MHz è l'imprecisione del quarzo di sistema espressa in parti per milione.
Ma non vi chiedo di partire da questo perché potrebbe essere sbagliato, quanto in realtà vorrei sapere come si stima l'imprecisione di un'applicazione del genere.
Qualcuno mi sa dare una mano a ragionare su questo? Sicuramente è pieno di esperti a riguardo.
Ecco il codice, riporto per semplicità solo le parti essenziali, senza gli header vari e tutte le sotto funzioni usate:
main.c
- Codice: Seleziona tutto
/******************************************************************************/
/* Main Program */
/******************************************************************************/
uint16_t start = 0;
uint16_t end = 0;
volatile uint8_t ovflw = 0;
volatile uint8_t passed = 0, measure = 0;
uint16_t period, totale;
int i;
void main(void)
{
/* Configure the oscillator for the device */
ConfigureOscillator();
/* Initialize I/O and Peripherals for application */
InitApp();
lcd_init();
write_comm4(LCD_ENTRYMODESET|LCD_ENTRYLEFT|LCD_ENTRYSHIFTDECREMENT);
while (write_comm4(LCD_BUSYCHECK) != 0);
while(1)
{
printf("Press start.");
for (i= 0; i<1500; i++) __delay_ms(1);
ovflw=0;
TMR3H = 0;
TMR3L = 0;
TMR0H = 0x48; // attesa 1 secondo, 1 step = 1/46875Hz
TMR0L = 0xe5;
T0CONbits.TMR0ON = 1;
T3CONbits.TMR3ON = 1; // via con misura
printf(".");
while(passed==0){
// printf("T0: %u", TMR0);
// write_comm4(LCD_CLEARDISPLAY);
//while (write_comm4(LCD_BUSYCHECK) != 0);
}
T3CONbits.TMR3ON = 0; // stop con misura
T0CONbits.TMR0ON = 0;
passed = 0;
printf(".");
// Calcola!
period = TMR3L;
period |= TMR3H << 8;
write_comm4(LCD_CLEARDISPLAY);
while (write_comm4(LCD_BUSYCHECK) != 0);
write_comm4(LCD_SETDDRAMADDR | 0x40);
while (write_comm4(LCD_BUSYCHECK) != 0);
printf("T: %x - ovf=%d", period, ovflw);
totale = period*(ovflw+1)/1; // Sample 1 secondo
write_comm4(LCD_RETURNHOME);
while (write_comm4(LCD_BUSYCHECK) != 0);
printf("f = %u Hz", totale);
for (i= 0; i<3500; i++) __delay_ms(1);
write_comm4(LCD_CLEARDISPLAY);
while (write_comm4(LCD_BUSYCHECK) != 0);
}
}
interrupts.c
- Codice: Seleziona tutto
/******************************************************************************/
/* Interrupt Routines */
/******************************************************************************/
/* High-priority service */
#if defined(__XC) || defined(HI_TECH_C)
void interrupt high_isr(void)
#elif defined (__18CXX)
#pragma code high_isr=0x08
#pragma interrupt high_isr
void high_isr(void)
#else
#error "Invalid compiler selection for implemented ISR routines"
#endif
{
/* This code stub shows general interrupt handling. Note that these
conditional statements are not handled within 3 seperate if blocks.
Do not use a seperate if block for each interrupt flag to avoid run
time errors. */
#if 1
if(PIR2bits.TMR3IF)
{
// hai overflow!
ovflw++;
PIR2bits.TMR3IF = 0;
}
#endif
}
/* Low-priority interrupt routine */
#if defined(__XC) || defined(HI_TECH_C)
void low_priority interrupt low_isr(void)
#elif defined (__18CXX)
#pragma code low_isr=0x18
#pragma interruptlow low_isr
void low_isr(void)
#else
#error "Invalid compiler selection for implemented ISR routines"
#endif
{
/* This code stub shows general interrupt handling. Note that these
conditional statements are not handled within 3 seperate if blocks.
Do not use a seperate if block for each interrupt flag to avoid run
time errors. */
#if 1
if (INTCONbits.TMR0IF)
{
T0CONbits.TMR0ON = 0;
passed = 1;
T0CONbits.TMR0ON = 1;
INTCONbits.TMR0IF = 0;
}
#endif
}
e infine le altre funzioni, inizializzazioni timer incluse:
- Codice: Seleziona tutto
void InitApp() {
//************************************** Porte digitali
ANCON0 = 0xff;
ANCON1 = 0b00011111;
//************************************************* Interrupt esterni e debug led
// TRISDbits.TRISD5 = 1; // pulsanti
// RPINR1 = 22; // RD5 - usa HW esterno e polling in INT per usare piu pulsanti
INTCON2bits.INTEDG1 = 0;
INTCON3bits.INT1IP = 0; // prior
INTCON3bits.INT1IE = 0; // attiva no
INTCON3bits.INT1IF = 0;
TRISDbits.TRISD7 = 0; //LED
PORTDbits.RD7 = 0;
TRISDbits.TRISD6 = 0;
PORTDbits.RD6 = 0;
//************************** RTCC module config
TRISCbits.TRISC0 = 1;
TRISCbits.TRISC1 = 1;
TRISBbits.TRISB1 = 0;
EECON2 = 0x55;
EECON2 = 0xAA;
RTCCFGbits.RTCWREN = 1;
// HALFSEC stato del secondo
RTCCFGbits.RTCOE = 1;
RTCCFGbits.RTCEN = 1;
PADCFG1 = 0b00000101; // 2^16 su RTCC pin
//******************************* LCD port config
TRISD = 0b00000000; //8 bit data
TRISBbits.TRISB5 = 0; // RS
TRISBbits.TRISB3 = 0; // E
TRISDbits.TRISD4 = 0; // R/#W
PORTBbits.RB5 = 0;
PORTBbits.RB3 = 0;
PORTDbits.RD4 = 0;
//*************************** timer3 config
/* pin esterno, presc 1, sincro, 16bit, off
*/
TRISAbits.TRISA5 = 1;
RPINR6 = 2; // T3CKI su RA5
T3CON = 0b10000010;
PIE2bits.TMR3IE = 1;
IPR2bits.TMR3IP = 1;
//******************************* timer0 config
/* include anche i suoi interrupt*/
/* off
* 16bit
* fosc/4
* 256 prescaler
*
*
*
*/
T0CON = 0b00000111;
/* abilita il timer int, bit 2 IF timer 0, low prior*/
INTCONbits.TMR0IE = 1;
INTCON2bits.TMR0IP = 0;
//***************************************** Priorità e int
/* interrupt enable di tutto il resto, PIE*/
/* interrupt priority per ogni periferica IPR*/
/*The RCON register contains bits used to determine the
cause of the last Reset or wake-up from Idle or Sleep
mode. RCON also contains the bit that enables
interrupt priorities (IPEN).*/
RCONbits.IPEN = 1;
INTCONbits.GIEH = 1;
INTCONbits.GIEL = 1;
}

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)


instead of
(Anonimo).
ain't
, right?
in lieu of
.
for
arithm.

