Intercettare change di tutti i PORT PinChangeInt

Mi servirebbe che al al programma venisse attirata la sua attenzione quando un qualsiasi pin subisce un cambio di stato che sia IN o OUT, tutti i pin in pratica dal 3 al 49 (anche se in mezzo 0-1-4-10-13-50-51-52-53 sono riservati al sistema), si potrebbe anche sulle analog, ma non è importante adesso.
Non riesco a farlo con il confronto di 2 matrici una istantanea e una hold poichè i tempi sono molto bassi o si perdono alcuni cambi o se ne registrano troppi.

Ho letto un po' di argomenti sugli interrupt, quasi tutti fanno riferimenti alla UNO, priorità alta/media/bassa, tutte le parti lette rimandano al datasheet dell'atmel come se fosse facile leggerlo :slight_smile: nel mio caso 2560 http://www.atmel.com/Images/doc2549.pdf

Si possono abilitare gli interrupt su molte porte, PORTD PORTE PORTH PORTK PORTG ma non ho capito se su una parte (forse 20) o su tutte e soprattutto trovo documentazione basata sulle porte già abilitate a farlo di default ad esempio pin 2-3 della UNO

ho provato a interpretare queste lib e questi esempi Google Code Archive - Long-term storage for Google Code Project Hosting. , ma l'argomento è in parte a me sconosciuto.
Qualcuno ha provato a fare qualcosa? può indicarmi come farlo via software, non hardware.

Saluti P.

I PCINT li puoi attivare su tutti i pin esterni del microcontrollore.
Una volta attivato un PCINT, ad ogni cambiamento di stato viene attivata la corrispondente ISR.

Le ISR sono però "comuni", nel senso che ogni porta ha il suo registro che controlla tutti gli 8 pin collegati. Se viene sollevato un interrupt su un pin viene chiamata la ISR corrispondente al registro che controlla quella porta. Ad esempio, l'Atmega328 ha 3 registri PCINT da 8 bit ciascuno.
Questo vale per tutti i microcontrollori.

Per l'Arduino c'è questa libreria:
http://playground.arduino.cc/Main/PinChangeInt
http://code.google.com/p/arduino-pinchangeint/downloads/list

Per i Tiny c'è quest'altra.
http://code.google.com/p/arduino-tiny/downloads/list

Se hai bisogno di altre info chiedi pure, che io i PCINT li ho usati diverse volte.

Scusa Leo, ho bisogno di un chiarimento.

Sapevo che sulla MEGA esistono 6 External Interrupt collegati ad altrettanti piedini. Se abilitati, in caso di variazione di stato vengono scatenati gli INT da 0 a 5.

I PCINT, invece, come si attivano?

cyberhs:
Scusa Leo, ho bisogno di un chiarimento.

Sapevo che sulla MEGA esistono 6 External Interrupt collegati ad altrettanti piedini. Se abilitati, in caso di variazione di stato vengono scatenati gli INT da 0 a 5.

I PCINT, invece, come si attivano?

I PCINT sono raggruppati ad 8 per 8. Quindi ogni flag controlla l'attivazione dei PCINT di una intera porta.
Datasheet alla mano (versione del 10/2012), vai al cap. 15 (alla pagina 112) per una breve descrizione poi al capitolo 15.2.5 (dalla pagina 115) e seguenti.
Per attivare un PCINT devi:

  1. disattivare gli interrupt globali
  2. mettere ad 1 il bit corrispondente al gruppo che ti interessa (PCIE0, PCIE1 o PCIE2) nel registro PCICR
  3. usare i 3 registri PCMSK0..2 per scegliere il PCINT da attivare.
  4. riattivare gli interrupt globali

Ad esempio, se vuoi attivare il PCINT sul pin 20, devi

  1. controllare a che gruppo corrisponde, il PCINT20 è del 3°
  2. impostare quindi ad 1 il bit PCIE2 in PCICR
  3. impostare ad 1 il bit PCINT del registro PCMSK2
  4. aggiungere una ISR per il vettore del 3°gruppo di PCINT: ISR(PCINT2_vect)

