come inviare pacchetti di dati via i2c tra due arduino?

Ciao a tutti, è la prima volta che utilizzo la libreria wire. Per inviare dati con la porta seriale da e per processing, ad esempio, dividevo i valori con una virgola e poi andavo a capo. Con wire, come si fa a inviare un pacchetto di dati da un arduino all'altro e a farli dividere una volta ricevuti? L'esempio nella libreria è per un solo valore. Tra l'altro, quali sono i vantaggi dell'i2c rispetto alla comunicazione su porta seriale? Grazie mille.

La seriale e la wire sono periferiche molto differenti, intanto la wire permette la connessione di più dipositivi alle stesse linee del bus (two-wire) ed ogni attore può essere Master o Slave, lo slave deve avere un indirizzo perchè il master possa inviare dati solo ad uno slave, e possibile anche fare una chiamata generale a cui tutti possono rispondere.

Il wire implementa un protocollo hardware complesso per fare tutto ciò e anche altro, per esempio 10 master che si contendono il bus e chi perde la contesa commuta da master a slave, e di master ne rimmarra uno solo come highlander e questo si sarà guadagnato il privileggio di impegnare il bus e dettare legge :P

Scusa il racconto un pò squilibrato ma in effetti le cose stanno così e credimi ci ho avuto a che fare di recente e ne sono uscito scombussolato, tanto che a livello di codice non ho combinato nulla.

Ciao.

Sì era la ragione per cui lo preferivo alla porta seriale, poi anche per imparare a usarlo, solo riesco a mandare un dato alla volta, non c'è un modo semplice per comporre un pacchetto da mandare da un master a uno slave? Grazie

Su un progettino dove ho usato il protocollo i2c ho messo un carattere "jolly" alla fine del messaggio da inviare (aggiungevo la chiocciola). Sull'arduino che riceveva ho concatenato i dati in arrivo in un array fatto apposta come buffer e l'ho riempito fino a quando non arrivava quel carattere che indica la fine del messaggio. Una volta che il messaggio è completamente arrivato analizzavo i dati poi svuotavo il buffer per ricevere il messaggio successivo. Ovviamente il carattere jolly deve essere usato soltanto come indicatore di fine messaggio e devi prevedere che la funzione che ti genera il pacchetto da inviare non lo possa inserire all'interno del pacchetto.

Sono funzioni che uso molto di rado, ho provato a generare uno string a partire da alcuni integrali con l'idea di scomporlo sullo slave e riconvertirlo in valori. Ma Wire.write non mi accetta la string come contenuto...

int r=100;
  int g=300;
  int b=200;
  int e=60;
  int s=150;

String val1 = String(r);
String val2 = String(g);
String val3 = String(b);
String val4 = String(e);
String val5 = String(s);
String valori = String(val1+","+val2+","+val3+","+val4+","+val5);

 Wire.beginTransmission(4); 
  Wire.write(valori);              
  Wire.endTransmission();

Di base tutte le trasmissioni e ricezioni avvengono byte dopo byte, seriale inclusa. Per fare la stessa cosa che fa la Serial, devi usare Wire.write() così:

byte *integrali[] = {
    100,
    300,
    200,
      60,
    150
};

for (byte idx = 1; idx < sizeof(integrali); idx++) {
    Wire.write(integral[idx] );
}

Verranno spediti in questo caso 5 valori contenuti nell’array.

Questo però non spedisce dei caratteri, ma proprio il valore numerico, non so se è questo che ti serve o devi per forza
operare con le stringhe.

Ci sono anche altri modi più comodi per inizializzare una sequenza di valori, ad esempio si può usare
una struttura C, che fa capo alla parola chiave “struct”.

Ciao.

secondsky: Ciao a tutti, è la prima volta che utilizzo la libreria wire. Per inviare dati con la porta seriale da e per processing, ad esempio, dividevo i valori con una virgola e poi andavo a capo. Con wire, come si fa a inviare un pacchetto di dati da un arduino all'altro e a farli dividere una volta ricevuti? L'esempio nella libreria è per un solo valore.

