Blink senza delay() utilizzando uno shift register 74HC595N

Buonasera a tutti prima di tutto.
Mi sto costruendo un piccolo robottino a scopo didattico e tra movimentazione e sensori i pin di un Arduino Uno stanno finendo. A questo punto ho inserito uno shift register (un classico 74HC595N) e sfruttare le sue uscite per attivare brevi animazioni led in determinate circostanze.
Premesso che una classica animazione dei led utilizzando il delay funziona senza problemi!
Io però devo assolutamente evitare il delay() e utilizzare i millis() per dire al registro a scorrimento di attivare un'uscita per un secondo, dopodichè disattivarla e contemporaneamente attivare un altra uscita. (il classico blink).

Allego parte del codice utilizzando il delay() --> funzionante

animazione1(1000);
void animazione1(unsigned int ritardo)
{
  for(int i =0; i<8; i++)
  {
    scriviRegistro(0x1 << i);
    delay(ritardo); //questo non deve esserci
  }
}
void scriviRegistro(byte valore)
{
  digitalWrite(LATCH_PIN, LOW);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, valore);
  digitalWrite(LATCH_PIN, HIGH);
}

Nonostante utilizzo il controllo dei millis() in altri parti del codice (per evitare il delay), con lo shift register mi sfugge qualcosa.
Probabilmente sarà una banalità ma ahimè la cosa mi sta sfuggendo di mano.
Ringrazio anticipatamente chi mi aiuta e/o mi consiglia
Andrea

Il problema non è lo shift register, ma la logica racchiusa nel for, perché il for non termina finché non ha concluso tutte le iterazioni.

In sostanza il for deve sparire, e la funzione animazione1 deve essere eseguita in più passaggi/chiamate (come un processo a sé stante che avanza per i fatti suoi).

Ad ogni passaggio si controlla il tempo trascorso, e se serve si effettua l'azione richiesta.

Può servire una variabile globale di abilitazione, che al temine della sequenza viene azzerata fermando il processo animazione (senza fermare altre cose).

1 Like

Una cosa del genere:

uint32_t t_animazione1;

-----

if(n<8 && (millis()-t_animazione1>999))
  {
  t_animazione1=millis();
  scriviRegistro...
  n+=1;
  }
  
1 Like

Immensamente grazie per il supporto.... in effetti non pensavo al fatto che il ciclo for andava per i fatti suoi.
Ho risolto il problema inserendo le 8 uscite dello shift register in un array e ad ogni scadenza di tempo scrivo il registro prendendo un indice dell'array.
Allego listato nel caso serva a qualcuno con la doverosa premessa che stilisticamente non è bello anche se funzionale.
Accetto qualsiasi miglioramento.

#define DATA_PIN 4
#define LATCH_PIN 3
#define CLOCK_PIN 2
//uscite shift register
byte uscita0 = 0x1;
byte uscita1 = 0x1<<1;
byte uscita2 = 0x1<<2;
byte uscita3 = 0x1<<3;
byte uscita4 = 0x1<<4;
byte uscita5 = 0x1<<5;
byte uscita6 = 0x1<<6;
byte uscita7 = 0x1<<7;
byte arrayUsciteShiftRegister[8]={uscita0,uscita1,uscita2,uscita3,uscita4,uscita5,uscita6,uscita7};
unsigned int contatore = 0;
//
unsigned long tempoAttuale;
unsigned long tempoPrecedente = 0;
unsigned int intervalloTemporale = 1000;

void setup()
{
  pinMode(DATA_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  animazione();
}
void scriviRegistro(byte valore)
{
  digitalWrite(LATCH_PIN, LOW);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, valore);
  digitalWrite(LATCH_PIN, HIGH);
}
void animazione()
{
  tempoAttuale = millis();
  if((tempoAttuale-tempoPrecedente)>intervalloTemporale)
  {
    tempoPrecedente = tempoAttuale;
    Serial.println(String(contatore));
    scriviRegistro(arrayUsciteShiftRegister[contatore]);
    contatore++;
    if(contatore>7)
    {
      contatore=0;
    }
  }
}

C'è giusto qualche variabile/istruzione di troppo che possono essere ottimizzate.

1 Like

Con contatore che va da 0 a 7, l'array fornisce valori da 2^0 a 2^7, cioè
scriviRegistro(1<<contatore);

A che serve String?...

tempoPrecedente non indica a che cosa serve... Dalle un nome! Io per le letture di millis() uso sempre variabili che iniziano per t_, così si riconoscono subito, per esempio t_animazione. Se ti servirà un'altra variabile, le potrai dare un nome diverso e adeguato.

Hai ragione! Lo scrissi subito così per ricordi di Java. Questione di abitudine! :smiley:

Ottimo!!!! ... e soprattutto grazie!

ok per 't_' iniziale per la variabile.... io ho sempre avuto dubbi su come chiamare la variabile 'tempoPrecedente'. Non so davvero come definirla! Consiglio?

uint32_t t_animazione
1 Like

Tra l'altro, confinando le variabili nell'ambito di visibilità più ristretto possibile (variabili locali, e dichiarate static se devono mantenere il valore tra una chiamata e l'altra), si possono sia evitare nomi troppo articolati (inevitabili nel namespace globale), sia riutilizzare gli stessi nomi brevi in più funzioni.

Personalmente, e rigorosamente all'interno di una funzione (possibilmente breve), tendo a usare variabili di una sola lettera che hanno sempre lo stesso significato:
c contatore
s stato
t tempo
i j indici
n m valori numerici temporanei
x ingresso
y uscita

Questa scrittura succinta naturalmente so essere in contrasto con le regole di stile :wink:

3 Likes

Non ricordo cosa c'è scritto di preciso nel libro, però sono quasi certo che faccia riferimento alla coerenza. Quindi se s sono stati lo saranno in tutte le funzioni. Se non basta s aggiungi qualcosa s1, s2 ecc. ijk ecc sono consigliati in tutti libri che ho letto, sempre come hai detto a tiro corto, cioè i in for annidati con tante if lunga più di una pagina no.

Per un programmatore abituato a leggere codice, se questo è coerente si fa presto ad ambientarsi.

Alcuni consigli spassionati circa la/e variabile/i determinanti.
variabileDeterminante = (s1 & s2) << CONSTANT ecc.
Ecco la variabile determinante dovrebbe avere un nome non breve quanto s1, s2.

Calcoli anche semplici dove (sarebbe espressioni) compaiono costanti di sistema, costanti di programma, variabili globali dovrebbero essere preceduti da un commento, es:

clkdiv = (float)frequency_count_khz(src) * (float)KILO / (PWM_FREQ * WRAP);

Risulta oscuro ed è estremamente determinante, quindi:

// src(1)
 // clkdiv   125000 x 1000 / (50 * 10000) = 250
clkdiv = (float)frequency_count_khz(src) * (float)KILO / (PWM_FREQ * WRAP);

Già è meno oscuro.

Ciao.

1 Like