I2C più SPI, funzioni non bloccanti, Ardu-semafori, etc.
Ciao Forum
Scusate il titolo strano ma riflette la confusione che ho in testa. Sempre a proposito del mio progettino, vorrei poter usare la seriale (nella sua declinazione SoftwareSerial) e anche I2C. Sui vari forum dicono che possono coesistere ma mi trovo in difficoltà nella concezione di alcune parti del progetto.
In sostanza ho tre sezioni principali, distribuite di conseguenza nei moduli sorgenti. Una si occupa dell'IO con SoftwareSerial, l'altra di IO con il protocollo I2C e la terza è lo schedulatore dei task, fatto con macchine a stati finiti, o meglio una loro implementazione.
Il modo di interagire dei primi due sistemi verso il programmatore è diverso. SoftwareSerial ha una funzione che permette di sapere se ci sono bytes in attesa e una per leggerli. Invece I2C con Wire chiede all'utente una funzione di callback che viene chiamata quando ci sono caratteri in attesa. La mia implementazione è la più semplice possibile: quando viene chiamata legge quello che c'è e lo copia in un buffer.
E' chiaro che la logica del programma dipende in parte da cosa fa l'hardware. Se fossi su un normale SO potrei ragionare in termini di threads e usare le relative tecniche. Ma non riesco a capire come funziona Arduino, quello che trovo in rete è troppo tecnico per me. Da un lato ho funzioni che sono chiaramente bloccanti, le mie. Dall'altro una delle due non sono io a chiamarla ma Arduino. Sono confuso sulle modalità con cui questo possa avvenire, ovvero se, come credo, quando viene chiamata l'esecuzione del mio programma viene sospesa fino a che non si esce dalla funzione di callback oppure Arduino ha una specie di multitasking via hardware e se è così non so come regolarmi per scrivere la mia libreria. La domanda è: la funzione di callback è eseguita contemporaneamente al flusso del programma dello sketch? O fino a che è in esecuzione viene eseguita solo quella?
Cerco di descrivere il problema. Su un arduino che chiamo R(ipetitore) possono arrivare in qualsiasi momento dei comandi da un H(ardware) connesso via I2C. Dovrei poterli leggere e comunicarli ai task in esecuzione su R, che sono assegnati dall'utente tramite funzioni di callback. Una volta elaborato il comando arrivato da H il risultato dovrebbe essere mandato via SofwareSerial a un altro arduino F(oglia), aspettare e ricevere da questo una risposta che R re-invia ad H via I2C. In pratica il tutto dovrebbe agire come uno strato intermedio per gestire la comunicazione tra un hardware H che nelle mie intenzioni è raspberry e degli arduini F fisicamente molto lontani connessi con RS485.
Gerarchicamente abbiamo:
*** H, per fissare le idee un raspberry che può mandare comandi quando gli pare e al limite mettersi in ascolto se si aspetta una risposta da R o F.
*** R, lo strato centrale che riceve i comandi da H e ne fa una prima elaborazione poi li trasmette a
*** F, che può ulteriormente elaborarli e trasmettere il risultato a R che nel frattempo si è messo in ascolto dopo aver trasmesso. Ovvio che R interrogherà sempre un F alla volta.
La logica di base che vorrei seguire è che il canale R-F segua la logica della connessione RS485 ovvero che lo slave F possa trasmettere solo se interrogato, in modo che sul bus SoftwareSerial ci sia sempre il segnale di un solo dispositivo. Riguardo invece al flusso dati H-R, acquisire sempre e comunque tutto quello che arriva da H ma utilizzare solo i pacchetti arrivati quando 1) R-F non stanno comunicando e 2) I task di R non stanno elaborando i comandi. Di fatto, catturare il comando da H solo quando tutti gli altri sistemi sono in idle. In questo modo R non potrebbe venire bloccato se il flusso dati da H diventa continuo o arrivano packet sbagliati.
Quello che pensavo di fare per impostare il codice di R è un flusso di questo tipo nel "loop" di Arduino:
1) Se R e F stanno comunicando, o prima ancora R sta elaborando, nella funzione di callback usata da I2C viene settato un flag che fa saltare la sezione di lettura; in pratica i caratteri vengono tolti dal buffer in uscita di H in ogni caso, ma non memorizzati nel buffer dei comandi arrivati ad R. Vengono invece presi in considerazione solo quando R e i vari F non fanno niente.
2) Controllo se in R è in corso l'elaborazione del comando da trasmettere a F.
3) Se è pronto, viene trasmesso a F e R aspetta la risposta di F. Come sopra, se stanno arrivando caratteri da H vengono ignorati.
4) Quando è arrivata a R la risposta di F, se ci sono caratteri dal buffer I2C anche stavolta vengono tolti dal buffer senza essere utilizzati. Se invece non ce ne sono si procede direttamente a trasmettere da R a H.
Ora per esempio per il punto 4, se mentre sto per trasmettere da R ad H, H trasmette? Non mi è chiaro come I2C gestisce questo tipo di comunicazione.
Come sarà evidente sono un po' confuso io. Non so se come impostazione potrebbe andare o se come temo ci sono problemi e funzionamenti dell'harware che renderebbero irrealizzabile questa soluzione. A favore di sicurezza potrei anche predisporre tutto in modo che possa parlare solo un dispositivo alla volta, nonostante a quanto pare I2C e seriali possono lavorare contemporaneamente. Ma non devo fare il software di un missile che deve prevedere tutti gli scenari. Penso che il tutto potrebbe funzionare permettendo la comunicazione H-R solo se il resto del sistema non sta facendo niente. Ci sarebbero race conditions in questo modo? O altri problemi e scenari che ora non vedo?
Ho paura che un progetto apparentemente banale possa diventare un osso duro quando si toccano i problemi di sospensione dei task in tanti scenari diveri. Magari le cose sono più semplici di come mi sembrano e spero sia così.
Grazie a tutti :)
Scusate il titolo strano ma riflette la confusione che ho in testa. Sempre a proposito del mio progettino, vorrei poter usare la seriale (nella sua declinazione SoftwareSerial) e anche I2C. Sui vari forum dicono che possono coesistere ma mi trovo in difficoltà nella concezione di alcune parti del progetto.
In sostanza ho tre sezioni principali, distribuite di conseguenza nei moduli sorgenti. Una si occupa dell'IO con SoftwareSerial, l'altra di IO con il protocollo I2C e la terza è lo schedulatore dei task, fatto con macchine a stati finiti, o meglio una loro implementazione.
Il modo di interagire dei primi due sistemi verso il programmatore è diverso. SoftwareSerial ha una funzione che permette di sapere se ci sono bytes in attesa e una per leggerli. Invece I2C con Wire chiede all'utente una funzione di callback che viene chiamata quando ci sono caratteri in attesa. La mia implementazione è la più semplice possibile: quando viene chiamata legge quello che c'è e lo copia in un buffer.
E' chiaro che la logica del programma dipende in parte da cosa fa l'hardware. Se fossi su un normale SO potrei ragionare in termini di threads e usare le relative tecniche. Ma non riesco a capire come funziona Arduino, quello che trovo in rete è troppo tecnico per me. Da un lato ho funzioni che sono chiaramente bloccanti, le mie. Dall'altro una delle due non sono io a chiamarla ma Arduino. Sono confuso sulle modalità con cui questo possa avvenire, ovvero se, come credo, quando viene chiamata l'esecuzione del mio programma viene sospesa fino a che non si esce dalla funzione di callback oppure Arduino ha una specie di multitasking via hardware e se è così non so come regolarmi per scrivere la mia libreria. La domanda è: la funzione di callback è eseguita contemporaneamente al flusso del programma dello sketch? O fino a che è in esecuzione viene eseguita solo quella?
Cerco di descrivere il problema. Su un arduino che chiamo R(ipetitore) possono arrivare in qualsiasi momento dei comandi da un H(ardware) connesso via I2C. Dovrei poterli leggere e comunicarli ai task in esecuzione su R, che sono assegnati dall'utente tramite funzioni di callback. Una volta elaborato il comando arrivato da H il risultato dovrebbe essere mandato via SofwareSerial a un altro arduino F(oglia), aspettare e ricevere da questo una risposta che R re-invia ad H via I2C. In pratica il tutto dovrebbe agire come uno strato intermedio per gestire la comunicazione tra un hardware H che nelle mie intenzioni è raspberry e degli arduini F fisicamente molto lontani connessi con RS485.
Gerarchicamente abbiamo:
*** H, per fissare le idee un raspberry che può mandare comandi quando gli pare e al limite mettersi in ascolto se si aspetta una risposta da R o F.
*** R, lo strato centrale che riceve i comandi da H e ne fa una prima elaborazione poi li trasmette a
*** F, che può ulteriormente elaborarli e trasmettere il risultato a R che nel frattempo si è messo in ascolto dopo aver trasmesso. Ovvio che R interrogherà sempre un F alla volta.
La logica di base che vorrei seguire è che il canale R-F segua la logica della connessione RS485 ovvero che lo slave F possa trasmettere solo se interrogato, in modo che sul bus SoftwareSerial ci sia sempre il segnale di un solo dispositivo. Riguardo invece al flusso dati H-R, acquisire sempre e comunque tutto quello che arriva da H ma utilizzare solo i pacchetti arrivati quando 1) R-F non stanno comunicando e 2) I task di R non stanno elaborando i comandi. Di fatto, catturare il comando da H solo quando tutti gli altri sistemi sono in idle. In questo modo R non potrebbe venire bloccato se il flusso dati da H diventa continuo o arrivano packet sbagliati.
Quello che pensavo di fare per impostare il codice di R è un flusso di questo tipo nel "loop" di Arduino:
1) Se R e F stanno comunicando, o prima ancora R sta elaborando, nella funzione di callback usata da I2C viene settato un flag che fa saltare la sezione di lettura; in pratica i caratteri vengono tolti dal buffer in uscita di H in ogni caso, ma non memorizzati nel buffer dei comandi arrivati ad R. Vengono invece presi in considerazione solo quando R e i vari F non fanno niente.
2) Controllo se in R è in corso l'elaborazione del comando da trasmettere a F.
3) Se è pronto, viene trasmesso a F e R aspetta la risposta di F. Come sopra, se stanno arrivando caratteri da H vengono ignorati.
4) Quando è arrivata a R la risposta di F, se ci sono caratteri dal buffer I2C anche stavolta vengono tolti dal buffer senza essere utilizzati. Se invece non ce ne sono si procede direttamente a trasmettere da R a H.
Ora per esempio per il punto 4, se mentre sto per trasmettere da R ad H, H trasmette? Non mi è chiaro come I2C gestisce questo tipo di comunicazione.
Come sarà evidente sono un po' confuso io. Non so se come impostazione potrebbe andare o se come temo ci sono problemi e funzionamenti dell'harware che renderebbero irrealizzabile questa soluzione. A favore di sicurezza potrei anche predisporre tutto in modo che possa parlare solo un dispositivo alla volta, nonostante a quanto pare I2C e seriali possono lavorare contemporaneamente. Ma non devo fare il software di un missile che deve prevedere tutti gli scenari. Penso che il tutto potrebbe funzionare permettendo la comunicazione H-R solo se il resto del sistema non sta facendo niente. Ci sarebbero race conditions in questo modo? O altri problemi e scenari che ora non vedo?
Ho paura che un progetto apparentemente banale possa diventare un osso duro quando si toccano i problemi di sospensione dei task in tanti scenari diveri. Magari le cose sono più semplici di come mi sembrano e spero sia così.
Grazie a tutti :)