Cambiare stato di un pin senza DigitalWrite

Buongiorno ragazzi, sono bloccato di fronte ad un problema stupido... Vorrei sapere come fare a cambiare lo stato di un pin utilizzando PORTD. Devo inserirlo in una ISR e non voglio utilizzare ne digitalWrite ne digitalRead, vorrei un equivalente di questo tanto per spiegarmi meglio:

digitalWrite(2, !digitalRead(2))

Con PORTD = B00000100 ok lo porto alto, ma al successivo ingresso nella ISR deve diventare PORTD = B00000000. Vorrei farlo con una sola riga. Negando quel bit. Se è 0 lo metti a 1, se è 1 lo metti a 0.

Come al solito vi ringrazio tantissimo.

Lollo82:
Con PORTD = B00000100 ok lo porto alto

nok :slight_smile: porti anche bassi tutti gli altri: PORTD |= B0000100;

Negando quel bit. Se è 0 lo metti a 1, se è 1 lo metti a 0.

Usa lo XOR Luke...

Molto più sempliceme, gli AVR hanno la possibilità di "pin toggle" (se 1 lo porta a 0 e viceversa) ... occorre scrivere su PIN (che normalmente è solo per la lettura) invece che su PORT :smiley:

PIND = _BV(PD2);  // che corrisponde al suo bit xxxxxXxx

... attenzione è proprio =, e NON |= , mi raccomando.

Guglielmo

P.S.: La macro _BV() è definita come #define _BV(bit) (1 << (bit)) in <avr/io.h>

Claudio_FF:
nok :slight_smile: porti anche bassi tutti gli altri: PORTD |= B0000100;
Usa lo XOR Luke...

Grande. Ti devo una birra...
Grazie mille!!! Mi stavo perdendo in un bicchiere d'acqua...

gpb01:
Molto più sempliceme, gli AVR hanno la possibilità di "pin toggle" (se 1 lo porta a 0 e viceversa) ... occorre scrivere su PIN (che normalmente è solo per la lettura) invece che su PORT :smiley:

PIND = _BV(PD2);  // che corrisponde al suo bit xxxxxXxx

Guglielmo

Grazie mille!!! Provo anche questa!!! In termini di velocità di commutazione cambia qualcosa rispetto alle due soluzioni???

Lollo82:
In termini di velocità di commutazione cambia qualcosa rispetto alle due soluzioni???

Mah ... probabilmente è più veloce la mia soluzione (... una singola scrittura) che fare un XOR (... legge il valore della porta, fa XOR, scrive nella porta) ...

Guglielmo

gpb01:
Mah ... probabilmente è più veloce la mia soluzione (... una singola scrittura) che fare un XOR (... legge il valore della porta, fa XOR, scrive nella porta) ...

Guglielmo

Ok allora uso questa. Mi serve avere due segnali uno negato rispetto all'altro il più sincronizzato possibile.

Guarda, la XOR dell'esempio, scritta il più compatto possibile, viene così compilata:

// PORTD ^= (1<<2);

in  r24, PORTD
ori r24, (1<<2)
out PORTD, r24

... mentre la versione cn la scrittura su PIN diventa:

// PIND = _BV(PD2);

ldi r24, (1<<2)
out PIND, r24

... quindi, alla fine, si risparmia una istruzione ad ogni "toggle".

Guglielmo

gpb01:
Guarda, la XOR dell'esempio, scritta il più compatto possibile, viene così compilata:

// PORTD ^= (1<<2);

in  r24, PORTD
ori r24, (1<<2)
out PORTD, r24



... mentre la versione cn la scrittura su PIN diventa:



// PIND = _BV(PD2);

ldi r24, (1<<2)
out PIND, r24



... quindi, alla fine, si risparmia una istruzione ad ogni "*toggle*".

Guglielmo

Grazie mille. Non mandarmi a cagare, un'ultima cosa. Con quella funzione posso negare un pin rispetto ad un altro???
Ho creato l'onda quadra che volevo sul pin 2, e vorrei la stessa ma negata sul pin 3. Utilizzando le normali istruzioni avevo scritto facendo probabilmente una porcata digitalWrite(3, !digitalRead(2)), ma funzionava (a parte un lievissimo ritardo da i due segnali).
Avevo pensato di farlo partire da HIGH nel setup ma non funziona, ho un picco alto al momento della commutazione e poi torna basso. Grazie ancora.

gpb01:
Guarda, la XOR dell'esempio, scritta il più compatto possibile, viene così compilata:

// PORTD ^= (1<<2);

in  r24, PORTD
ori r24, (1<<2)
out PORTD, r24



... mentre la versione cn la scrittura su PIN diventa:



// PIND = _BV(PD2);

ldi r24, (1<<2)
out PIND, r24



... quindi, alla fine, si risparmia una istruzione ad ogni "*toggle*".

Guglielmo

Scusa lo OT, Guglielmo, ma si può programmare anche con queste istruzioni?
Sono le stesse che uso quando programmo in ladder i plc (LD LDI AND ANI OR ORI OUT RST SET ecc...)

Lollo82:
Con quella funzione posso negare un pin rispetto ad un altro???

No, quella fa semplicemente il "toggle" di un pin e basta.

Guglielmo

steve-cr:
Scusa lo OT, Guglielmo, ma si può programmare anche con queste istruzioni?

Quello è l'assembler del ATmega328P ... se te lo studi naturalmente puoi programmare anche con quello :grin:

QUESTO è un ottimo libro per imparare. :wink:

Guglielmo

Mi serve avere due segnali uno negato rispetto all'altro il più sincronizzato possibile.

Se vuoi che i due segnali siano perfettamente sincronizzati devi comandarli con una sola istruzione.

Inizializza i due pin uno alto e uno basso nel setup.
Nella tua routine di interrupt carica su PIND un byte con i due bit corrispondenti ai pin ad uno: in questo modo togglano contemporaneamente.

Qui sotto trovi un esempio, senza l'interrupt, con il segnale generato nel loop.

void setup() 
{
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);

  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);  
}

void loop() 
{
  PIND = 0B00001100;
  delay(2);
}

Marco

Sulimarco:
Se vuoi che i due segnali siano perfettamente sincronizzati devi comandarli con una sola istruzione.
Inizializza i due pin uno alto e uno basso nel setup .....

Grazie Marco, con questo ho ottenuto proprio quello che volevo. Non mi veniva perchè da pirla avevo sbagliato il nome della isr di overflow e quindi non mi riaggiornava TCNT a 70 una volta cambiato lo stato.
Anche se vi ci pulirete il didietro un karma a tutti voi non ve lo toglie nessuno. Grazie ancora per avermi aiutato a seguire una strada a me ancora poco chiara.

>Lollo82: Quando si quota un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce. Gli utenti da device "mobile" ringrazieranno per la cortesia :wink:

Guglielmo

P.S.: Ho troncato io il tuo "quote" qua sopra ...