anche la seriale in realtà invia un solo valore alla volta. per questo di solito si usa un ciclo ed un carattere terminatore per leggere i dati. In I2C si fa la stessa cosa. Se non la stai già facendo con la seriale, allora i tuoi programmi funzionano di culo, ovvero hai un loop abbastanza lungo perchè tutti i dati ti arrivino, ma capirai che non è certo una bella soluzione, sopratutto quando devi debuggare il tuo sistema.

Eh, che vergogna avere dei programmi che funzionano di culo al giorno d'oggi :-) Comunque sì, in genere usavo questo tipo di approccio per digitare i dati sulla seriale e farli eseguire:

    while (Serial.available() > 0) {

        valR= Serial.parseInt();  
        valG = Serial.parseInt();          
        valB = Serial.parseInt();  

        if (Serial.read() == '\n')         {

analogWrite (ledR,valR);
analogWrite (ledG,valG);
analogWrite (ledB,valB);

        }

In questo modo avevo uno "slave" che eseguiva i miei comandi, ovviamente però vorrei che fosse un altro arduino a impartirli e la cosa si fa più complicata temo. Comunque questo sistema lo trovavo molto facile e intuitivo, sarei alla ricerca di qualcosa di analogo.

dunque, prima di tutto l'I2C ha un'architettura master/slave, al contrario della seriale dove entrambi i dialogatori sono sullo stesso piano.

è solo il master che può INIZIARE una richiesta, lo slave non può aprire bocca se non interrogato, quindi abbiamo già deciso chi sarà master, chi slave, o se l'I2C non va bene in questo caso (lasciamo stare per ora il multi-master, sappi che è possibile) ora, dato che il master fa una richiesta, specifica anche il numero di byte che vuole leggere. un float sono 4byte se rappresentato in binario, ma se rappresentato in stringa può avere diversa lunghezza, quindi come si fà? semplice, si fanno 2 richieste: prima la lunghezza della stringa da leggere, e poi la richiesta della stringa! in questo modo è possibile evitare il byte del carattere terminatore, e si può usare un for al posto del while. Anzi se non erro la lettura in wire è bloccante, quindi finchè non leggi tutti i byte sei bloccato; questo è un grosso vantaggio per i principianti, ma un grave problema di stabilità delle applicazioni (se crascha l'I2C per qualche disturbo/falso contatto si impalla tutto il micro), per questo esistono mille implementazioni della wire non bloccanti

Non ho mai approfondito la lettura della libreria Wire dell'Arduino. Però mi viene una domanda: essendo il tutto implementato, nel caso del 328 in hardware, dopo aver ricevuto i comandi, non dovrebbe essere svolto tutto in "background" ovvero parallelamente ad altri compiti oppure ha una modalità sincrona?

se è per questo anche la seriale sui pin 0 e 1 è hardware...

il punto è: a che punto finisce l'implementazione hardware e quando inizia quella software? non lo so neanche io, non avendo mai riscritto wire. so che si occupa di informarti dello STATO della linea e lanciare qualche interrupt (occupato, dati in arrivo, etc..), insomma come un telefono di casa. squilla se arriva dati, è occupato se qualcun'altro sta chiamando (oppure no, multimaster, e anche coi telefoni di casa si può fare che alzi mille cornette e si parla in contemporanea) etc.. però chi si occupa di comporre il numero, inviare, ricevere, interpretare e persino rispondere al messaggio è l'utente, non il telefono ;)

ok. :wink:

Ho tradotto parte del datasheet del 328, riguardante proprio twi guarda caso. Leggete, se trovate errori postate la traduzione corretta e per chi se la sente traduca anche il resto.

