Go Down

Topic: Velocizzare l'IO delle porte digitali (Read 22133 times) previous topic - next topic

astrobeed


C'è un modo per impostare contemporaneamente 2 o 3 bit di una porta (PORTB/C/D) lasciando invariati tutti gli altri nello stato in cui sono.


No, almeno non con una singola operazione perché prima devi leggere tutti i bit della porta e poi devi applicare una opportuna mascheratura per lasciare invariati quelli che non devi scrivere.
SBI e CBI usano solo due cicli macchina, anche se ne usi otto in fila in totale fanno 16 cicli macchina che è sempre meno di quelli che dovresti usare con qualunque altro sistema, rammento che CBI e SBI sono istruzioni assemby specifiche per modificare un singolo bit di un registro, meglio di queste non esiste nulla.
Scientia potentia est

PaoloP



C'è un modo per impostare contemporaneamente 2 o 3 bit di una porta (PORTB/C/D) lasciando invariati tutti gli altri nello stato in cui sono.


No, almeno non con una singola operazione perché prima devi leggere tutti i bit della porta e poi devi applicare una opportuna mascheratura per lasciare invariati quelli che non devi scrivere.
SBI e CBI usano solo due cicli macchina, anche se ne usi otto in fila in totale fanno 16 cicli macchina che è sempre meno di quelli che dovresti usare con qualunque altro sistema, rammento che CBI e SBI sono istruzioni assemby specifiche per modificare un singolo bit di un registro, meglio di queste non esiste nulla.


Perchè, in questo modo non stai settando vari bit con una operazione?
Lo scopo dell'OR è proprio quello di non dover leggere perchè lascia gli altri bit inalterati

Comunque settando la porta puoi settare più pin contemporaneamente.
Quote
PORTD |= B10101000; // sets digital pins 7,5,3 HIGH


pablos71

#32
Mar 09, 2013, 05:32 pm Last Edit: Mar 09, 2013, 05:51 pm by pablos Reason: 1
Quote
Stato della porta prima 01010101 vorrei che lo stato della porta sia 01010010  cambiando gli ultimi 3 bit lasciando invariati i primi 5 tutti insieme.


o conosci il byte da mandare al registro in un unica soluzione
01010101 = 85 in dec
01010010 = 82 in dec

PORTD = 82; ecco il tuo comando in un unico colpo!! In realtà riscrive tutto, ma i restanti 5 bit sono gli stessi di prima

o ricostruisci il byte
bitWrite(PORTD, 0, 0);  //bit 0
bitWrite(PORTD, 1, 1);  //bit 1
bitWrite(PORTD, 2, 0);  //bit 2
gli altri bit 3/4/5/6/7 restano invariati

oppure bitset ....
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

leo72


Lo scopo dell'OR è proprio quello di non dover leggere perchè lascia gli altri bit inalterati

Ma lui voleva anche spengere dei bit, quindi AND. E non puoi fare OR e AND insieme.
Però col sistema della variabile temporanea che gli ho illustrato, può fare tutto con una sola operazione sul registro della porta, ovviamente "costruendo" prima le modifiche sulla variabile tampone.

erpomata


astrobeed


Ma lui voleva anche spengere dei bit, quindi AND. E non puoi fare OR e AND insieme.


Esatto, infatti alla fine sei sempre costretto a leggere il port, applicare le mascheratura, AND e OR, per ottenere il nuovo valore da scrivere nel port, ecco perché si fa sempre prima a settare/resettare i singoli bit tramite CBI e SBI, anche dovendo agire su tutti i bit alla fine ci si mette meno tempo (cicli macchina) di altri sistemi.
Scientia potentia est

Maurotec



Ma lui voleva anche spengere dei bit, quindi AND. E non puoi fare OR e AND insieme.


Esatto, infatti alla fine sei sempre costretto a leggere il port, applicare le mascheratura, AND e OR, per ottenere il nuovo valore da scrivere nel port, ecco perché si fa sempre prima a settare/resettare i singoli bit tramite CBI e SBI, anche dovendo agire su tutti i bit alla fine ci si mette meno tempo (cicli macchina) di altri sistemi.


Quindi questa operazione:
PORTA &= (PORTA & 248) | value;
PORTA = (PORTA & 248) | value;
Impiega più cicli macchina sempre in ogni occasione rispetto alla manipolazione con _BV(bit)?

Comunque in tutti i casi con _BV(bit) operi su un singolo bit per istruzione, quindi manipoli la porta in tempi diversi.
oppure componendo più _BV(bit) | _BV() | ecc agisci su più bit in una unica soluzione, agendo nello stesso istante.

