funzioni, strutture, bit fields e puntatori..

ovvero, il C che non ti insegnano a scuola! per farla breve, ecco cosa ho io:

// impostazioni varie
typedef struct
{
unsigned int channel : 4;
unsigned int ottava : 4;
unsigned int sens : 1;
unsigned int patch : 7;
unsigned int waveform : 3;
unsigned int soglia : 9;
unsigned int tonalita : 4;
}Settings;

questa è la definizione della struttura contentente le impostazioni per il mio sketch, che vengono modificate selezionando le relative voci da un menù (mostrato su display 4x20 e gestito da 4 bottoni e un solido set di funzioni, di cui vado profondamente orgoglioso e che non mancherò di postare in caso qualcuno ne avesse bisogno). Ora, visto che per ogni impostazione il lavoro da fare è sempre quello (mostra una stringa sullo schermo, mostra la variabile X, ascolta lo stato della pulsantiera, eventualmente aggiorna la variabile e il display), avrei voluto creare una singola funzione e delegarle il compito di gestire tutte queste impostazioni, passandogliele come parametro. Per spiegare meglio, posto un po' del codice, già commentato:

// editSetting
// si occupa di gestire la modifica delle impostazioni
void editSetting(char str, / vuoto più assoluto su cosa scrivere qui */)
{
/*memorizzo il vecchio valore della variabile */

byte state = IDLE;
byte save = 255;
lcd.setCursor(0, 1);
lcd.print(str);
lcd.setCursor(9, 2);

while(save != 255)
{
state = buttonCheck(A0)
switch(state)
{
case UP: /* incremento la variabile */
break;

case DOWN: /* decremento la variabile */
break;

case SEL: save = 1;
break;

case BACK: save = 0;
break;
}
if(state==SEL || state== BACK)
break;
lcd.print(/variabile/);
}
if(state == SEL)
/esce normalmente/
if(state == back)
/reimposta la variabile al vecchio valore ed esce/
}

come potrete capire la (mia) difficoltà sta nel lavorare con la variabile passata per referenza (non per valore!), in quanto contenuta in una struttura peraltro suddivisa in bit fields.. confido in voi :wink:

Non riesco a capire quale sia il tuo problema, una volta che hai definito la struttura, a livello globale, questa è sempre disponibile in tutto il programma, non devi passare nulla, devi semplicemente utilizzare i singoli elementi, p.e. nel tuo caso "Settings.channel".

Nel caso definisci la struttura all'interno di una funzione, quindi è locale, se devi passare dei valori lo devi fare singolarmente, p.e.

"editSetting(string, Settings.channel, Settings.ottava)"

La definizione della funzione dovrà essere fatta in questo modo:

"editSetting(char *string, int channel, int ottava)"

Ovviamente perdi il concetto stesso della struttura visto che i valori vengono riassegnati a variabili locali, soluzione che va bene solo per passare dei parametri, ma non per ritornarli, in questo caso è molto meglio usare definizioni globali.

da reminescenze del C++ fatto a scuola ricordo che le strutture si facevano così, naturalmente doveva essere dichiarata globale

struct Settings
{
  unsigned int channel;
  unsigned int ottava;
  unsigned int sens;
  unsigned int patch;
  unsigned int waveform;
  unsigned int soglia;
  unsigned int tonalita;
};

poi si poteva dichiarare una variabile del tipo della struttura come una normale dichiarazione di variabili

Settings pippo;

per poi richiamare i vari valori puoi fare così;

pippo.ottava = 10;

e per passarle ad una funzione così

void setup()
{
   pippo=editSettings(string, pippo);
}

la definizione della funzione sarebbe così

Settings editSettings(char string[],Settings impostazioni)
{
     impostazioni.channel=10;
     ecc ecc
     return impostazioni;
}

poi può essere che ricordo male... sono passati quasi dieci anni :stuck_out_tongue:

astrobeed:
Non riesco a capire quale sia il tuo problema, una volta che hai definito la struttura, a livello globale, questa è sempre disponibile in tutto il programma, non devi passare nulla, devi semplicemente utilizzare i singoli elementi, p.e. nel tuo caso "Settings.channel".
...

