PIT 2012: Workshop@UniNA
Arduino: Open Hardware
- a cura di Luciano Esposito con il patrocinio del Preside della Facoltà di Ingegneria
dell'Università degli Studi di Napoli Federico II:
Prof. Piero Salatino
e con il sostengo del Prof. Antonio Pescapè
Comics Unina Research Group
Indice
Argomenti trattati
•
Cos'è Arduino e com'è fatto
•
IDE e processo di sviluppo
•
Programmazione su Arduino
•
Il protocollo I2C
•
Controllo di dispositivi esterni
Propedeuticità
•
Buona conoscenza del C/C++
•
Rudimenti di architettura dei calcolatori
•
Un po' di elettronica di base
Cos'è Arduino?
•
È una piattaforma di sviluppo software e fornisce un supporto per
la prototipazione rapida di hardware
•
È compatibile con tutti i sistemi operativi
•
Lo sviluppo software si basa su un semplice ed intuitivo IDE
(Integrated Develpment Environment)
•
È controllabile tramite USB
•
Gli schemi elettrici ed il codice sorgente degli applicativi a
supporto (IDE, compilatore, programmatore, ecc.) sono disponibili
in rete: open hardware e open software
•
Progetto COMPLETAMENTE italiano
Com'è fatto?
•
Il cuore principale di Arduino è il chip Atmega della Atmel
•
Possiede numerosi slot di I/O sia analogici sia digitali
•
Dispone di due slot da 3,3V e 5V per alimentare circuiti esterni
•
Utilizza un connettore USB per alimentazione e/o controllo, ed un
connettore standard per l'alimentazione
Il microcontrollore Atmega 328P
All'interno del chip Atmega non è presente solo un processore ma
un intero sistema di elaborazione che più tecnicamente è detto
SoC (system-on-a-chip).
Troveremo infatti oltre alla CPU anche:
•
EEPROM (Electrically Erasable Programmable
Read-Only Memory) memoria non volatile
•
Memoria RAM
•
Registri e dispositivi I/O
•
Dispositivo per la comunicazione seriale
(USART)
•
Oscillatore
•
Convertitori A/D e D/A (SAR e DAC )
•
Ecc...
La CPU AVR
●
È di tipo RISC (Reduced Instruction Set Computer).
Istruzioni a lunghezza variabile con PC (Program
Counter) a 14 bit
●
Utilizza un'architettura che separa la memoria
istruzioni dalla memoria dei dati (tipo Harvard)
●
Possiede una pipeline a singolo stadio (solo pre-
fetch) capace di eseguire una singola istruzione per
periodo di clock
●
Gestisce le interruzioni
●
Possiede 32 registri ad 8 bit general purpose
●
Possiede lo stack register ed un set di istruzioni che
ne permettono la gestione
●
Cinque modi di indirizzamento (Diretto, Indiretto,
Indiretto con offset, Indiretto con pre e post
incremento)
Le memorie AVR (1/2)
Il microcontrollore ATMega 328P possiede due spazi di memoria
fondamentali: la memoria istruzioni (Memory Flash) e la memoria
dati (SDRAM)
Memoria Istruzioni è organizzata in due spazi: Boot Flash Section (solo
read) e Application Flash Section (capacità di 32 Kbyte ma con un
parallelismo di 16 bit)
●
●
Memoria Dati è organizzata secondo l'indirizzo di memoria:
●
Indirizzi da 0x0000 a 0x001F accedono ai 32 registri general purpose
●
Indirizzi da 0x0020 a 0x005F accedono a 64 registri I/O
●
Indirizzi da 0x0060 a 0x00FF accedono 160 Extended register I/O
●
Indirizzi da 0x0100 a 0x08FF accedono alla SRAM interna (capacità
2Kbyte)
Le memorie AVR (2/2)
È presente all'interno del chip anche una memoria EEPROM da
1KByte accessibile mediante 3 registri utilizzando particolari
istruzioni
●
Registro EEAR (EEPROM Address Register) a 16 bit
●
Registro EEDR (EEPROM Data Register) a 8 bit
●
Registro EECR (EEPROM Control Register) a 8 bit
I dispositivi I/O (1/2)
Sono presenti all'interno del chip diversi dispositivi per gestire i vari
I/O:
●
8-bit e 16-bit Timer/Counter con PWM
Si tratta di dispositivi in grado di generare un segnale utilizzando la
tecnica PWM (Pulse-Width Modulation) che consente di ottenere una
tensione media variabile che dipende dalla durata e dall'ampiezza di ogni
singolo impulso. Tale dispositivo utilizza un contatore a 8 o 16 bit per
ottenere tale tensione.
USART (Universal Synchronous and Asynchronous serial Receiver and
Transmitter)
●
Tale dispositivo converte flussi di bit di dati da un formato parallelo a un
formato seriale asincrono (o sincrono) o viceversa. Può operare sia in
modalità slave sia in modalità master
I dispositivi I/O (2/2)
●
Convertitore A/D a 10 bit
Tale dispositivo permette di trasformare un segnale analogico in un
segnale numerico.
Il microcontrollore usa un convertitore a successive approssimazioni
(SAR) con un circuito del tipo Sample and Hold per mantenere costante
il livello di tensione acquisito.
La tensione di riferimento interna è di 1,1V altrimenti può essere
impostata o dall'esterno mediante il pin AREF (Analog REFerence) o
utilizzando la tensione che Arduino può erogare a circuiti esterni (5V o
3,3V)
●
2-wire Serial Interface
Si tratta di un dispositivo capace di connettere al microcontrollore
ulteriori dispositivi, fino ad un massimo di 128, utilizzando solo due fili
uno per i dati (SDA) ed uno per il clock (SCL)
Ulteriori dispositivi
●
Dispositivi per la generazione e gestione di più clock
Gestione dell'alimentazione e risparmio energetico (sleep mode, idle
mode, power-save mode, ecc...)
●
●
Timer watchdog
Dispositivi per il controllo delle cause di reset, registri per il controllo dei
timer watchdog, ecc...
●
●
Vettore per il controllo delle interruzioni interne ed esterne
debugWIRE on-chip – sistema che utilizza un'interfaccia bidirezionale
per controllare il flusso del programma e per scrivere all'interno delle
memorie non volatili (EEPROM e FLASH)
●
Il ciclo di sviluppo SW (1/3)
Il nostro programma (o sketch) deve passare attraverso tre fasi
prima di poter essere eseguito su Arduino. Similmente al processo
di sviluppo di un programma in C/C++ le fasi sono:
●
Pre-proccessing
●
Compilazione e collegamento
●
Caricamento sulla board
Il ciclo di sviluppo SW (2/3)
L'ambiente di sviluppo di Arduino compie alcune trasformazioni allo
sketch prima di compilarlo.
Fase 1: Pre-proccessing
- Aggiunge allo sketch la libreria WProgram.h che contiene tutte le
definizioni delle funzioni necessarie alla propria board Arduino per poter
funzionare
1
- L'ambiente di sviluppo cerca le definizioni delle funzioni scritte
dall'utente e ne crea i prototipi, cioè le dichiarazioni
2
- Il preprocessore inserisce tutti i prototipi di funzione ed i vari
statements #include prima del blocco di definizione delle variabili o dei
tipi strutturati.
3
Il ciclo di sviluppo SW (3/3)
Fase 2: Compilazione e collegamento
Lo sketch così completo è compilato tramite i compilatori avr-gcc e avr-g++
secondo l'architettura e le esigenze della propria board Arduino.
Si generano così file con estensione .o (file oggetto) che vengono
successivamente collegati dal linker, insieme alle librerie, arrivando infine
ad un unico file con estensione .hex
Fase 3: Caricamento sulla board
Il file .hex, risultato finale della compilazione, viene caricato dal
programma avrdude sulla board. Il processo di caricamento viene eseguito
controllando alcune variabili di configurazione tipiche della board sulla
quale si dovrà caricare lo sketch (nome della board, tipo di
microcontrollore, tipo di bootloader, ecc..)
L'IDE di sviluppo
Tutte le fasi viste in precedenza sono trasparenti all'utente e
vengono effettuate dall'IDE (Integrated Develpment Environment) di
Arduino
Possiede un'interfaccia semplice ed
intuitiva ed è disponibile per tutti i sistemi
operativi.
Applicazione open source
Programmare su Arduino
Analizziamo un semplice esempio:
const unsigned int LED_PIN = 13;
const unsigned int PAUSE = 500;
La procedura setup()
è utile per permettere void setup() {
l'inizializzazione di
pinMode(LED_PIN,OUTPUT);
alcune funzioni tipiche }
di Arduino
void loop(){
digitalWrite(LED_PIN, HIGH);
La procedura loop()
delay(PAUSE);
gestisce il ciclo principale
digitalWrite(LED_PIN, LOW);
del programma
delay(PAUSE);
} Questa funzione imposta
l'intervallo di tempo da
attendere
Questa funzione imposta il
pin 13 sulla scheda Arduino
come output elettronico
Queste funzioni
permettono di
impostare il valore
alto o basso (5V) sul
pin 13 mediante il tag
HIGH e LOW
Il protocollo I2C (1/2)
Protocollo di comunicazione seriale di tipo master/slave con bus sincrono
Sono presenti due linee di comunicazione dati:
● SDA (Serial DAta line) per i dati
● SCL (Serial Clock Line) per il clock
Vi è inoltre la presenza di due linee di servizio:
● GND o massa
● Vdd alimentazione
L'indirizzamento dei dispositivi avviene mediante indirizzi a 7 bit per un
totale di 128 dispositivi di cui, però, 16 riservati scendendo ad un
massimo di 112 effettivamente controllabili
Il protocollo I2C (2/2)
Un bus I2C per poter operare ha bisogno di due nodi:
● Nodo master: dispositivo che emette il segnale di clock
● Nodo slave: nodo che si sincronizza sul segnale di clock senza poterlo
controllare
In generale ci sono 2 modi distinti di operare sia per il master che per lo
slave (non mutuamente esclusivi durante una sessione di
comunicazione):
● Un master trasmette, controlla il clock e invia dati agli slave
● Un master riceve, controlla il clock ma riceve dati dallo slave
● Lo slave trasmette, il dispositivo non controlla il clock ma invia dati al
master
● Lo slave riceve, il dispositivo non controlla il clock e riceve dati dal
master
Il nunchuk Wii (1/3)
Il nunchuk wii dispone di:
●
Un accelerometro su 3 assi (X, Y, Z)
●
Una levetta capace di fornire la propria
posizione rispetto agli assi X ed Y
●
Due pulsanti Z e C
Utilizza il protocollo I2C per comunicare
con la Wii lo stato dei pulsanti, la
posizione della levetta e i valori
dell'accelerometro
Il nunchuk Wii (2/3)
L'unica operazione che un dispositivo master può effettuare con il
nunchuk wii è la lettura dei valori dei vari sensori e dei dispositivi presenti
nel controller
La lettura avviene quando il master richiede tale operazione, leggendo un
pacchetto dati di 6 byte proveniente dal controller
Il nunchuk Wii (3/3)
●
Il byte 0 contiene il valore dell'asse X della levetta mentre il byte 1
contiene il valore dell'asse Y. Variano tra 29 e 228 (circa) e sono valori
interi senza segno ad 8 bit
●
I valori di accelerazione lungo gli assi X,Y e Z sono numeri a 10 bit. I
byte 2, 3 e 4 contengono gli 8 bit più significativi, mentre i due bit meno
significativi sono contenuti all'interno del byte 5
●
Il byte 5 è suddiviso in gruppi di uno o più bit. Il bit 0, meno significativo,
contiene lo stato del pulsante Z (0 se è premuto altrimenti vale 1). Il bit 1
contiene invece lo stato del pulsante C con medesima semantica del bit
precedente. I restanti bit, come già detto, contengono i bit meno
significativi dei valori di accelerazione
Collegamento con Arduino
Collegamento fisico:
Il plug del nunchuk possiede 6 connettori ma solo
4 di questi sono utilizzati.
●
Il connettore Data andrà collegato al pin
analogico 4 (A4)
●
Il connettore Clock andrà collegato al pin
analogico 5 (A5)
●
Il connettore 3,3V al rispettivo pin su Arduino
●
Il connettore GND al pin di massa su Arduino
Il collegamento software verrà effettuato mediante l'interfaccia Wire
disponibile con l'IDE di Arduino
L'interfaccia Wire
È una classe che consente la comunicazione mediante il protocollo
I2C con il dispositivo 2-wire Serial Interface
I principali metodi sono:
●
begin() o begin(address) – Inizializza la classe Wire ed il bus I2C
●
beginTrasmission(address) – Comincia una comunicazione con un dispositivo
slave avente l'indirizzo “address”
●
endTrasmission() - Termina una comunicazione con un dispositivo slave
●
send(byte) – Invia un byte al dispositivo slave [vedi anche write()]
●
requestFrom(address, count) – Usato dal dispositivo master per richiedere count
byte al dispositivo slave
●
receive() - legge i byte ricevuti dal dispositivo slave [vedi anche read()]
Leggere dal Nunchuk (1/5)
#include <Wire.h>
Includo la libreria Wire.h
unsigned char outbuf[6];
Buffer dove verrà
contenuto il pacchetto
proveniente dal nunchuk
unsigned char joy_x_axis;
unsigned char joy_y_axis;
int cnt = 0;
int z_button = 0;
int c_button = 0;
int dtime = 1000;
TwoWire link;
void setup(){
Serial.begin (9600);
link.begin ();
nunchuck_init ();
Serial.print ("Finished setup\n");
}
Oggetto istanza della
classe TwoWire definita
all'interno della libreria
Wire.h
Inizializzazione programma
Metodo che inizializza la
comunicazione tra master
e slave
Leggere dal Nunchuk (2/5)
void nunchuck_init(){
Procedura che avvia e
link.beginTransmission (0x52);
gestisce l'handshake tra
link.send (0x40);
arduino ed il nunchuk
link.send (0x00);
link.endTransmission ();
}
void send_zero(){ Procedura che richiede al
nunchuk di trasmettere un
link.beginTransmission (0x52);
altro pacchetto dati
link.send (0x00);
link.endTransmission ();
} void loop(){
Loop principale del
link.requestFrom (0x52, 6);
programma che si occupa
while (link.available ()) {
di decodificare il pacchetto
outbuf[cnt] = decode(link.receive());
proveniente dal nunchuk
cnt++;
}
cnt = 0;
send_zero();
printNunchuckData();
delay(dtime);
}
Leggere dal Nunchuk (3/5)
unsigned char decode ( unsigned char b ){
Funzione di decodifica di
return ( b ^ 0x17 ) + 0x17;
ogni byte proveniente dal
}
nunchuk
void printNunchuckData(){
Procedura di stampa dei
joy_x_axis = outbuf[0];
valori letti dal nunchuk
joy_y_axis = outbuf[1];
unsigned int accel_x_axis = ( (unsigned int) (outbuf[2] << 2) | ( outbuf[5] >> 2 & 0x03) );
unsigned int accel_y_axis = ( (unsigned int) (outbuf[3] << 2) | ( outbuf[5] >> 4 & 0x03) );
unsigned int accel_z_axis = ( (unsigned int) (outbuf[4] << 2) | ( outbuf[5] >> 6 & 0x03) );
unsigned char z_button = 0;
unsigned char c_button = 0;
Leggere dal Nunchuk (4/5)
switch ( outbuf[5] & 0x03 ) {
case 0: c_button = 0;
z_button = 0;
break;
case 1:
c_button = 0;
z_button = 1;
break;
case 2:
c_button = 1;
z_button = 0;
break;
case 3:
c_button = 1;
z_button = 1; break;
}
Lettura dei bit
rappresentativi dello stato
dei pulsanti presenti sul
nunchuk
Leggere dal Nunchuk (5/5)
Serial.print("Asse X: ");
Serial.print(outbuf[0],DEC);
Serial.print("\t");
Serial.print("Asse Y: ");
Serial.print(outbuf[1],DEC);
Serial.print("\t");
Serial.print("Acc X: ");
Serial.print(outbuf[2],DEC);
Serial.print("\t");
Serial.print("Acc Y: ");
Serial.print(outbuf[3],DEC);
Serial.print("\t");
Serial.print("Acc Z: ");
Serial.print(outbuf[4],DEC);
Serial.print("\t");
Serial.print("Button Z: ");
Serial.print(z_button,DEC);
Serial.print("\t");
Serial.print("Button C: ");
Serial.print(c_button,DEC);
Serial.print("\t");
Serial.print("\n");
}
Stampa sul monitor di tutti i
valori (come interi positivi in
base dieci) provenienti dal
nunchuk mediante la
classe Serial
Servomotori
Particolare tipo di motore generalmente di piccola potenza, le
cui condizioni operative, a differenza dei motori tradizionali,
sono soggette ad ampie e spesso repentine variazioni sia nel
campo della velocità che della coppia motrice, alle quali si
deve adattare con la massima rapidità e precisione.
Da un servomotore elettrico è generalmente richiesta bassa inerzia, elevata
linearità tensione/velocità e corrente/coppia, rotazione (o traslazione) uniforme con
bassa oscillazione di coppia angolare (o forza) senza posizioni preferenziali e
capacità di sopportare picchi impulsivi di potenza.
Possiede già all'interno tutta l'elettronica e la meccanica necessaria alla
movimentazione e al controllo.
Collegamento e controllo
Collegare un servomotore all'Arduino è molto semplice. Di norma tali dispositivi
prevedono tre cavi. Due di questi sono di alimentazione e vanno quindi collegati ai
rispettivi slot della board (5V e GND). Il terzo cavo è solitamente usato per il
controllo.
Il controllo di un servomotore può essere effettuato collegando il cavo di controllo
all'uscita PWM dell'Arduino gestito dalla libreria, fornita dall'IDE, Servo.h.
La movimentazione sarà gestita mediante un apposito metodo che posizionerà il
servomotore ad una specifica angolazione definita via software dal programmatore.
Controllare il servomotore (1/3)
#include <Wire.h>
#include <Servo.h>
...
TwoWire link;
Servo Motor1;
void setup(){
link.begin ();
nunchuck_init ();
Motor1.attach(9);
}
void nunchuck_init(){
...
}
void send_zero(){
...
}
Inclusione delle librerie
Servo.h e Wire.h per il
controllo rispettivamente
del servomotore e nunchuk
Istanza classi TwoWire e
Servo
Inizializzazione del
nunchuk e collegamento
software del servomotore al
pin 9 dell'arduino
Controllare il servomotore (2/3)
void loop(){
Ricezione e decodifica di
link.requestFrom (0x52, 6);
ogni byte ricevuto dal
nunchuk
while (link.available ()) {
outbuf[cnt] = decode(link.receive());
cnt++;
}
cnt = 0;
send_zero();
Avvio della procedura per il
controllo del servomotore
control();
delay(dtime);
}
unsigned char decode ( unsigned char b ){
Funzione di decodifica
...
}
Controllare il servomotore (3/3)
void control() {
unsigned char pos;
pos = outbuf[0];
Motor1.write(pos);
}
Lettura della posizione
della levetta del nunchuk e
posizionamento del
servomotore
NB. Questo piccolo esempio permette la semplice movimentazione del
servomotore, ma non tiene conto di vari effetti di disturbo che potrebbero verificarsi
muovendo la levetta del nunchuk. Ci sarebbe bisogno di una funzione che
eliminasse i vari disturbi... lascio a voi il completamento del programma ^_^
Ringraziamenti
Un sentito ringraziamento a chi ha permesso lo svolgersi di tutto
questo:
al Preside della Facoltà di Ingegneria dell'Università degli
Studi di Napoli Federico II: Prof. Piero Salatino
e al Prof. Antonio Pescapè, nostro eterno supporter
Ai ragazzi dell'associazione NaLug – Napoli GNU/Linux Users Group
http://www.nalug.net
[email protected]
Riferimenti
Riferimenti:
www.arduino.cc
● Datasheet ATMega328P
● http://it.wikipedia.org
● www.atmel.com
●
Luciano Esposito
[email protected]
Scarica

Programmare su Arduino