Go Down

Topic: Subroutine interrota e precauzioni per interrupt (Read 7620 times) previous topic - next topic

RobertoBochet

Quindi mi devo scordare lo new? o quest'ultimo posizionerà i dati sullo stack?

leo72

Il compilatore avr-gcc costruisce un heap e ci memorizza tutte le variabili dinamiche.
http://www.nongnu.org/avr-libc/user-manual/malloc.html
http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

yoshi93


Il compilatore avr-gcc costruisce un heap e ci memorizza tutte le variabili dinamiche.


Aggiungendo un po' di overhead al tuo programma quindi in genere,se puoi farne a meno,è meglio.

RobertoBochet

#18
Sep 21, 2014, 10:54 pm Last Edit: Sep 22, 2014, 12:49 pm by RobertoBochet Reason: 1
Quindi un allocazione dinamica dei dati del buffer non è consigliabile?

Edit:
Intanto che attendo risposta ho provato a cimentarmi nella creazione del buffer, mi sono discostato leggermente dalla composizione segnalata da wikipedia
Header
Code: [Select]
#ifndef Buffer_h
#define Buffer_h

class Buffer
{
protected:
uint8_t size;//Dimensione in byte del buffer
uint8_t* array;//Puntatore alla prima cella di memoria allocata
uint8_t* firstElement;//Puntatore al primo elemento inserito
uint8_t* firstSpace;//Puntatore al primo spazio libero

public:
Buffer(uint8_t size);
~Buffer();

bool IsEmpty();//Verifica che il buffer sia vuoto
bool IsFull();//Verfica che il buffer sia pieno

bool Push(uint8_t value);//Inserisce un byte nel buffer
uint8_t Pull();//Preleva e restituisce il primo byte inserito
uint8_t Pop();//preleva e restituisce l'ultimo byte inserito
};

#endif

C++
Code: [Select]
#include <avr/io.h>
#include <stdlib.h>
#include "Buffer.h"

Buffer::Buffer(uint8_t size)
{
this->size = size;//Imposto la varibile protetta che indica le dimensioni del buffer
this->array = this->firstElement = this->firstSpace = NULL;//Inizializzo i puntatori a NULL
if(this->size > 0)//Verifico che la dimensione sia maggiore di 0
{
this->array = (uint8_t*) calloc(this->size, sizeof(uint8_t));//Alloco ed inizializzo le celle necessarie ad archiviare i dati
if(this->array != NULL)//Verifico che l'allocazione sia riuscita
{
this->firstSpace = this->array;//Imposto il puntatore al primo spazio libero alla prima cella alllocata
}
}
}

Buffer::~Buffer()
{
free(this->array);//Libero la memoria allocata per contenere i dati del buffer
}

bool Buffer::IsEmpty()
{
return (this->firstElement == NULL);//Se il puntatore al primo elemento punta a NULL segno che il buffer è vuoto restituisco true
}

bool Buffer::IsFull()
{
return (this->firstElement == this->firstSpace);//Se i puntatori del primo elemento e primo spazio libero puntano alla stessa cella segno che il buffer è pieno restituisco true
}

bool Buffer::Push(uint8_t value)
{
if(!this->IsFull())//Controllo che il buffer non sia pieno
{
*this->firstSpace = value;//Imposto il valore nel primo spazio disponibile nel buffer
this->firstSpace++;//Sposto il puntatore una cella in avanti
if(this->array + this->size == this->firstSpace) this->firstSpace = this->array;//Se il puntatore sconfina in una zona al di fuori del buffer lo si reimposta alla prima cella allocata
return true;//Restituisco true se il valore è stato inserito correttamente
}
else return false;//Restituisco false se lo spazio nel buffer è finito
}