Traduzione non fedele di parte del datasheet ATmega328 (parte 1). { L'interfaccia seriale 2-wire (TWI) è la soluzione ideale per le tipiche applicazioni con i microcontrollori Il protocollo permette al proggettista di sistemi di interconnettore fino a 128 differenti dispositivi usando solo due line bidirezionali, una per il clock(SCL) e una per i dati (SDA). Il solo hardware esterno necessario per realizzare il bus è un singolo resistore di pull-up per ogni linea del bus TWI.

Tutti i dispositivi connessi al bus hanno un'indirizzo individuale e i meccanismi per risolvere la comunicazione sono previsti dal protocollo TWI.

Figura 21-1 TWI Bus interconnection (pag 215)

La terminologia TWI: - Master Il dispositivo che inizia e termina la trasmissione e genera anche il clock SCL. - Slave Il dispositivo indirizzato dal master - Trasmitter Il dispositivo che pone i dati sul bus - Receiver Il dispositivo che legge i dati dal bus

Entrambe le linee del bus sono connesse al positivo dell'alimentazione attraverso un resistore di pull-up. Le porte del bus di tutti i dispositivi compatibili con il bus TWI sono open-drain o open-collector. Un livello LOW su una linea del bus è generato quando uno o più dispositivi forniscono in uscita uno zero. Un livello HIGH è presente solo quando tutti i dispositivi connessi al bus si pongono in alta impedenza, permettendo ai resistori di pull-up di portare il livello della linea HIGH. Nota che tutti i dispositivi AVR connessi al bus TWI devono essere alimentati con ordine per permettere alcune operazioni.

Il numero di dispositivi che possono essere connessi al bus è limitato solo dalla capacità del bus che non deve superare i 400 pF e dalla grandezza di 7-bit dell'indirizzo di slave.

Due set di specifiche sono trattate qui, una per le velocità del bus sotto i 100Khz, e una valida per velocità del bus fino a 400Khz.

Trasferimenti di bits Ogni bit trasferito sul bus è accompagnato da un'impulso sulla linea di clock SCL. Il livello della linea dati SDA deve essere stabile quando la linea SCL è HIGH. La sola eccezione a questa regola è quando si genera una condizione di start o di stop.

Fig 21-2 Data Validity (pag. 217)

Condizione START e STOP Il master inizia e termina una trasmissione di dati. La trasmissione è iniziata quando il master emette una condizione di START sul bus e termina quando il master emette la condizione di STOP. Dopo una condizione di START il bus è considerato occupato e nessun altro master dovrebbe provare a prendere il controllo del bus. Uno speciale caso si verifica quando una nuova condizione di START viene emessa, questa è conosciuta come condizione REPEATED START ed è usata quando il master desidera iniziare un nuovo trasferimento senza cedere il controllo del bus. Dopo la condizione REPEATED START il bus è considerato occupato fino a che una condizione di stopo non viene emessa. Il comportamento è identico a START, e quindi in tutto il documento START è utilizzato per descrivere sia START e REPEATED START, se non diversamente specificato.

Come si vede in Fig 21-3 lo START e lo STOP avvengono cambiando il livello della linea SDA quando la linea SCL è HIGH

continua nel post seguente......

Io cercavo indicazioni circa l'interrupt su twi, cioè ogni quando la isr viene esguita e non ho trovato nulla di chiaro, ma ho sperimentato che dopo uno start la isr viene chiamata ripetutamente, non so il motivo.

Chi ne sa di più e pregato di far luce sul twi del 328.

Ciao.

...continuazione del post precedente

Address packet format Tutti i pacchetti di indirizzo trasmessi sul bus TWI sono lunghi 9 bit, dei quali 7 rappresentano l'indirizzo, uno per il bit di controllo READ/WRITE e uno per ACK bit (acknowledge bit).

Se il bit READ/WRITE è impostato a 1 viene eseguita una lettura, se impostato a 0 viene eseguita una scrittura.

READ = 1 WRITE = 0

Il bit di controllo READ/WRITE è chiamato SLA+R in caso di lettura o SLA+W in caso di scrittura.

Quando uno slave riconsce l'indirizzo inviato sul bus, dovrà mettere a 0 il pin SDA in corrispondenza del nono impulso di clock, questo corrisponde al bit ACK. In pratica lo slave comunica con il master per dirgli che lo slave che ha indirizzato esiste e ha risposto con il bit di conoscenza indirizzo.

Se lo slave indirizzato è occupato, o per altre ragioni non può rispondere alla richiesta del master, la linea SDA deve essere lasciata HIGH in corrispondeza del nono impulso di clock.

Il master allora può trasmettere la condizione di STOP, oppure REPEATED STARD per iniziare una nuova trasmissione senza per questo rilasciare il bus.

L'indirizzo di uno slave può essere scelto liberamente dallo sviluppatore, ma l'indirizzo 0000 000 è riservato per la chiamata generale. La trasmissione dell'indirizzo inizia dal bit più significativo MSB.

Una chiamata generale è usata quando il master vuole trasmettere lo stesso messaggio a tutti gli slave connessi al bus. In caso di chiamata generale, tutti gli slave dovrebbero rispondere ponendo il bit ACK LOW. Quando una chiamata generale è seguita dal bit WRITE, tutti gli slave devono rispondere con ACK LOW, in questo caso tutti i pacchetti dati inviati dal MASTER saranno ricevuti da tutti gli SLAVE che hanno risposto con ACK LOW.

Nota che una chiamata generale seguita dal bit READ non ha alcun significato e può portare confusione nel caso in cui più slave stanno trasmettendo dati differenti.

Tutti gli indirizzi del seguente formato 1111 xxx dovrebbero essere riservati per scopi futuri.

Fig. 21-4 Address Packet Format (Pag. 218)

Data Packet Format

Tutti i pacchetti di dati trasmessi sul bus TWI sono lunghi 9 bits, composti da un byte di dati e ACK bit. Durante il trasferimento dei dati, il Master genera il clock e le condizioni START e STOP, mentre il ricevitore è responsabile del bit ACK. Se il ricevitore non risponde ACK allora lascia la linea SDA HIGH e questo sarà segnalato come un NACK, cioè non conoscenza.

Quando il ricevitore ha ricevuto l'ultimo byte, o per alcune ragioni non può riceverne altri, di questo si dovrebbe informare il trasmittente inviando un NACK dopo l'ultimo byte. Come per l'address packet anche i data packet sono inviati a partire dal bit più significativo.

Fig 21-5 Data Packet Format (Pag.219)

Combinare address e data packets insieme

Una trasmissione di base è composta da una condizione di START, seguita da SLA+R/W, a cui segue uno o più pacchetti dati, e alla fine una condizione di STOP. Un messaggio vuoto è composto da uno START e STOP senza nulla in mezzo, questo è considerato illegale. Nota che il Wired-ANDing della linea SCL può essere usato per implementare handshaking tra MASTER e SLAVE.

Lo SLAVE può estendere il periodo LOW di SCL, tenendo LOW il pin SCL che normalmente è di pertinenza del MASTER. Questo può tornare utile nel caso in cui la frequenza di trasmissione impostata dal MASTER risulta essere troppo alta per lo SLAVE, questo però non modifica il periodo dell'impulso HIGH di SCL, perchè è determinato dal MASTER. Come conseguenza si ha che lo SLAVE può ridurre la velocità di trasferimento impostata dal MASTER.

La Fig 21-6 mostra una tipica trasmissione dati. Nota che diversi data byte possono essere trasmessi tra il bit SLA+R/W e la condizione di STOP, questo viene deciso dal protocollo software.

Fig. 21-6 Typical Data Trasmission

Multi-master Bus System, Arbitration e Synchronization

Il protocollo TWI permette di connettere più MASTER al bus. Particolati misure sono state prese per assicurare che la trasmissione abbia buon fine anche se più MASTER iniziano la trasmissione nello stesso istante.

Due sono i problemi che sorgono nella connessione multi-master:

  • Deve essere realizzato un algoritmo che permettere ad un solo MASTER di completare la trasmissione. Tutti gli altri MASTER dovrebbero cessare di trasmettere quando scoprono di avere perso il processo di selezione, detto anche processo di arbitrazione. Quando un MASTER scopre di aver perso il processo di arbitrazione dovrebbe passare in modalita SLAVE per controllare se è stato indirizzato dal MASTER che ha vinto il processo di arbitrazione. Il fatto che, più master hanno iniziato la trasmissione allo stesso tempo, non deve essere rilevabile agli slave, vale a dire, i dati trasferiti sul bus non devono essere danneggiati.

  • I master possono usare differenti frequenze di clock, in tal caso un'algoritmo si occupa di sincronizzare le frequenze di clock di tutti i master così da permettere la trasmissione in modo sincrono. Questo facilità il processo di arbitraggio.

Il wired-ANDing del bus è usato per risolvere emtrambe questi problemi. Il clock fornito da tutti i master viene combinato in uno solo, dove il periodo HIGH verrà dal master che ha il periodo HIGH più breve e il periodo LOW viene dal master che ha il periodo LOW più lungo.

Si noti che tutti i master in ascolto sulla linea SCL, in effetti iniziano a contare il loro periodo HIGH o LOW, quando la combinazione ottenuta va HIGH o LOW rispettivamente.

Fig. 21-7 SCL Sinchronization Between Multiple Master

L'arbitrazione è effettuata da tutti i master monitorando continuamente la linea SDA dopo avere inviato dati, If il valore letto sulla linea SDA non corrisponde a quello che ha inviato esso ha perso l'arbitrazione. Si noti che un master può perdere l'arbitrazione solo quando esso ha emesso HIGH sulla linea SDA, mentre un altro Master ha emesso LOW. Il MASTER che ha perso l'arbitrazione dovrebbero immediatamente porsi in modo SLAVE controllando se il MASTER vincitore li ha indirizzati,

Il master che hanno perso il processo di arbitrazione devono lasciare la linea SDA HIGH, ma è permesso loro di generare il clock fino alla fine del dato corrente o pacchetto indirizzo. Il processo di arbitrazione procede fino a che non rimane un solo MASTER e questo può comportare l'invio di molti bit. Se più MASTER provano ad indirizzare lo stesso SLAVE il processo di arbitrazione continua ma si sposta in Data Packet.

Fig. 21-8 Arbitration Between Two Master (Pag.221)

continua nel post successivo...

....continuazione dal post precedente

Ci sono operazione considerate illegali durante il processo di arbitrazione. Una arbitrazione non può avvenire tra:

  • Un REPEATED START e un data bit
  • Un STOP e un data bit
  • Un REPEATED STARD ed uno STOP

La responsabilità di assicurare che queste operazione illegali non si verifichino ricade sul software utente. Questo implica che in multi-master, tutti i trasferimenti di dati devono usare la stessa composizione di SLA+R/W e pacchetti dati. In altre parole tutte le trasmissioni devono contenere lo stesso numero di pacchetti dati, diversamente il risultato dell'arbitrazione è indefinito.

Overview of the TWI Module

Il modulo TWI è composto da diversi sub-moduli come mostrato in Fig. 21-9.

SCL e SDA pins

Questi pin sono l'interfaccia con il resto della MCU. Il driver di uscita contiene un limitatore di slew-rate per conformità alle specifiche TWI. Lo stadio d'ingresso contiene un soppressore di rumore che sopprime gli impulsi più brevi di 50ns.

La resistenza di pull-up si questi pin può essere abilitata come spiegato nella sezione I/O port, abilitandola per entrambe i pin può in alcuni casi eliminare la necessità di resistenze esterne. Se si decide di usare delle pull-up esterne e meglio disabilitare quelle interne.

Bit Rate Generator Unit

Questa unità controlla il periodo del segnale SCL quando si opera in Master mode. Il periodo è controlla impostando il valore del bitrate nel registro (TWBR) e il prescaler tramite i bits del registro (TWSR).

In modalità SLAVE il bitrate e prescaler non influiscono, ma ha importanza rilevante la frequenza di clock a cui lavora la MCU, questa deve essere almeno 16 volte maggiore della frequenza di SCL.

La frequenza di clock del pin SCL è generata in accordo alla seguente equazione:

SCL frequency = MCU clock / 16+2(TWBR)+(prescalerValue)

Bus Interface Unit

Questa unità contiene il data e address shift register (TWDR), uno START/STOP controller e un hardware per il processo di arbitration. Il registro TWDR contiene l'indirizzo o il data byte da trasmettere, oppure l'indirizzo o data byte ricevuto. In aggiunta a TWDR questa unità contiene anche il registro (N)ACK in trasmissione o in ricezione, comunque il registro (N)ACK non è accessibile direttamente da software. In ricezione può essere impostato o pulito maipolando il registro TWCR. In trasmissione il valore del bit (N)ACK ricevuto può essere determinato dal valore di TWSR.

Il controller START/STOP è responsabile della generazione e rilevamento di START, REPEATED START e STOP. Il controller è capace di rilevare le condizioni di START e STOP anche quando la MCU è in uno stato sleep. Abilitando il risveglio se il master ha inviato il giusto indirizzo. ?????? non so se è così

Se una trasmissione è stata iniziata come Master, l'hardware che rileva l'attività di arbitrazione continua a monitorare la trasmissione provando a determinare se un processo di arbitrazione è in atto. Se il MASTER ha perso l'arbitrazione l'unità di controllo START/STOP viene informata così le giuste azioni verranno intraprese e gli appropriati codici di stato verranno generati.

Address Match Unit

pag 223 }

