În cadrul acestui laborator, vom aborda protocolul de comunicație SPI, utilizat în lumea embedded pentru transmisia datelor între 2 sau mai multe dispozitive. Scopul nostru final va fi cel de a construi pe plăcuța de laborator un player audio care să citească fișiere .wav de pe un card SD și să le redea utilizând speaker-ul integrat.
SPI este un standard sincron dezvoltat de Motorola. Acesta funcționează în modul full-duplex (transferul de date are loc în ambele direcții simultan).
Dispozitivele comunică utilizând o arhitectură Master-Slave (este permis un singur dispozitiv Master și unul sau mai multe dispozitive Slave). Dispozitivul Master este cel care inițiază comunicarea.
SPI mai este numit și four wire, deoarece în cadrul acestui protocol se transmit semnale pe 4 linii independente. Cele patru semnale utilizate sunt următoarele:
Putem vedea că există două semnale utilizate pentru transmiterea datelor: MOSI și MISO. Transmiterea datelor pe SPI se face sincron, folosind semnalul de ceas SCLK. Semnalul CS / SS este utilizat pentru a selecta dispozitivul Slave cu care dorim să comunicăm la un moment dat.
Mai multe dispozitive Slave pot fi conectate în același timp la un singur dispozitiv Master. Semnalele MOSI, MISO și SCLK sunt partajate. Pentru fiecare dispozitiv Slave, dispozitivul Master are câte un semnal CS / SS. Când Masterul dorește să comunice cu un Slave, dispozitivul Master setează pe LOW semnalul CS / SS care duce către dispozitivul Slave dorit (celelalte semnale CS / SS sunt puse pe HIGH). Astfel, dispozitivul Slave cu CS / SS pe LOW știe că Master-ul comunică cu el.
Pentru a începe comunicarea, dispozitivul Master trebuie să se asigure că ceasul său este setat la o frecvență cel mult egală cu frecvența suportată de dispozitivul Slave (de obicei, până la câțiva MHz). Apoi, Master-ul selectează dispozitivul Slave dorit, punând 0 pe linia CS / SS asociată acestuia.
În timpul unui ciclu SPI, transmisia este full-duplex:
În general, comunicarea SPI implică doi regiștrii de shiftare (unul în Master și unul în Slave), conectați circular, la fel ca în figura de mai jos.
De obicei, primul bit mutat pe MISO / MOSI este cel mai semnificativ bit, în timp ce un bit nou este adăugat la poziția cea mai puțin semnificativă din registru. După ce întregul cuvânt a fost trimis, prin shiftare, Master-ul și Slave-ul au schimbat integral valorile din cei doi regiștrii de shiftare. Dacă mai există date de transmis, procesul începe din nou. Când nu mai există date de transmis, dispozitivul Master întrerupe generarea semnalului de ceas (SCLK) și plasează semnalul CS / SS asociat cu Slave-ul pe HIGH (1 logic).
Pe tot parcursul procesului descris mai sus, dispozitivele Slave care nu au fost selectate de semnalul SS asociat vor ignora semnalele de pe SCLK și MOSI și nu vor genera nimic pe MISO. Master-ul poate selecta un singur Slave la un moment dat.
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 ne-am obișnuit, pentru configurarea și utilizarea interfeței vom folosi regiștrii: de control, de stare și de date.
Descrierea completă a regiștrilor asociați interfeței SPI o găsiți în datasheet, capitolul SPI – Serial Peripheral Interface (pagina 214). În continuare, vom face o scurtă trecere în revistă a regiștrilor și a câmpurilor relevante din aceștia.
Acesta este registrul din care citim datele pe care le-am primit / în care scriem datele pe care vrem să le transmitem. Scrierile în acest registru inițiază o nouă transmisie atunci când interfața funcționează în module Master. Datele citite din acestu registru vin din registrul de shiftare.
Plăcuța de laborator este dotată cu un cititor de carduri SD care poate comunica cu microprocesorul nostru prin protocolul SPI, acesta fiind și usecase-ul la care vom lucra astăzi.
Figura de mai jos ilustrează modul de conectare al cititorului SD la microprocesor. După cum se poate observa, pentru a seta semnalele descrise în secțiunea 2 a laboratorului, ne putem folosi de o parte din pinii cu care am lucrat și în laboratoarele trecute.
Cardurile SD cu care vom lucra astăzi sunt formatate cu sistemul de fișiere FAT32. Pentru a ușura lucrul cu acest sistem de fișiere, ne vom baza pe biblioteca Petit FAT Filesystem, atât datorită dimensiunii mici (2-4KB), cât și faptului că folosește un minim de memorie RAM (46 octeți plus stivă).
API-ul expus de bibliotecă oferă 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
pf_write(NULL, 0, &w)
ca să finalizezi scrierea (flush).
Dacă nu faci acest pas, citirile ulterioare pot eșua cu o eroare de tip disk error.
1. SPI Control (2p total)
Implementați funcțiile de bază care asigură comunicarea microcontroller-ului prin SPI: SPI_init() și SPI_exchange() din fișierul spi.c. Urmați comentariile TODO1 și țineți cont de următoarele:
CHECKPOINT (1p): SPI_exchange() trebuie să trimită și să primească date corect.
2. SD Card Control (2p total)
Implementați funcțiile SD_init(), SD_receive() și SD_transmit() din sd.c. Urmați TODO2.
CHECKPOINT (1p): LCD ar trebui să afișeze 'Mounted!' după câteva secunde.
3. Citirea temperaturii (2p)
Configurați ADC-ul și citiți temperatura de la un senzor analog. Implementați adc_init() și adc_read().
4. Logarea temperaturii pe cardul SD (2p)
5. Navigarea prin loguri (2p total)
Implementați next_log_entry() pentru a citi fișierul log.csv linie cu linie.
CHECKPOINT (1p): Trebuie să puteți naviga prin toate logurile.