Porting codice da ATmega328 ad ATtiny84

Salve a tutti! Sono stato per un bel po' assente da questo hobby causa lavoro. Ho ripreso adesso un progetto che non avevo completato qualche anno fa, un orologio con dei led WS2812B.
Il codice gestisce 24 led, un DS3231 e 3 pulsanti, funziona perfettamente su un Arduino UNO ma non appena lo carico su una scheda custom con MCU un ATiny84 con quarzo esterno a 16MHz sembra che si pianti tutto.
Ho fatto varie prove e funziona se carico il codice o solo per pilotare i led da DS3231 o solo per gestire i tre pulsanti (ho provato con varie librerie). A questo punto mi sembra palese che il problema sia la mancanza di risorse dell'Attiny84, ma mi piacerebbe studiare più a fondo la questione e capirci di più, quindi chiedo a voi qualche dritta su cosa andare a studiare.

Sotto vi lascio il codice con le parti salienti

#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__) 
    #include <Adafruit_NeoPixel.h>
    #define DEBUG_MODE // attivazione automatica modalità DEBUG tramite seriale
#elif defined(__AVR_ATtiny84__) // ATtiny84
   #include <tinyNeoPixel.h> //libreria per led WS2812B
#endif

#include <RTClib.h> //libreria per DS3231
#include <EasyButton.h>

#ifdef DEBUG_MODE
  #define DBEGIN(speed) Serial.begin (speed)
  #define DWHILE(...) while(!Serial){}
  #define DPRINT(...) Serial.print (__VA_ARGS__)
  #define DPRINTLN(...) Serial.println (__VA_ARGS__)
#else
  #define DBEGIN(...) do {} while (0)
  #define DWHILE(...) do {} while (0)
  #define DPRINT(...) do {} while (0)
  #define DPRINTLN(...) do {} while (0)
#endif

//MODALITÀ DEBUG
//###################################################


#define PIN 7 // pin di collegamento della linea data dei led WS2812
#define NUMPIXELS 24 //numero led

//############################################################
//definizione dei led in funzione della loro posizione
#define SEC 23 // led dei secondi

// definizione dei led che compongono le ore pentali
#define ORE_5_1 22
#define ORE_5_2 21
#define ORE_5_3 20
#define ORE_5_4 19

// definizione dei led che compongono le unità delle ore
#define ORE_1_1 15
#define ORE_1_2 16
#define ORE_1_3 17
#define ORE_1_4 18

// definizione dei led che compongono i minuti pentali
#define MIN_5_1  14
#define MIN_5_2  13
#define MIN_5_3  12
#define MIN_5_4  11
#define MIN_5_5  10
#define MIN_5_6  9
#define MIN_5_7  8
#define MIN_5_8  7
#define MIN_5_9  6
#define MIN_5_10 5
#define MIN_5_11 4

// definizione dei led che compongono le unità dei minuti
#define MIN_1_1 0
#define MIN_1_2 1
#define MIN_1_3 2
#define MIN_1_4 3
//###################################################################

#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__) // Arduino Leonardo o UNO
    #define PUL_SET_PIN 6
    #define PUL_PIU_PIN 4
    #define PUL_MENO_PIN 12
#elif defined(__AVR_ATtiny84__) // ATtiny84
   // definizione pin pulsanti
    #define PUL_SET_PIN 1
    #define PUL_PIU_PIN 2
    #define PUL_MENO_PIN 3
#endif

// definizioni stati di funzionamento
#define RUN 1 // modalità di visualizzazione orario
#define MODIFICA_ORE 10 //Modalità impostazione orario


#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__)// Arduino Leonardo
    Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#elif defined(__AVR_ATtiny84__) // ATtiny84
   tinyNeoPixel pixels = tinyNeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#endif


RTC_DS3231 rtc; // Istanza dell'oggetto RTC