Attenzione ad una cosa. Se tu imposti 2 PCINT, entrambi di uno stesso gruppo, quando verrà chiamata la corrispondente ISR tu non saprai a priori qual è stato a sollevare l'interrupt. Quindi se ti servono più di un PCINT conviene usarli di gruppi differenti.

Grazie Leo, non ti ho risposto ieri perchè il forum era lentissimo.

I link che hai messo sono gli stessi che ho messo io :slight_smile: in più avevo letto questo di Lesto che tratta il 328
http://arduino.cc/forum/index.php/topic,36241.0.html

questo di un Cinese per il 2560... ho pensato chissà che per una volta un Italiano copia da un Cinese

anche questo dove dice che sono 24 i pin che supportano gli interrupt
http://aeroquad.com/archive/index.php/t-75.html

Forse non si può fare su tutti i gruppi A B C D E F G H K J

ciao

pablos segui il mio codice, come noterai sono pochi i registri usati e non ci sono rigiri di librerie esterne, se non per le costanti.

Io credo che i registri siano gli stessi, solo che anzichè fermarsi all'INT2 vanno oltre, in oltre devi fare attenzione che a livello di registri devi vedere la numerazione dei PIN da datasheet, e scordarti di quella arduino. Altro particolare: noterai che intercetterai tutti gli interrupt, per esempio quelli della seriale.. occhio a non fare casini, anche se non dovrebbe esserci nulla che un buon reset non possa sistemare :slight_smile:

(accidenti, ero proprio agli inizi! quello dev'essere stato uno dei miei primi post!)

Diciamo che ho trovato un compromesso tra la mia necessità e quello che è possibile fare senza troppe complicazioni, magari utile anche ad altri.

A me non servono interrupt di microsecondi, velocità importantissima tra 2 interfaccie dati, i miei change sono provocati dalla mano umana tramite pulsanti o l'attivazione di un pin dal programma stesso, quindi ho tempo a sufficienza per intercettare il cambio.

Perchè mi serve sapere quale pin è stato attivato dal programma quando è lui stesso a sapere il numero?
Perchè non esistono dei pin prestabiliti che facciano da IN o da OUT, solo quando viene letta la eeprom si sa e questi possono cambiare in base a una nuova scrittura di un file INI su SD e un restart di arduino.

con questo sketch trovo in modo eccellente quale gruppo PORT A,B,C ecc subisce un cambio

//--------------------timer--------------------------------------------------------
unsigned long previousMillis_e = 0;  
unsigned long interval_e = 100;
//---------------------------------------------------------------------------------
byte Port[] = {0,0,0,0,0,0,0,0,0}; //A,B,C,D,E,G,H,J,L  ... valori hold
byte cont; // contatore per pulsante port 32(test)

void setup() 
{
  delay(1000);
  Serial.begin(9600);//debug
  pinMode(32, INPUT); 
  DDRL = 255;  // set a OUTPUT i pin 42-43-44-45-46-47-48-49
  PORTL = 0;  // metto a LOW i pin 42-43-44-45-46-47-48-49
}

void loop() 
{  
  unsigned long currentMillis_e = millis();
  if(currentMillis_e - previousMillis_e > interval_e) 
  {   
        previousMillis_e = currentMillis_e;             
 
    if(Port[2] !=  PINC){ 
    Port[2] =  PINC;
    Serial.println("CHANGE_C 30-31-32-33-34-35-36-37 >> val byte = " + (String)PINC);//debug      
    }
  
    /*if(Port[3] != PIND){
    Serial.println("CHANGE PORT_D 18-19-20-21-38 >> val byte = " + (String)PIND);//debug
    }*/
  
    if(Port[5] !=  PING){
    Port[5] =  PING; 
    Serial.println("CHANGE PORT_G 39-40-41-4  >> val byte = " + (String)PING);//debug
    }
  
    if(Port[8] !=  PINL){ 
    Port[8] =  PINL;  
    Serial.println("CHANGE PORT_L 42-43-44-45-46-47-48-49  >> val byte = " + (String)PINL);//debug
    }   
    
//-----------test con pulsante passo-passo------------------------       
        if (digitalRead(32)) cont++; else cont=0;
    
        if (digitalRead(42) == 0 && cont == 1){       
          PORTL |= (1<<7); cont++;
        } 
        
        if (digitalRead(42) == 1 && cont == 1){        
          PORTL &= ~(1<<7); cont++;
        }       
//------------------------------------------------------------------     
 }
}

Il programmino restituisce un byte .... esempio:
pulsante sul pin 32 PORTC
led sul pin 42 bistabile o passo-passo PORTL

risposta del dubug con una pressione e rilascio del pulsante

CHANGE PORT_C 30-31-32-33-34-35-36-37 >> val byte = 32
CHANGE PORT_L 42-43-44-45-46-47-48-49 >> val byte = 128
CHANGE PORT_C 30-31-32-33-34-35-36-37 >> val byte = 0

una seconda pressione e rilascio

CHANGE_C 30-31-32-33-34-35-36-37 >> val byte = 32
CHANGE PORT_L 42-43-44-45-46-47-48-49 >> val byte = 0
CHANGE_C 30-31-32-33-34-35-36-37 >> val byte = 0

Domanda:
Mi piacerebbe sapere da questo byte con un calcolo veloce quale pin ha scatenato l'evento in base a questa tabella

B|42|43|44|45|46|47|48|49

0 byte 1
1 byte 2
2 byte 4
3 byte 8
4 byte 16
5 byte 32
6 byte 64
7 byte 128

Grazie
Saluti

occio che PINC non sono i pin cambiati ma lo stato attuale dei PINC, devi fare una xor con lo stati precedente per sapere quali pin sono cambiati

forse non ho capito ben la domanda ma

byte b = PINC; //valori attuali, invece una varibaile di tipo byte e nome "a" contiene il vechio valore di PINC

byte cambiati = b^a; //trobo la "maschera dei pin cambiati", se è cambiato il bit corrispondente è ad 1

int B[] = {42,43,44,45,46,47,48,49};

for (int i=0; i < 8; i++){
  if ( cambio & (1<<(8-i) ) ){ //controllo lo stato del bit in posizione 8-i
     //il pin B[i] è cambiato!
  }else{
     //il pin B[i] è sempre lo stesso!
  }
}

salvo ogni 100 ms il cambiamento nell'array e leggo di conseguenza ogni 100 ms lo stato precedente, il tutto funziona, chiaramente ho un change generico di quel gruppo in questo caso qualsiasi bit del PINL

----- millis() 100ms------
if(Port[8] != PINL)
{
Port[8] = PINL;

La domanda era se ottengo in lettura

128 so che il bit 7 è high
129 so che i bit 0 e 7 sono high
24 so che i bit 3 e 4 sono high
ecc ecc

volevo assegnare i numeri dei pin reali senza una complessa elaborazione che fa perdere tempo al loop, hai risposto alla domanda comunque, ora ci do un occhio
Ho molte lacune nella scrittura di programmi con operatori logici :roll_eyes:
grazie

In hardware/arduino/variants/standard/pins_arduino.h ci sono delle macro che puoi usare per recuperare il pin di Arduino dato il bit della porta e la porta stessa.
Sono le stesse funzioni usate dalla digitalWrite/Read e dalla analogWrite/Read.

Vedi si ti possono servire

Ok la libreria a cui ti riferisci e' questa presente anche nell'IDE

Se non sbaglio vengono memorizzate tramite progmem tutti i riferimenti PORT + lettera in ordine da 0 a 69, fin qui ok, ma come estraggo da qui avendo il byte la giusta posizione dell'array PGM?

grazie
ciao

Se esamini il file wiring_digital.c presente nel core capisci come fare.
Questo è contenuto nella digitalWrite:

	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);

Passandogli il numero di pin dell'Arduino le 2 funzioni ti restituiscono la porta in "port" ed il corrispondente bit in "bit".
Credo sia quello che ti serve, giusto?

Mmmm una cosa così ma inverso

Servirebbe un DigitalPortToPin ovvero so che il PORD bit 2 ha subito un cambiamento >>> restituiscimi il num del Pin corrispondente.

Leggendo l'array digital_pin_to_port_PGM (è di tipo progmem) puoi sapere il pin in base alla porta e bit.
Ad esempio, PD2 è il pin 2.
Basta che ti ricordi l'ordine: da 0 a 7 sono per PD0..PD7, poi da 8 a 13 sono PB0..PB5 e da 14 a 19 sono per PC0..PC5.
Guarda il file pins_arduino.h, ci sono tutte le funzioni e gli array per le conversioni per ottenere bit e porte. Non so cosa si possa usare.

Ci sono vicino ho applicato il pezzo di Lesto per trovare i bit cambiati e funziona, ma ottengo un numero in uscita sbagliato

//--------------------timer--------------------------------------------------------
unsigned long previousMillis_e = 0;  
unsigned long interval_e = 50;
//---------------------------------------------------------------------------------
byte Port[] = {0,0,0,0,0,0,0,0,0}; //A,B,C,D,E,G,H,J,L  ... valori hold
byte cont; // contatore per pulsante port 32(test)

void setup() 
{
  delay(1000);
  Serial.begin(9600);//debug
  pinMode(32, INPUT); 
  DDRL = 255;  // set a OUTPUT i pin 42-43-44-45-46-47-48-49
  //PORTL = 0;  // metto a LOW i pin 42-43-44-45-46-47-48-49
}

void loop() 
{  
  unsigned long currentMillis_e = millis();
  if(currentMillis_e - previousMillis_e > interval_e) 
  {   
        previousMillis_e = currentMillis_e;             
 
    if(Port[2] !=  PINC){
    byte C[] = {30,31,32,33,34,35,36,37};
    BitChange(Port[2], PINC, C);   
    Port[2] =  PINC;
    Serial.println("CHANGE PORT_C 30-31-32-33-34-35-36-37 >> val byte = " + (String)PINC );//debug     
    }
  
    /*if(Port[3] != PIND){
    Serial.println("CHANGE PORT_D 18-19-20-21-38 >> val byte = " + (String)PIND);//debug
    }*/
  
    if(Port[5] !=  PING){
    Port[5] =  PING; 
    Serial.println("CHANGE PORT_G 39-40-41-4  >> val byte = " + (String)PING);//debug
    }
  
    if(Port[8] !=  PINL){ 
    byte L[] = {42,43,44,45,46,47,48,49};
    BitChange(Port[8], PINL, L); 
    Port[8] =  PINL;  
    Serial.println("CHANGE PORT_L 42-43-44-45-46-47-48-49 >> val byte = " + (String)PINL );//debug
    }   
        
//----------------------test con pulsante-----------------------       
        if (digitalRead(32)) cont++; else cont=0;
    
        if (digitalRead(42) == 0 && cont == 1){                
          PORTL |= (1<<7); cont++;
        } 
        
        if (digitalRead(42) == 1 && cont == 1){              
          PORTL &= ~(1<<7); cont++;
        }       
 //------------------------------------------------------------------     
 }
}

void BitChange(byte Hold_Val_Port, byte New_Val_Port, byte Pin[]) 
{ 
   byte Cambio = New_Val_Port ^ Hold_Val_Port;
   
       for ( byte i=0; i < 8; i++)
       {
         if ( Cambio & (1<<(8-i)))  //controllo lo stato del bit in posizione 8-i             
         Serial.print("Il pin " + (String)Pin[i] + " e' cambiato! ...  ");//debug   <<<<<<<<<<<< ERRORE!!!
       }  
}

ricapitolando ho:
Un pulsante sul pin 32 quindi PORTC
Un led sul pin 42 quindi PORTL

premo il pulsante e lo rilascio immediatamente

se metto

Serial.print("Il pin " + (String)Pin[i] + " e' cambiato! ...  ");

ottengo questa uscita
Il pin 33 e' cambiato! ... CHANGE PORT_C 30-31-32-33-34-35-36-37 >> val byte = 32 <<< ERRORE
Il pin 43 e' cambiato! ... CHANGE PORT_L 42-43-44-45-46-47-48-49 >> val byte = 128 <<< ERRORE
Il pin 33 e' cambiato! ... CHANGE PORT_C 30-31-32-33-34-35-36-37 >> val byte = 0 <<< ERRORE


se metto ivece -1 pensavo di correggere entrambi i valori

Serial.print("Il pin " + (String)Pin[i-1] + " e' cambiato! ...  ");

ottengo invece questa uscita
Il pin 31 e' cambiato! ... CHANGE PORT_C 30-31-32-33-34-35-36-37 >> val byte = 32 <<< ERRORE
Il pin 42 e' cambiato! ... CHANGE PORT_L 42-43-44-45-46-47-48-49 >> val byte = 128 <<< GIUSTO
Il pin 31 e' cambiato! ... CHANGE PORT_C 30-31-32-33-34-35-36-37 >> val byte = 0 <<< ERRORE

Perchè??? dannazione :sweat_smile:

Tabella dal file pins_arduino.h - Pin definition functions for Arduino

PL , // PL 7 ** 42 ** D42
PL , // PL 6 ** 43 ** D43
PL , // PL 5 ** 44 ** D44
PL , // PL 4 ** 45 ** D45
PL , // PL 3 ** 46 ** D46
PL , // PL 2 ** 47 ** D47
PL , // PL 1 ** 48 ** D48
PL , // PL 0 ** 49 ** D49

PC , // PC 7 ** 30 ** D30
PC , // PC 6 ** 31 ** D31
PC , // PC 5 ** 32 ** D32
PC , // PC 4 ** 33 ** D33
PC , // PC 3 ** 34 ** D34
PC , // PC 2 ** 35 ** D35
PC , // PC 1 ** 36 ** D36
PC , // PC 0 ** 37 ** D37

Risolto, grazie per le idee e l'aiuto

void BitChange(byte Hold_Val_Port, byte New_Val_Port, byte Pin[]) 
{ 
   byte Cambio = New_Val_Port ^ Hold_Val_Port;
   
       for ( byte i=0; i < 8; i++)
       {        
         if ( Cambio & 1<<i)  //controllo lo stato del bit in posizione 8-i         
         Serial.print("Il pin " + (String)Pin[i] + " e' cambiato! ...  BIT n = " + (String)i + " ");//debug                 
       }  
}

byte C[] = {30,31,32,33,34,35,36,37};
diventa
byte C[] = {37,36,35,34,33,32,31,30};

byte L[] = {42,43,44,45,46,47,48,49};
diventa
byte L[] = {49,48,47,46,45,44,43,42};

risposta di debug prima pressione e rilascio

Il pin 32 e' cambiato! ... BIT n = 5 CHANGE PORT_C 37,36,35,34,33,32,31,30 >> val byte = 32
Il pin 42 e' cambiato! ... BIT n = 7 CHANGE PORT_L 49,48,47,46,45,44,43,42 >> val byte = 128
Il pin 32 e' cambiato! ... BIT n = 5 CHANGE PORT_C 37,36,35,34,33,32,31,30 >> val byte = 0

risposta di debug seconda pressione e rilascio
Il pin 32 e' cambiato! ... BIT n = 5 CHANGE PORT_C 37,36,35,34,33,32,31,30 >> val byte = 32
Il pin 42 e' cambiato! ... BIT n = 7 CHANGE PORT_L 49,48,47,46,45,44,43,42 >> val byte = 0
Il pin 32 e' cambiato! ... BIT n = 5 CHANGE PORT_C 37,36,35,34,33,32,31,30 >> val byte = 0

Quindi serviva invertire l'ordine dei pin negli array.

si cambiare l'ordine e modificare il confronto, stampando il debug del for mi si è evidenziato l'errore

da
if ( Cambio & (1<<(8-i)))

a
if ( Cambio & 1<< i)

ho ridotto inoltre il debounce da 100ms a 50ms

ahhah lol e pensare che mi sono sbattuto apposta a fare (8-i) per invertire le uscite in base alla maschera :slight_smile: