Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

C: buona prassi per lo scambio di dati tra i moduli

Raccolta di codici sorgenti

Moderatore: Foto UtentePaolino

1
voti

[1] C: buona prassi per lo scambio di dati tra i moduli

Messaggioda Foto Utenteboiler » 26 mag 2020, 16:37

Ciao a tutti

Di C per microcontroller ne ho scritto parecchio, ma non sono un programmatore vero.
Una cosa è sapere l'italiano, un'altra è scrivere come il Manzoni.

Con l'esperienza si migliora (si spera), ma nascono anche piú dubbi, dovuti al fatto che si diventa piú esigenti verso sè stessi.

Uno di questi dubbi è come gestire lo scambio di informazioni all'interno di un programma per microcontroller scritto in C senza orientazione agli oggetti.
Ci sono diverse possibilità e mi piacerebbe sentire il vostro parere su quale sia quella da preferire. Magari ne scaturisce una discussione interessante ;-)

Faccio un esempio di programma per illustrare la situazione.
Questo diagramma non rappresenta necessariamente un'architettura, è solo per capire cosa abbiamo.

Immaginiamo di avere un modulino che misura la temperatura e la mette a disposizione su una linea seriale.

Lo scheduler (usando per esempio un timer) richiede una volta al secondo una conversione all'ADC.
Questo la esegue e genera un interrupt. In seguito all'interrupt i dati estratti dall'ADC e messi in memoria come rawData. A questo punto il modulo di calcolo li prende e li converte in una temperatura, usando dei dati di calibrazione del sensore che sono presenti in memoria. Chiamiamoli gain e offset. Questi dati vengono configurati via UART, così come via UART avviene la lettura della temperatura.

L'esempio si può espandere a piacere (scelta dell'unità tra °C e K, modifica dell'intervallo di misura, impostazione di un moving average,...)

La necessità è la stessa: ci sono delle variabili in memoria che devono essere accessibili a piú moduli, per esempio UART e modulo di calcolo.

Come risolvere il problema?

1) Variabili globali
Questa opzione la metto per completezza, ma è un approccio antiquato che non andrebbe usato in un moderno programma ben strutturato. Funzionerebbe, perché lo scheduler assicura che non possa esserci una comunicazione che modifica i dati di calibrazione mentre questi vengono usati dal modulo di calcolo. Ma come detto, vogliamo qualcosa di ben fatto ;-)

2) Modulo DATA
Questa possibilità rispecchia in un certo senso il diagramma qui sopra. In un file si definiscono delle variabili statiche, ma dallo scope limitato a quel file. Si lavora poi con dei "getter" e "setter". È la soluzione che uso di solito, ma sinceramente non ne sono pienamente soddisfatto. Per diversi motivi:
- è vero che posso mettere dei controlli nei setter e che le variabili sono incapsulate in un file, ma la differenza mi sembra piú filosofica che altro: tutti i cani e porci che includono data.h hanno accesso in qualsiasi momento alle variabili
- la leggibilità del codice ne risente

3) Variabili dove vengono usate
È una soluzione simile alla seconda, ma invece di mettere tutto in un file dedicato, metto rawData in driverADC.c, gain e offset li metto nel modulo di calcolo e così via.
In questo modo limito un po' l'accesso, ma anche no. Per esempio UART.c avrà inclusi tutti gli header files di tutti i moduli in cui c'è una variabile configurabile. E quindi ottengo un'architettura a spaghetti, dove tutti comunicano con tutti. Anche questa non è una bella soluzione.

Voi come fate?

Saluti Boiler
Avatar utente
Foto Utenteboiler
16,5k 5 7 13
G.Master EY
G.Master EY
 
Messaggi: 2953
Iscritto il: 9 nov 2011, 12:27

6
voti

[2] Re: C: buona prassi per lo scambio di dati tra i moduli

Messaggioda Foto UtenteGuidoB » 27 mag 2020, 0:17

Tra i software embedded che mi sono capitati sottomano, quelli "modulari" sono i più ordinati e facili da modificare e far crescere, e con meno errori.

Alcuni concetti vengono dalla programmazione a oggetti, anche se solito questi software sono fatti in C. In soldoni:

- Si scompone il sistema in "moduli".

- Ogni modulo mette a disposizione degli altri un file .h con la sua API (Application Programming Interface).

- Ogni modulo può "usare" altri moduli per fare il suo lavoro, includendo (con #include) le loro API.

- Ogni modulo ha al suo interno le "sue" variabili, che sono private, cioè non condivise con altri moduli.

- Non ci sono variabili in comune tra moduli diversi.

Se l'architettura è ben fatta, esiste una "gerarchia" tra i moduli. I moduli di astrazione superiore "usano" i moduli di astrazione inferiore. A può usare B e C, B può usare C e D, ma C non può usare A o B. La gerarchia tra i moduli non deve avere percorsi di uso "circolari".

La API di un modulo è un file .h che contiene i prototipi di tutte (e sole) le funzioni che esso mette a disposizione degli altri moduli, oltre a tutti i #define, tipi, enum ecc. necessari per usare le sue funzioni. Se per usare le sue funzioni sono necessari i #define o tipi o enum definiti in altri moduli che esso usa, allora in questo .h dell'API saranno presenti anche gli #include delle API dei moduli necessari.

Gli #include delle API degli altri moduli che esso usa internamente, ma che non necessita rendere visibili esternamente, andranno preferibilmente nei suoi file .c, oppure in altri file .h suoi (non API) che non vengono inclusi da altri moduli.

Nelle API è vietato rendere visibili esternamente (con extern) variabili del modulo. Se necessario, vanno accedute con setters e getters, magari definiti nel .h dell'API come funzioni inline per una maggiore efficienza.

Ci possono essere variabili globali interne al modulo (definite in files .c) utilizzabili dalle sue funzioni interne, ma non visibili dall'esterno (un po' come gli attributi privati di una classe in C++, accessibili dai metodi della classe ma non da classi esterne).

Una volta chiari questi criteri (riassunti), si possono applicare a un sistema embedded.

Nel tuo caso per esempio io farei un modulo UART che si occupa solo di comunicazione attraverso la uart (la API conterrà uartInit(...), uartSend(...), ecc.). Si può discutere se fare una uartReceive(...) basata su polling periodico da parte di chi la usa, oppure una uartSubscribeReceiver(...) per passare l'indirizzo di una funzione di callback che il modulo UART chiamerà ogni volta che arriva un "pezzo" di informazione (una linea di testo completa per esempio, o un pacchetto binario completo).

Poi farei un modulo ROUTER che usa il modulo UART e si occupa di smistarne i pacchetti, cioè inviarli ai moduli a cui servono. Per capire a che moduli sono destinati, all'inizio di ogni pacchetto ci sarà una intestazione (un enum per esempio) che indica il tipo di pacchetto. I moduli interessati si saranno "abbonati" (subscribed) ai pacchetti che gli interessano, e solo a quelli. Quando arriva un pacchetto alla UART, la UART lo passerà al ROUTER che a sua volta lo passerà (passando tipicamente in una callback solo il puntatore al buffer del pacchetto) a tutti i moduli che si sono abbonati a quel pacchetto. Gli abbonati possono solo leggere il buffer del pacchetto senza modificarlo, salvandosi nelle loro variabili interne le eventuali informazioni che devono persistere, e ritornare al più presto dalla callback. Si può fare anche in altri modi, con messaggi al posto delle callback, per esempio.

Poi farei un modulo THERMOMETER con un'API tipo questa:
- thermometerInit(...)
- thermometerGetTemperature(...)
- thermometerGetAverageTemperature(...)
- thermometerCalibrate(...)
- thermometerPollDevices(...)

thermometerCalibrate(...) potrebbe essere una callback che riceve un pacchetto con i dati di calibrazione, poi memorizzati in variabili INTERNE al modulo THERMOMETER.
thermometerGetTemperature(...) e thermometerGetAverageTemperature(...) forniscono a richiesta la temperatura, in gradi Celsius o Fahrenheit a seconda dell'inizializzazione. In alternativa l'unità di misura si potrebbe specificare in un parametro delle funzioni thermometerGetTemperature(...) o thermometerGetAverageTemperature(...) (dipende da come si ritiene più comodo).
thermometerGetTemperature(...) e thermometerGetAverageTemperature(...) ritornano l'ultima temperatura o l'ultima media disponibili, che vengono calcolate a partire da variabili INTERNE al modulo THERMOMETER.
Il modulo THERMOMETER userà i moduli ADC, CALCULATION e TIMER. All'inizializzazione passerà al modulo TIMER (chiamando timerSetPeriodicCall(...)) un puntatore alla sua funzione thermometerPollDevices(...) con la richiesta di chiamarla una volta al secondo (per mantenere le sue variabili interne sempre aggiornate).

