Tips & Tricks - PIC

 

R-M-W nei PIC16

  • Il problema dell' R-M-W nei PIC16
  • Come evitarlo
  • Note

Il problema dell' R-M-W

Agli utilizzatori dei PIC dovrebbe essere noto il problema di latch indesiderato dello stato di un I/O digitale, chiamato R-M-W Problem.
Esso si manifesta quando si cerca di modificare due o più bit dello stesso port, agendo con istruzioni successive, ad esempio:

        bsf    PORTB, 1
   bsf    PORTB, 2

ne risulta la possibilità che il bit 0 sia settato, ma che il bit 2 resti a zero.
Vediamo di capire perchè questo può succedere.

Per poter avere le idee ben chiare, però, occorre conoscere come funziona un I/O.


Come funziona un I/O

Questa è la struttura semplificata di quanto sta dietro un pin di I/O digitale nei Baseline e Midrange (PIC10/12/16).

Il Data Bus è collegato a:
  • un latch dati (Data Latch)
  • un latch di direzione (TRIS Latch)
  • un gate di lettura (RD Port)

Il Data Latch è collegato al pin attraverso un buffer, costituito da MOSFET a canale P e N. Il buffer è abilitato dal livello 0 in uscita dal TRIS Latch ed è disabilitato quando è 1; quindi questo registro determina se il livello logico scritto nel Data Latch viene trasferito o meno al pin.
Il valore contenuto nel TRS Latch e nel Data Latch viene prelevato dal bus dati rispettivamente quando si indirizza una scrittura al port (o gpio) e al TRIS (segnali WR Port e WR Reg).

Il gate di lettura collega il pin direttamente con il bus dati, trasferendovi la tensione presente sul pin, la quale deve essere ovviamente a livello logico 0 o 1 per essere trattata come dato.

Va considerato che i latch sono memorie di genere RAM, ovvero mantengono il loro contenuto fino a che è presente la tensione di alimentazione o vengono riscritte.
Osserviamo anche il fatto fondamentale che il contenuto del Data Latch e del TRIS Latch possono essere scritti, ma non riletti, in quanto non esiste alcun gate che permette questo.
Si deve osservare, quindi, che:

  • una scrittura sul registro PORT scrive il Data Latch
  • una lettura del PORT legge lo stato reale del pin, non il valore conservato nel latch.

Anche se questa parrebbe una differenza ininfluente, è invece proprio questa situazione a creare  il problema, assieme al meccanismo che i PIC implementano per modificare un registro di I/O.

 Per una comprensione migliore, schematizziamo ulteriormente le funzioni:

Scrivendo il PORT o il GPIO, ad es. con
  movwf GPIO
il dato passa da bus dati al Data Latch.

Se il TRIS è a 1, il buffer è scollegato dal pin ed il livello logico del Data Latch non può passare all' esterno (ma resta conservato nel latch): il livello logico impostato è "presente", ma non ha effetti all'esterno. 
Se portiamo il TRIS a 0, 
   movlw  0 
 TRIS   GPIO 

il buffer viene collegato al pin ed il livello logico immagazzinato nel Data Latch appare all' esterno
Se richiedo una lettura, ad es. con
   movwf GPIO,W  
il segnale  RD attiva il gate di ingresso e il pin è collegato al bus dati.

Da notare che la lettura del PORT può avvenire con qualsiasi valore del TRIS, dato che si legge comunque lo stato del pin: TRIS, semplicemente, abilita e disabilita il buffer in uscita.


Da qui deriva che l' impostazione dell'I/O come uscita si riferisce al fatto che il buffer è collegato o meno al pin e che il contenuto del Data Latch viene riflesso o meno all' esterno. 

Ne consegue che possiamo avere diverse situazioni a seconda dello stato dei bit del Tris Latch:

