Nel corso degli ultimi articoli dedicati all’Attiny85 e l’encoder hai letto come usare quest’ultimo come master ed arduino come slave.
L’amico Cristian mi ha giustamente suggerito, nell’ultimo incontro, di invertire i ruoli I2C dei due componenti in gioco, è nato quindi questo articolo: AtTiny85 Encoder – Attiny Slave.
L’idea di usare l’attiny85 come master nella comunicazione rende il codice lato arduino meno complesso in quanto ogni invio di dati da parte dell’encoder arriva su arduino che ha una funzione sempre in ascolto sull’indirizzo specificato, come hai letto negli articoli della scorsa settimana.
Il limite della soluzione resta che logicamente dovresti avere un solo master in una comunicazione I2C e più slave, inoltre il “cervello” della comunicazione, quindi il master, non è di certo l’encoder ma arduino , o genuino, che hanno il controllo di tutto il resto del progetto e l’encoder viene interrogato dal microcontrollore quando gli occorre recuperare il dato di rotazione.
Vantaggi del AtTiny85 Encoder – Attiny Slave
Riprendendo i vantaggi esposti nel precedente paragrafo avere l’Attiny85 slave ti offre i seguenti vantaggi:
- un unico Arduino/Genuino Master I2C
- più di un encoder come slave
- controllo delle comunicazioni dal microcontrollore principale
- interrogazione puntuale dell’encoder slave in funzione della fase del progetto
Connessione del attiny85 slave
lo schema elettrico non varia tra la soluzione in cui l’arduino è slave e l’attiny85 master o questa proposta in quanto è sempre l’attiny85 a occuparsi della lettura dell’encoder ad esso connesso:
ciò che varia è lo sketch che devi caricare sull’attiny85 e di conseguenza anche quello dell’arduino.
Lo sketch dell’AtTiny85 slave
// Code for the ATtiny85 #define I2C_SLAVE_ADDRESS 0x8 // Address of the slave #include <TinyWireS.h> #include "avr/interrupt.h"; #define encA 3 #define encB 4 #define swtc 1 volatile byte currentValue = 0; volatile int lastEncoded = 0; void setup() { TinyWireS.begin(I2C_SLAVE_ADDRESS); // join i2c network //TinyWireS.onReceive(receiveEvent); // not using this TinyWireS.onRequest(requestEvent); pinMode(encA, INPUT_PULLUP); pinMode(encB, INPUT_PULLUP); pinMode(swtc, INPUT_PULLUP); digitalWrite(encA, HIGH); digitalWrite(encB, HIGH); GIMSK = 0b00100000; // Enable pin change interrupts PCMSK = 0b00011010; // Enable pin change interrupt PB3,PB4,PB1 sei(); // Turn on interrupts } void loop() { // This needs to be here TinyWireS_stop_check(); } // Gets called when the ATtiny receives an i2c request void requestEvent() { TinyWireS.send(currentValue); TinyWireS.send(digitalRead(swtc)); } ISR(PCINT0_vect) { byte MSB = digitalRead(encA); //MSB = most significant bit byte LSB = digitalRead(encB); //LSB = least significant bit int encoded = (MSB << 1) |LSB; //converting 2 pin value to 1 number int sum = (lastEncoded << 2) | encoded; //adding it to the //previous encoded value if(sum==0b1101 || sum==0b0100 || sum==0b0010 || sum==0b1011) currentValue++; if(sum==0b1110 || sum==0b0111 || sum==0b0001 || sum==0b1000) currentValue--; lastEncoded = encoded; //store this value for next time }
Le parti principali dello sketch non sono variate, ossia la lettura e decodifica della quadratura dell’encoder per capire il verso di rotazione della manopola, ciò che varia è la parte relativa alla comunicazione i2c a partire dalla linea 02: definisci l’indirizzo su cui l’attiny dovrà rispondere alle chiamate i2c, se usi più slave ogniuno deve avere un indirizzo differente;
linea 04: includi la libreria “TinyWireS.h” al posto della TinyWireM.h in quanto la “S” finale indica la parte Slave;
linee 05-12: restano fondamentalmente invariate, ho eliminato qualche variabile non più necessaria;
linea 16: inizializzi la nuova istanza della libreria TinyWireS passandole l’indirizzo su cui deve rispondere;
linea 18: onRequest in questo caso è il metodo che usi sull’Attiny85, nei precedenti esempi lo hai utilizzato sull’Arduino, e richiama la funzione requestEvent ogni volta che riceve una richiesta dal Master;
linee 20-29: restno invariate;
linea 34: la funzione loop() è molto più semplice, si preoccupa solo di lanciare il metodo TinyWireS_stop_check();
linee 38-41: la funzione requestEvent invocata dal metodo onRequest alla linea 18, ogni volta che viene richimata invia 2 byte: il primo è il numero calcolato sulla rotazione dell’encoder ed il secondo è il valore di pressione del pulsante di cui l’encoder è dotato.
linee 43-58: sono invariate rispetto agli sketch precedenti e si preoccupano della decodifica dell’encoder.
Lo sketch dell’Arduino / Genuino Master IIC
Il codice dello sketch lato arduino è più semplice:
//Code for the Arduino Master #include <Wire.h> #define TARGET_ID 0x08 void setup() { Wire.begin(); // join i2c bus (address optional for master) Serial.begin(9600); // start serial for output } void loop() { Wire.requestFrom(TARGET_ID, 2); // request 1 byte // from slave device while(Wire.available() > 0) { byte i = Wire.read(); byte sw = Wire.read(); Serial.print( "Enc: " ); Serial.print( i ); Serial.print( " - Switch: " ); Serial.println( sw ); } delay(100); }
le linee si sono ridotte a sole 27 in cui le principali differenze risiedono nella linea 04: in cui definisci l’indirizzo o gli indirizzi se fossero più di uno gli slave;
linee 06-10: l’inizializzazione della Wire avviene senza dover specificare l’indirizzo sul quale rispondere, lo hai inserito nella componente slave;
linea 13: componi la chiamata i2c verso il target di 2 byte;
linee 16-24: ad ogni ricezione del buffer I2C scrivi sul monitor seriale i valori che ti arrivano dall’incoder;
linea 26: attendi 100 millisecondi prima di eseguire un nuovo ciclo di loop();
Il video della soluzione con Attiny85 slave e Arduino/Genuino Master i2C
Con entrambi gli sketch funzionanti ecco il risultato:
6 commenti
1 ping
Vai al modulo dei commenti
Ciao,
ho provato a realizzare il tuo progetto ma mi da errore compilando lo scketch del attiny85:
Arduino:1.7.11 (Windows 7), Scheda:”ATtiny85 @ 1 MHz (internal oscillator; BOD disabled)”
Nel platform.txt di terze parti non è definito il compiler.path. Si prega di segnalare questo problema al suo sviluppatore.
Encoder1.ino: In function ‘void setup()’:
Encoder1.ino:18:15: error: ‘class USI_TWI_S’ has no member named ‘onRequest’
Encoder1.ino:20:19: error: ‘INPUT_PULLUP’ was not declared in this scope
Encoder1.ino: In function ‘void loop()’:
Encoder1.ino:33:26: error: ‘TinyWireS_stop_check’ was not declared in this scope
Errore durante la compilazione
Autore
Ciao Marco,
purtroppo il mio sketch è stato sviluppato e testato con l’IDE del sito arduino.cc, oggi alla versione 1.6.12.
L’ide che stai utilizzando potrebbe avere delle differenti implementazioni ed uso delle schede di terze parti.
Ciao,
innanzitutto ti ringrazio per il tempo che dedichi a pubblicare ed a spiegare il codice.
Vorrei chiederti se secondo te è possibile utilizzare lo sketch che hai pubblicato anche per controllare la posizione dell’asse di un motore DC collegato ovviamente ad un encoder, in particolare avrei bisogno di capire la massima frequenza alla quale si possono leggere i valori ed il tempo impiegato alla trasmissione della lettura.
Grazie
Autore
Ciao Dino,
si è possibile usare l’attiny per fare quello che desideri, dovrai modificare, ovviamente, il codice in quanto questo non possiede tale possibilità e sacrificare l’I2C.
Cosa mi consigli per la comunicazione tra PC e Arduino? a parte la USB ovviamente
Grazie
Autore
Ciao Dino,
è molto variabile, direi che sarebbe utile conoscere il progetto ed il Pc ce vuoi usare, potresti usare sia WiFi sia Bluetooth
[…] la descrizione dello sketch nell’articolo encoder attiny slave in cui lo sketch è stato spiegato linea per linea, in questa occasione è importante sapere […]