Go Down

Topic: Curiosità sulla programmazione condizionale (Read 413 times) previous topic - next topic

Moce993

Buongiorno,

una mera curiostà, tra gli esempi che riporto qui sotto voi quale preferite e perchè ?

VERSIONE 1
Code: [Select]

#define ledPin 15

unsigned int blinkingFrequency = 1000;
unsigned long oldBlinkingTime = 0;
bool ledStateOn;

void setup() {
 
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

}

void loop() {

  if(millis() - oldBlinkingTime >= blinkingFrequency) {

    if(ledStateOn == true) {
      digitalWrite(ledPin, LOW);
      ledStateOn = !ledStateOn;
    } else {
      digitalWrite(ledPin,HIGH);
      ledStateOn = !ledStateOn;
    }

    oldBlinkingTime = millis();
  }
}


VERSIONE 2
Code: [Select]


#define ledPin 15

unsigned int blinkingFrequency = 1000;
unsigned long oldBlinkingTime = 0;
bool ledStateOn;

void setup() {
 
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

}

void loop() {

  if(millis() - oldBlinkingTime >= blinkingFrequency) {

    digitalWrite(ledPin, ledStateOn? HIGH : LOW);
    ledStateOn = !ledStateOn;
   
    oldBlinkingTime = millis();
  }
}


VERSIONE 3
Code: [Select]


#define ledPin 15

unsigned int blinkingFrequency = 1000;
unsigned long oldBlinkingTime = 0;

void setup() {
 
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);


}

void loop() {

  if(millis() - oldBlinkingTime >= blinkingFrequency && digitalRead(ledPin) == HIGH) {
   
    digitalWrite(ledPin, LOW);
    oldBlinkingTime = millis();
   
  }else if(millis() - oldBlinkingTime >= blinkingFrequency && digitalRead(ledPin) == LOW) {
   
    digitalWrite(ledPin, HIGH);
    oldBlinkingTime = millis();
  }
}


so che molto dipende da come uno è abituato a programmare, ma questa mia curiosità nasce dal fatto che molte volte mi fermo a pensare come sia meglio scrivere il mio codice, sicuramente qualcuno di più esperto potrà dire la sua per sanare questa mia curiosità  :D

fabpolli

preferisco la quarta versione  :)
Code: [Select]


#define ledPin 15

unsigned int blinkingFrequency = 1000;
unsigned long oldBlinkingTime = 0;
bool ledStateOn;

void setup() {
 
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

}

void loop() {

  if(millis() - oldBlinkingTime >= blinkingFrequency) {

    digitalWrite(ledPin, !ledStateOn);
    ledStateOn = !ledStateOn;
    oldBlinkingTime = millis();
  }
}

In quanto booleana ledStateOn per pilotare direttamente lo stato del pin.
A parte questo la terza è la peggiore secondo me perché coinvolge in un solo if dua parti logiche che è mi piace più tenere separate (Es. in futuro di aumentazione le condizioni a parità di tempo di millis devi modificare tuti gli if e eventualmente aggiungerne altri)
La prima l'if è "unnecessary" e la seconda è uguale alla mia quarta ma senza il ternario che di nuovo non serve.

Federico66

preferisco la quarta versione  :)
sono d'accordo, e aggiungo che se ledStateOn non serve ad altro, io la eliminerei.

Code: [Select]

digitalWrite(ledPin, !digitalRead(ledPin));



Federico

"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

Datman

Si è fatto tardi e ho letto sommariamente, ma il nome blinkingFrequency non è corretto: non è una frequenza, ma un semiperiodo, quindi si potrebbe chiamare Ton.
Hi,I'm Gianluca from Roma.I play&work with electronics since I was16(1984).
After 25yrs of maintenance on cameras&video mixers,since 2013myJob is HDTVstudios design.
Since Jan2015 IPlayWith Arduino:bit.ly/2F3LPWP
Thanks 4 a Karma if U like my answer

Moce993

#4
Oct 09, 2019, 08:05 pm Last Edit: Oct 09, 2019, 08:06 pm by Moce993
In quanto booleana ledStateOn per pilotare direttamente lo stato del pin.
Azz... vero  :smiley-roll-sweat:

comunque la terza versione pure a me non piace l'ho messa così per vedere cosa ne pensavate, diciamo io preferisco la prima versione... (più per immediatezza di comprensione in fase di lettura che per altro).



sono d'accordo, e aggiungo che se ledStateOn non serve ad altro, io la eliminerei.

Code: [Select]

digitalWrite(ledPin, !digitalRead(ledPin));

Verissimo e si ottiene lo stesso risultato con una variabile in meno, ma mi sorge un dubbio non sono due cose diverse ? Cioè utilizzare digitalRead per vedere lo stato di un pin non è diverso rispetto al controllare lo stato di una variabile boleana ? Parlo a livello del micro.

Federico66