Anche le funzioni di risposta a interruzione dovrebbero essere interne ai moduli che le usano. Se una interruzione serve a più moduli, da una procedura di risposta a interruzione principale si chiameranno in sequenza le loro funzioni di risposta a quella interruzione (anche questo si può implementare con subscriptions e callbacks volendo).

Un modulo si può strutturare internamente come una "macchina a stati". Aiuta a mantenere l'ordine nella complessità, ma non è indispensabile.

Più il sistema diventa grande e più nasce l'esigenza di strutturarlo bene per garantire una buona manutenibilità.

Per altre informazioni più o meno correlate prova a guardare anche questo intervento e quelli richiamati.
Big fan of ƎlectroYou!
Avatar utente
Foto UtenteGuidoB
15,6k 7 12 13
G.Master EY
G.Master EY
 
Messaggi: 2364
Iscritto il: 3 mar 2011, 16:48
Località: Madrid

1
voti

[3] Re: C: buona prassi per lo scambio di dati tra i moduli

Messaggioda Foto Utenteboiler » 4 giu 2020, 18:06

Grazie mille per la risposta, Foto UtenteGuidoB.
Apprezzo il fatto che ti sia dato da fare per scrivere un post così esteso.
Ci ho messo un po' a risponderti perché volevo prendermi sufficiente tempo.

I concetti di OOP mi sono noti perché programmo anche in C# (e in C++ quando ero ancora giovane, ma non ho piú il fisico :mrgreen: )
Piú o meno li uso anche nel modo in cui descrivi quando programmo C per embedded, con qualche limitazione.

Moduli a cui accedono molti altri moduli mettono a disposizione nel loro header file troppa roba:

Qui la banana ha accesso al frullatore, ma anche all'affettatrice (che non le serve). E viceversa per il salame.

Non ci avevo mai pensato, ma leggendo il tuo intervento mi è venuta l'idea che si potrebbero suddividere i prototipi:


Questo è un approccio che permetterebbe di fare un po' di pulizia. È simile al concetto di interface nell'OOP.

Veniamo però all'hot-topic :ok:

Per capire a che moduli sono destinati, all'inizio di ogni pacchetto ci sarà una intestazione (un enum per esempio) che indica il tipo di pacchetto.

Questo (se interpreto correttamente) è un approccio che eviterei, perché sposta parte dell'architettura nel protocollo di comunicazione. Per restare in tema OOP, viola il principio dell'information hiding.

O intendevi qualcosa come il numero di registro in Modbus? Questo è quello che faccio già ora.

Ho fatto uno schizzo dell'architettura che suggerisci. Correggimi se ti ho frainteso:


La mia domanda principale era "come fare se una variabile va usata da due moduli distinti".
Non ho un esempio concreto, ma non è difficile generarne uno: in un sistema con piú interfacce voglio poter fornire la temperatura sull'uart, sulla ethernet, scriverla periodicamente in una scheda SD e mostrarla su un display. Inoltre c'è un modulo di allarme che la richiede anche lui una volta al secondo.
È facile immaginarsi che il tutto non si limita alla temperatura, ma vale anche per i valori di calibrazione e tutto il resto. L'architettura di cui sopra diventa un piatto di spaghetti.