il problema molto probabilmente sono io :slight_smile: Ti spiego meglio, visto che questa funzione verrà usata per agire anche su altri parametri, con visibilità variabile, allora vorrei poter passare il parametro da modificare per referenza, e rendere la funzione estremamente versatile! ora, il mio problema è nel gestire i puntatori, considerando inoltre che non ho la minima idea di come si comportino con i bit fields.

plrmntonio:
da reminescenze del C++ fatto a scuola ricordo che le strutture si facevano così, naturalmente doveva essere dichiarata globale
...

le tue reminescenze sono correttissime, è solo che sto gestendo la struttura attraverso i bit fields (riconoscibili da quel ":n" dopo la dichiarazione), metodo efficace per gestire piccole variabili (es. dei flag) ma inutile nella programmazione dei pc, che dispongono di ram in quantità. inoltre, dove possibile, vorrei evitare di ricorrere a variabili globali.

legacy:
Se ho capito bene quello che chiedi.

Io sono piu' esplicito e non dico "per referenza" ma per indirizzo, perche' se lo guardi in assembler gli stai passando proprio l'indirizzo della prima cella di memoria dove e' contenuta la struttura, cosa che io in C rendo molto esplicita e pulita con questo modo di scrivere

per esempio
p_settings = get_address(settings);
do_let(p_settings, value);

grandissimo!! ottimo codice, dopo un paio d'occhiate sembra proprio essere ciò che mi serviva!! giusto un paio di incertezze: la simbologia "->" rappresenta la stessa cosa del punto? la funzione di init che fa di preciso? grazie ancora!

legacy:
Se non ti spettina, nell'allegato ho rimosso i bit field perche' per quello che faccio non e' possibile usarli, per fare un microparser che forse ti puo' interessare.

Quest'affare infatti permette di parsare una stringa, proveniente per esempio dalla function che controlla la pulsantiera, in cui possono essere esplicitate delle azioni sulle tue variabili, in parallelo o una alla volta, tanto lui esegue tutto quello che trova fino al delimitatore ';'.

La cosa interessante e' che le ho definite e passate come struttura, e viene fatto controllo in-range const scolpito nella pietra del codice con le define, non mi e' sembrato opportuno farlo variabile a runtime, volendo si fa anche quello, e si ottimizza, ora e' solo una bozza.

il parser e' altamente tollerante (nel senso che li ignora) agli errori di sintassi.

azioni possibili

  • incremento '+', a+ fa a=a+1
  • decremento '-', a- fa a=a-1
  • incremento veloce ">', a> fa a=a*2
  • decremento veloce "<", a< fa a=a/2
  • complemento "~", a~ prende il massimo valore che a puo' assumere e gli sottrae a, a = max - a
  • xor '^', un po' macchinoso, esempio 6a^, fa xor(a,6)
  • reset '@', assegna a tutte la variabili i loro valori iniziali, definiti nella value_init
  • assegnazione, a3, fa a=3

boh, se ti e' utile lo allego, a me ha dato un paio di ideucce :grin:

{a,b,c,d,e,f,g} sono i field della tua struttura
per esempio "a" e' il tuo "channel", puo' assumere valori che vanno da 0 a 15, e ha valore iniziale pari ad 1

quello che segue e' il debug del parsing della stringa "a1,b2,c3,d4,e5,f6,g7; a+,b+,c+,d+; a+, a~, a4, 2a^, 6a^"

setting_string[]={ a=1(let) b=2(let) c=3(let) d=4(let) e=5(let) f=6(let) g=7(let) } -> settings[]={ a=1, b=2, c=3, d=4, e=5, f=6, g=7 }
setting_string[]={ a+=2 b+=3 c+=4 d+=5 } -> settings[]={ a=2, b=3, c=4, d=5, e=5, f=6, g=7 }
setting_string[]={ a+=3 a~=12(rangemax=15) a=4(let) =2(let) a^=6 =6(let) a^=0 } -> settings[]={ a=0, b=3, c=4, d=5, e=5, f=6, g=7 }
setting_string[]={ }

ho dato un'occhiata al file, e devo dirti che l'idea del parser è molto interessante, ad esempio per scambiare tramite seriale una lista di operazioni da fare, come se si trattasse di macro.. si possono sostituire pezzi di codice da remoto etc.. unica pecca che noto (forse mi sbaglio) è che sembrerebbe incapace di gestire numeri di più cifre..

