This is an old revision of the document!
Scopul laboratorului de astazi este multiplu: -
Pe partea de protocoale de comunicatii, astazi vom introduce un nou protocol: SPI.
<note>Va reamintim ca am mai studiat un protocol semestrul acesta: **U(S)ART**.</note>
Referitor la biblioteci si stocarea/redarea continutului audio: vom folosi o biblioteca pentru a accesa un sistem de fisiere(FAT) aflat pe un card de memorie si a pune niste manele la buzzer.
Pentru a porni comunicația dispozitivul master trebuie să seteze ceasul la o frecvență cel mult egală cu frecvența suportată de slave (de regulă până la câțiva MHz). Master-ul selectează apoi dispozitivul slave dorit, punând 0 pe linia SS spre acesta. În timpul unui ciclu SPI, transmisia este full-duplex:
Comunicația pe SPI implică de obicei existența a două registre de shiftare ( Shift Registers), unul în master și unul în slave, conectate circular (<imgref image3>).
<imgcaption image3|Modul de transmisie a unui octet între slave și master în comunicația SPI></imgcaption>
De obicei primul bit shiftat pe liniile de MISO/MOSI este bitul cel mai semnificativ, în timp ce un nou bit este adăugat pe poziția cea mai puțin semnificativă din registru. După ce întregul cuvant a fost trimis, prin shiftare, master-ul și slave-ul au interschimbat valorile din cele două registre de shiftare. Dacă mai există date de transmis, procesul este reluat. Când nu mai există date de transmis, masterul întrerupe generarea ceasului și, în general, pune 1 pe linia de SS asociata slave-ului. Dispozitivele slave care nu au fost selectate prin semnalul SS asociat lor vor ignora semnalele de pe SCK și MOSI și nu vor genera nimic pe MISO (care se va afla într-o stare neutră - stare de impedanță mărită). Master-ul poate selecta doar un singur slave la un moment dat.
Exemplu de transmisie simultană de date între master și slave:
<imgcaption image4|Transmisia unui byte între master și slave.></imgcaption>
Microcontroller-ul Atmega324 suportă și comunicația serială prin I2C. Acest standard necesită doar două linii de comunicație, unul pentru semnalul de ceas și altul pentru date, deci comunicația este half-duplex, funcționând cu până la 400Kbps. De obicei este preferat SPI-ului atunci cand avem nevoie să interconectăm mulți slaves. Unul dintre motivele pentru care I2C este preferat fiind că selecția slave-ului se face prin adresare, nu printr-un semnal adițional de chip-select.
Ambele standarde sunt folosite cu succes pentru comunicația cu periferice lente ce sunt accesate intermitent (EEPROM-urile si ceasurile de timp real sunt exemple de astfel de device-uri). Totuși, SPI se mulează mai bine ca I2C pe aplicațiile care folosesc stream-uri de date (spre deosebire de aplicațiile unde se citesc/scriu diverse locații din slave device). Un exemplu de aplicație ce folosește stream-uri este comunicația dintre micropocesoare sau DSP-uri (digital signal processors). De asemenea SPI poate atinge rate de transfer semnificativ mai mari decat I2C, interfețele SPI putând funcționa la zeci de MHz și este eficient mai ales în aplicații ce îi folosesc capacitatea de a realiza conexiuni full duplex, ca de exemplu comunicarea dintre un “codec” (coder-decoder) și un DSP, ce presupune trimiterea de sample-uri în ambele direcții.
Datorită faptului că nu exista suport built-in pentru adresarea device-urilor, SPI necesită mai mult efort și mai multe resurse hardware decat I2C când avem mai mulți slave. Pe de altă parte SPI este mai simplu și mai eficient în aplicații point-to-point (single master, single slave) din aceleași motive: lipsa adresării device-urilor presupune mai puțin overhead.
Sumarizând, putem enumera următoarele avantaje ale SPI:
Dintre dezavantaje amintim:
Microcontroller-ul ATMega324 pune la dispoziția utilizatorilor săi și o interfata SPI, aceasta putând funcționa atât ca master cât și ca slave. Dupa cum deja ne-am obișnuit, pentru configurarea și utilizarea interfeței lucrăm cu registre: de control, de stare și de date.
<imgcaption image5 | Schema bloc a interfaței SPI din interiorul controller-ului ATmega324></imgcaption>
Descrierea completă a registrelor o găsiți în datasheet în capitolul SPI - Serial Peripheral Interface.
Acesta este registrul din care citim datele pe care le-am primit sau în care scriem datele pe care vrem să le transmitem. Scrierile în acest registru inițiază o nouă transmisie iar citirile se fac din registrul de shiftare.
Interfața SPI este folosită ca standard de comunicație în toate cardurile SD și MMC. Din această cauză este foarte ușor să interfațăm un card de memorie la microcontroller-ul nostru și să câștigăm câțiva gigabytes de memorie pentru datele pe care vrem să le salvăm sau să le citim în aplicația noastră. Conectarea la microcontroller se face ca în <imgref image9>:
<imgcaption image9 | Interfațarea cardului SD la interfața SPI de la ATmega324.></imgcaption>
Pentru a ușura accesul la datele de pe card, este foarte util să nu scriem direct în spațiul de memorie ci să folosim un modul filesystem care să permită microcontroller-ului să scrie și să editeze fișiere formatate FAT32. Există mai multe implementări și biblioteci disponibile pe internet dar am ales să folosim Petit FAT Filesystem din cauza dimensiunii mici (2-4KB) și a faptului că folosește un minim de memorie RAM (46 octeți plus stivă).
API-ul pentru filesystem conține următoarele funcții:
FRESULT pf_mount (FATFS*); // Mount/Unmount pentru un volum FRESULT pf_open (const char*); // Deschide un fișier FRESULT pf_read (void*, WORD, WORD*); // Citește dintr-un fișier FRESULT pf_write (const void*, WORD, WORD*); // Scrie într-un fișier FRESULT pf_lseek (DWORD); // Mută pointer-ul de citire/scriere FRESULT pf_opendir (DIR*, const char*); // Deschide un director FRESULT pf_readdir (DIR*, FILINFO*); // Citește un director
Având atât spațiu la dispoziție, putem sa implementăm un player audio foarte simplu care să citească fișiere WAV de pe card și să le redea prin PWM pe difuzorul de pe placă. Folosim fișiere de tip WAV pentru că acestea conțin stream-ul audio necomprimat (decodificarea unui fișier MP3 este peste putințele unui AVR și necesită un circuit decoder extern gen STA013 sau VS1011).
Teoria care stă în spatele redării fișierului este simplă: pentru a înregistra o melodie se eșantionează cu o anumită frecvență semnalul analogic de la intrare (microfon). De frecvența de eșantionare depinde calitatea semnalului redat la playback: cu cât eșantionăm mai repede, cu atât stocăm mai multă informație și, conform teoremei lui Nyquist, putem reproduce o bandă de frecvențe mai mare. Banda de frecvență pentru o înregistrare audio telefonică (sau radio AM) este de 0-4000Hz și o putem obține dacă eșantionăm semnalul de intrare la 8000Hz, sau de 8000 de ori pe secundă. Dacă vrem sa mărim calitatea înregistrării astfel încât să reproducă mai fidel întregul spectru audibil (pentru om este aproximativ 20Hz-20000Hz) va trebui să eșantionăm cu o viteza de 40000Hz. Aceste rate de eșantionare sunt standardizate în industria muzicala și au următoarele valori: 8000Hz, 11025Hz, 16000Hz, 22050Hz, 32000Hz, 44100Hz, 48000Hz, 88200Hz, 96000Hz, 176400Hz, 192000Hz.
Calitatea înregistrării este dată și de numărul de biți pe care este stocat fiecare eșantion în parte. Formatul WAV poate stoca date pe 8, 16, 24 sau 32 de biți.
La redare datele conținute în fișierul WAV trebuie să fie redate cu aceeași rată cu care au fost înregistrate, pentru a produce un playback inteligibil. Să presupunem că avem un fișier WAV care a fost înregistrat cu o rata de 44100Hz și 8 biți per eșantion, MONO. Pentru a-l reda corect trebuie să avem un timer care să genereze o întrerupere cu frecvența de 44100Hz. În fiecare întrerupere trebuie să citim un sample din fișier (un octet) și să-l punem pe ieșirea unui al doilea timer care a fost configurat în modul PWM cu rezoluția de 8 biti.
music
de pe cardul SD pe prima linie la LCD-ului, la apăsarea simultană a butoanelor PB2 și BTN1 (pinul PA5).mounting…
mai mult de 3-4 secunde, încercați sa resetați placa.pf_opendir
pentru a deschide un director pentru iterație.pf_readdir
pentru a itera prin conținutul directorului.pf_readdir
nu returnează un cod de eroare când ajunge la sfârșitul directorului, ci returnează un fișier cu nume vid (stringul ””
).FILINFO
se găsește în fișierul pff.h
la liniile 96-102..WAV
) din directorul music
la apăsarea butonului PD6. Afișați numele fișierului redat pe prima linie a LCD-ului.~
ca ↵
.play
, care primește ca parametru calea absolută a fișierului.play
este blocantă. Pentru oprire apăsați simultan PB2 și PD6..WAV
) din directorul music
. Butonul PB2 va cicla prin fișierele audio, afișând numele fișierului selectat pe prima linie a LCD-ului. Butonul PD6 va începe redarea fișierului selectat.get_music
.AM_DIR
și AM_HID
setați în câmpul fattrib
.SD_init
, SD_receive
și SD_transmit
au fost implementate în fișierul sd.o
. Va trebui să ștergeți sd.o
și să implementați cele 3 funcții în fișierul sd.c
. Modificați Makefile-ul în concordanță, prin ștergerea lui sd.o
din comanda de compilare și adăugarea lui sd.c
în lista de dependențe.SPI_init
și SPI_exchange
din spi.h
.SPI_init
și SPI_exchange
au fost implementate în fișierul spi.o
. Va trebui să ștergeți spi.o
și să implementați cele 2 funcții în fișierul spi.c
. Modificați Makefile-ul în concordanță, prin ștergerea lui spi.o
din comanda de compilare și adăugarea lui spi.c
în lista de dependențe.speech
conține fișiere care redau unele numere și denumiri. Revedeți exercițiul 1.say_number
care redă corect un număr între 0 și 59.say_time
care redă ora completă primită ca parametru.