Si potrebbe risolvere mettendo uno smistatore unico per tutte le interfacce e tutti i moduli, ma la sua logica diventerebbe piuttosto complessa (per finire l'architettura è quella che è, ne posso nascondere una parte in una black-box, ma è come nascondere la polvere sotto al tappeto :-P ).

L'altra proposta che facevo nel mio messaggio iniziale e che mi sembra piú semplice è la seguente:

Le variabili sono in data.c, mentre una serie di data_*.h mettono a disposizione dei moduli i prototipi dei getter/setter ai quali hanno accesso.

Ovviamente questa struttura la userei solo per le variabili che devono essere condivise tra diversi moduli. Quelle interne sono nel modulo stesso.

Ci vedi dei problemi?

Saluti, Boiler
Avatar utente
Foto Utenteboiler
16,5k 5 7 13
G.Master EY
G.Master EY
 
Messaggi: 2953
Iscritto il: 9 nov 2011, 12:27

1
voti

[4] Re: C: buona prassi per lo scambio di dati tra i moduli

Messaggioda Foto UtenteGuidoB » 8 giu 2020, 1:18

Ciao Foto Utenteboiler,
boiler ha scritto:Grazie mille per la risposta, Foto UtenteGuidoB.
Apprezzo il fatto che ti sia dato da fare per scrivere un post così esteso.

È un piacere e anche un'occasione per riflettere su concetti che a volte per la fretta trascuriamo :ok: .

boiler ha scritto:Moduli a cui accedono molti altri moduli mettono a disposizione nel loro header file troppa roba:

Qui la banana ha accesso al frullatore, ma anche all'affettatrice (che non le serve). E viceversa per il salame.

In questo esempio piuttosto astratto e molto OOP vedo una classe Cucina un po' troppo eterogenea. Vedrei meglio una classe base ElettrodomesticoDiCucina da cui derivano (ereditano) le classi Affettatrice, Frullatore, Forno ecc.
Poi vedrei una classe Ingrediente da cui derivano Salame e Banana.

A questo punto Banana non dovrebbe usare un generico ElettrodomesticoDiCucina, ma solo un Frullatore per farsi frullare ( Banana::frulla()). E così abbiamo fatto un po' d'ordine.

Bisognerebbe calarsi in un caso concreto per capire se sarebbe ancora più ordinato, invece di dare alla Banana la capacità di frullarsi e al Salame la capacità di affettarsi, inventare una nuova classe Cuoco che usa sia ElettrodomesticoDiCucina (con tutte le sue classi derivate) che Ingrediente (con tutte le sue classi derivate).

A questo punto il Cuoco potrebbe usare il Frullatore (che ha il metodo frulla per frullare un generico Ingrediente) per frullare una Banana. Nel codice del metodo Cuoco::preparaFrappè() potrebbe esserci l'istruzione:
frullatore.frulla(banana); :D , molto espressiva.

boiler ha scritto:Non ci avevo mai pensato, ma leggendo il tuo intervento mi è venuta l'idea che si potrebbero suddividere i prototipi:


Questo è un approccio che permetterebbe di fare un po' di pulizia. È simile al concetto di interface nell'OOP.

Oppure si potrebbero fare (stavolta ragioniamo in C) tanti moduli separati (Affettatrice, Frullatore, Forno ecc.), ciascuno con le sue funzioni specifiche (affettatriceAffetta(), frullatoreFrulla(), fornoCuoci() ecc.) e che per le parti comuni si appoggino al modulo ElettrodomesticoDiCucina, che avrà per esempio le funzioni ElettrodomesticoDiCucinaAccendi() e ElettrodomesticoDiCucinaSpegni().

Per far questo i moduli Affettatrice, Frullatore ecc. includeranno ElettrodomesticoDiCucina.h nei loro .c e useranno le sue funzioni ElettrodomesticoDiCucinaAccendi(), ElettrodomesticoDiCucinaSpegni() ecc. per implementare le proprie affettatriceAccendi(), frullatoreSpegni() ecc.

Anche qui, per capire bene come è meglio fare, dovrei calarmi in un esempio concreto.

boiler ha scritto:Veniamo però all'hot-topic :ok:

Per capire a che moduli sono destinati, all'inizio di ogni pacchetto ci sarà una intestazione (un enum per esempio) che indica il tipo di pacchetto.

Questo (se interpreto correttamente) è un approccio che eviterei, perché sposta parte dell'architettura nel protocollo di comunicazione. Per restare in tema OOP, viola il principio dell'information hiding.

O intendevi qualcosa come il numero di registro in Modbus? Questo è quello che faccio già ora.

Non conosco Modbus, comunque secondo me si può mantenere l'information hiding in questo modo:
una classe Pacchetto contiene l'attributo tipo, al quale si può accedere con pacchetto.getTipo().
Da Pacchetto derivano le classi PacchettoTemperatura, PacchettoCalibrazioneTermometro ecc, ciascuna con gli attributi addizionali che necessita e le corrispondenti funzioni getters e setters.
Allo smistatore di pacchetti interessa solo accedere al tipo, attributo comune a tutti i pacchetti, e in base al valore del tipo lo invierà ai moduli interessati.

Calato in C, un pacchetto potrebbe essere un modulo che esporta in un .h una typedef struct contenente il tipo (un campo di tipo typedef enum { ... } pacchettoTipo) e un puntatore a void per il corpo del pacchetto. Per ciascun tipo sarà definita in un .h una typedef struct contenente tutti i campi necessari in quel caso.
Definirà anche una funzione inline pacchettoTipo pacchettoGetTipo(const Pacchetto *) e l'information hiding è salva.

Volendo, se non ci sono pacchetti molto più lunghi degli altri, si può evitare il puntatore a void, e definire tutti i pacchetti in un solo .h, in un solo typedef struct che contiene il tipo, seguito da una union di tutti i tipi di corpi dei pacchetti.

Come potrebbe funzionare lo smistatore? Quando riceve un pacchetto, legge il tipo, e con quello seleziona la riga di una sua tabella che contiene la lista dei puntatori alle callback function di tutti i moduli che vogliono ricevere quel tipo di pacchetto. Chiamerà in sequenza tutte le callback function della lista, passando come argomento (const Pacchetto * pacchetto).

La tabella si può costruire dinamicamente all'inizializzazione o anche durante il funzionamento (ogni modulo chiama una funzione dello smistatore passandogli i tipi di pacchetti che vuole ricevere e le corrispondenti callback function), oppure staticamente (in questo caso non ho molto chiaro come si potrebbe fare per mantenere un buon isolamento tra i moduli, cioè che lo smistatore non debba conoscerli tutti nel suo codice).

boiler ha scritto:Ho fatto uno schizzo dell'architettura che suggerisci. Correggimi se ti ho frainteso:


La mia domanda principale era "come fare se una variabile va usata da due moduli distinti".
Non ho un esempio concreto, ma non è difficile generarne uno: in un sistema con piú interfacce voglio poter fornire la temperatura sull'uart, sulla ethernet, scriverla periodicamente in una scheda SD e mostrarla su un display. Inoltre c'è un modulo di allarme che la richiede anche lui una volta al secondo.
È facile immaginarsi che il tutto non si limita alla temperatura, ma vale anche per i valori di calibrazione e tutto il resto. L'architettura di cui sopra diventa un piatto di spaghetti.

Si potrebbe risolvere mettendo uno smistatore unico per tutte le interfacce e tutti i moduli, ma la sua logica diventerebbe piuttosto complessa (per finire l'architettura è quella che è, ne posso nascondere una parte in una black-box, ma è come nascondere la polvere sotto al tappeto :-P ).

L'altra proposta che facevo nel mio messaggio iniziale e che mi sembra piú semplice è la seguente:

Le variabili sono in data.c, mentre una serie di data_*.h mettono a disposizione dei moduli i prototipi dei getter/setter ai quali hanno accesso.

Ovviamente questa struttura la userei solo per le variabili che devono essere condivise tra diversi moduli. Quelle interne sono nel modulo stesso.

Ci vedi dei problemi?

Io ho avuto esperienze negative col polling, per cui se posso lo evito. Inoltre ogni modulo è "sdoppiato", avendo una parte (il valore del dato che fornisce) memorizzato fuori, in un modulo Data che lo unisce a tutti gli altri... non è molto bello.

Invece un modulo DistributoreDati che non memorizzi dati ma funzioni come "colla" che tiene insieme tutto il sistema secondo me va meglio.
Come Data, permette ai diversi moduli (Termometro, Uart, Ethernet, Sd, Display, Allarme) di non doversi conoscere (interfacciare) tra loro. Ognuno dovrà conoscere solo il modulo DistributoreDati.

Una grossa semplificazione (non indispensabile) per il DistributoreDati (ma ancor più per Data) sarebbe rappresentare i dati che devono essere distribuiti con un unico tipo, per esempio un intero long (int32_t).
Nel caso fosse necessario rappresentare le temperature al decimo di grado, per contenerle in un long userei il decimo di grado come unità di misura, e una temperatura di -23,4 gradi sarebbe rappresentata da -234.
Il DistributoreDati non dovrà conoscere il significato di ciascun dato, ma solo a chi deve comunicarlo quando è necessario.

Modulo Termometro: si occuperà di leggere il sensore (via ADC o SPI o quel che sia, usando i moduli driver opportuni, magari con un suo polling interno se non è possibile evitarlo), e di fornire la temperatura al modulo DistributoreDati, all'inizio del funzionamento e ogni volta che questa cambia. Per comunicare il dato, chiamerà la funzione
DistributoreDatiNuovoValore(TipoDato tipoDato, uint32_t valore);
TipoDato sarà un enum con tanti valori quanti sono i tipi di dati da distribuire.

Comunicare un dato solo quando cambia permette di evitare il polling, e permette anche di essere tempestivi nella comunicazione (non deve scadere il periodo di polling prima che gli altri moduli conoscano il nuovo valore). Poi in casi particolari si può fare un ibrido (comunicare un dato ogni volta che cambia ma non più spesso di una volta al secondo...).

Non è necessario che il modulo DistributoreDati mantenga una copia dei valori dei dati (potrebbe anche farlo se risultasse utile, ma cercherei di evitarlo).
Infatti l'unica cosa che deve fare, ogni volta che riceve un nuovo dato, è comunicarlo a chi lo deve ricevere.

Per farlo, potrebbe utilizzare il tipoDato ricevuto per accedere alla riga corrispondente di una tabella, da cui accede alla lista delle funzioni callback dei vari moduli da chiamare per comunicare il nuovo valore.

Invece di usare una tabella di callback si può fare hardcoded, utilizzando uno switch:

Codice: Seleziona tutto
switch(tipoDato) {
  case tipoDatoTemperatura:
    uartNuovoValore(tipoDato, valore);     /* Comunica nuovo valore al modulo Uart */
    ethernetNuovoValore(tipoDato, valore); /* Comunica nuovo valore al mod. Ethernet */
    sdNuovoValore(tipoDato, valore);       /* Comunica nuovo valore al modulo Sd */
    displayNuovoValore(tipoDato, valore);  /* Comunica nuovo valore al modulo Display */
...


Oppure ancora, per ogni modulo si può tenere una mappa di bit (uno per elemento dell'enum TipoDati) in cui porre a 1 (true) quelli corrispondenti ai dati delle cui variazioni deve essere aggiornato. Poi in un ciclo scorrere tutti i moduli e chiamare la funzione <nomeModulo>NuovoValore(...) solo se il bit è a 1.

Il modulo Sd, che deve scrivere periodicamente il valore della temperatura, manterrà localmente una variabile temperaturaAttuale che verrà aggiornata ogni volta che il modulo DistributoreDati chiamerà sdNuovoValore(tipoDatoTemperatura, valore). Ogni volta che dovrà scrivere su SD la temperatura, il modulo Sd potrà leggerla localmente dalla sua variabile temperaturaAttuale.
Il modulo Sd potrà eseguire localmente anche i filtraggi e le conversioni di unità di misura che gli dovessero servire.
In caso di cambio del periodo di scrittura, del metodo di filtraggio, dell'unità di misura l'unico modulo impattato sarà Sd.

Questo metodo di scatenare chiamate di funzioni da un modulo all'altro (esempio: cambia la temperatura, il modulo Termometro chiama distributoreDatiNuovoValore(...) che a sua volta chiama uartNuovoValore(...) che a sua volta chiama il driver della Uart per trasmetterlo, e così via per ethernetNuovoValore(...), sdNuovoValore(...), displayNuovoValore(..), per tornare finalmente al Termometro chiamante) è semplice, tempestivo, ma funziona solo se il codice è monolitico.
Inoltre bisogna fare attenzione a che una di queste chiamate non provochi a sua volta un cambio in un valore, che scatenerebbe una nuova catena di chiamate quando la precedente non è ancora terminata.

Se c'è pericolo che succeda, o se ci sono vari processi, queste chiamate dovranno essere trasformate in "eventi" da passare tra processi utilizzando una coda o i servizi del sistema operativo sottostante, per essere elaborati in sequenza, uno alla volta.

Se ci sono chiamate a funzioni bloccanti, per esempio se la Uart non ritorna il controllo prima di aver trasmesso tutti i caratteri, l'esecuzione potrebbe essere rallentata, e quindi sarebbe il caso di rendere le chiamate non bloccanti, o almeno memorizzare il dato da trasmettere e solo in seguito trasmetterlo.

Vantaggi del DistributoreDati rispetto a un modulo Data che contiene i dati a cui si accede con polling:
1) si evita il polling, che carica il processore per far qualcosa solo poche volte;
2) la distribuzione dei nuovi valori è tempestiva (non bisogna attendere il ciclo di polling);
3) il DistributoreDati non deve conoscere i dati che distribuisce e non deve immagazzinarli per conto di altri (maggiore indipendenza tra moduli).

Se dovessi partire da zero farei qualcosa del genere. Poi bisogna sempre fare i conti con quel che c'è già.

Saluti,

GuidoB
Big fan of ƎlectroYou!
Avatar utente
Foto UtenteGuidoB
15,6k 7 12 13
G.Master EY
G.Master EY
 
Messaggi: 2364
Iscritto il: 3 mar 2011, 16:48
Località: Madrid


Torna a Firmware e programmazione

Chi c’è in linea

Visitano il forum: Nessuno e 3 ospiti