uint8_t Buffer::Pull()
{
uint8_t value;
if(!this->IsEmpty())//Crontollo che il buffer non sia vuoto
{
value = *this->firstElement;//Copio il valore nella variabile che verrà restituita
this->firstElement++;//Sposto il puntatore una cella in avanti
if(this->array + this->size == this->firstElement) this->firstElement = this->array;//Se il puntatore sconfina in una zona al di fuori del buffer lo si reimposta alla prima cella allocata
if(this->firstElement == this->firstSpace) this->firstElement = NULL;//Se i due puntatori finisco a puntare alla stessa cella come se il buffer fosse pieno reimposto il puntatore al primo elemento a null
return value;//Restituisco il valore
}
else return 0;//Restituisco 0 nel caso il buffer sia vuoto
}

uint8_t Buffer::Pop()
{
uint8_t value;
if(!this->IsEmpty())//Crontollo che il buffer non sia vuoto
{
if(this->firstSpace == this->array) this->firstSpace += this->size-1;//Se il puntatore al primo spazio libero punta alla prima cella allocata allora lo sposto all'ultima cella allocata
else this->firstSpace--;//In caso contrario sposto il puntatore al primo spazio libero indietro di una cella
if(this->firstElement == this->firstSpace) this->firstElement = NULL;//Se i due puntatori finisco a puntare alla stessa cella come se il buffer fosse pieno reimposto il puntatore al primo elemento a null
return *this->firstSpace;//Restituisco il valore
}
else return 0;//Restituisco 0 nel caso il buffer sia vuoto

}


Fatta al volo ma se non mi bocciate il calloc dovrebbe andare.

Ps Il compilatore mi ha dato particolarmente noie sull'utilizzo di nullptr

yoshi93

Personalmente sui pic avevo implementato il tutto nella vecchia maniera stile C (niente classi,niente malloc/free e solo una funzione da richiamare all'interno dell' ISR). Con le classi viene fuori una cosa bella pesantuccia però è molto più di 'classe' (capita la battuta?). Ormai che hai iniziato continua così, magari ti tornerà utile dopo per implementare una seriale software e per ridurre  la complessità riscrivendola in C c'è sempre tempo.
Il nullptr è una cosa piuttosto recente perché a differenza di NULL è proprio un tipo di dato quindi alcuni compilatori magari non lo riconoscono.

RobertoBochet

La calloc la vedo piuttosto necessaria, se no come dici tu torno al C, e cosi perderei la dinamicità dell'array.

yoshi93

Volendo puoi anche fare una classe che si limita a fornire un'interfaccia ma non crea niente al suo interno. Dichiari l'array del buffer all'esterno globale così lo usi all'interno della classe evitandoti la calloc().

RobertoBochet

Per interfaccia intendi una classe a cui si possa passare una matrice e questa fornisca i metodi per gestirla? scusa ma derivo dal C# dove il significato di interfaccia è tutta un altra cosa.
Si potrebbe essere una buona idea :) Mi dispiace però rinunciare all'heap sugli AVR,  non ho ancora capito bene come mai l'heap va a pesare cosi tanto rispetto allo stack, mi potresti illuminare yoshi, mi faresti un enorme favore.

Grazie

PS ho modificato il codice della coda per riadattarlo per bene alla funzione di buffer migliorando il codice, ora lo aggiorno

yoshi93

#23
Sep 22, 2014, 01:05 pm Last Edit: Sep 22, 2014, 01:19 pm by yoshi93 Reason: 1
L'allocazione dinamica della memoria sui microcontrollori è sconsigliata perché ti rende l'eseguibile più grosso e quindi in grossi progetti hai meno linee di codice disponibili ma soprattutto perché ti fa perdere il controllo della ram occupata se non gestita bene. Se hai un programma che alloca dinamicamente oggetti potrebbe succedere per una tua svista  l'heap cresca troppo andando a collidere contro lo stack. La differenza tra stack ed heap è che il primo ha un'implementazione hardware cioè ha un registro che contiene gli indirizzi di ritorno delle chiamate a subroutine mentre heap no.
Tutto ciò ovviamente è condizionato da come il compilatore gestisce le risorse, lo stack ad esempio può essere implementato via software andando quindi in ram e causando possibili collisioni con l'heap.
Tutto ciò ovviamente non ti vieta di usare malloc,new ecc. se vuoi e se il compilatore lo consente solo che in futuro potrebbe essere necessario passare a qualche trucco statico per evitare problemi o salvare un po' di spazio quindi chi ben comincia è già a metà dell'opera.

