Come risolvere interferenze dall'interrupt?

Ciao a tutti

Sto completando lo stroboscopio. Mi capita saltuariamente che la scritta "OVR" sul display, che indica il superamento del 10% di duty cycle con un trigger esterno, non venga cancellata o che addirittura funzioni al contrario. L'unica spiegazione è che, poiché il trigger esterno attiva un interrupt, questo vada a cambiare le carte in tavola durante l'esecuzione della funzione OVR_display(), che controlla sia la scritta "OVR" che lo stato del LED rosso, spento o intermittente.
Come posso risolvere il problema? Ho provato con ATOMIC_BLOCK (#include <util/atomic.h>), ma non funziona più lcd.print!

Grazie
Gianluca

volatile uint32_t cont_match=0; // Conta i compare Match di 20us ciascuno.
volatile bool over=false; // Per evitare il superamento del 10% di duty cycle con il trigger esterno (Ext).

setup()
{
//...
attachInterrupt(0, EXT_TRIGGER, FALLING); // Ext.
}

void EXT_TRIGGER() // Chiamata dal trigger esterno.
  {
  if(!ind_freq) // Se è 0, cioè Ext:
    {
    if(shutter>=485000L/cont_match) // f OK. Metto 485000 anziché 500000 per lasciare un po' di tolleranza.
      {
      cont_match=0;
      PORTD|=0b10000000; // Lampo ON.
      PORTB|=(0b00000010 | Ticon); // LED verde (PB1) e Tic (PB0) se è attivato.
      over=false; 
      }
    else // f OVER.
      {
      cont_match=0;
      over=true;
      }
    }
  }

void OVR_display() // Chiamata periodicamente nel loop().
  {
  if(!over) // Non over.
    {
    PORTC &= (byte)~0b00000100; // Spegne il LED rosso se è acceso. Senza il casting, esce a 16 bit!
    }
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
    {
    if(!ind_freq && over)
      {
      if(millis()-t_interm_rosso>249) // Fa lampeggiare il LED rosso.
        {
        t_interm_rosso+=250;
        PINC |= 0b00000100;
        }
      if(over != over_prec)
        {
        lcd.setCursor(12,0);
        if(over) lcd.print("OVR");
        else     lcd.print("   ");
        }        
      over_prec=over;
      }
    } // END ATOMIC
  }

C'è un errore...

Comunque l'ATOMIC andava a interferire con la temporizzazione principale!
Ho abolito il nucleare e ho fatto semplicemente un aggiornamento periodico dello stato della scritta "OVR":

void OVR_display()
  {
  if(!over) // Non over.
    {
    PORTC &= (byte)~0b00000100; // Spegne il LED rosso se è acceso. Senza il casting, esce a 16 bit!
    }
  if(!ind_freq)
    {
    if(millis()-t_aggiorn_OVR>500)
      {
      t_aggiorn_OVR+=500;
      lcd.setCursor(12,0);
      if(over) lcd.print("OVR");
      else     lcd.print("   ");
      }
    if(over)
      {
      if(millis()-t_interm_rosso>249) // Fa lampeggiare il LED rosso.
        {
        t_interm_rosso+=250;
        PINC |= 0b00000100;
        }
      }
    }
  }

Però anche così funziona:

void EXT_TRIGGER()
  {
  if(!ind_freq) // Se è 0, cioè Ext:
    {
    if(shutter>=485000L/cont_match) // f OK. Metto 485000 anziché 500000 per lasciare un po' di tolleranza.
      {
      if(millis()-t_over>500)
        {
        over=false; 
        cont_match=0;
        PORTD|=0b10000000; // Lampo ON.
        PORTB|=(0b00000010 | Ticon); // LED verde (PB1) e Tic (PB0) se è attivato.
        }
      }
    else // f OVER.
      {
      cont_match=0;
      over=true; t_over=millis();
      }
    }
  }

void OVR_display()
  {
  if(!over) // Non over.
    {
    PORTC &= (byte)~0b00000100; // Spegne il LED rosso se è acceso. Senza il casting, esce a 16 bit!
    }
  if(!ind_freq)
    {
    if(over!=over_prec)
      {
      over_prec=over;
      lcd.setCursor(12,0);
      if(over) lcd.print("OVR");
      else     lcd.print("   ");
      }
    if(over)
      {
      if(millis()-t_interm_rosso>249) // Fa lampeggiare il LED rosso.
        {
        t_interm_rosso+=250;
        PINC |= 0b00000100;
        }
      }
    }
  }

Cioè quando si dice; chi fa da se fa per tre (te).
:smiley:

Tanto per argomentare circa ATOMIC così sai cosa fa di preciso.
Salva il registro SREG di cui un bit indica se gli interrupt globali sono o meno abilitati.

Disabilita gli interrupt globali
Esegue il codice all'interno delle graffe e per finire rimette SREG come lo aveva trovato prima.

Quindi ad esempio se gli interrupt sono disabilitati restano disabilitati sia durante {} che dopo.

L'equivalente sarebbe:
byte _sreg = SREG;
cli();
// tuo codice
SREG = _sreg;

Ecco perché non funge la Serial.print().

Ciao.

1 Like

Grazie! :slight_smile:

Impedendo il ritorno di over a false prima di 500ms con if(millis()-t_over>500) il difetto non si è mai manifestato.
Forse ho finito il programma dello strobocopio. Certo, però, che nonostante tutto quello che ci ho messo (con 3 interrupt "sotto") ho ancora il 64% +80% di spazio libero... :smile:

#define RED_LED_OFF() PORTC &= (byte)~0b00000100

Fai anche una macro per

PINC |= 0b00000100;

Così non devi scriverci neanche il commento.

ah ovviamente il tipo di dato usato per le costanti manifeste è sempre int, sia che esprimi in decimale, binario, ottale ed esadecimale. Per cui il cast esplicito è necessario.

Si risparmi flash non usando digitalWrite/Read ecc.

Ciao.

Sì, hai ragione. Infatti già l'ho fatto per leggere lo stato del pulsante, senza dover pensare che il pulsante premuto è a livello basso:
#define puls_prem !(PIND&0b01000000)

Grazie.
Ciao

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.