Lampeggio LED

Progetti Arduino
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Lampeggio LED

Messaggio da pgv »

Buongiorno a tutti,

capita abbastanza spesso di voler far lampeggiare un LED, e non a caso il primissimo esempio e' Blink. Tuttavia, Blink ha la caratteristica non del tutto ottimale di utilizzare chiamate a delay() (che sono bloccanti) per determinare il tempo di lampeggio. Come conseguenza, Arduino non puo' eseguire altre istruzioni mentre aspetta di dover cambiare lo stato del LED e produrre il lampeggio. Ci sono altre soluzioni basate sulla memorizzazione del numero di millisecondi passati dall'inizio e confrontarli con il valore voluto, ma anch equesti metodi richiedono una contabilita' che personalmente trovo noiosa.
Vi propongo uno sketch breve breve in cui, una volta definito che un LED deve lampeggiare, il lampeggiamento viene gestito "in automatico" tramite una routine di servizio all'interruzione di fine corsa del Timer0, la stessa che e' usata per la funzione millis(). L'estensione a periodi diversi da 200 ms e a Duty Cycle diversi dal 50% e' lasciata per esercizio. Senza ulteriore indugio:

Codice: Seleziona tutto

// 98 chiamate della routine di interrupt del Timer0,
// ossia 98*1.024 millisecondi = 100.4 ms
#define TLAMPEGGIO 98
// Definiamo anche a quale pin e' connesso il LED che deve lampeggiare
#define QUALEPIN 13

int conteggioLed;       // Contatore per sapere quante volte
                        // e' stata gia' eseguita la ISR del Timer0
bool statoLed;          // Se true il LED lampeggia
bool lampeggioLed;      // per ricordare se il LED e' ON o OFF

void setup() {
  // Il Timer0 e' gia' usato per la funzione millis(), quindi
  // noi ci agganciamo e lampeggiamo il LED quando necessario
  TIMSK0 |= _BV(OCIE0A);
  // Ricordiamoci di definire il pin del LED come uscita
  pinMode(QUALEPIN, OUTPUT);
  // e finalmente, inizializziamo le variabili conteggioLed
  conteggioLed = 0;  
  // e lampeggioLed (spento)
  lampeggioLed = false;
}

// Questa funzione e' chiamata ogni millisecondo (per cui manteniamola breve!) 
SIGNAL(TIMER0_COMPA_vect) 
{
  if (!statoLed)         {  // il LED deve stare spento
    digitalWrite(QUALEPIN, LOW);  // E basta...
  } else {                  // il LED deve lampeggiare
    conteggioLed++;         // Incrementiamo il contatore delle esecuzioni
    if (conteggioLed >= TLAMPEGGIO) { // Sono passati 100 ms
      lampeggioLed = !(lampeggioLed); // Invertiamo lo stato
      // Scriviamo il nuovo valore sul PIN
      digitalWrite(QUALEPIN, (lampeggioLed ? HIGH : LOW)); 
      // Azzeriamo il conteggio per ricominciare
      conteggioLed = 0;
    }
  }
}

void loop() {
  statoLed = true;  // Non bisogna dimenticarsi di dare l'ordine di lampeggiare!
  
  // Metti qui il codice da eseguire ripetutamente mentre il LED lampeggia
  while(1) { 
  // Non fare niente! Arduino in vacanza! 
  }
}
Resto a disposizione per chiarimenti.
Guido
Messaggi: 1443
Iscritto il: dom 18 mar 2018, 20:21

Re: Lampeggio LED

Messaggio da Guido »

Scusa, siccome l'istruzione "TIMSK0 |= _BV(OCIE0A)" mi e' totalmente sconosciuta, potresti dirmi dove posso trovare documentazione al riguardo ?
grazie
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: Lampeggio LED

Messaggio da pgv »

Ahime', la spiegazione "corretta" sarebbe "nella sezione 15.9 del datasheet della CPU", che nel mio caso e' a pagina 18 di
http://ww1.microchip.com/downloads/en/D ... 02061B.pdf
pero' mi sento molto a disagio a suggerire la lettura di un manuale di piu' di 650 pagine... Cerchero' di spiegare in breve e in italiano.