Cioè utilizzare digitalRead per vedere lo stato di un pin non è diverso rispetto al controllare lo stato di una variabile boleana ?
non capisco bene la domanda, in che senso diverso?
se ti riferisci al tempo di esecuzione, non saprei risponderti, ma azzarderai che cambia ben poco.

In ogni caso, la mia è una semplificazione per questo specifico esempio, nella stragrande maggioranza dei casi, probabilmente farai il contrario, assegnerai il valore della digitalRead a qualche variabile che utilizzerai per svariati controlli.

Sia ben chiaro che si sta discutendo di un esempio specifico, e non è possibile generalizzare.

Federico
"La logica vi porterà da A a B. L'immaginazione vi porterà dappertutto." A. Einstein

AmericanDreamer

La digitalread è certamente più lenta

E qualcuno ha detto che va solo sugli AVR
Non so se è vero
Ma fa risparmiare una variabile, servisse

Io preferisco la 5' versione
Code: [Select]

{
    digitalWrite(ledPin,  ledStateOn =!ledStateOn);
    oldBlinkingTime = millis();
}



gpb01

#7
Oct 09, 2019, 09:36 pm Last Edit: Oct 09, 2019, 09:39 pm by gpb01
se ti riferisci al tempo di esecuzione, non saprei risponderti, ma azzarderai che cambia ben poco.
Visto che in questo thread mi sembrate tutti piuttosto "scafatelli" :smiley-mr-green: ...
... ottimizziamo ancora di più e rendiamo massima la velocità di quel "PIN toggle".


Prima di tutto dovete avere ben a mente i vari pin di Arduino a cosa corrispondono nella MCU ...

Code: [Select]
//                  ARDUINO ATMEGA328P
//
//                       +-\/-+
//                 PC6  1|    |28  PC5 (AI 5) (D 19)
//           (D 0) PD0  2|    |27  PC4 (AI 4) (D 18)
//           (D 1) PD1  3|    |26  PC3 (AI 3) (D 17)
//           (D 2) PD2  4|    |25  PC2 (AI 2) (D 16)
//       PWM (D 3) PD3  5|    |24  PC1 (AI 1) (D 15)
//           (D 4) PD4  6|    |23  PC0 (AI 0) (D 14)
//                 VCC  7|    |22  GND
//                 GND  8|    |21  AREF
//                 PB6  9|    |20  AVCC
//                 PB7 10|    |19  PB5 (D 13)
//       PWM (D 5) PD5 11|    |18  PB4 (D 12)
//       PWM (D 6) PD6 12|    |17  PB3 (D 11) PWM
//           (D 7) PD7 13|    |16  PB2 (D 10) PWM
//           (D 8) PB0 14|    |15  PB1 (D 9)  PWM
//                       +----+
//
//
//                    BIT NUMBERS
//
//  +-----+-----+-----+-----+-----+-----+-----+-----+
//  | Px7 | Px6 | Px5 | Px4 | Px3 | Px2 | Px1 | Px0 |
//  +-----+-----+-----+-----+-----+-----+-----+-----+
//
//  x = B (PORTB), C (PORTC), D (PORTD)

... così sapete come individuare un dato pin digitale (usiamo per il nostro esempio il pin D10) e trovare in che porta e posizione è ... se guardate nello schemino, (D 10) corrisponde al PIN PB2 della porta B.

A questo punto sfruttiamo una particolarità del ATmega328P ... normalmente l'identificativo PINx indica una lettura dalla porta e si usa in input, ma ... se ci scriviamo, detta MCU effettua automaticamente il "toggle" del pin.

Per cui, dato che ad ogni giro volete invertire il pin rispetto a come era (ovvero fare appunto un toggle), fissato nel nostro esempio che dobbiamo agire sulla porta B e sul pin B2, scriveremo:

Code: [Select]
PINB = _BV(PB2);
... ed il gioco è fatto!  Più veloci di così a fare il toggle è impossibile :D

Guglielmo

P.S.: la macro _BV() è definita in <avr/io.h> come: #define _BV(bit)   (1 << (bit))
Search is Your friend ... or I am Your enemy !

speedyant

Interessante. Ma quanto veloce? Riesco a vederlo su un oscilloscopio da 25MHz?

gpb01

#9
Oct 09, 2019, 10:06 pm Last Edit: Oct 09, 2019, 10:09 pm by gpb01
Interessante. Ma quanto veloce? Riesco a vederlo su un oscilloscopio da 25MHz?
Beh ... certo, NON più veloce del clock della MCU non ti pare?  :smiley-evil:

Anzi, quel toggle viene così tradotto dal compilatore (listato assembler):

Code: [Select]
ldi r24, (1<<2)
out PINB, r24

... quindi, almeno due cicli macchina per un toggle ;)

Guglielmo
Search is Your friend ... or I am Your enemy !

Moce993

Sia ben chiaro che si sta discutendo di un esempio specifico, e non è possibile generalizzare.
Si hai ragione, forse ho sbagliato ad usare come esempio il digitalWrite, la mia voleva essere una considerazione più generica tipo la seguente:

Code: [Select]
bool condition = true;
unsigned int runTimeFrequency = 1000;
unsigned long oldRunTime = 0;

void setup() {
 
  Serial.begin(115200);

}

void loop() {

  if(millis() - oldRunTime >= runTimeFrequency) {

    if(condition == true) {
      Serial.println("A");
    } else {
      Serial.println("B");
    }

    condition = !condition;
    oldRunTime = millis();
   
  }
}


OPPURE

Code: [Select]

bool condition = true;
unsigned int runTimeFrequency = 1000;
unsigned long oldRunTime = 0;

void setup() {
 
  Serial.begin(115200);

}

void loop() {

  if(millis() - oldRunTime >= runTimeFrequency) {
   
    Serial.println(condition? "A" : "B");
    condition = !condition;
    oldRunTime = millis();
   
  }
}


Forse così sono riuscito a rendere più generico l'esempio  ::)

non capisco bene la domanda, in che senso diverso?
Intendevo a basso livello i "passaggi", passatemi il termine, che il micro deve fare per verificare una boleana sono diversi rispetto a quelli che deve fare per controllare lo stato di un pin ? (sono ignorante in materia...) e di conseguenza anche i tempi di reazione.

Ma visto che con il mio esempio ho scoperchiato un vaso di pandora e visto che abbiamo alzato il tiro ne approfitto :D

Visto che in questo thread mi sembrate tutti piuttosto "scafatelli" :smiley-mr-green: ...
... ottimizziamo ancora di più e rendiamo massima la velocità di quel "PIN toggle".
Dipende cosa intendi per "scafatelli"  :smiley-sweat: (parlo per me ovviamente)
... ed il gioco è fatto!  Più veloci di così a fare il toggle è impossibile :D
:o  :o  :o Ottima spiegazione. Ovvio che bisogna conoscere molto bene il datasheet.

Ma è una caratteristica solo del ATMega328P oppure si può collocare questa caratteristica all'interno di una più vasta macro famiglia ?

gpb01

#11
Oct 10, 2019, 06:18 am Last Edit: Oct 10, 2019, 06:18 am by gpb01
Ma è una caratteristica solo del ATMega328P oppure si può collocare questa caratteristica all'interno di una più vasta macro famiglia ?
Famiglia AVR  ;)

Guglielmo
Search is Your friend ... or I am Your enemy !

torn24

Moce993   se usi if else o un operatore ternario  non cambia molto!

Se tu dovessi tradurre in pseudocodice entrami i programmi, lo pseudocodice sarebbe identico.

L' operatore ternario lo tradurresti  se vero allora A altrimenti B , e if else altrettanto  :)

gpb01

#13
Oct 10, 2019, 08:25 am Last Edit: Oct 10, 2019, 08:31 am by gpb01
In ogni caso l'operatore ternario (per quanto ad alcuni di voi piaccia particolarmente) è DA EVITARE ed in alcuni ambienti è proprio proibitio.

IBM, nel suo documento "IBM Rational Test RealTime Code Review", che integra alcune delle regole del MISRA C, ha espressamente messo la regola E12.51 (trovate il tutto QUI):

E12.51- Ternary expression ?: should not be used.

Ricordatevene e adattatevi, che vi piaccia o meno ... tanto alla fine, un IF scritto per esteso o un IF fatto con l'operatore ternario, generano lo stesso codice macchina, quindi ... peggiorla la leggibilità, ma NON migliora il codice generato.

Guglielmo
Search is Your friend ... or I am Your enemy !

C1P8

Vediamo se riesco a spiegare le mie idee

Versione 1
Il test uguale uguale true è pleonastico
Anche ripetere ledstate uguale a not ledstate è una inutilità

Versione 2
L'operatore ternario permetterebbe di semplificare eventuali espressioni 'senza' dover esplicitamente usare un branch
Non è uno statement, ma un operatore, si può usare in espressioni complesse con tanti operandi
Li serve, ma qui è un overkill

Versione 3
Perché ripetere un test sui tempi?
Un semplice if nidificato semplifica il test, e lo rende più leggibile

La versione 4 è quella 'più meglio'
Nel senso che è quella canonica, in linea con tutte le regole, anche di leggibilità

La versione (senza numero) col digitalread
Non è portabile, non è leggibile da un non arduinista (la prima volta che la ho vista mi sono domandato se aveste fumato roba buona)
E non va se l'uscita è collegata con ingressi pulluppati (provare per credere, io ci ho perso un pomeriggio)

la versione 5 è la 4, più elegante ma meno leggibile
Inoltre una simile ottimizzazione in scrittura è inutile con compilatori moderni
(Che, spesso purtroppo, ottimizzano di loro sponte)
„Quando il sole della cultura è basso, i nani hanno l'aspetto di giganti"
Un Karma non costa nulla, ma dimostra che hai apprezzato, e che ne è valsa la pena.........

Go Up