maurotec, credo che la ISR sia legata al clock, probabilmente stile change. ma nella libreria wired trovi anche twi.c e .h che saranno un ottimo punto di partenza, no? in oltre se cerchi su iternet, come ti dicevo trovi mille implementazioni. se vuoi ci possiamo lavorare sopra, a me piacerebbe creare una libreria i2c simile alla serial, nel senso che abbia un buffer di richeste e di risposte, che lavorano "in background" tramite interrupt.

poi con calma mi leggo il datasheet, che descire lo standard twi più che parlare dell'hardware... ma se non erro twi è una implementazione opensource/openhardware, quindi non mi stupirei se tutti i micro hanno lo stesso circuito, o molto simile.

La wire di arduino lo guardata e ci ho capito poco, in particolare non capisco i due puntatori a funzione con il come “event”.

La isr twi di arduino implementa una macchina a stati con switch case e usa due buffer, come li usa non lo so perchè non ho studiato la classe, ma solo il codice C.

In pratica dopo uno start si possono scrivere e leggere un numero qualunque di byte e questo lo deve stabbilire il protocollo software, diversamente senza protocollo ogni scrittura o lettura il master deve sempre emettere la condizione di star e poi inviare il bit Read/Write e infine dopo aver ricevuto ACK invia un solo byte, per il prossimo deve fare nuovamente start.