TRIS Funzione PORT Note
1 Ingresso digitale scrittura Scrivendo un dato in PORTx o GPIO, il dato resta immagazzinato nel Data Latch fino a nuova scrittura o a mancanza di alimentazione.
Non c'è nessun effetto sul pin perchè il Tris=1 disabilita il buffer di uscita
lettura Leggendo PORTx o GPIO, si passa direttamente al bus dati il valore logico applicato al pin
0 Uscita digitale scrittura Tris=0 abilita il buffer di uscita. Scrivendo un dato in PORTx o GPIO, il dato viene immagazzinato nel Data Latch fino a nuova scrittura o a mancanza di alimentazione e passato al buffer del pin.
lettura Leggendo PORTx o GPIO, si passa direttamente al bus dati il valore logico applicato al pin

Ricapitoliamo le conseguenze:

  • Posso scrivere qualsiasi valore nel registro PORTx o GPIO, ma questo valore viene riflesso sul pin corrispondente solamente se il buffer di uscita è abilitato da TRIS=0.
    Con  TRIS=1 è comunque possibile la scrittura del Data Latch, che ha come effetto solamente il caricamento del dato nel latch; se carico un dato nel registro GPIO, ad esempio con clrf GPIO questo valore non va perso, ma resta nel latch fino alla caduta della tensione di alimentazione o ad una nuova scrittura. Nel momento in cui portassi TRIS=0 questo valore verrebbe riflesso sul pin fisico.
  • Per contro, è molto importante capire che la lettura di PORTx o GPIO non legge lo stato del Data Latch, ma il valore effettivo che il pin assume nel preciso momento della lettura e che, come vedremo, può essere diverso da quello del Data Latch.
    La lettura può essere effettuata indipendentemente dal valore impostato nel TRIS, dato che, come abbiamo appena visto, l'operazione consiste nella abilitazione del gate di lettura e nel trasferimento del dato sul bus.

Come  può crearsi il problema R-M-W

Vediamo il meccanismo attraverso cui si forma il problema R-M-W.

Dallo schema di principio qui sopra, vediamo che tra il latch dati e il pin è interposto un buffer totem pole a MOSFET. Questo buffer è in grado di fornire una corrente di 25 mA e i semiconduttori hanno una resistenza interna di conduzione, anche se minima.
25 mA non è una corrente immensa, ma è più che sufficiente per gli scopi per cui è stato progettato il chip.  Però è ben possibile applicare al pin carichi non proprio ideali, come ad esempio qualcosa di capacitivo.

Sappiamo che un condensatore scarico è, al momento dell' applicazione della tensione, l' equivalente di un corto circuito e che per completarne la carica occorre un tempo che dipende dall' impedenza della sorgente di tensione e delle capacità.

Quindi, portando a livello 1 uno dei pin della figura a fianco, il LED si accenderà con un ritardo proporzionale alla capacità del condensatore, in quanto questo, nella sua carica, shunterà gran parte della corrente al momento iniziale.

Questo fa si che, misurando con uno strumento la tensione sul pin, ci si troverà con una curva tensione-tempo tipica della carica di un condensatore.

Dal momento in cui è applicata tensione al momento in cui il condensatore è caricato al 100% trascorre un tempo determinato dalla impedenza della sorgente (in questo caso il buffer del pin) e dalla capacità del condensatore.

Quanto maggiore è la capacità del condensatore, tanto maggiore sarà il tempo necessario per la stabilizzazione della tensione. La resistenza R è quella di conduzione dei MOSFET del buffer.

Può capitare che il carico capacitivo del pin sia tale da richiedere alla tensione un tempo maggiore di quello del ciclo delle istruzioni (che, ricordiamo, a 20 MHz è di soli 200 ns, mentre la carica di una capacità potrebbe richiedere micro o milli secondi) per arrivare a livello 1.

E se le istruzioni si succedono ad una velocità superiore a quella di variazione della tensione del pin, si verifica una discordanza tra il valore impostato nel Data Latch e quello reale presente sul pin stesso.


R-M-W

Occorrono ancora due considerazioni.

La prima riguarda questo fatto: eseguendo una istruzione di lettura o scrittura diretta ad uno specifico bit di un port, come bsf PORTB,0 oppure btfsc PORTB,0 non si accede al solo bit indicato, ma a TUTTI i bit che fanno parte del port.
Quindi, ad esempio, btfsc PORTB, 0 non legge e verifica il solo bit 0, ma legge l' intero port a cui appartiene il bit, che poi sarà il solo ad essere considerato. Questo avviene perchè non è possibile, data la struttura dei chip, leggere o scrivere un singolo bit, ma occorre passare attraverso il bus dati, che ha ampiezza di 8 bit, e che accede a tutti i bit del port contemporaneamente.
In questo senso gli schemi di principio visti all'inizio, possono essere fuorvianti; occorre pensare il singolo bit come parte di un port.  Questa struttura costruttiva e logica è quella che fa si che gli I/O, di conseguenza, siano raccolti in gruppi logici (PORT o GPIO) con ampiezza massima di 8 elementi.
Così, se il microcontroller ha meno di 8 I/O, ad esempio 6 come nei PIC12Fxxx, il registro GPIO sarà comunque composto da 8 bit, di cui i due più alti non sono implementati. Per contro, se il chip dispone di più di 8 I/O, essi saranno raggruppati in più PORT, ciascuno di 8 elementi al massimo. Così, nel 16F84, dove abbiamo disponibili 13 I/O, li troviamo raccolti in due PORT, uno da 8 e l'altro da 5 (che, comunque, è un byte dati di 8 bit di cui i tre più alti non sono implementati).

Il secondo punto riguarda il meccanismo di accesso e scrittura dei PORT. Esso si basa sul clock interno che è pari a 1/4 della frequenza dell'oscillatore principale.
L' operazione di modifica di un bit (o di un byte) avviene, come tutte le istruzioni ad un ciclo, utilizzando 4 impulsi del clock primario. Così, se il quarzo dell' oscillatore è 4 MHz, occorreranno 4 impulsi per completare una istruzione, il cui ciclo effettivo sarà 1/4 del clock, ovvero 1 MHz (Fosc/4).

Il clock  principale (clock interno RC o esterno) è diviso per quattro per generare quattro impulsi non sovrapposti, cioè Q1, Q2, Q3 e Q4. Il program counter è incrementato ad ogni Q1. Un ciclo di istruzione è costituito dai quattro cicli: l'istruzione di viene recuperata ed eseguita in una pipeline che tratta una istruzione mentre decodifica ed esegue la precedente. Una istruzione che impegna in esecuzione un solo ciclo, è completata nelle quattro fasi, a cui si sovrappone nella pilpeline il fetch della istruzione successiva.  (Fanno eccezione istruzioni che richiedono la modifica del PC, come goto e return, che richiedono due cicli in quanto la pipeline, che aveva già immagazzinato l' indirizzo dell' istruzione successiva, deve essere ricaricata con il giusto valore imposto dall' istruzione). In particolare, la memoria dati viene letta durante il Q2 e scritta durante il Q4.

Quindi, dietro alla semplice bsf PORTB,0 troviamo, non vista, una azione complessa:

  1. viene abilitato il gate di lettura e tutto lo stato dei pin del gruppo a cui il pin appartiene (in questo caso PORTB) viene passato al bus dati
  2. questo byte viene salvato in un registro interno, non accessibile se non dai meccanismi della CPU
  3. il bit indicato (in questo caso il bit0) viene modificato nel registro interno
  4. il registro viene copiato nel Data Latch

Da qui deriva la dicitura R-M-W:

  1. R - lettura del port e copia in un registro provvisorio interno
  2. M - modifica del valore letto con quello voluto, sempre nel registro provvisorio
  3. W - copia del registro provvisorio modificato nel Data Latch

Facciamo un esempio pratico: vogliamo portare a 1 i bit 4 e 5 di GPIO.  Ai pin sono collegati carchi esterni, ad esempio due LED, ma al GP5 è collegata anche una certa capacità. La cosa che parrebbe più ovvia è:

