Arduino UNO R4 - FreeRTOS Time Slicing

Esaminata la libreria Wire di Arduino UNO R4 ho supposto che il problema potesse proprio risiedere nelle sole due funzioni che applicano un controllo di timeout.

La cosa fatta bene sarebbe stata riscrivere da capo la libreria Wire per la gestione del I2C :roll_eyes:, ma non ne avevo alcuna voglia per cui ho cercato una soluzione più veloce e facilmente applicabile anche ad altre librerie che dovessero presentare lo stesso problema.

Isolate le sole due funzioni con problemi di tempo, ho scritto io due piccole funzioni aggiuntive per sospendere temporaneamente il task switching nella parte con le tempistiche delicate, per poi riattivarlo.

Ho quindi aggiunto, in testa alla libreria Wire di Arduino UNO R4, file Wire.cpp, il seguente spezzone di codice:

/* -------------------------------------------------------------------- gpb01 */

static inline void disableScheduler (void) {
#if defined (INC_ARDUINO_FREERTOS_H) || defined (INC_FREERTOS_H)
  if ( xTaskGetSchedulerState( ) == taskSCHEDULER_RUNNING ) {
    vTaskSuspendAll();
    return;
  } else return;
#else
  return;
#endif
}

static inline void resumeScheduler (void) {
#if defined (INC_ARDUINO_FREERTOS_H) || defined (INC_FREERTOS_H)
  if ( xTaskGetSchedulerState( ) == taskSCHEDULER_SUSPENDED ) {
    if( !xTaskResumeAll() ) {
      taskYIELD ();
    }
    return;
  } else return;
#else
  return;
#endif
}

/* -------------------------------------------------------------------- gpb01 */

che, nel caso NON si usi FreeRTOS™, lascia il tutto inalterato (le due funzioni NON fanno nulla) mentre, se in testa al modulo Wire.cpp si aggiunge la riga:

#include "Arduino_FreeRTOS.h"

... indicando che si vuole usare la libreria in ambiente FreeRTOS™, abilita le dovute verifiche e le dovute azioni per fermare temporaneamente e ripristinare il task switching.

Le due funzioni sono state dichiarate static inline per velocizzarle al massimo (possiamo semplificare dicendo che le funzioni inline non vengono chiamate come le normali funzioni, ma inserite direttamente come codice).

Dopo di che ho modificato le due funzioni:

uint8_t TwoWire::write_to(uint8_t address, uint8_t* data, uint8_t length, unsigned int timeout_ms, bool sendStop)
e
uint8_t TwoWire::read_from(uint8_t address, uint8_t* data, uint8_t length, unsigned int timeout_ms, bool sendStop)

... inserendo le dovute chiamate alle mie funzioni.

Il risultato è la libreria Wire modificata che vi allego: R4_Wire.zip (8.5 KB) che contiene tutte le modifiche descritte.

Ho ricompilato lo stesso programma usato in precedenza e che dava problemi e ...
... sono ormai più di 24 ore che gira senza problemi e senza apprezzabili riduzioni della performance. :grin:

Probabilmente NON è la soluzione più elegante (... si sarebbe dovuto riscrivere tutta la libreria per immaginarla girare in un ambiente time sharing), ma è sicuramente una soluzione veloce, applicabile allo stesso modo anche ad altre librerie che dovessero presentare lo stesso problema se si mette, nel file FreeRTOSConfig.h il parametro:

#define configUSE_TIME_SLICING (1)

... attivando così il time slicing.

Se la provate e riscontrate problemi, o se invece avete una soluzione migliore, fatemelo sapere che vediamo di risolvere o metterla in piedi. :wink:

Guglielmo