//instanze della libreria EasyButton per la gestione dei pulsanti con disabilitazione pullup interno e stato attivo low
EasyButton pul_set(PUL_SET_PIN, 35, false, false);
EasyButton pul_piu(PUL_PIU_PIN, 35, false, false);
EasyButton pul_meno(PUL_MENO_PIN, 35, false, false);

//########################################
//char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
uint8_t stato = 0; //variabile per definire lo stato dell'orologio
uint8_t luminosita = 10; // impostazione della luminosità dei led 
uint8_t ore = 0, minuti = 0, secondi = 0, minuti_unita = 0, minuti_cin = 0, ore_unita = 0, ore_cin = 0;
int8_t ultimi_minuti = -1; // imposto ultimi_minuti a -1 in modo che anche all'avvio sarà sempre verificato l'if per la funzione visualizza (anche con orario con minuti 00)
uint8_t i = 0;
const uint32_t ritenuta = 3000;
bool set_cliked = false, set_long_clicked = false, piu_clicked = false, meno_clicked = false;


//########################################
const uint8_t riga_D [4]  = {ORE_5_1, ORE_5_2, ORE_5_3, ORE_5_4};
const uint8_t riga_C [4]  = {ORE_1_1, ORE_1_2, ORE_1_3, ORE_1_4};
const uint8_t riga_B [11] = {MIN_5_1, MIN_5_2, MIN_5_3, MIN_5_4, MIN_5_5, MIN_5_6, MIN_5_7, MIN_5_8, MIN_5_9, MIN_5_10, MIN_5_11};
const uint8_t riga_A [4]  = {MIN_1_1, MIN_1_2, MIN_1_3, MIN_1_4};
//########################################

void visualizza();     // funzione per visualizzazione dell'orario corrente
void imposta_orario(); // funzione per impostare l'orario tramite i pulsanti e salvarlo nell'RTC

void setup() {
  //#################################
  DBEGIN(9600);
  DWHILE();
  delay(300);
  DPRINTLN("MODALITA' DEBUG ATTIVA");
  //##############################

  pixels.begin();
  pixels.setBrightness(luminosita);

  if (! rtc.begin()) {
    DPRINTLN("RTC OFF");
    while (1) delay(10);
  }

  if (rtc.lostPower()) {
    DPRINTLN("RTC LOST POWER");
    //modalità impostazione orario
    stato = MODIFICA_ORE;
  }
  else{
    DPRINTLN("Stato: RUN");
    stato = RUN;
  }

  //Inizializzazione e configuzione pulsanti
  pul_set.begin();
  pul_piu.begin();
  pul_meno.begin();

  pul_set.onPressed(set_click);
  pul_set.onPressedFor(ritenuta, set_long_click);
  pul_piu.onPressed(piu_click);
  pul_meno.onPressed(meno_click);

}

void loop() {

  pul_set.read();
  pul_piu.read();
  pul_meno.read();

  switch (stato){
    case RUN:
      visualizza();
      if(set_long_clicked){
        set_long_clicked = false;
        DPRINTLN("Attivo modalità modifica orario");
        stato = MODIFICA_ORE;
      }
    break; //fine RUN

    case MODIFICA_ORE:
      imposta_orario();
    break; //fine MODIFICA_ORE
  }
}


