Arduino UNO R3 vs 2560 R3

Non posso affermare di aver fatto una "vera" prova comparitiva fra questi due processori, ma indubbiamente i risultati mi lasciano un certo sconcerto.

Su varie schede Arduino, ho caricato un software che genera sul pin 13 una frequenza proporzionale alla velocità con cui esegue il loop. O meglio, moltiplicando per x2 i risultati ottenuti si ottiene il numero di loop/secondo che il processore riesce ad eseguire.

#define pinTest 13

void setup () {
  pinMode(pinTest,OUTPUT);   
}

void loop () {
  digitalWrite(pinTest,!digitalRead(pinTest));
}


/*
   UNO R3       Italy  58.684 Hz
   UNO R3       ??     58.681 Hz
   RobotDyn UNO        58.687 Hz
   Pro Mini            58.680 Hz

   Mega 2560 R3 Italy  36.906 Hz
   Mega 2560 R3 ??     36.825 Hz
*/

Risulta palese che nel confronto il modello MEGA 2560 esce penalizzato di un 37% c.a. Qualcuno sa darmi una giustificazione in merito?

All'uscita del 2560 mi sembrava aver letto in rete che la sua archittetura era diversa dal 328, ed oltre alla maggior memoria risultava più efficiente.

Il core del Atmega2560 è lo stesso del Atmega328, salvo la capacità del primo di indirizzare più flash e ram, pertanto a parità di codice macchina e clock il tempo di esecuzione è identico. Il tuo sketch di test ha un grosso difetto, usi la DigitalRead e la DigitalWrite, due istruzioni che sono dipendenti dal numero di port presenti sul micro, dato che il 2560 ha più port del 328 il codice macchina generato risulta leggermente più complesso di quello del 328 e questo incide negativamente sull'esecuzione dello stesso. Se rifai il test senza usare la Digital Read/Write, usa una variabile per cambiare lo stato del pin e scrivi direttamente sul port, vedrai che i tempi di esecuzione sono identici.

Giusto per spazzare via ogni dubbio ecco il tuo test fatto nel corretto modo, utilizzo delle macro sbi e cbi per commutare il pin 13, in questo modo si ottiene la massima velocità possibile.

Sketch per il test, decommentare/commentare le opportune righe se 328 o 2560.

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  // ATMEGA328 pin13 PORTB5
  sbi(PORTB, 5);
  cbi(PORTB, 5);
  
  // ATMEGA2560 pin 13 PORTB 7
  // sbi(PORTB, 7); 
  // cbi(PORTB, 7);
}

Ed ecco le misure stumentali, perfettamente identiche.

Atmega2560
mega2560.png

Atmega328
uno.png

Avevo sospettato che ci fosse stato di mezzo il compilatore, ma non avevo pensato ad utilizzare l'accesso diretto alle porte.

Ora all'evidenza del secondo post non ho alcun dubbio.

Chiedo solo due precisazioni - osservo gli screenshot allagati, mi confermi che ora la frequenza in uscita ora è 1.98662 MHz ? - l'istruzione "_SFR_BYTE" si riferisce al codice macchina ?

Grazie ad ogni modo dell'aiuto prestato.

lelebum: Avevo sospettato che ci fosse stato di mezzo il compilatore, ma non avevo pensato ad utilizzare l'accesso diretto alle porte.

Il compilatore è innocente. :) La "colpa" è di wiring, il framework che utilizza Arduino per semplificare la programmazione. Le istruzioni digitalWrite() e digitaRead() sono molto complesse, se vai a vedere il relativo sorgente nel core te ne rendi conto da solo, e sono legate al tipo di processore usate, per poter scrivere/leggere i singoli pin usano una serie di istruzioni, invece di una sola come ho fatto io, il numero di quante sono dipende dal tipo di MCU. Anche usare una sola istruzione in più, o in meno, ovviamente porta a sensibili differenze a livelli di tempo di esecuzione.

Chiedo solo due precisazioni - osservo gli screenshot allagati, mi confermi che ora la frequenza in uscita ora è 1.98662 MHz ?

Si, è la massima frequenza che puoi ottenere da un Atmega con clock a 16 MHz commutando un pin tramite accesso diretto al registro del port, sempre parlando di ambiente Arduino, se lo fai in C puro si arriva fino a poco più di 4 MHz, se ci fai caso l'impulso a 1, sbi() + cbi(), dura solo 125ns, ovvero due cicli macchina, il resto del tempo è quello necessario alla loop per far ripartire il ciclo.

  • l'istruzione "_SFR_BYTE" si riferisce al codice macchina ?

No, è un modo per dire al compilatore di accedere direttamente ad un registro hardware del processore, SFR = Special Function Register, trovi maggiori info qui nelle avr-libc.

Vedrò di approfondire meglio gli argomenti per migliorare le mie applicazioni.

Grazie per le indicazioni davvero precise.