; set IOpin 
    bsf   GPIO, GP5  
    bsf   GPIO, GP4  

ma questa può non essere una buona soluzione. Tenendo presente quanto scritto prima, vediamo perchè.

Immaginiamo per semplicità che GPIO sia inizialmente tutto a 0. La prima linea:

    bsf GPIO, GP5 

esegue queste operazioni 

  • Q1: l' istruzione bsf è decodificata

  • Q2: viene letto lo stato attuale del 
    GPIO
    = 00000000 e questo valore è salvato in un registro provvisorio

  • Q3: viene modificato il bit 5 portandolo a 1; il registro provvisorio conterrà 00100000

  • Q4: il registro provvisorio viene copiato nel Data Latch

Questa operazione è completata in un ciclo, che a 4 MHz dura 1 microsecondo.

Il condensatore esterno collegato al pin comincia a caricarsi alla massima corrente erogabile dal pin e richiede un certo numero di microsecondi, ad esempio 3 us.

Intanto il processore non si è certo fermato in attesa della carica del condensatore, ma sta provvedendo all' esecuzione dell' istruzione
bsf successiva:

   bsf   GPIO, GP4

  • Q1: l' istruzione bsf è decodificata

  • Q2: viene letto lo stato attuale del GPIO che è 00000000 , dato che il bit 5 non ha ancora raggiunto il livello1 e questo valore è salvato nel registro provvisorio che varrà 00000000 

  • Q3: viene modificato il bit 4 portandolo a 1; il registro provvisorio conterrà 00010000 

  • Q4: il registro provvisorio viene copiato nel Data Latch 

Il LED collegato a GP4 si accenderà, mentre il LED a GP5 resterà spento !!!
Cosa è successo ?

  • Q2 ha letto lo stato reale di GPIO dopo pochi nano secondi dalla fine dell' istruzione precedente.

  • Il condensatore si sta ancora caricando ed il suo livello non è ancora arrivato a 1 (vedi curva nel diagramma precedente). 

  • Viene letto non il valore salvato nel Data Latch, ma il livello logico sul pin in quel preciso istante e  questo livello non è ancora a 1 !!!

  • Di conseguenza, la modifica in Q3 non opera su 001000000, come ci si aspetterebbe, ma su 00000000 (dato che lo stato di bit 0 non è ancora 1). Quindi modifica comporta 00010000 e non 00110000 ed è questo il valore che viene scritto nel Data Latch

Il pin GP5 ha fatto un tentativo di andare a livello 1, ma il successivo bsf lo ha cancellato: il settaggio del bit 5 è sparito!!!

Questo accade anche se modifichiamo un solo bit, ad esempio:

; impulso sul bit 5 
    bsf   GPIO, GP5  
    bcf   GPIO, GP5  

dato che succede quanto segue:

      bsf GPIO, GP5

  1. viene abilitato il gate di lettura e tutto lo stato dei pin del GPIO viene passato al bus dati
    Dato che il livello logico al pin 5  è a 0, la lettura rende 00000000
  2. questo byte viene salvato nel registro interno, che contiene ora  0000000
  3. il bit 5 vene modificato nel registro interno, che ora vale 00100000
  4. il registro viene copiato nel GPIO, ovvero scritto nel latch dati che ora viene modificato con il valore 00100000

Se il bit 5 non ha collegato capacità sensibili, il livello al pin sale a 1 in un tempo molto breve (ns).
Se però al pin è collegata una certa capacità che determina un tempo di oltre 1us, la seconda istruzione produce questo:

   bcf GPIO, GP5

  1. viene abilitato il gate di lettura e tutto lo stato dei pin del GPIO viene passato al bus dati
    Dato che il livello logico al pin 5 non è ancora arrivato a 1, la lettura rende 00000000
  2. questo byte viene salvato nel registro interno, che contiene ora  0000000
  3. il bit 5 vene azzerato nel registro interno, che ora vale 00000000
  4. il registro viene copiato nel GPIO, ovvero scritto nel latch dati che ora viene modificato con il valore 00000000