Io penso che se la twi di arduino usa il buffer ci deve essere un metodo per inviare una array di caratteri, magari non prende string direttamente.

poi con calma mi leggo il datasheet, che descire lo standard twi più che parlare dell’hardware… ma se non erro twi è una implementazione opensource/openhardware, quindi non mi stupirei se tutti i micro hanno lo stesso circuito, o molto simile.

Twi deve per forza essere aderente allo standard i2c altrimenti non potrebbe dialogare con sensori, memorie ecc che usano i2c, quindi si tratta dello standard i2c chiamato twi per motivi di licenza.

A me serve una lib C per il twi, quindi disponibile a discutere e scrivere codice di test, ma solo C non C++ e no arduino IDE, perchè non c’è lo.

Ciao.

bhe allora più tardi quando sono a casa (se funziona internet ]:D) apro un post apposta (ahahah gioco di parole che non fa ridere nessuno) così ci possiamo sbizzarrire a farci suggerire trucchetti.

Io come limite impongo l'essere il più possibile "invisibile" all'utente (interrupt driven), asincrona (come la read della seriale) e quindi antiblocco.

Tu imponi C e nessun riferimento all'IDE (che cmq a priori credo sarebbe fuori discussione)

propongo di implementare solo il master/slave, roba più complessa la lasciamo a chi conosce bene il protocollo :) Partiamo dal master visto che ho un sacco di sensori slave da usare come riferimento, poi lo slave speri diventi una specie di master semplificato.

Tu imponi C e nessun riferimento all’IDE (che cmq a priori credo sarebbe fuori discussione)

La mia più che una imposizione è una constatazione del fatto che non posso scrivere e testare con il core,
quindi C significa codice funzionale, poi il code design ognuno lo può sviluppare come crede.

Non so se mi sono spiegato, ad esempio partiamo dalla funzione che invia dati allo slave, questa deve prendere
come parametro un’array di byte (meglio se prende un const char*). So per certo (ma faro un test in serata) sollevando
una condizione di start con:

 // send START condition
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

avviene l’esecuzione della isr, e per qualche mistero il codice esce dalla isr e ci rientra e così via
fino a che non invii uno stop.

Ciao.

guarda quà: http://arduino.cc/playground/Main/WireLibraryDetailedReference