TIMSK0 e' il nome di uno dei registri speciali della CPU di Arduino, in particolare e' il registro destinato a contenere la maschera degli interrupt per il Timer/Counter (Temporizzatore/Contatore). Si tratta di un registro che, ribadisco, per la particolare CPU dell'Arduino Uno che sto usando (ATMega328P) possiede solamente i tre bit meno significativi, che si chiamano rispettivamente
  • OCF0B - "Timer/Counter 0 Output Compare B Match Flag". Traduco dal datasheet:
    Questo bit prende il valore 1 quando avviene un "Confronto Uguaglianza" tra il Temporizzatore/Contatore 0 e il valore contenuto nel registro OCR0B ("Output Compare Register0 B"). Questo bit automaticamente riprende il valore 0 quando viene chiamate la corrispondente routine di servizio di interruzione. In alternativa, esso puo' essere azzerato scrivendovi esplicitamente 0. Quando il bit I nel registro SREG, OCIE0B ("Timer/Counter 0 Compare B Match Interrupt Enable"), e OCF0B hanno il valore 1, viene chiamata la routine di servizio di interruzione per il Confronto Uguaglianza del Timer 0.
  • OCF0A - "Timer/Counter 0 Output Compare A Match Flag".
    Questo bit prende il valore 1 quando avviene un "Confronto Uguaglianza" tra il Temporizzatore/Contatore 0 e il valore contenuto nel registro OCR0A ("Output Compare Register0 A"). Questo bit automaticamente riprende il valore 0 quando viene chiamata la corrispondente routine di servizio di interruzione. In alternativa, esso puo' essere azzerato scrivendovi esplicitamente 0. Quando il bit I nel registro SREG, OCIE0A ("Timer/Counter 0 Compare A Match Interrupt Enable"), e OCF0B hanno il valore 1, viene chiamata la routine di servizio di interruzione per il Confronto Uguaglianza del Timer 0.
  • TOV0 - "Timer/Counter 0 Overflow Flag"
    Questo bit prende il valore 1 quando avviene un overflow nel Temporizzatore/Contatore 0. Esso riprende automaticamente il valore 0 quando viene chiamata la corrispondente routine di servizio di interruzione. In alternativa, esso puo' essere azzerato scrivendovi esplicitamente 0. Quando il bit I di SREG, TOIE0 ("Timer/Counter 0 Overflow Interrupt Enable), e TOV0 hanno il valore 1, viene eseguita la routine di servizio di interruzione per l'Overflow del Timer 0.
Per dirla invece in parole meno pompose: l'istruzione misteriosa

Codice: Seleziona tutto

TIMSK0 |= _BV(OCIE0A);
non fa che effettuare un OR bit a bit del contenuto del registro TIMSK0 con il "Bit Value" corrispondente al bit OCIE0A che e' definito da qualche parte sotto Arduino.h (credo). Il suo effetto e' di abilitare le interruzioni quando il valore contenuto nel Timer 0 e' uguale al valore contenuto nel registro

Codice: Seleziona tutto

OCR0A 
. Siccome il Timer 0 e' utilizzato per far funzionare la funzione millis() (e puo' essere utilizzato per le uscite in PWM) e' preferibile NON cambiare il valore di OCR0A e accontentarsi di una chiamata ogni 1024 millisecondi.

Da notare che _BV e' una MACRO la cui definizione e'

Codice: Seleziona tutto

#define _BV(bit) (1 << (bit))
e che e' valida solo per valori di bit tra 0 e 15. Quindi _BV(5) per esempio restituisce 0000,0000,0010,0000 in binario.
Listruzione che viene subito dopo, SIGNAL(TIMER0_COMPA_vect) e' un'altra macro del compilatore GCC per AVR ed e' spiegata tra l'altro in
https://ccrma.stanford.edu/courses/250a ... rupts.html - serve a dichiarare che il codice che segue deve essere "linkato" in modo da essere eseguito in risposta all'interrupt specificato come parametro.
Guido
Messaggi: 1443
Iscritto il: dom 18 mar 2018, 20:21

Re: Lampeggio LED

Messaggio da Guido »

Grazie.
Provero' a rileggermi il tutto nei prossimi giorni, ma credo che ho passato l'eta' durante la quale mi sarei divertito a studiare bene questa cosa. Adesso coltivo male quelle quattro cose che ancora ricordo e tiro avanti. Vedremo nei prossimi giorni, se sono rose fioriranno.
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: Lampeggio LED

Messaggio da pgv »

Sono arrivato anche io a "quell'eta'" ed ho imparato a servirmi di quel che ricordo... Prendila come una formula magica, per me funziona!
Guido
Messaggi: 1443
Iscritto il: dom 18 mar 2018, 20:21

Re: Lampeggio LED

Messaggio da Guido »

Rinuncio. Mi e' venuto mal di testa ;)
Ho riscritto lampeggio led con temporizzazioni non bloccanti per utenti sub-normali come me, e poi per ripassare un po' di questo linguaggio C che e' una disgrazia. Ma come si fa a concepire un linguaggio dove e' molto facile sbagliare e nell'IDE non c'e' un sistema per controllare il valore delle variabili per esempio :
/* dalla guida:
x++; // increment x by one and returns the old value of x
++x; // increment x by one and returns the new value of x
x-- ; // decrement x by one and returns the old value of x
--x ; // decrement x by one and returns the new value of x
*/
L'unica spiegazione che potrei dare di queste scelte e' che questo linguaggio sia nato quando la Ram nei PC era misurabile in Mb e la capienza dei floppy era nell'ordine delle centinaia di kbytes, e quindi anche le dimensioni dei sorgenti dovevano essere ridotte al massimo. Boh
Ad ogni modo puo' darsi che qualche principiante trovi uno spunto di interesse.