Il livello logico del pin non cambia!!!
Se si è usata questa sequenza per dare un breve impulso sul pin, ad esempio per uno strobe di una periferica e la capacità i ingresso di questa e dei collegamenti genera ' errore, non si avrà alcun impulso in uscita.

Quindi, nei Baseline e nei Midrange, istruzioni consecutive di modifica dello stato degli I/O di uno stesso port devono essere evitate ed occorre un approccio diverso.


Alcune considerazioni

R-M-W problem si presenta solo nei casi in cui il carico capacitivo è troppo elevato rispetto alla cadenza di modifica imposta sul port, o, più in generale, quando il carico applicato al pin è eccessivo.
Casi possibili sono:

  • carico capacitivo, ad esempio rete RC per eliminare la componente variabile da un segnale
  • pilotaggio diretto di gate di grossi MOSFET
  • carico induttivo collegato direttamente
  • corrente eccessiva, ad es. LED senza resistenza di limitazione
  • cablaggi in uscita lunghi e disordinati

Il problema è proporzionale alla frequenza del clock. A 20MHz una istruzione è eseguita in 200ns (200 miliardesimi di secondo!) e quindi si risente l' effetto anche di piccole capacità.

Ovvero, i due fattori chiave sono la capacità del carico e la frequenza di commutazione.

Quindi, nel classico comando di porte logiche, basi di transistor per piccoli segnali, Logic Level MOSFET, operazionali o buffer del genere ULN2003 o simili, usati per pilotare display o relè o altri carichi pesanti che superano i 25mA della capacità diretta del pin,  oppure anche comandando LED direttamente collegati al pin con la relativa resistenza di limitazione, entro i limiti di corrente indicati, il problema, in genere non si verifica e non occorre applicare modifiche al programma.
Ugualmente
è difficile incontrare
problemi comandando piccoli relè reed, display LCD e simili altri carichi a bassa capacità anche se collegati con brevi cablaggi ordinati
Per il pilotaggio di MOSFET
è consigliato comunque l' uso dei buffer specifici (MCP1404 e simili) che elimina completamente ogni possibilità di insorgenza del problema anche con componenti non Logic Gate. Da osservare che il comando di MOSFET in PWM con il pin di uscita del modulo CCP/ECCP non comporta il problema, dato che il pin è azionato singolarmente dal modulo. Non è detto, però, che una azione contemporanea di altri bit sullo stesso port non crei situazioni inaspettate, anche se non risultano segnalazioni in tal senso.

Si può concludere che, in generale, onde evitarsi problemi, è particolarmente opportuno, per prima cosa, collegare ai pin solamente carichi "regolari", utilizzando quanto possibile buffer, che eliminano il problema.  Quando ciò non è possibile per qualsiasi ragione, e, comunque, in ogni caso in cui si comandino uscite contemporanee di bit sullo stesso port, è sempre il caso di verificare sufficientemente il comportamento del proprio specifico circuito ed agire di conseguenza.  
Nel dubbio, meglio implementare comunque una soluzione software piuttosto che rischiare di trovarsi nei guai, durante il funzionamento, per il mancato comando di un pin.

E questo, non solo per realizzazioni destinate all' uso al di fuori del laboratorio, ma anche nelle sperimentazioni, onde abituarsi a considerare ogni aspetto dei problemi collegati alla gestione di carichi sulle uscite del microcontroller, dato che, a parte casi assolutamente straordinari, l' aggiunta delle istruzioni necessarie alla correzione del problema R-M-W non comporta significativi rallentamenti del programma, occupando ben pochi cicli di istruzione.

Vediamo allora come agire.


Missione I/O sicuro

Microchip consiglia diverse soluzioni.

Soluzione 1: 

Mantenere la capacità del carico sufficientemente bassa da non provocare l' effetto ritardo. 

