Programmazione di microcontrollori STM32: Protocollo I2C e ... · “Sono i sogni a far vivere...
Transcript of Programmazione di microcontrollori STM32: Protocollo I2C e ... · “Sono i sogni a far vivere...
Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Misure per l’automazione e produzione industriale
Programmazione di microcontrollori STM32: Protocollo I2C e comunicazione con sensore Anno Accademico 2015/2016 Relatore Candidato Ch.mo prof. Rosario Schiano Lo Moriello Maria Concetta Turco matr. N46001685
“Sono i sogni a far vivere l'uomo. Il destino è in buona parte nelle nostre mani, sempre che sappiamo chiaramente quel che vogliamo e siamo decisi ad ottenerlo”
Indice
Introduzione ........................................................................................................ 4
Capitolo 1: Microcontrollore STM32 ................................................................. 5
1.1 Board STM32F4Discovery ................................................................... 6
Capitolo 2: I2C (Inter-Integrated Circuit) ........................................................... 7
2.1 Realizzazione bus I2C .......................................................................... 9
2.2 I2C (STM32F4xx) .............................................................................. 12
2.3 Caratteristiche principali I2C .............................................................. 12
Capitolo 3: Il sensore ........................................................................................ 14
3.1 Interfaccia digitale di accelerazione lineare ............................................ 15
3.2 Interfaccia digitale campo magnetico ..................................................... 15
Capitolo 4: Registri ed implementazione .......................................................... 17
4.1 Trasmettitore master................................................................................ 19
4.2 Ricevitore master .................................................................................... 19
4.3 Trasmettitore slave .................................................................................. 20
4.4 Ricevitore slave ....................................................................................... 20
4.5 Implementazione ..................................................................................... 21
Conclusioni ....................................................................................................... 28
Bibliografia ....................................................................................................... 29
Introduzione
I primi microcontrollori sono nati negli anni ’70 come alternativa ai
microprocessori. Tuttavia, mentre il primo era utilizzato per applicazioni a scopo
più generale, il secondo, essendo un dispositivo elettronico digitale integrato che
integra in uno stesso chip il processore, la memoria permanente, la memoria
volatile e i pin di I/O oltre ad altri eventuali blocchi specializzati, viene utilizzato
nei sistemi per applicazioni specifiche di controllo digitale detti sistemi
embedded.
I microcontrollori della famiglia STM32 sono tra i più diffusi: li troviamo nelle
stampanti, negli scanner, nelle attrezzature mediche, nei sistemi di allarme, nei
video citofoni…
Infatti, essi sono stati progettati per interagire direttamente con il mondo esterno
tramite un programma residente nella propria memoria interna e mediante
l’utilizzo di pin specializzati o configurabili dal programmatore. Essi sono
disponibili in tre capacità elaborative: 8bit, 16 bit e 32 bit.
In questo elaborato verrà utilizzato il microcontrollore STM32F401xC per
analizzare e mostrare un esempio pratico di uno dei protocolli principali di
comunicazione tra i dispositivi: il protocollo Inter-Integrate Circuit (I2C).
4
Capitolo 1: Microcontrollore STM32
L’STM32 è un microcontrollore della STMicroeletronics, un‘azienda franco-
italiana, per la produzione di componenti elettronici a semiconduttore, a 32 bit
basato sul core ARM Cortex -M. Tale processore garantisce elevate prestazioni ed
offre numerosi vantaggi: eccellenti prestazioni di elaborazione con la gestione
dell’interrupt veloce, miglioramento del sistema di debug con l’introduzione di
ampie capacità di breakpoint, consumo di energia ultra-basso con modalità di
sospensione integrata, robustezza della security platform con unità di protezione
della memoria integrata(MPU).
Il Cortex riduce l’area del processore in quanto adopera componenti di sistema
fortemente accoppiati tra loro ed integra anche un controller delle interrupt
configurabile NVIC per interrupt non mascherabili.
L’insieme del core del processore e dell’NVIC fornisce una velocità di esecuzione
nell’ISR (Interrupt Service Routine) riducendo la latenza dell’interrupt stessa.
Il controllo del software può essere:
• PRIVILEGIATO: il software può utilizzare tutte le istruzioni e quindi ha
accesso a tutte le risorse;
• NON PRIVILEGIATO: non è possibile accedere al timer di sistema,
all’NVIC ed inoltre potrebbe anche essere limitato l’accesso alle memorie
e alle periferiche.
Il processore può operare nella modalità Thread, utilizzata per eseguire il software
applicativo, la quale può essere privilegiata e non; oppure in modalità Handler,
5
utilizzata invece per gestire le eccezioni: in questo caso, l’esecuzione del software
è sempre privilegiato.
Il dispositivo presentato in questo elaborato è l’STM32F401xC che si basa
proprio sulle alte prestazioni dell’ARM Cortex M4 RISC a 32 bit.
Esso dispone di un’unità a virgola mobile (FPU) a singola precisione, implementa
le istruzioni DSP (Digital Signal Processor) e un’unità di protezione della
memoria MPU che migliora la sicurezza dell’applicazione.
1.1 Board STM32F4Discovery Nella board STM32Discovery si trova il microcontrollore
STM32F401VCT6 avente una memoria flash di 256 KB
e una memoria RAM di 64 KB in un pacchetto LQFP100.
Si alimenta attraverso il bus USB o attraverso un ingresso
esterno di 3V o 5 V. E’ dotato di alcuni sensori
appartenenti alla famiglia ST MEMS: un giroscopio
digitale a 3 assi L3GD20; un sistema-in-package dotato di
sensore di accelerazione lineare digitale 3D e di un
sensore magnetico digitale 3D LSM303DLHC; un
microfono digitale omnidirezionale MP45DT02. Presenta
inoltre un convertitore audio digitale-analogico DAC con
driver dell'altoparlante di classe D integrato CS43L22.
Su tale board sono integrati 8 LED: LD1 per la
comunicazione USB, che è rosso di default e verde nel
momento in cui le comunicazioni sono attive; LD2 di colore rosso che indica che
la board è attiva; quattro LED utente: LD3 (arancione), LD4 (verde), LD5 (rosso)
e LD6 (blu) connessi rispettivamente all’ I/O PD13, PD14, PD15, PD16
dell’STM32F401VCT6; due LED OTG USB: LD7 è di colore verde e indica
quando VBUS è presente su CN5 ed è connesso a PA9 dell’STM32F401VCT;
LD8, rosso, indica la sovracorrente da VBUS di CN5 ed è connesso all’I/O PD5
dell’STM32F401VCT.
Sono presenti inoltre due pulsanti, uno utente e uno di reset e un OTG USB con
micro-connettore AB.
6
Capitolo 2: I2C (Inter-Integrated Circuit)
La comunicazione tra due o più dispositivi elettronici avviene attraverso un
insieme di linee dette bus. Affinché tali elementi interagiscano tra di loro
correttamente, è necessario andare a stabilire delle specifiche di comunicazione
che vanno sotto il nome di protocollo.
L' I2C, Inter-Integrated Circuit, è un protocollo di trasmissione seriale: regola la
comunicazione tra due dispositivi in cui le informazioni, i bit, vengono inviati al
dispositivo che li riceve in modo sequenziale.
Questo tipo di comunicazione, detta half-duplex1, ha preso il sopravvento sulla
trasmissione parallela, perché più economica, richiede un minor numero di fili ed
è più robusta alle interferenze e agli errori di trasmissione.
Con il passare degli anni sono stati sviluppati vari standard che si differenziano
soprattutto per le diverse modalità di sincronizzazione.
Vi sono infatti 3 diversi tipi di comunicazione:
• SINCRONA: il trasmettitore invia i dati con una frequenza costante. Oltre
alla linea dati seriale SDA ve ne è anche un’altra seriale clock SCL.
Quindi, grazie al segnale di clock il ricevitore sa quando finisce ed inizia
l’invio di un nuovo bit.
• ASINCRONO: in questo tipo di comunicazione non vi è alcun tipo di
sincronizzazione tra il trasmettitore e il ricevitore. Il trasmettitore quando
è pronto invia prima un bit di START che segnala l’inizio della
7
trasmissione, poi i dati effettivi, un bit di parità opzionale che controlla se
si sono verificati errori durante la trasmissione, ed infine un bit di STOP
per indicare la fine della trasmissione direttamente al ricevitore.
• ISOCRONA: questo tipo di tecnica di trasmissione si ottiene quando si
vuole far comunicare un dispositivo di tipo sincrono con uno di tipo
asincrono.
Si evince, dunque, che l’I2C è un sistema di comunicazione seriale sincrona
bifilare, proprio perché l’interfaccia I2C è collegata al bus fisico attraverso due
segnali bidirezionali, uno dati, SDA, e uno clock, SCL.
La periferica I2C presente nell’STM32 funge da interfaccia tra il microcontrollore
e il bus I2C seriale. Esso supporta la modalità standard fino a 100kHz e la
modalità FM fino a 400kHz, ma la frequenza dell’I2C può essere aumentata fino a
1MHz.
Per quanto riguarda le caratteristiche dell’interfaccia I2C, essa soddisfa i requisiti
del protocollo di comunicazione I2C standard ma ha delle limitazioni: i pin I/O,
SDA e SCL sono mappati per non essere dei veri Open-Drain2, però quando sono
configurati come tali, il PMOS3 collegato tra il pin di I/O e VDD è disabilitato.
1 Comunicazione in entrambe le direzioni ma non simultaneamente. 2 Un terminale viene connesso a massa quando un valore di tensione alto viene applicato al gate mentre presenta un’alta impedenza quando viene applicato al gate un valore basso di tensione. 3 Particolare tipo di transistori MOS che si “accendono” solo se il transistore presente al loro gate è minore della loro soglia di transizione.
8
2.1 Realizzazione bus I2C Per realizzare un bus I2C occorrono due resistenze di pull-up collegate
all’alimentatore standard VDD, una per ciascuno dei due segnali SDA e SCL.
Nell’I2C la comunicazione avviene tra due dispositivi, detti MASTER e SLAVE,
dove il master controlla il bus ed emette il segnale di clock, mentre lo slave si
limita a ricevere e a sincronizzarsi sul segnale di clock senza poterlo, però
controllare.
Il numero di dispositivi che possono essere collegati dipende dalla massima
capacità parassita del bus.
9
Il master inizia la comunicazione con la generazione di un segnale di START,
transizione dal livello logico alto verso il basso dell’SDA, mentre l’SCL è alto.
Invio segnale di START
In seguito invia l’indirizzo dello slave che può essere di lunghezza pari a 7 o 10
bit con il quale vuole comunicare ed un altro bit per indicare se vuole inviare o
ricevere dati dallo slave.
Quando viene inviato un indirizzo, ogni dispositivo verifica se tale indirizzo
coincide con il proprio; se così fosse allora esso stesso si considererebbe
indirizzato dal master. Una volta riconosciuto lo slave indirizzato esso prende il
controllo dalla linea SDA sul successivo impulso alto di clock e la forza bassa
(ACK4). Il protocollo I2C richiede che ogni byte trasmesso deve essere concluso
con l’invio di un ACK o un NACK5, dove la condizione di ACK identifica la
corretta ricezione di un byte mentre il NACK indica di conseguenza un errore
nella ricezione dei dati.
Invio segnale di ACK
4 Acknowledge
10
Quando viene ricevuto il segnale NACK il master può generare una condizione di
STOP o una condizione di RESTART. Se il master invia un segnale di STOP,
vuole dire che non viene trovato nessun indirizzo slave corrispondente e dunque
quest’ultimo lascia l’SDA a valore logico alto così che il master può interrompere
il trasferimento; se invece il master invia un segnale di RESTART, esso senza
perdere il controllo del bus può riavviare un nuovo trasferimento dati.
.
Invio segnale di STOP
Quando lo slave, invece, è riconosciuto, viene trasmesso un sub-address a 8 bit
(SUB), dove i 7 bit meno significativi rappresentano l’indirizzo del registro
effettivo mentre il MSB6 consente l’incremento automatico dell’indirizzo.
Se il MSB nel campo SUB è pari a 1, l’indirizzo del registro viene
automaticamente incrementato per consentire letture o scritture multiple.
Invio e ricezione di un bit
5 Not Acknowledge
11
Il numero di byte per il trasferimento è illimitato, se però un ricevitore non può
ricevere un altro intero byte, esso può mantenere la linea SCL bassa così da
forzare il master in una condizione di attesa. Il trasferimento riprende solo quando
il ricevitore, lo slave, è pronto per ricevere un altro byte e rilascia la linea dati.
2.2 I2C (STM32F4xx) I microcontrollori STM32F4xx posseggono fino a 3 periferiche per l’utilizzo del
bus I2C: I2C1, I2C2, I2C3 che possono operare in modalità multimaster e slave.
Tutte e tre le periferiche sono connesse al bus APB1 a 42MHz. Attraverso
l’apposita interfaccia è possibile dunque la trasmissione dei dati.
2.3 Caratteristiche principali I2C L’I2C supporta la modalità multimaster, ovvero sullo stesso bus possono essere
presenti più master. Tale modalità comporta l’utilizzo di una procedura di
arbitraggio ottenuta attraverso il controllo delle linee SDA ed SCL.
L’interfaccia I2C può funzionare in modalità master o slave. Nel caso si trovi in
modalità master può generare segnali di clock, START e STOP, mentre se si trova
in modalità slave, rileva il bit di STOP generato dal master, rileva l’indirizzo I2C
e può riconoscere anche 2 indirizzi slave.
Essa supporta la generazione e l’individuazione di indirizzi a 7 o 10 bit ed anche
differenti velocità di comunicazione: fino a 100 kHz per la velocità standard e fino
a 400 kHz per l’alta velocità, ma la frequenza del bus I2C può essere aumentata
fino a 1MHz.
L’I2C possiede filtri sia analogico del rumore che digitale programmabile; flag di
stato per indicare la fine del byte di trasmissione, per specificare se il bus è
occupato o meno e se si tratta di un trasmettitore o un ricevitore; flag di errore per
indicare il mancato riconoscimento dopo la trasmissione dell’indirizzo o dei dati,
l’errato rilevamento della condizione di START o STOP e l’overrun6 o
l’underrun7 se il clock stretching8 è disattivato.
6 Superamento della dimensione massima del buffer andando a sovrascrivere spazi di memoria adiacenti 7 Condizione in cui la lettura o la scrittura del buffer è alimentato con dati ad una velocità più lenta di quanto richiesto
12
Un’altra caratteristica fondamentale dell’I2C è l’utilizzo di 2 condizioni di
interrupt: la prima serve per indicare che la trasmissione dell’indirizzo o dei dati è
andata a buon fine, la seconda per la condizione di errore. Infine possiede un
buffer di 1 byte con capacità di un DMA (Direct Memory Access).
8 Opzionale. Meccanismo preposto a rallentare la velocità di clock proposta dal master in quanto lo slave non riesce a cooperare con esso
13
Capitolo 3: Il sensore
L’accelerometro è un dispositivo in grado di misurare l’accelerazione mediante il
calcolo della forza rilevata rispetto alla massa dell’oggetto.
Lo possiamo immaginare come una
sfera posta al centro di un cubo
sospesa mediante l’utilizzo di 3 molle
ortogonali tra loro che sono a loro
volta agganciate al centro di ogni
faccia del cubo. Muovendo il cubo
nello spazio, la sfera al suo interno si
muoverà ed così possibile andare a
misurare il grado di compressione9
dovuto all’allungamento e alla
compressione delle molle.
L’accelerometro oggigiorno è utilizzato nella maggior parte delle applicazioni
tecnologiche come smartphone, tablet e può essere ad uno, due o tre assi.
Il system-in-package LSM303DLHC è un sistema che supporta l’accelerazione
lineare digitale 3D e un sensore di rilevamento del campo magnetico digitale 3D.
Infatti, grazie all’interfaccia I2C prima introdotta è possibile misurare sia
l’accelerazione lineare che il campo magnetico ad esso applicato per mostrare poi
l’output in formato digitale. L’LSM303DLHC è dotato di due segnali di dati
pronti (RDY) che indicano quando sono disponibili un nuovo set di dati di
9 Permette di stabilire se c’è stata un’accelerazione e in caso affermativo ci informa anche sulla relativa direzione
14
accelerazione misurati e dati magnetici andando così a semplificare la
sincronizzazione dei dati nel sistema che usano il dispositivo.
La sensibilità dell’accelerazione lineare descrive il guadagno del sensore
accelerometro e può essere determinato applicando 1g di accelerazione ad esso.
La misurazione può essere effettuata puntando l’asse d’interesse verso il centro
della Terra, rilevare il valore di uscita, ruotare il sensore di 180° e rilevando di
nuovo il valore di uscita. Cosi facendo viene applicato ± 1 g di accelerazione al
sensore. Sottraendo il valore di uscita grande da quella più piccola e dividendo il
risultato per 2 porta alla effettiva sensibilità del sensore.
3.1 Interfaccia digitale di accelerazione lineare L’invio dell’indirizzo slave viene completato con un ulteriore bit per indicare il
tipo di operazione da effettuare, lettura o scrittura. Se il bit è 1 vi sarà una lettura,
quindi dopo i due sub-address byte deve essere inviata una condizione di START
ripetuta.
Se invece il bit è 0 vi sarà una scrittura e quindi il master trasmette allo slave ma
con direzione invariata.
Per leggere più byte è fondamentale il settaggio del bit più significativo del sub-
address. Ciò vuol dire che il MSB deve essere pari a 1 mentre i restanti bit
rappresentano l’indirizzo del primo indirizzo da leggere. L’LSM303DLHC
prevede due modalità di funzionamento, l’accelerazione in modalità normale che
garantisce un’elevata risoluzione e la modalità a basso consumo che invece riduce
ulteriormente il consumo di corrente.
3.2 Interfaccia digitale campo magnetico Come per l’interfaccia digitale di accelerazione lineare anche per l’interfaccia
digitale del campo magnetico l’invio dell’indirizzo slave viene completato con un
ulteriore bit per indicare il tipo di operazione da effettuare, lettura o scrittura. Se il
bit è 1 vi sarà una lettura, quindi dopo i due sub-address byte deve essere inviata
una condizione di START ripetuta.
Se invece il bit è 0 vi sarà una scrittura e quindi il master trasmette allo slave ma
con direzione invariata.
15
Tale interfaccia utilizza un puntatore di indirizzo per indicare la locazione dalla
quale leggere o nella quale scrivere. Tali locazioni sono inviati dal master al
dispositivo slave seguito dall’indirizzo di 7 bit più un bit per indicare una lettura o
scrittura.
L’LSM303DLHC permette l’incremento automatico del puntatore che porta
all’aggiunta di due caratteristiche molto importanti:
1. Quando si accede all’indirizzo 12 o superiore, il puntatore modifica l’indirizzo
a 00;
2. Quando si raggiunge l’indirizzo 08, il puntatore torna indietro all’indirizzo 03
16
Capitolo 4: Registri ed implementazione
L’interfaccia I2C può funzionare in 4 diverse modalità:
• Trasmettitore slave
• Ricevitore slave
• Trasmettitore master
• Ricevitore master
Tale interfaccia funziona per default in modalità slave.
Il passaggio da master -> slave avviene automaticamente se si verifica una
condizione di arresto, mentre il passaggio da slave -> master avviene dopo una
condizione di start.
Analizziamo quali sono i registri interessati e i relativi settaggi.
Sappiamo che l’I2C per default funziona in modalità slave andando ad impostare
il bit START nel registro I2C_CR1 alla modalità master dopo aver riportato a
zero il bit BUSY del registro I2C_SR2.
Il bit BUSY vale 0 se non vi sono comunicazioni sul bus, altrimenti se il bus è già
occupato vale 1.
17
Esso viene settato via hardware sul rilevamento dell’SDA o SCL basso o tramite
una condizione di STOP.
Il bit SB (Start Bit) del registro I2C_SR1 quando è in modalità master e non vi è
nessuna condizione di start assume valore 0, altrimenti 1.
Può essere resettato via software da una lettura del registro SR1 seguita da una
lettura del registro DR o via hardware quando il bit PE, peripheral enable, del
registro I2C_CR1 è pari a 0 e quindi la periferica è disabilitata.
Successivamente il master attende una lettura del registro SR1 seguita da una
scrittura nel registro DR con l’indirizzo slave.
Viene poi inviato sulla linea SDA l’indirizzo slave.
Il bit ADDR del registro I2C_SR1 è impostato via hardware. Esso può
comportarsi sia da master che da slave e viene riportato a zero via software
leggendo il registro SR1 seguito da una scrittura di SR2 o via hardware quando
PE è pari a 0.
Per la modalità slave se tale bit è pari a 1 è stato trovato un indirizzo
corrispondente a quello ricevuto, in caso contrario vale 0; mentre per la modalità
master se il trasferimento dell’indirizzo non è ancora terminato il bit ADDR sarà
pari a 0, altrimenti vale 1.
Per azzerare i flag associati all’indirizzo, il master esegue una lettura del registro
SR1 seguita da una lettura del registro SR2. Arrivati a questo punto il master può
decidere se mettersi in modalità trasmettitore o ricevitore a seconda dell’LSB10
dell’indirizzo slave inviato. Se esso è pari a 0 il master sarà in modalità
trasmettitore altrimenti ricevitore con bit pari a 1.
10 Least Significant Bit
18
4.1 Trasmettitore master A questo punto il master invia il byte dal registro DR all’SDA tramite il registro
di scorrimento interno. Il master attende finché viene scritto il primo byte nel
registro I2C_DR. Quando viene ricevuto l’impulso di riconoscimento, il bit TxE
del registro I2C_SR1 è settato.
Se TxE è configurato e il byte di dati trasmesso non è stato scritto nel registro
prima dell’ultima trasmissione dati, il bit BTF del registro I2C_SR1 viene portato
a livello logico alto e l’interfaccia attende finché BTF non viene riportato a zero
da una scrittura I2C_DR che porta SCL a livello logico basso.
TxE vale 0 quando il Data Register non è vuoto altrimenti vale 1.
Tale bit viene azzerato via software da una scrittura nel registro DR o via
hardware dopo una condizione di START o STOP o quando PE è pari a 0.
Il bit BTF vale 0 nel caso in cui il trasferimento byte non è terminato, in caso
contrario vale 1. Esso viene impostato a zero via hardware sia in ricezione,
quando viene ricevuto un nuovo byte, includendo anche l’impulso ACK, e il DR
non è stato ancora letto (RxNE=1), che in trasmissione quando deve essere inviato
un nuovo byte e non è stato ancora scritto nel DR (TxE=1).
BTF può essere azzerato via software da una lettura o una scrittura nel registro
DR oppure via hardware in seguito ad una condizione di START o STOP.
Al termine della scrittura dell’ultimo byte nel registro DR occorre impostare via
software il bit STOP nel registro I2C_CR1 per generare una condizione di arresto.
Una volta fatto ciò l’interfaccia torna automaticamente alla modalità slave.
4.2 Ricevitore master Se invece il master si comporta come un ricevitore, dopo l’invio dell’indirizzo e
dopo aver portato a zero il bit ADDR, l’I2C si mette in modalità ricevitore master
acquisendo così byte dall’SDA nel registro DR. Dopo ogni byte viene
riconosciuto l’impulso se il bit ACK e il bit RxNE sono impostati a 1.
Se il bit RxNE del registro I2C_SR1 è impostato a 1 e i dati nel registro DR non
sono stati letti prima dell’ultima ricezione dei dati, il bit BTF viene impostato a
valore logico alto e l’interfaccia attende finché esso non viene riportato a zero da
una lettura nel registro DR che porta SCL a livello logico basso.
19
Il bit RxNE è configurato a livello logico alto quando il DR non è vuoto in
modalità ricevitore ed è riportato a zero via software da una lettura o scrittura del
registro DR o via hardware quando PE è pari 0.
Infine per chiudere la comunicazione il master invia un NACK per l’ultimo byte
ricevuto dallo slave.
Lo slave, ricevuto il NACK, rilascia il controllo dell’SDA e dell’SCL così che il
master può inviare una condizione di STOP o RESTART.
Come per il master, anche lo slave può trovarsi in modalità ricevitore e
trasmettitore. Ciò viene indicato dal bit TRA nel registro I2C_SR2, il quale se è 0
sono stati ricevuti i byte dati, se è 1 i byte dati sono stati trasmessi.
4.3 Trasmettitore slave Dopo la ricezione dell’indirizzo e dopo aver pulito il bit ADDR, lo slave invia il
byte dal registro DR sull’SDA tramite il registro a scorrimento interno. Lo slave
tiene basso l’SCL fino a quando l’ADDR non viene portato a zero e il DR non
viene riempito con i dati da trasmettere. Quando viene ricevuto l’impulso di
riconoscimento, se il bit TxE si trova a livello logico alto e i dati non sono stati
ancora scritti nel registro I2C_DR prima della fine della trasmissione, il bit BTF
viene settato e l’interfaccia attende finché quest’ultimo non viene controllato da
una lettura di I2C_SR1 seguita da una scrittura nel registro I2C_DR che porta
SCL basso.
4.4 Ricevitore slave Dopo aver ricevuto l’indirizzo e dopo aver riportato a zero il bit ADDR, lo slave
riceve il byte dall’SDA nel registro DR tramite il registro a scorrimento interno.
Dopo ogni byte ricevuto, l’interfaccia genera un impulso di riconoscimento se il
bit ACK è a livello logico alto e imposta il bit RxNE a 1.
Se il bit RxNE è impostato a 1 e i dati del registro DR non vengono letti prima
della fine della prossima ricezione dati, il bit BTF è portato a livello logico alto e
aspetta finché esso non viene portato a zero da una lettura del registro I2C_DR
che tiene SCL basso.
20
Al termine dell’ultimo trasferimento, viene generata una condizione di STOP dal
master. L’interfaccia rileva questa condizione e setta il bit STOPF del registro
I2C_SR1 così da generare un interrupt quando il bit ITEVTEN è abilitato. Il bit
STOPF assume valore 0 se non viene rilevata nessuna condizione di arresto
altrimenti sarà pari a 1.
Tale bit viene portato a valore logico alto dall’hardware quando viene rilevata la
condizione di stop sul bus dallo slave dopo un ACK, e viene invece portato a 0 via
software leggendo il registro SR1 seguito da una scrittura nel registro CR1 o via
hardware quando PE è pari a 0.
4.5 Implementazione Vediamo adesso un esempio di utilizzo del protocollo I2C e la comunicazione con
il sensore nel microcontrollore STM32F4xx. Il seguente codice mostra la
configurazione dell’accelerometro e la lettura degli assi x, y, z.
#include "stm32f4xx.h"
#define SB 0x1
#define ADDR 0x2
#define BUSY 0x2
#define BTF 0x4
#define RxNE 0x40
#define TxE 0x80
#define N 6
short int x,y,z;
short int dummy;
short int buffer[N]={0};
int main(){
RCC->APB1ENR |= 1<<21; //Abilito I2C1
RCC->AHB1ENR |= 1<<1; //Abilito GPIOB
//Configurazione PB6 e PB9
GPIOB->MODER |= (1<<13 | 1<<19); //Setto PB6 e PB9 in modalità
//Alternate Function
21
GPIOB->OTYPER |= (1<<6 | 1<<9); //Setto PB6 e PB9 in modalità
// Open-Drain
GPIOB->PUPDR |= (1<<12 | 1<<18); //Setto P6 e P9 in Pull-Up ->
//comunicazione basso-alto più
//veloce
GPIOB->AFR[0] |= 1<<26; //AF4 per PB6
GPIOB->AFR[1] |= 1<<6; //AF4 per PB9
//Configurazione I2C1
//L'accelerometro lavora ad una frequenza di 100 KHz
//Le seguenti istruzioni servono per impostare I2C1 affinché
//lavori a tale frequenza
I2C1->CR1 |= 1; //Abilito la periferica I2C attraverso il bit PE
I2C1->CR1 &= ~(0x2); //Abilito il bus in modalità I2C
I2C1->CR2 |= 1<<3; //Metto la frequenza di clock della
//periferica a fclock = 8 MHz
I2C1->CCR &= (0<<15 | 0<<14); //Metto DUTY=0 perché non voglio
//il duty cycle. Metto FS=0 perché
//voglio lavorare in STANDARD MODE
I2C1->CCR |= 0x28; //Mettiamo la CCR = 5us * fclock
I2C1->TRISE |= 9; //Perché è pari a fclock+1
//Configurazione accelerometro
//SCRITTURA MULTIPLA
while ((I2C1->SR2 & (uint16_t)BUSY) ==1); //Aspetto che il bus
// sia libero
I2C1->CR1 |= 1<<8; //Genero Start Condition attraverso il bit
//START
while((I2C1->SR1 & (uint16_t)SB)==0); //Aspetto che la Start
//Condition sia stata
//generata
dummy = I2C1->SR1; //Pulisco il bit SB(leggo il registro SR1)
//Invio Slave Address
I2C1->DR = 0x32; //Modalità scrittura
while ((I2C1->SR1 & (uint16_t)ADDR)==0); //Aspetto che
//l'indirizzo venga
//matchato
22
//Pulisco il bit ADDR attraverso una lettura dei registri SR1 ed
//SR2
dummy = I2C1->SR1;
dummy = I2C1->SR2;
//Invio configurazione(SUB Address)
while((I2C1->SR1 & (uint16_t)TxE) == 0); //Aspetto che il buffer
//di trasmissione sia
//vuoto (TxE=1)
I2C1-> DR =0x20; //Pulisco il bit TxE inviando al registro DR il
//registro nel quale voglio scrivere
//CTRL_REG1_A (20h)
while((I2C1->SR1 & (uint16_t)TxE) == 0); //Aspetto che il buffer
//di trasmissione sia
//vuoto (TxE=1)
I2C1-> DR = 0x17; //Pulisco il bit Txe inviando al registro DR
//la configurazione del registro CTRL_REG1_A:
//0x17=00100111 , dove:
//0010(Normal/low-power mode(10 Hz))
//0111(abilito gli assi x,y,z)
//Visto che è l'ultima trasmissione controllo oltre al bit TxE
//anche il bit BTF, attendo quindi che il buffer di trasmissione
//sia vuoto e che il trasferimento sia terminato
while(((I2C1->SR1 & (uint16_t)TxE) == 0) && (I2C1->SR1 &
(uint16_t)BTF) == 0);
I2C1->CR1 |= 1<<9; //Genero Stop Condition attraverso il bit
//STOP
//LETTURA MULTIPLA DEI VALORI DEI 3 ASSI
while(1){
while((I2C1->SR2 & (uint16_t)BUSY) == 1); //Attendo che il bus è
//libero
I2C1->CR1 |= 1<<8; //Genero Start Condition attraverso il bit
//START
while((I2C1->SR1 & (uint16_t)SB) == 0); //Attendo che la Start
//Condition sia stata
//generata
dummy = I2C1->SR1; //Pulisco il bit SB(leggo il registro SR1)
23
//Invio Slave Address
I2C1->DR = 0x32; //Modalità scrittura
while ((I2C1->SR1 & (uint16_t)ADDR) == 0); //Attendo che
//l'indirizzo venga
//matchato
//Pulisco il bit ADDR attraverso una lettura dei registri SR1 ed
//SR2
dummy = I2C1->SR1;
dummy = I2C1->SR2;
//Invio registro da cui voglio leggere (SUB Address)
while((I2C1->SR1 & (uint16_t)TxE) == 0); //Attendo che il buffer
//di trasmissione sia
//vuoto
I2C1->DR = (0x28)|(0x80); //Invio il registro OUT_X_L_A e sommo
//anche 1 al primo bit così dopo ogni
//lettura il registro si
//incrementerà
//Visto che è l'ultima trasmissione controllo oltre al bit TxE
//anche il bit BTF, attendo quindi che il buffer di trasmissione
//sia vuoto e che il trasferimento sia terminato
//Verifico che l'indirizzo sia completamente inviato e BTF è
//basso
while(((I2C1->SR1 & (uint16_t)TxE)==0) && ((I2C1->SR1
&(uint16_t)BTF)==0));
I2C1->CR1 |= 1<<8; //Genero Start(Restart)Condition attraverso
//il bit START
while((I2C1->SR1 & (uint16_t)SB) == 0); //Attendo che la Start
//Condition sia stata
//generata
dummy = I2C1->SR1; //Pulisco il bit SB (leggo il registro SR1)
I2C1->CR1 |= 1<<10; //Setto il bit ACK(per inviare una conferma
//di ricezione dopo la ricezione di un byte
//Invio Slave Address
I2C1->DR = 0x33; //Modalità lettura
while ((I2C1->SR1 & (uint16_t)ADDR) == 0); //Attendo che
//l'indirizzo venga
//matchato
24
//Pulisco il bit ADDR attraverso una lettura dei registri SR1 ed
//SR2
dummy = I2C1->SR1;
dummy = I2C1->SR2;
//Trasmissioni multiple
for (int i=0; i<6; i++){
while((I2C1->SR1 & (uint16_t)RxNE) == 0); //Attendo che il
//buffer di
//ricezione sia
//pieno(RxNE=1)
buffer[i] = I2C1->DR; //Ricevo il dato
if(i==4) //Se siamo alla penultima ricezione
I2C1->CR1 &=0xFBFF; //Setto il bit ACK a 0, cosicché
//all'ultimo ciclo verrà inviato il NACK
}
I2C1->CR1 |= 1<<9; //Genero Stop Condition attraverso il bit
//STOP
x = ((int16_t)((uint16_t)buffer[1]<<8) + buffer[0])/16;
y = ((int16_t)((uint16_t)buffer[3]<<8) + buffer[2])/16;
z = ((int16_t)((uint16_t)buffer[5]<<8) + buffer[4])/16;
}
}
25
Se invece vogliamo effettuare una singola lettura il codice è il seguente:
//LETTURA SINGOLA
while((I2C1->SR2 & (uint16_t)BUSY) == 1); //Aspetto che il bus sia
//libero
I2C1->CR1 |= 1<<8; //Genero Start Condition attraverso il bit
//START
while((I2C1->SR1 & (uint16_t)SB) == 0); //Aspetto che la Start
//Condition sia /stata
//generata
dummy=I2C1->SR1; //Pulisco il bit SB (leggo il registro SR1)
//Invio Slave Address
I2C1->DR = 0x32; //Modalità scrittura
while((I2C1->SR1 & (uint16_t)ADDR) == 0); //Aspetto che
//l’indirizzo venga
//matchato
//Pulisco il bit ADDR attraverso una lettura dei registri SR1 ed
//SR2
dummy=I2C1->SR1;
dummy=I2C1->SR2;
//Invio configurazione (SUB Address)
while((I2C1->SR1 & (uint16_t)TxE) == 0); //Aspetto che il buffer
//di trasmissione sia
//vuoto(TxE=1)
I2C1->DR = 0x20; //Pulisco il bit TxE inviando al registro DR il
//registro nel quale voglio scrivere
//CTRL_REG_A(20h)
//Controllo il bit TxE ed il bit BTF. Attendo quindi che il buffer
//di trasmissione sia vuoto e che il //trasferimento sia terminato
while(((I2C1->SR1 & (uint16_t)TxE) == 0) && ((I2C1->SR1 &
(uint16_t)BTF) == 0));
I2C1->CR1 |= 1<<8; //Genero Start (Restart) Condition attraverso
//il bit START
26
while((I2C1->SR1 & (uint16_t)SB) == 0); //Attendo che la Start
//Condition sia stata
//generata
//Invio Slave Address
I2C1->DR = 0x33; //Modalità lettura
while((I2C1->SR1 & (uint16_t)ADDR) == 0); //Attendo che
//l’indirizzo venga
//matchato
//Pulisco il bit ADDR attraverso una lettura dei registri SR1 ed
//SR2
dummy=I2C1->SR1;
dummy=I2C1->SR2
while((I2C1->SR1 & (uint16_t)RxNE) == 0); //Attendo che il buffer
//di ricezione sia
//pieno(RxNE=1)
data=I2C1->DR; //Ricevo il dato
I2C1->CR1 |=1<<9; //Genero Stop Condition attraverso il bit STOP
27
Conclusioni
In questo elaborato è stato utilizzato il microcontrollore STM32F401xC, basato su
ARM Cortex M4 per offrire prodotti dalle prestazioni molto elevate, dal
funzionamento a bassa tensione e dalla bassa potenza pur garantendo un elevata
efficienza, e l’utilizzo per piccoli e per grandi progetti.
In particolare, è stato analizzato e mostrato un esempio pratico di uno dei
protocolli principali di comunicazione tra i dispositivi: il protocollo Inter-Integrate
Circuit (I2C).
Dopo aver descritto l’accelerometro, si è analizzato come il protocollo regola la
trasmissione tra un dispositivo master e uno slave, la sua realizzazione, le
caratteristiche principali, l’insieme dei registri e i relativi settaggi che ne
permettono un’implementazione pulita nell’STM32F4xC.
I microcontrollori oggigiorno sono in continua evoluzione. Nonostante essi non
siano idonei per utilizzi generici ma più specifici, utilizzabili quindi in campi più
limitati, possiamo dire che nello specifico campo, svolgono il lavoro nel modo più
efficiente possibile.
28
Bibliografia
[1] STMicroeletronics group, RM0368 Reference Manual, 2014, 835
[2] STMicroeletronics grouop, Ultra-compact high-performance
eCompass module: 3D accelerometer and 3D magnetometer , 2013, 42
[3] STMicroeletronic group, ARM Cortex –M4 32b MCU+FPU, 150
DMIPS, 256 KB Flash/64KB RAM, 11 TIMs, 1 ADC, 11 comm.
interfaces, 2014, 134
[4] STMicroeletronics, http://www.st.com
29