Codice: Seleziona tutto

// Lampeggio_Led_A_Modo_Mio con delay non bloccanti
// tempo in cui il led restera' acceso oppure spento in millis
#define CycleLedMax  500
// Definiamo anche a quale pin e' connesso il LED che deve lampeggiare
#define PinLed 13

int intCycleLed=0;       
unsigned long ulngTimeStart = 0;

void setup() {
    Serial.begin(9600);
    pinMode(PinLed, OUTPUT);
    ulngTimeStart = millis();
}

void loop() {
  if ((millis() - ulngTimeStart) <= CycleLedMax)
  {
       ++intCycleLed;
       Serial.print("Non fa niente-Tempo:");
       Serial.println(intCycleLed);
     }
   else 
     {
       // inverte lo stato di pinLed
       if (digitalRead(PinLed)==HIGH)
       {
       digitalWrite(PinLed,LOW);
       }
       else
       { 
       digitalWrite(PinLed,HIGH);
       }
     Serial.print("Inverte stato led. Stato:");
     Serial.println (digitalRead(PinLed));
          // azzera il tempo del ciclo   
     intCycleLed = 0;
     ulngTimeStart = millis();

  }
}
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: Lampeggio LED

Messaggio da pgv »

Il linguaggio C e' nato nel 1972-73, quando la memoria dei computer (PDP-7, PDP-11) si misurava in kilobyte, nemmeno in megabyte! Ed erano macchine di Dipartimento, non desktop, ovviamente. Floppy??? Nastri magnetici!!! RAM? Memorie a ferrite!!! Ci ho lavorato. Un giorno ci fu un problema col PDP 11/35 del Dipartimento di Fisica, e chiamammo il servizio. Arrivo' un tale con una scheda di memoria nuova (256 kword a 16 bit, circa mezzo metro per mezzo metro, non scherzo), estrasse la memoria esistente e la ripose in una valigetta, dicendo che la portava in laboratorio per aa=nalizzare i contenuti. Ogni bit era salvato come magnetizzazione in una ferrite, e i dati restavano in memoria anche dopo spento...

Quanto alle istruzioni di pre- e post incremento/decremento, dopo un po' uno ci si abitua:
  1. Post-incremento: prima fai tutto il resto, poi incrementa x. Per esempio, se x inizialmente contiene il valore 10,
    dati[x++] = 3; // Salva il valore 3 nell'elemento dati[10], poi incrementa x (che assume il valore 11)
  • Pre-incremento: prima incrementa x, poi fai tutto il resto. Per esempio, se x inizialmente contiene il valore 10,
    dati[++x] = 3; // Incrementa il valore di x, che assume il valore 11, poi salva il valore 3 nell'elemento dati[11]
  • Post-decremento: prima fai tutto il resto, poi decrementa x. Per esempio, se x inizialmente contiene il valore 10,
    dati[x--] = 3; // Salva il valore 3 nell'elemento dati[10], poi decrementa x (che assume il valore 9)
  • Pre-decremento: prima decrementa x, poi fai tutto il resto. Per esempio, se x inizialmente contiene il valore 10,
    dati[--x] = 3; // decrementa il valore di x, che assume il valore 9, poi salva il valore 3 nell'elemento dati[9]
Quattro istruzioni (leggermente) diverse, quattro risultati diversi. Alla fine:
  1. x contiene 11, dati[10] contiene 3;
  • x contiene 11, dati[11] contiene 3;
  • x contiene 9, dati[10] contiene 3;
  • x contiene 9, dati[9] contiene 3;
Guido
Messaggi: 1443
Iscritto il: dom 18 mar 2018, 20:21

Re: Lampeggio LED

Messaggio da Guido »

Se usavano le memorie in ferrite il linguaggio criptico sara' sembrato l'ultimo dei problemi :D
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: Lampeggio LED

Messaggio da pgv »

Ho fatto di peggio in gioventu', compreso progettare, costruire e debuggare una scheda mezzo metro per mezzo metro in wire-wrap, con un MC68020, circuiti TTL e circuiti ECL, utilizzando un microVAC II e il linguaggio FORTH. QUELLO era un linguaggio "interessante"... A voi "Hello World" in FORTH. Ed e' perfino disponibile per Arduino!!!

Codice: Seleziona tutto

/*
 * say hello world
 */

/* start at this address */
gotoStart

/*
 * program starts here
 */
start

  C" Hello World" 1+ dup strlen type cr

return
Guido
Messaggi: 1443
Iscritto il: dom 18 mar 2018, 20:21

Re: Lampeggio LED

Messaggio da Guido »

pgv ha scritto: ven 13 nov 2020, 8:35 il linguaggio FORTH. QUELLO era un linguaggio "interessante"... A voi "Hello World" in FORTH. Ed e' perfino disponibile per Arduino!!!
Ma mi sembra un linguaggio piu' umano: qui il codice, e presumo anche le funzioni, terminano con un "return" e non con una parentesi graffa ;)
Rispondi