Però anche se ho letto tutti i post, non ho capito se si tratta di una curiosità, oppure di un problema reale che non si sa come affrontare.

Ciao.

astrobeed


Impiega più cicli macchina sempre in ogni occasione rispetto alla manipolazione con _BV(bit)?


Ovviamente non è possibile dare una risposta assoluta, tocca valutare caso per caso, però se usi CBI e SBI hai la certezza matematica che per modificare un singolo bit di un registro, vale anche per quelli delle periferiche e non solo dei port, viene usata una singola istruzione assembly, ovvero una singola word della flash, e due cicli macchina sempre e comunque.
Scientia potentia est

leo72

Ho preso l'Arduino ed ho compilato il seguente sketch:

Code: [Select]
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

void setup() {
    sbi(PORTD, 1);
}

void loop() {
}

468 byte di esegubile.
Alla riga 55, ecco l'istruzione in assembly:
Code: [Select]
  a6: 9a 59      sbi $0b, 1
Confermato quanto da te detto.

OK. Andiamo avanti. Ho rifatto il test col seguente codice:
Code: [Select]
void setup() {
    PORTD |= (1<<1);
}

void loop() {
}

468 byte di eseguibile. Curioso... Disassemblo e alla riga 55 cosa mi appare?
Code: [Select]
  a6: 9a 59      sbi $0b, 1
Il compilatore quindi ha ottimizzato ed in output ha dato lo stesso codice.

erpomata

Bè quindi il compilatore lavora benissimo.

Grazie per la prova.

PaoloP

Sarebbe interessante cambiare ottimizzazione del compilatore da "size" a "speed".
Incrementa la dimensione del codice, ma dovrebbe anche aumentarne le prestazioni.
Chissà se inseriranno un selettore nelle opzioni dell'IDE.

andrea86



Si ma se io volessi impostare, ad esempio i bit 0 e 1 rispettivamente a  HIGH ed a LOW ma preservando gli altri bit della porta non lo posso fare.


Per settare i singoli bit di un port, o un registro, puoi usare le macro SBI(reg,bit) e CBI(reg,bit), usano le omonime istruzioni assembly e richiedo solo due cicli macchina per l'esecuzione.

SBI = Set Bit in I/O Register
CBI = Clear Bit in I/O Register

Code: [Select]

//SBI e CBI
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))



Astro, non mi è molto chiaro il tuo post, puoi fare un esempio pratico?

leo72

@andrea86:
non bisogna saltare i post  :P


Code: [Select]
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

void setup() {
    sbi(PORTD, 1);
}

void loop() {
}


sbi è l'abbreviazione di set bit.
La sintassi è
sbi(byte, bit)
imposta ad 1 il bit in posizione bit del byte byte.

andrea86

@leo72

è che mi mancano troppi "pezzi" e non sto al passo.

Cioè (_SFR_BYTE(sfr) |= _BV(bit)) è come fare PORTD |= B00000100; ( o meglio, invece di passargli il byte gli si dice solo quale bit mettere ad 1 )

e se sì, per cosa sta _SFR_BYTE e _BV.

Poi c'è la questione del #define, perché definire il nome della funzione cbi e sbi e poi dirgli cosa fare? non sono funzioni già conosciute dal compilatore?

pablos71

#44
Mar 10, 2013, 12:33 pm Last Edit: Mar 10, 2013, 12:59 pm by pablos Reason: 1
Quote
Cioè (_SFR_BYTE(sfr) |= _BV(bit)) è come fare PORTD |= B00000100; ( o meglio, invece di passargli il byte gli si dice solo quale bit mettere ad 1 )


si, ti basta fare delle prove per vedere
Cambiando il nome  magari è più chiaro

Code: [Select]

#define on(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define off(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

void setup() {
DDRD=255; //tutti i pin del registro D in out
}

void loop() {
on(PORTD, 1);
delay(1000);
on(PORTD, 2);
delay(1000);
off(PORTD, 1);
delay(1000);
off(PORTD, 2);
delay(1000);
}


Define on ... definisci cosa deve fare "on"  è una macro
Define off ... definisci cosa deve fare "off" è una macro
usando funzioni speciali dell avr http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html

Ma è la stessa cosa di prima solo che raccogli le istruzioni in una piccola macro non cambia nulla dal fare

Code: [Select]

PORTD |= (1<<1); //on
delay(1000);
PORTD |= (1<<2); //on
delay(1000);
PORTD &= ~(1<<1); //off
delay(1000);
PORTD &= ~(1<<2); // off
delay(1000);
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

Go Up