P.S. A differenza del C# il C++ non è completamente a classi quindi non devi necessariamente creare una classe per qualsiasi   cosa (infatti il main è una funzione e non un metodo).

P.P.S. Con interfaccia intendo l'insieme di metodi pubblici che ha una classe. Il solo file header definisce un'interfaccia.

leo72

Se parliamo di Atmel, lo stack è gestito tutto in RAM, come l'heap. Non ha una implementazione hardware.
Quando serve, il codice vi riversa il contenuto dei registri e da lì dentro li recupera. Sta all'utente rispettare la struttura LIFO dello stack per cui se metti nello stack A,B,C devi poi tirar fuori C,B,A, pena l'incasinamento di tutto. Il salvataggio e recupero del PC, Program Counter, cioè l'indirizzo della prossima istruzione che deve essere useguita dalla CPU, viene invece gestitoin automatico dal compilatore.

Quello che dice Yoshi è vero, cioè che con l'allocazione dinamica della memoria si rischia una bella collisione fra heap e stack: l'heap è posto dopo lo spazio riservato alle variabili statiche inizializzate ed a quelle non inizializzate e cresce verso l'alto mentre lo stack è posto a partire dall'ultima locazione della RAM e cresce verso il basso. Se non si sta attenti, si rischia che l'heap o lo stack o entrambi crescano più del previsto e si accavallino con risultati disastrosi per il codice.

RobertoBochet

#25
Sep 22, 2014, 02:57 pm Last Edit: Sep 22, 2014, 02:59 pm by RobertoBochet Reason: 1
Ok ok ho capito, quindi non ho vere e proprie limitazioni, semplicemente devo scendere a compromessi per una questione di spazio, ok questa è una buona notizia.
Ti ho chiesto dell'interfaccia, perché dal tuo post sembrava che intendessi per il temine interfaccia una classe completa che avesse l'unica differenza di non allocare variabili dinamiche al suo interno.
Non mi sento obbligato ad usare classi in C++ ma ho scelto di spostarmi ad esso, dal C puro, esclusivamente per avere il supporto di queste, come dicevi tu rende il codice più di classe  ;) anche se la classe da quello che sto capendo sui microcontrollori perde MOLTA importanza.

Edit: Ma il micro non rileva queste "collisioni"?

leo72

No, non le rileva. E' una gestione lasciata completamente all'utente.
Ecco perché si sconsiglia sempre di fare allocazione dinamica sulle MCU, sia per il sovraccarico sul codice che il compilatore e/o il programmatore deve inserire per gestire questa cosa sia per i ristretti quantitativi di RAM che possono portare a incidenti di questo tipo.

yoshi93


Se parliamo di Atmel, lo stack è gestito tutto in RAM, come l'heap. Non ha una implementazione hardware.

@leo72
Niente stack hardware per atmel? Grazie dell'informazione  :).

Sì, le classi se ci sono le usi per lo più come layout del codice e non per molto altro, ovviamente parlo sempre di MCU ad 8/16 bit.

leo72


@leo72
Niente stack hardware per atmel? Grazie dell'informazione  :).


Per lo meno per gli AVR8.
Tant'è che ad esempio l'Atmega2560, che è uno di quei pochi chip Atmel che può gestire anche RAM esterna, può spostare lo stack sulla memoria addizionale.

yoshi93


Tant'è che ad esempio l'Atmega2560, che è uno di quei pochi chip Atmel che può gestire anche RAM esterna, può spostare lo stack sulla memoria addizionale.


Questa funzionalità c'è sui pic18 in poi, in aggiunta allo stack hardware se si devono gestire solo le chiamate assembly a subroutines.

@RobertoBochet:
Scusa se ti ho un po' spammato il thread, giuro che smetto  :smiley-roll-sweat:  :).

Go Up