in ogni caso una serie di funzioni di questa portata è preclusa a priori dal mio progetto, in quanto dispongo di davvero pochissima memoria ram (situazione che mi ha spinto ad utilizzare i bit field per risparmiarne poche decine). spero tu possa aiutarni ugualmente..

risposta per segnarmi la discussione, spero di poterci dare un'occhiata presto

legacy:
UP. Come va ?

ciao e scusa il ritardo!! non ho avuto modo di vedere/provare ulteriormente il tuo codice, ma in compenso ho risolto il mio problema! (senza l'uso dei bit fields però)

come prima cosa, ecco la struttura contenente le impostazioni:

// impostazioni varie
typedef struct
{
byte channel;
byte octave;
byte sens;
byte patch;
byte waveform;
byte threshold;
byte tonality;
} Setting;

questa è la funzione:

// editSetting
// si occupa di gestire la modifica delle impostazioni
// title: titolo da mostrare nella prima riga
// descr: descrizione da mostrare nella seconda riga
// val: indirizzo della variabile da modificare
// list[]: array di stringhe contenente i valori da mostrare sullo schermo
void editSettingT(String title, String descr, byte *val, char *list[])
{
byte len = 0; //lunghezza dell'array di valori
while(strcmp(list[len],NULL))
len++;

byte old_val = *val;
byte act_val = *val;
byte pre_val = 255;
byte state = IDLE;
byte save = 255;

lcd.clear();
lcd.setCursor(0, 0);
title.toUpperCase();
lcd.print(title);

lcd.setCursor(0, 1);
lcd.print(descr);

while(save == 255)
{
if(pre_val != act_val)
{
lcd.setCursor(0, 2); lcd.print(" ");
lcd.setCursor(2, 2); lcd.print("<##");
lcd.setCursor(15, 2); lcd.print("##>");
lcd.setCursor(7, 2);
lcd.print(list[act_val]);
pre_val = act_val;
}
state = buttonCheck(A0);
switch(state)
{
case UP: if(act_val < len-1)
act_val++;
else
act_val = 0;
break;

case DOWN: if(act_val > 0)
act_val--;
else
act_val = len-1;
break;

case SEL: save = 1;
break;

case BACK: save = 0;
break;
}
}
if(state == SEL)
*val = act_val;
if(state == BACK)
*val = old_val;
}

old_val è una sorta di backup della variabile da editare (nel caso si esca senza salvare)
act_val è lo stato della variabile che viene mostrato sullo schermo (e che in caso di salvataggio, verrà assunto dalla mia variabile)
pre_val è lo stato precedente, che uso per limitare il refresh dello schermo

per rendere un po' più chiara la cosa, ecco un'esempio di chiamata:

case 17: editSettingT(getMenuTitle(current_menu), "forma d'onda", &default_set.waveform, lst_waveform); break;

spero di esserti stato utile :wink:

puoi ben dirlo! presto posterò qualche screen nella sezione progetti, ti dico solo che il file principale (che sto opportunamente suddividendo in classi e headers) è a quota 450 linee di codice, per non contare le altre librerie già implementate.. :wink:

diciamo che comprende una parte di sintesi musicale con classiche forme d'onda e magari un filtro analogico passivo (simil-sintesi sottrattiva), una parte di controller midi con banco midi e canale selezionabile, e una parte drum-machine, con la possibilità di attivare una sorta di touch sensibility e di scegliere tra diversi preset. Inoltre (e qui credo sia la parte più utile musicalmente parlando) visto che i pad sono solo 8, si suoneranno note all'interno di una scala prefissata (per le scale maggiori, minori e pentatoniche minori sto implementando gli algoritmi) con la possibilità di switchare la fondamentale e l'ottava. presenterà quindi una midi out, headphones/audio out, display 4x20 per i menù e la personalizzazione, 4 bottoni di navigazione, 8 pads sensibili alla dinamica (si spera) e per il segnale audio, un'eventuale stadio di amplificazione e filtering. il tutto, visto che si tratta della mia tesina, accuratamente documentato e spiegato :slight_smile:

più un octapad xD appena entro in possesso di una macchina fotografica posto qualche foto!

1