Il parametro D090 del foglio dati "output-high voltage" dichiara una tensione minima di uscita di Vdd-0.7V per una corrente di 3 mA  e il parametro D080 "output-low voltage" dà una tensione di 0.6V per assorbire una corrente di 8.5 mA.  Questi livelli di tensione assicurano un corretto funzionamento delle logiche di ingresso dei pin, indipendentemente dal fatto che siano TTL o trigger.
Inoltre i parametro D101  limita a 50pF la capacità del carico per poter rispettare le temporizzazioni corrette.  Va notato che questi parametri sono dipendenti dalla temperatura, il che vuol dire che non vanno considerati come valori di sicurezza generale, ma come limiti dai quali prendere una distanza di sicurezza.

Se il carico è adeguato, la sequenza di istruzioni:

; set IOpin 
    bsf      PORTB, 0  
    bsf      PORTB, 1  

funzionerà sicuramente, senza problemi, dato che il tempo di ritardo del bit 0 sarà molto minore del ciclo di istruzione.

In sostanza, basta collegare ai pin carichi adeguati, non capacitivi (come possono essere gate di MOSFET, cavi lunghi, ecc.) e, dove sia richiesto comandare tali carichi, inserire un buffer.
 

Soluzione 2:

Usare la tecnica dello shadow-register.
Per ogni registro di uscita, si riserva in memoria un registro RAM che serve da copia e si usa questo registro-ombra per le modifiche.

Ad esempio:

; set IOpin 
  
bsf   PB_Shadow,RB0  ; set bit 0 nel registro copia
   movf 
PB_Shadow,W    ; copia il registro ombra
   movwf PORTB         
; nel PORTB
   b
s
  PB_Shadow,RB1  ; set bit 1 nel registro copia
   movf 
PB_Shadow,W    ; copia il registro ombra
   movwf PORTB         
; nel PORTB

Questo funziona in ogni caso ed è la soluzione generalmente usata, ma ha, per contro, l' aumento delle istruzioni e la necessità di utilizzare più memoria.


Soluzione 3: 

Usare un ritardo software.
Questo vuol dire inserire un ritardo con istruzioni in modo che la cadenza delle azioni sullo stesso port sia tale da permettere la carica di eventuali capacità collegate. 
Ad esempio:

; set IOpin 
   bsf   P
ORTB,       ; set bit 0
   
call  wait_10US      ; attendi 10 microsecondi
   bsf   PORTB, 1       ; set bit 1
   
call  wait_10US      ; attendi 10 microsecondi

Questo funziona, ma ha, per contro molti elementi:

  • l' aumento delle istruzioni 
  • il rallentamento dell' esecuzione
  • occorre verificare che il wait introdotto sia realmente in grado di evitare il problema, che dipende dal carico collegato
  • il ritardo dipende dalla frequenza dell' oscillatore e la routine può dover essere modificata


Soluzione 4: 

Polling dello stato dell' uscita.
Dopo aver effettuato una modifica al port, attendere che la modifica sia effettiva prima di avviarne una successiva. Ad esempio:

; set IOpin 
   bsf   PORTB,       ; set bit 0
chk_0
  
btfss PORTB, 0       ; attendi fino a che il pin è
  
goto  chk_0          ; andato a livello 1

   bsf   PORTB, 1       ; set bit 1
chk_1
  
btfss PORTB, 1       ; attendi fino a che il pin è
  
goto  chk_1          ; andato a livello 1

Questa soluzione è simile alla precedente, ma il tempo impegnato è ottimizzato.

Ovviamente, comandando buffer con ingresso a bassa capacità, in generale si può evitare la correzione del software.
 

Soluzione 5:

Passare da Baseline e Midrange a Enhanced Midrange e PIC18F. Per questi il problema R-M-W non esiste, data la diversa struttura del port, come vediamo nella pagine seguenti.


