Ottimizzazione del codice

Buongiorno a tutti!
Come si può intendere dai miei ultimi thread il mio obiettivo era quello di riuscire a leggere/scrivere una memoria flash via SPI.
Bene, ci sono riuscito! Ora però mi trovo davanti ad un piccolo scoglio:le performance.
Il tempo di lettura vero e proprio è di circa 15min, mentre quello di trasferimento circa 4min, e stavo pensando ad una maniera per ridurli.
-Per quanto riguarda la lettura (ossia la comunicazione via SPI e l’archiviazione delle pagine di memoria dentro un buffer) ho già sostituito i metodi digitalRead() e digitalWrite() con la lettura/scrittura diretta da porte e uso variabili della dimensione minima per contenere i dati, oltre all’inlining sulle funzioni chiamate ciclicamente.

-Per quanto riguarda il trasferimento il codice è all’incirca così

uint8_t buffer[528];
for(uint16_t  i=0;i<32768;++i)
  {
            Serial.write(buffer,528);
  }

Noto che impostando il baudrate oltre 500000 non ho nessun incremento di velocità di trasferimento. E’ normale?

Avreste da darmi qualche consiglio generale su come diminuire un eventuale overhead e risparmiare cicli di clock per avere una maggiore efficienza?

Grazie in anticipo!

La seriale di Arduino funziona al massimo a 115Kb. Di più non va. Puoi spiegare meglio che trasferimento di dati devi effettuare?

Ho letto in parecchi post proprio qui sul forum che il limite di 115200 è imposto solo dal monitor dell'IDE, mentre usandone uno alternativo(tipo putty) posso alzare il baudrate. Infatti impostandolo a 500k ottengo prestazioni superiori rispetto a 115k. I dati che devo trasferire sono appunto il contenuto della SPI Flash: leggo una pagina, la memorizzo nel buffer e la spedisco via seriale. Sono 32K pagine da 528 byte l'una.

Se oltre 500Kb non ottieni incremento di velocità vuol dire che è arrivato il suo limite fisico.

cirowner:
-Per quanto riguarda il trasferimento il codice è all’incirca così

uint8_t buffer[528];

for(uint16_t  i=0;i<32768;++i)  { Serial.write(buffer,528);  }



Noto che impostando il baudrate oltre 500000 non ho nessun incremento di velocità di trasferimento. E' normale?

Di quel codice manca la cosa più importante. Nella setup() penso che usi Serial.begin(xxx); Che valore hai indicato?
Tieni conto che la funzione begin() fa dei calcoli, guarda nel core di Arduino la HardwareSerial.cpp, funzione begin.
Fa una serie di calcoli e poi setta i registri della USART:

// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
  *_ubrrh = baud_setting >> 8;
  *_ubrrl = baud_setting;

Nei calcoli potrebbero esserci dei valori massimi.Ad esempio:

baud_setting = (F_CPU / 4 / baud - 1) / 2;

che dipende dalla frequenza della CPU e dal baud (unsigned long) che tu gli passi. E’ da verificare.
Su Arduino Uno F_CPU dovrebbe essere 16000000L

Se guardi il datasheet del Atmel atmega328p , tabella Table 19-9 troverai quali valori sono possibili per il baude rate e il registro. Il titolo della tabelle è "Examples of UBRRn Settings for Commonly Used Oscillator Frequencies"

http://www.atmel.com/devices/atmega328p.aspx?tab=documents

Io chiamo il metodo Serial.begin(500000);

HardwareSerial.cpp

void HardwareSerial::begin(unsigned long baud)
{
  uint16_t baud_setting;
  bool use_u2x = true;

#if F_CPU == 16000000UL
  // hardcoded exception for compatibility with the bootloader shipped
  // with the Duemilanove and previous boards and the firmware on the 8U2
  // on the Uno and Mega 2560.
  if (baud == 57600) {
    use_u2x = false;
  }
#endif

try_again:
  
  if (use_u2x) {
    *_ucsra = 1 << _u2x;
    baud_setting = (F_CPU / 4 / baud - 1) / 2;
  } else {
    *_ucsra = 0;
    baud_setting = (F_CPU / 8 / baud - 1) / 2;
  }
  
  if ((baud_setting > 4095) && use_u2x)
  {
    use_u2x = false;
    goto try_again;
  }

  // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
  *_ubrrh = baud_setting >> 8;
  *_ubrrl = baud_setting;

  transmitting = false;

  sbi(*_ucsrb, _rxen);
  sbi(*_ucsrb, _txen);
  sbi(*_ucsrb, _rxcie);
  cbi(*_ucsrb, _udrie);
}

Sapresti dirmi il significato dell’u2x? Sembra che se settato ad 1 si riescano ad avere fino a 2Mbps

Quel che dico prendilo con le pinze. Interpreto il codice e datasheet. Qui l'esperto credo sia l'admin @Leo72 Direi che è un flag, il registro USART lavora anche in accoppiata al registro UCSRA. Un bit di quel registro permette di interpretare il valore di UBRR x1 o x2. Logicamente il valore di UBRR deve essere adeguato a quel bit. In base a quel bit (che segue la booleana use_u2x) allora il calcolo fa /4 oppure /8 . Perciò, per il registro UBRR, se si calcola con /4 ed il valore > 4095 allora lo si ricalcola /8 e si adegua il bit del UCSRA

Con 500000 e F_CPU=16000000UL val= (16000000UL / 4 / 500000 - 1) / 2 => (8-1) /2 => 3,5 => 3 credo, perciò 0.5M come da tabella

Questa è la mia tabella

Però nel codice non vedo limitazioni a baudrate più alti di un tot valore, mi chiedo perché anche ad alzarlo non ci sia differenza.

Devi dare un valore che faccia venire quel calcolo 1, quali valori sopra a 500.000 hai dato? Hai provato 1.000.000 ? o qualcosa di più? Prova anche a scrivere il valore come 1000000UL

Serial.begin(1000000UL);

C’è un “ma”. Tra il 328 ed il computer c’è un chip che funge da convertitore seriale che fa tante cosine via software, il limite è lui.
Difatti Astrobeed ha sempre detto che con la vecchia 2009, che aveva un FT232, si poteva lavorare senza problemi a velocità anche di 1Mbps perché quel chip regge tranquillamente queste velocità.

Non so ora né le specifiche del 16U2 né del suo firmware, ma immagino che il problema sia lì.

con u2x ad 1 per avere UBRRn ad 1(2Mb) serve un baudrate >800.000 e <1.333.334, mentre con u2x a 0 per avere UBRRn a 0(1Mb) bisogna avere un baudrate >666.666
Infatti non capisco dove sia il problema ad usare 1.000.000, a questo punto penso anch’io che sia un limite del 16u2.

E riguardo al codice per l’SPI ci sono linee guida per eliminare overhead magari non usando le funzioni built-in?

L'SPI va in HW, mi pare che il suo limite sia 4 MHz sull'Arduino. Non so se puoi migliorarla. Ma a me pare inutile se poi hai un collo di bottiglia a monte, ossia la linea tra Arduino e PC.

Del tempo di totale solo 1/5 è usato in trasferimento, il resto è tutto lettura della Flash. Per le librerie non uso la SPI ufficiale ma una dedicata, modulando direttamente Clock e Mosi, quindi non ha dei limiti imposti.