Glitch.
Il termine glitch è usato per indicare un picco breve ed improvviso,
inatteso, in una forma d'onda.
La presenza di impulsi inaspettati può essere causa di seri problemi negli
apparati elettronici che impiegano contatori, latch, flip-flop, ecc.
Un glitch può dipendere anche da una non corretta azione durante il cambio di
condizioni dell'uscita che comanda la linea del segnale.
Possono essere le stesse sequenze di istruzioni a produrre glitch.
La prima delle cause che generano un glitch la troviamo in una non corretta
esecuzione dell'inizializzazione degli I/O del microcontroller.
Glitch al cambio di direzione di un I/O.
All'avvio, i pin dei PIC sono configurati come ingressi, il che si riflette
con tutti i bit del registro di direzione TRIS uguali a 1.
Solitamente incontriamo la necessità di modificare la direzione, dato che
alcuni pin serviranno come uscite per comandare dispositivi esterni.
Nella maggior parte degli esempi che troviamo nel WEB, la sequenza tipica
delle istruzioni è questa:
TRISA
= 0 //
all pin on PORTA=out
PORTA = 0b11110000 //
RA<7:4>=1, RA<3:0>=0 |
ma non è corretto, in quanto questo può generare glitch della durata di
una ciclo istruzione. Vediamo perchè
Dopo il reset iniziale (POR) il registro TRIS è presettato a 1, ma i latch di
uscita del port assumono un valore casuale.
Prendiamo, ad esempio, il bit 7: al POR il suo latch contiene 1. Nel momento
in cui portiamo la direzione da ingresso ad uscita, il latch si collega al
buffer del pin, che assume un livello 1.
Con la seconda istruzione, forziamo un valore nei latch; per il bit 7 è 1 e
quindi non ci sono variazioni di stato.
Però, sempre come esempio, il latch del bit 2 al POR contiene pure 1. Come
sopra, nel momento in cui portiamo la direzione da ingresso ad uscita, il
latch si collega al buffer del pin, che assume un livello 1.
Ma la seconda istruzione porta il bit a 0: avremo il pin che, per la durata di
un ciclo ha avuto un glitch alto.
il problema è subdolo in quanto la logica della sequenza di istruzioni è
perfettamente corretta.
La presenza di questo impulso può essere del tutto indifferente per il
dispositivo comandato dal pin, ma può anche non esserlo. In ogni caso, è
del tutto inutile creare un impulso che, al minimo, aumenterà il rumore
elettrico del circuito.
La soluzione è molto semplice: basta invertire la sequenza.
PORTA = 0b11110000 //
preset latch RA<7:4>=1,
RA<3:0>=0
TRISA = 0
// all pin on PORTA=out |
Prima carichiamo i latch con i valori desiderati, poi commutiamo la
direzione come uscita: i valori caricati si presenteranno ai pin senza la
generazione di impulsi indesiderati.
Ancor meglio se abbiamo a disposizione i registri LAT, come nei PIC18 e negli
Enhanced Midrange:
LATA
= 0b11110000 //
preset latch RA<7:4>=1,
RA<3:0>=0
TRISA
= 0 //
all pin on PORTA=out
|
Evitare glitch all'accensione.
In ogni caso va considerato con attenzione
il
fatto che, subito
dopo il reset, i pin sono configurati come ingressi,
quindi ad alta impedenza e non possono fornire
corrente per comandare un carico. In effetti,il livello logico è
indeterminato e questa è la condizione iniziale per qualsiasi pin.
Condizione che, all'arrivo della tensione di alimentazione, può protrarsi per
un tempo sensibile che comprende la somma di vari tempi:
- l'uscita dal reset
- l'eventuale presenza del PowerOnTimer (PWRT)
- le istruzioni necessarie ad arrivare alla definizione dei livelli logiciai
pin
tempo che, complessivamente, può arrivare oltre 100ms.
Molti oggetti collegati ai pin del PIC, come ad esempio LED, non risentono
minimamente di questi problemi, ma altri dispositivi possono avere terminato
il loro reset prima di quello del microcontroller è trovarsi le linee di
ingresso a livelli indeterminati, dato che tutti gli I/O sono ancora
configurati come ingressi (ad alta impedenza).
Ad esempio, comandi di motori o bobine, carichi e periferiche complesse, ecc.
possono trovarsi in condizioni critiche se agli ingressi appaiono tensioni
nell'area indeterminata tra i livelli logici ammessi.
Se
quanto collegato al microcontroller richiede che, all'arrivo della tensione di
alimentazione, ci siano livelli logici certi, si rende indispensabile una
soluzione.
Anche qui la soluzione è semplice: basta collegare sulle linee di uscita
del microcontroller dei pull-up
o pull-down: fino a che il programma non ha preso il controllo delle
uscite, il loro livello sarà determinato dalle resistenze esterne, che
assicurano una situazione adeguata per la periferica collegata.
Attenzione: i weak pull-up integrati, in questa fase, NON possono essere
utili in quanto NON sono abilitati fino a che questo non viene comandato
dall'esecuzione del programma
Una ulteriore possibilità è quella, dove possibile, di mantenere resettata
la periferica fino a quando il programma del microcontroller ne ha preso il
comando, ad esempio usando un pin del microcontroller stesso o un reset
controller esterno che tenga bloccata la periferica fino a che il programma
non ne ha preso il controllo.
.In ogni caso, è opportuno considerare le necessità dei dispositivi
collegati ai pin del PIC e le loro esigenze ed operare sempre per eliminare
possibili situazioni problematiche.
Commutare lo stato di un pin di IO senza
produrre glitch
Spesso è necessario agire su un pin di IO variandone lo stato in funzione
di una condizione di una altro registro o di un flag. Questo può essere una
causa di glitch inaspettati.
Caso tipico è lo shift di un byte di dati su una linea sincrona (comando
di shift register, periferiche seriali data+clock, ecc).
Una soluzione elegante prevede un preset del bit di destinazione e questo
consente di risparmiare linee di codice:
;
set IOpin equal to carry saving 1 cycle
bcf
IOPin ;
preset IO pin low
btfsc
STATUS, C ; if
carry 0, branch and no op
bsf
IOPin ;
otherwise set IO pin |
A fronte di una riduzione del tempo di esecuzione, si viene però a creare
la possibilità di un glitch, ovvero di un impulso indesiderato di breve
durata.
Vediamo quando può succedere questo.
Se STATUS,C = 0, la sequenza viene
eseguita così:
;
IOpin è a livello 1
;
set IOpin equal to carry saving 1 cycle
1 bcf
IOPin ;
il pin viene presettato a 0
2 btfsc
STATUS, C ; se C=0,
salta la riga successiva
bsf
IOPin ;
non eseguita
3 istruzione
successiva |
Ovvero la riga 1 presetta il IOPin = 0,
il branch viene eseguito e l' impostazione del pin non viene cambiata dal test. Nessun
impulso indesiderato.
Ma se STATUS,C = 1, la sequenza viene
eseguita così:
;
IOpin è a livello 1
;
set IOpin equal to carry saving 1 cycle
1 bcf
IOPin ;
il pin viene
presettato a 0
2 btfsc
STATUS, C ;
se C=1,
esegue la riga
successiva
3 bsf
IOPin ;
IOpin viene riportato a 1
4 istruzione
successiva |
- Inizialmente IOPin = 1
- La riga 1 presetta IOPin = 0
- Il branch non viene eseguito
- e l' impostazione del pin a 1 viene invertita
dalla riga 3
Ecco generato un impulso indesiderato!
La sua durata è due cicli (btfsc
non eseguito = 1 ciclo + 1 ciclo del bsf
seguente)
Solamente se il branch viene eseguito il glitch non viene generato.
E
questo può renderlo "pseudo casuale" (la sequenza da origine al glitch solo se
il test non è eseguito), mettendo a dura prova le capacità di debug dello
sviluppatore.
Il fatto può non provocare conseguenze, ma in vari casi il rapido impulso
sull' IO può essere accolto dalle periferiche collegate e dare origine a
conseguenze inaspettate. Ad esempio, se si comanda il clock di uno shft
register, di un contatore o di un flip flop o si scambiano dati seriali.
Anche qui, il problema è subdolo in quanto la logica della sequenza di istruzioni è
perfettamente corretta e il programmatore non considera questa possibilità;
quindi il caso è estremamente difficile da debuggare.
E' come detto prima, va anche considerato che la commutazione rapida di un I/O da origine a fenomeni
di disturbo elettromagnetico e a consumo di energia, soprattutto se si applica
ad un carico di potenza.
Per evitare questa situazione bisognerà ricorrere ad un ciclo a 4
istruzioni, che, a fronte di un codice di dimensione maggiore e quindi più
lento, non produce alcun glitch in uscita
;
Set IOpin equal to carry, but w/o glitch
btfsc
STATUS, C ; branch
if carry = 0
bsf
IOPin ;
if not (carry = 1) set pin
btfss
STATUS, C ; branch
if carry = 1
bcf
IOPin ;
if not (carry = 0) clear pin |
Con questo non sono eseguite commutazioni inutili perchè lo stato dell' IO
viene cambiato solo in conseguenza a uno dei due test: se prima dei test
il pin era a 0 e il Carry è 0, il pin resterà a 0; sarà portato a 1
solamente se il Carry è a 1.
-
Se il cambio di stato del bit non è diretto ad un IO,
ma ad un altro registro interno, il problema del glitch non si pone, in
quanto non ha alcun riflesso all'esterno del chip ed è allora preferibile
la prima sequenza di istruzioni, perchè più breve.
- Quanto evidenziato non ha nulla a che fare con il
problema
dell' R-M-W: si tratta di una diversa questione.
|