void visualizza(){
  DateTime now = rtc.now();
  ore = now.hour(), DEC;
  minuti = now.minute(), DEC;
  secondi = now.second(), DEC;

  //lampeggio dei secondi
  if(secondi%2 == 0){
    pixels.setPixelColor(SEC, pixels.Color(255,255,255));
  }
  else{
    pixels.setPixelColor(SEC, pixels.Color(0,0,0));
  }
  //############################################

  if(minuti != ultimi_minuti){
    
    DPRINT(ore);
    DPRINT(":");
    DPRINT(minuti);
    DPRINT(":");
    DPRINTLN(secondi);
    DPRINTLN("\n");

    ore_cin = ore / 5;
    DPRINTLN(ore_cin);
    ore_unita = ore % 5;
    DPRINTLN(ore_unita);
    minuti_cin = minuti / 5;
    DPRINTLN(minuti_cin);
    minuti_unita = minuti % 5;
    DPRINTLN(minuti_unita);

    pixels.clear();

    //impostazione led per le unità dei minuti
    for( i = 0; i < minuti_unita; i++){
      pixels.setPixelColor(riga_A[i], pixels.Color(255,255,255));
    }

    //impostazione led per i minuti cin
    for( i = 0; i < minuti_cin; i++){
      pixels.setPixelColor(riga_B[i], pixels.Color(255,255,255));
    }

    //impostazione led per le unità delle ore
    for( i = 0; i < ore_unita; i++){
      pixels.setPixelColor(riga_C[i], pixels.Color(255,255,255));
    }

    //impostazione led per le ore cin
    for( i = 0; i < ore_cin; i++){
      pixels.setPixelColor(riga_D[i], pixels.Color(255,255,255));
    }
    ultimi_minuti = minuti;

  }
  pixels.show();
}

void imposta_orario(){
  DPRINTLN("Stato: MODIFICA_ORE");
  pixels.clear();
  pixels.setPixelColor(SEC, pixels.Color(255,255,255));
  pixels.show();
  
  DPRINTLN("Attesa");
  delay(10000); //funzione di appoggio per test funzionamento


  stato = RUN;        //alla fine della procedura di modifica imposto il nuovo stato
  ultimi_minuti = -1; //imposto la variabile al -1 in modo da forzare subito l'aggiornamento della visualizzazione (in caso di impostazione orario con minuti = ai minuti precedentemente visualizzati)
}


// funzione di callback per le varie modalità di pressione per i pulsanti
void set_click(){
  DPRINTLN("Cliccato SET");
  set_cliked = true;
}     
void set_long_click(){
  DPRINTLN("Lunga Pressione SET");
  set_long_clicked = true;
}
void piu_click(){
  DPRINTLN("Cliccato PIU");
  piu_clicked = true;
}
void meno_click(){
  DPRINTLN("Cliccato MENO");
  meno_clicked = true;
}

Da studiare c'è fondamentalmente il datasheet del ATtiny84: Atmel ATtiny 24:44:84.pdf (4.4 MB) ... e capire che sei su un oggetto che, contrariamente a ATmega328P ha solo 8KB di flash e SOLO 512 bytes di SRAM, per cui ... occorre stare molto attenti con l'uso della memoria :roll_eyes:

Guglielmo

certo, ho letto e confrontato i datasheet dei due micro.
Quello che mi piacerebbe capire o comunque avere un minimo di indicazioni su come riuscire ad interpretare l'utilizzo della memoria. Per quanto riguarda la flash credo che gli 8kB siano sufficienti perché riesco a caricare il codice e riesco a lasciare ancora qualcosa libero (informazione letta direttamente dalla console in fase di compilazione del codice). Credo che il problema si sposti sulla SRAM a questo punto, vorrei imparare a capire dal mio codice di quanta memoria viene usata in fase di esecuzione del codice

Molte cose confluiscono nella SRAM ... questo documento può chiarirti un po' le idee:
Memory Areas and Using malloc() on AVR.pdf (210.3 KB)
... anche se tu non usi malloc().

In più, considera ad esempio che, ogni WS2812 occupa, come minimo, 3 bytes di SRAM per conservare le informazioni di colore, più tutte le variabili che usa la libreria ... :roll_eyes:

Guglielmo

Grazie della dritta. Mi sembra un argomento molto più complicato di quello che pensavo.
Avevo immaginato che fosse più facile "calcolare" di quanta SRAM il codice avesse bisogno.

Per i WS2812 essendo molto noto il modo in cui lavora la libreria avevo già considerato la memoria necessaria, infatti parliamo di "soli" 24 led.
A questo punto proverò ad eliminare la libreria per gestire i pulsanti visto che è l'unica di cui sicuramente posso fare a meno o nel caso estremo porto tutto su un ATmega328p in modo da avere risorse a sufficienza.

Grazie mille per l'aiuto!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.