Note finali

  1. Il problema R-M-W riguarda i registri di comando degli I/O e non i registri interni del chip, che non possono avere carichi irregolari. Quindi, se la sequenza:
    ; set bit 
        bsf   GPIO, GP5  
        bsf   GPIO, GP4 
        bsf   GPIO, GP
     

    è a rischio, dato che il successo dipende dal carico collegato ai pin, questa:

    ; set bit in RAM dati 
        bsf  
    RAM1, 5  
        bsf  
    RAM1
    , 4 
        bsf  
    RAM1, 2  

    ha successo, dato che si tratta di un registro interno.
     

  2. Va compreso che quanto evidenziato non ha nulla a che fare con il problema della formazione di glitch a seguito di commutazioni indesiderate dei pin di uscita e neppure con il problema del debounce per i segnali in ingresso.
    I primi dipendono da come viene gestita l' uscita. Il secondo dipende dalle caratteristiche della sorgente del segnale in ingresso.

e ancora...

Secondo Microchip, esistono altre forme di R-M-W non sempre considerate, e pertanto più subdole, che appaiono in altre situazioni critiche, principalmente riguardanti l' uso di moduli di comunicazione integrati assieme ad I/O digitali sullo steso port.
Il caso noto riguarda l'uso di MSSP in modo I²C , mentre si vuole modificare un bit del TRIS dello stesso registro. Il cambio di direzione da ingresso a uscita e viceversa, a seconda di come è impostato il latch del pin, potrebbe generare situazioni di mancato comando o di glitch.
In generale, è consigliato di mettere i pin a cui è necessario cambiare dinamicamente la direzione in port diversi da quello usato dal modulo MSSP, evitandosi così ogni problema.
Se però questo è realmente necessario, per sicurezza occorre modificare il bit voluto e in ogni caso settare anche i bit usati dall' I²C prima di scrivere il valore nel TRIS register, oppure usare la tecnica dello shadow-register.

Inoltre, se si implementa un bit banging come I²C master, emulando la funzione in software senza l' uso dell' MSSP occorre cura particolare nella gestione dei bit che vengono modificati dal programma.

I²C utilizza un bus open collector pull-up esterno, e, manovrando i bit del TRIS register per cambiare la direzione dei pin,  occorre fare attenzione.  Un bus open collector, ovvero sostenuto da una resistenza di pull-up. potrebbe avere una elevata capacità, il dipendenza dalla sua estensione fisica e dal numero di periferiche collegate. Microchip dà questo esempio:

; Initialization of the port-pin's (i2c-pins are inputs,
; appropriate port-bits are cleared)

   BANKSEL TRISC       ;
select Bank for accessing the
                       ;
tris-register
   bsf TRISC,RC3       ;
C,3 is input (SCL)
   bsf TRISC,RC4       ;
C,4 is input (SDA)
   BANKSEL PORTC       ;
select Bank for accessing the
                       ;
port-register

   movlw b'
11100111
'
   andwf PORTC,F       ;
clear the i2c-port-register-bits
   ...
;
Generate Start-Bit
   BANKSEL TRISC
   bcf TRISC,RC4       ; C,4 (SDA) is output-now
; as port-register-bit is zero, it will go low

   call Delay_4_us     ; short delay of 4 usec (required for
                       ; 100 kHz-bus)

   bcf TRISC,RC3       ; C,3 (SCL) is output-now
; as port-register-bit is zero, it will go low

 

L' azione consecutiva rapida su due bit dello stesso port, a causa della capacità introdotta dal bus open collector richiede che sia verificato il raggiungimento del livello voluto.
Se
tra l' inizializzazione e la generazione dei bit seriali non è stata utilizzata alcuna istruzione non si può essere sicuri che i bit PORTC3: 4 siano ancora a zero (come SDA e SCL, le linee sono alte, se la comunicazione non è attiva).
Una soluzione proposta è
quella di includere l'  inizializzazione dei pin della I²C all' inizio del programma ed evitare qualsiasi istruzioni RMW su altri bit dello stesso port mentre la comunicazione I²C è in corso (tra cui va considerata anche una eventuale routine di interrupt, che potrebbe essere ancora attiva).

 


Vediamo ora come il Problema R-M-W è stato risolto da Microchip nei PIC18, attraverso una semplice variazione della struttura dei registri dei PORT.