Chiarimento sull'utilizzo delle funzioni

Vorrei sapere se è possibile avere più variabili indietro, come risultato di una funzione. In pratica la classica funzione int moltiplicazione(int x, int y) { int result; result = x * y; return result; } ritorna una sola variabile int come risultato della moltiplicazione. Ma se volessi fare una funzione dove voglio indietro più di una variabile come risultato, è possibile?

Io penso proprio di sì, essendo il linguaggio usato nell'IDE Arduino un C-Based (spero di non aver detto cavolate :) )

Dalla “regia” mi dicono che è possibile, utilizzando il comando struct (http://en.wikipedia.org/wiki/Struct_(C_programming_language))
Non so se questo è supportato da arduino… continuo ad informarmi…

Scusa, perchè non usi variabili globali? Le riempi nella tua funzione e le usi fuori.

giangi72: Scusa, perchè non usi variabili globali? Le riempi nella tua funzione e le usi fuori.

Perchè la stessa funzione la vorrei richiamare per cose diverse quindi gli passerei sia in input che in output le variabili su cui lavorare ogni volta per un "contesto" diverso.

Allora puoi passare dei puntatori alla funzione. In questo modo vai a modificare direttamente l'indirizzo di memoria a cui puntano i puntatori...

xelendilx:
Allora puoi passare dei puntatori alla funzione. In questo modo vai a modificare direttamente l’indirizzo di memoria a cui puntano i puntatori…

Intendevi puntatori alla variabile immagino.
Direi che è il sistema migliore

Si, intendevo passare puntatori alla variabile come argomento della funzione :D

Chiedo scusa ma cosa vorresti fare precisamente? Le funzioni restituiscono una sola variabile se non sono di tipo void. Nel tuo caso la funzione ogni volta che viene chiamata restituirà un intero. Mi sembra di capire che a te interessi moltiplicare svariate volte due numeri che a seconda del contesto cambiano. Non basterebbe richiamare più volte la stessa funzione? Se vuoi "di colpo" più variabili potresti mettere come variabile di ritorno un vettore. O ancora meglio allochi un vettore e ciclando la chiamata della funzione, ad ogni cella+1 del vettore aggiungi il risultato.

ehm ehm puntatori...

i puntartori si dichiarano con * davanti. un punattore appunto "punta" ad un area di memoria. Per ottenere l'indirizzo di un'area di memoria di una variabile basta metterci un & davanti. Per assegnare un valore alla area di memoria punatata dal puntatore si mette * davanti piccolo esempio:

void moltiplicazione(int x, int y, int *risultato)
{
(*result) = x * y;
}

void loop(){
int a=1, b=2, c;
moltiplicazione(a, b, &c);
}

attenzione: è ERRATO fare ciò:

int *c;//dichiaro un puntatore
(*c)=5;//metto 5 nell'area di memoria punatta da c è SBAGLIATO

è sbagliato perchè come tutte le variabili nel lingiuaggio c va inizializzata, e si usa la funzione malloc():

int *c;//dichiaro un puntatore
c=(int)malloc( sizeof(int) );
(*c)=5;//metto 5 nell'area di memoria punatta da c

come potete facilmente dedurre, se nella dimensione della malloc avessi moltiplicato il sizeof (necessario perchè malloc pensa a BIT) per un valore positivo, avrei ottenuto un'array di int.

e infatti gli array sono puntatori, in particolare solo l'indirizzo della prima cella dell'array:

int a[5];
//è come fare
int *a;
a = (int)malloc(sizeof(int)*5);

e dato che le successive celle sono tutte in sequenza è facile poi muoversi attraverso l'array:

a[3] = 5;
//è come fare
*(a+sizeof(int)*3)=5

Premesso e promesso che mi leggerò con calma tutti i post (oggi ho avuto una durissima giornata lavorativa) provo a spiegarvi cosa sto facendo e provo a postare il codice che vorrei convertire in "funzione" (o libreria, o simile, aiutatemi voi a capire cosa è meglio). Mi sono creato la mia soluzione fatta in casa per applicare il debounce ad un tasto di input. So che esistono già tutorial, righe di codice e librerie già fatte a tale scopo, ma l'idea era proprio di farlo a scopo didattico (sto imparando praticamente da zero nozioni di programmazione, di elettronica, di arduino, ecc.). Dato che il programmino funziona più o meno come vorrei, l'idea era quello di convertirlo appunto in un funzione da poter applicare ad uno o più tasti senza includerlo ogni volta nel codice principale. Questo che segue è il codice che in questo caso è applicato all'accensione/spengimento di un led. In pratica con questo programmino viene gestita sia la pressione del tasto senza errori di rimbalzo, sia l’eventuale pressione prolungata dello stesso (ad esempio finalizzata allo scorrimento delle righe di testo di un menù senza dover premere il tasto enne volte). Alcune soluzioni di debounce infatti, per come sono concepite, ti obbligano a premere il tasto e rilasciarlo per forza per poi premerlo e rilasciarlo di nuovo se vuoi considerare ad es. 2 input. Io invece ho trovato un compromesso di millisecondi, affinché il tasto non sia considerato un rimbalzo (quindi una lettura errata), ma che ti dia comunque la possibilità di tenerlo premuto per imputare più volte con una pressione prolungata. PS: questa soluzione per adesso si basa solo sulla logica delle resistenze pull-up perché si aspetta che la pressione di un tasto generi un segnale LOW. Questo perché vorrei rendere anche l’hardware più semplice possibile e quindi sfruttare le resistenze pull-up integrate in arduino (oltretutto tutti gli ingressi digitali le hanno). Penso che tra hardware e software, più minimalista di così non si può.

int inPin = 7;
int outPin = 13;
int val;
int lettolow = LOW;
long time = 0;
boolean LED = LOW;
void setup()
{
  pinMode(inPin, INPUT);
  digitalWrite(inPin,HIGH); 
  pinMode(outPin, OUTPUT);
  digitalWrite(outPin,LED); 
}

void loop()
{
    val = digitalRead(inPin);
    if (val==LOW && lettolow==LOW)
    {
        lettolow=HIGH;
        time= millis();
        LED = !LED;
        digitalWrite(outPin,LED);
    }
    if (millis()>=(time+250))
    {
        lettolow=LOW;
    }
}

Questa che segue è la prima bozza di conversione del codice in funzione, ma le variabili rimangono globali. L'azione è "triggerata" dalla variabile action, usata come handle tra la funzione e la routine da dove questa viene chiamata:

int inPin = 7;
int outPin = 13;
long time = 0;
boolean lettolow = LOW;
boolean action = LOW;
boolean LED = LOW;

void DR_con_debounce (int pin)
{
  if (digitalRead(pin) == LOW && lettolow == LOW)
  {
    lettolow = HIGH;
    action = HIGH;
    time = millis();
  }
  if (millis() >= (time+250))
  {
    lettolow = LOW;
  }
}

void toggle_led()
{
  LED = !LED;
  digitalWrite(outPin,LED);      
}

void setup()
{
  pinMode(inPin, INPUT);
  digitalWrite(inPin,HIGH); 
  pinMode(outPin, OUTPUT);
}

void loop()
{
  DR_con_debounce(inPin);
  if (action == HIGH)
  {
    toggle_led();
    action = LOW;
  }
}

Il tutto funziona, ma non ero convinto però di questo sistema perchè esternamente alla funzione c'è questo scambio di variabile tra la funzione e la routine principale e poi uno deve capire come è fatta la funzione per poterla usare correttamente.

A questo scopo ho fatto una modifica:

int inPin = 7;
int outPin = 13;
long time = 0;
boolean lettolow = LOW;
boolean action = LOW;
boolean LED = LOW;

boolean DR_con_debounce (int pin)
{
  if (digitalRead(pin) == LOW && lettolow == LOW)
  {
    lettolow = HIGH;
    time = millis();
    return action = HIGH;
  }
  if (millis() >= (time+250))
  {
    lettolow = LOW;
  }
  return action = LOW;
}

void toggle_led()
{
  LED = !LED;
  digitalWrite(outPin,LED);      
}

void setup()
{
  pinMode(inPin, INPUT);
  digitalWrite(inPin,HIGH); 
  pinMode(outPin, OUTPUT);
}

void loop()
{
  if (DR_con_debounce(inPin)==HIGH)
  {
    toggle_led();
  }
}

Il tutto funziona: la funzione ritorna direttamente la variabile action che è HIGH solo la prima volta, per conferma che si può eseguire l'operazione (in questo caso accensione/spengimento led) e la resetta subito dopo per evitare che questa venga ripetuta. L'azione è quindi triggerata dai ms impostati (come nel programma iniziale). Devo solo capire se così come utilizzo può andare, in base a che cosa serve il tasto di input.

Da qui in poi (anche se a questo punto non sono convinto se il tutto non sia diventato troppo macchinoso) ho cercato di poter "generalizzare" la funzione per poterla usare non solo con uno specifico tasto, ma richiamandola ogni volta anche per tasti differenti (quindi non usando variabili globali), quindi l'unico modo ogni volta è passargli le varibili relative allo specifico tasto per "ricordare" alla funzione dove era rimasta la volta scorsa. Le variabili quindi che dovrei portare dentro e fuori dalla funzione sono: - action (che già lo fa per mezzo della conversione della funzione a "int"). - pin: il tasto in questione. - lettolow: forse da rinominare in "LastState" o qualcosa del genere. - time: in questa variabile viene salvato il tempo in ms dalla lettura del tasto premuto. - TimeThreshold: volendo si può utilizzare anche questa variabile al posto dei 250ms da me impostati fissi nel programma. A questo scopo (ammesso che tutto ciò abbia un senso pratico, ma al momento non mi viene in mente una soluzione migliore) è nata la richiesta di questo post: poter passare alla funzione più variabili. Fin qui ci sono, perchè in input se ne possono passare quante se ne vuole (miafunzione (int var1, int var2, ..., int varn)). Ma come poter avere indietro anche le modifiche che vengono apportate a queste variabili?

Spero di non avervi annoiato troppo e soprattutto di essere stato chiaro

bene, stai strutturando il codice secondo il paradigma ad oggetti. La differenza tra oggetti e programmazione normale è che nel secondo caso hai le librerie che possono contenere solo funzioni o le strutture che possono contenere solo variabili, invece nella programmazione ad oggetti (OOP per gli amici) ogni classe contiene funzioni e variabili (quindi una fusione tra libreria e struttura). l'OOP è nato apposta per consentire di scrivere codice più pulito, più facilmente riutilizzabile, e un paio di altri trucchetti come overloading dei metodi, strutture modulari ecc... Le funzioni e variabili possono essere pubbliche, ovvero chiunque può usarle, o private, ovvero solo la classe stessa può usarle. Una volta che hai creato una classe (la classe è il file fisico) puoi utilizzarla, creando un'oggetto (la classe quando viene caricata in memoria). Per esempio tu crei una classe bottone che nel costruttore (ovvero quando crei l'oggetto) prende in input il pin del bottone. Poi avrai un metodo (le funzioni in OOP si chiamano metodi) per esempio update() che chiami ogni loop, e un metodo per ottenere ogni singola informazione. A questo punto hai una classe che puoi riutilizzare più volte per ogni bottone (puoi anche fare array di oggetti), e portare in giro per i vari progetti semplicemente copiando i file della classe. Ogni classe è composta da 2 file: il .h che contiene la dichiarazione di classe, dei suoi metodi e varibili, e il file .cpp che contiene il vero e proprio codice.

però forse ti sto facendo fare il passo più lungo della gamba :-)

La cosa non mi spaventa, sono qui per imparare :D Giudica tu soltanto, quanto posso essere in grado di imparare tutte queste nozioni essendo a questo livello...

Prima di avvicendarmi eventualmente in questo tipo di cose però vorrei capire almeno se la logica del debounce che ho creato ha significato o se ci sono altre soluzioni più convenienti. Questo perchè, se è vero che voglio imparare un sacco di cose, è anche vero che non voglio applicarle a situazioni (in questo caso il debounce software), solo per complicarmi la vita!

In questo momento avrei bisogno di una soluzione pratica, ma pulita al problema del bonce, e se la via per "pulire" il codice è quella da te suggerita ben vengano i tuoi suggerimenti!