Esercitazioni
PIC - Assembly
|
Conclusioni
Una domanda comune è: C o Assembler?
Uno dei principali vantaggi di utilizzare un linguaggio di alto livello (come
C, Pascal o BASIC) è che il programmatore non deve comprendere l'architettura dell'
hardware usato, dato che il compilatore provvede a fornire la giusta
interfaccia alle funzioni del linguaggio attraverso librerie. Così, ad esempio, nell' uso di
memoria RAM in un microcontroller, questa può essere general purpose o
condivisa, in banchi di varia dimensione e gestibili in modo diverso a seconda
dell' hardware; chi scrive un programma di alto livello sa poco o nulla di questa gestione della
memoria: questo compito è stato delegato a chi ha scritto il compilatore.
Altrettanto nell' uso di periferiche come UART o I2C.
Assembly è una programmazione a basso livello; questo vuol dire che il linguaggio non mette a disposizione altro che il set di istruzioni del
microcontroller. Un microcontroller può svolgere una serie di semplici
operazioni, generalmente tra 30 e 100
, ognuna delle quali agisce su un numero di bit pari alla ampiezza
del bus dati. La concatenazione di queste istruzioni elementari in forme
logiche adeguate (algoritmi) determina azioni complesse.
Ad esempio, se ho a disposizione un processore con dati a
8 bit, per effettuare una operazione di somma su numeri di 16 o 32 bit dovrò
ripetere più volte le istruzioni elementari su 8 bit, costruendo di un algoritmo. E ogni riga
assembler riguarda una e
una sola istruzione.
Con un linguaggio di alto livello, come C o BASIC, ogni riga scritta può
essere tradotta dal compilatore in molte o moltissime istruzioni; non c'è
un rapporto diretto tra i due. La scrittura di codice in C o Basic è molto più
semplice volumetricamente. In particolare, affrontando calcoli (ad es. in
virgola mobile) o operazioni logiche complesse, il linguaggio ad alto livello
probabilmente dispone già al suo interno delle funzioni adeguate, cosa che
non esiste nell' Assembly.
Per contro, posso comunque utilizzare librerie di funzioni esterne create per
l' Assembly o posso crearmene di personali a seconda delle necessità.
In teoria, poi, un programma C può essere ricompilato per funzionare su diversi
microcontrollori, con un numero limitato di modifiche, mentre un sorgente Assembly, utilizzando le istruzioni
proprie di un processore specifico, non può essere portato su uno diverso se non
riscrivendolo completamente. In pratica, però, le differenze di hardware, anche tra elementi
della stessa famiglia, possono rendere la cosa meno semplice del previsto
anche per i linguaggi ad alto livello. Oltre al fatto che non esiste "un
C", ma una pletora di variazioni sullo standard, a volte tali da
richiedere la riscrittura di ampie parti del sorgente.
Inoltre è
tipico dei microcontroller la necessità di modificare le impostazioni hardware per accedere ad
IO, impostare timer o utilizzare interfacce di comunicazione seriali; questo significa manipolare i registri di controllo hardware che sono specifici
di quel tale chip e di cui occorre conoscere le caratteristiche.
La programmazione con C o BASIC o altri linguaggi evoluti ha l' essenziale
pregio di derivare da una filosofia propria generalizzabile, ma
contemporaneamente questo è un grave difetto, dato che fa il possibile per
nascondere al programmatore la struttura dell' hardware su cui lavora.
Se questa situazione è poco rilevante per chi scrive programmi gestionali o
su personal computer, dove sono disponibili potenti funzioni, librerie e dll, nel caso di una situazione strettamente legata al chip
ed alle sue periferiche integrate, come nel caso dei microcontroller, la mancata conoscenza delle caratteristiche specifiche di queste
periferiche può essere uno scoglio ben difficilmente superabile.
È molto più facile capire l' hardware e come si interfaccia con il resto del
microcontroller se si inizia con l'imparare l' Assembly, perché questo costringe a
conoscere di più sul
dispositivo usato, su come funziona e su come trarne il meglio. Dopo aver scritto
qualcosa in Assembly, sarà possibile affrontare un linguaggio ad alto livello
in modo molto più cosciente ed efficace.
Questo elemento è particolarmente importante, dato che, in qualsiasi caso, la conoscenza delle
caratteristiche
specifiche di un certo microcontroller è determinante per le possibilità di
utilizzarlo correttamente; conoscere alla perfezione un linguaggio evoluto, ma non sapere
come poterlo applicare sullo specifico componente, è, evidentemente, inutile.
Un chip potrà avere certe risorse di memoria e moduli integrati differenti da un' altro: c'è bisogno di modificare le impostazioni hardware per accedere agli I / O, impostare i timer o utilizzare interfacce di comunicazione, come ad esempio le porte seriali, e questo significa
la necessità di manipolare i registri di controllo hardware, specifici
di quel microcontroller, in ogni caso a livello di bit.
È molto più facile capire il meccanismo di questi registri e come si interfacciano con il resto del
microcontroller se si impegna un certo tempo a considerare il linguaggio Assembly,
che mette in diretto contatto col dispositivo e con il suo funzionamento, con le diverse risorse disponibili e le
relative limitazioni.
Da sfatare, poi, come abbiamo accennato, la leggenda della facile portatilità del
programma tra microcontroller di famiglie molto diverse tra di loro. E, ancor
più, tra i vari "C" disponibili anche per le stesse famiglie di
microcontroller, dove le caratteristiche della
versione del linguaggio possono essere tali da necessitare la riscrittura di
molte righe.
Certamente C, con la sua offerta di operazioni matematiche, tipi, funzioni,
cicli for, while, if .. else, è più semplice nell' uso di questi elementi.
In Assembly, tipicamente ogni riga del codice ha un suo valore unitario e solo
in dipendenza delle possibilità dell' Assembler, più linee costituiscono un
determinato blocco; le " funzioni" devono essere create a seconda delle
esigenze o ottenute da librerie disponibili. Un algoritmo
complesso può richiedere la concatenazione di molte linee, anche centinaia.
Per contro, un vantaggio dell'Assembly è che il codice ottenuto può essere il minimo
possibile e l' impiego di risorse (memoria) minimizzato. Meno istruzioni,
esecuzione più veloce, cosa che sui piccoli microcontroller, con poche
risorse disponibili, può diventare l' unica via percorribile per realizzare
con essi una applicazione pratica.
In ultimo possiamo dire che comunemente si ritiene che Assembly sia più "complesso" e
difficile da imparare del C o del BASIC. Questa impressione è del tutto
errata.
I linguaggi evoluti richiedono un tempo di apprendimento che può essere anche
molto elevato, in quanto, sono "complessi", proprio per la particolarità di
mettere a disposizione molte funzioni e possibilità logiche. Questo
corrisponde ad una quantità sensibile di regole per applicare correttamente
tutta questa complessità.
Per contro Assembly, in sostanza, non richiede che la conoscenza di pochissime regole
formali e del set di istruzioni usato, cose che sono
abbastanza semplici e si apprendono in brevissimo tempo.
Anche la "leggibilità" maggiore del C rispetto all' Assembly o
viceversa dipende solamente dalle capacità del programmatore, dato che la leggibilità
intrinseca di un programma BASIC o C, disordinato o privo del supporto di
adeguati commenti, è molto minore di quella di un sorgente Assembly scritto
con un minimo di buon senso.
Ricordiamo che esiste una gara internazionale che premia il programma in C
meno comprensibile !
Certamente un programma Assembly, richiedendo molte più righe di sorgente, se
non è supportato da macro, subroutines, librerie, può essere più pesante da
correggere, gestire, mantenere. Ma è sufficiente una forma di lavoro ordinata ed un efficace commento per poter scrivere codice che risulti
leggibile sia ad altri programmatori come anche a sé stessi dopo qualche
tempo. Per queste ragioni ha importanza la forma che deve assumere il sorgente per essere un vero
"lavoro" e non un pasticcio poco sensato (cose queste, assolutamente
vere per qualsiasi altro linguaggio).
Quello che realmente "spaventa" il programmatore nato nell' ambiente di
linguaggi ad alto livello è costituito da due elementi:
- la necessità di utilizzare le istruzioni del processore
- la necessità di conoscere come funziona l' hardware
Le istruzioni non sono le funzioni del C e se non si hanno a disposizione
librerie adeguate, l' impresa di scrivere da zero algoritmi complessi con gli
opcodes può non essere una cosa semplice, sopratutto per il principiante.
Ed è certamente (troppo) comune che un programmatore abbia una sufficiente
conoscenza del linguaggio ad alto livello, ma poca o nulla conoscenza di come
farlo agire nel manipolare bit di controllo di periferiche; usando le funzioni
pre programmate del linguaggio può evitare la necessità di conoscere come
funziona l' UART o l' MSSP, ma se deve scrivere una qualunque applicazioni al
di fuori delle possibilità di queste funzioni, si trova del tutto spaesato.
Possiamo concludere che, certamente, a causa della sua essenzialità e semplicità, il linguaggio
Assembly non può che risultare più difficoltoso
quando si tratta di manovrare dati complessi (matrici, floating point, ecc), in
quanto NON comprende alcuna funzione pre definita, come è comune per
il C, ma
demanda al programmatore la responsabilità di occuparsi di ogni dettaglio della gestione
dei registri, della memoria, delle operazioni complesse, oltre che dello sviluppo logico di
algoritmi, componendo in modo opportuno le istruzioni elementari, il
che porta a listati piuttosto lunghi rispetto a quelli equivalenti per
linguaggi più evoluti.
Per questi casi la soluzione è disporre di librerie adeguate, che esistono in
quanto fornite, ad esempio, dal costruttore del microcontroller. Se non
disponibili, è necessario costruirsele. Con qualche
tecnica adeguata si possono ridurre i disagi dell' Assembly, ad esempio, facendo uso
di modularità nella programmazione, realizzando librerie, subroutines e macro
istruzioni si alleggerisce in modo sensibile il lavoro di programmazione.
Per contro, Assembly sarà imbattibile in applicazioni su microcontroller con
risorse limitate e quando si voglia un completo e massimamente efficiente
controllo dell'hardware (tanto che i linguaggi ad alto livello prevedono la
possibilità di inserire tratti di codice Assembly).
Quindi, ha senso programmare in C (o altro) quando si è di fronte a strutture
di una certa estensione, con calcoli matematici o logiche complesse, ma questo
non permetterà da solo
di capire il modo in cui il programma agisce sulle diverse risorse disponibili o le sue
limitazioni se non si impara anche il linguaggio Assembly prima. Iniziare con
Assembly, comprendere il dispositivo, per poi passare a C, dove è necessario,
è il nostro consiglio.
|