User Tools

Site Tools


Sidebar

Laboratorul 0: Aplicații introductive

Capitole utile din Datasheet ATmega324

  • 1. Pin Configurations - pag. 2
  • 14. I/O-Ports - pag. 72

0. Structura laboratorului

1. Ce este un microcontroller?

Un microcontroller(µC) este o componentă electronică care integrează un microprocesor şi dispozitive periferice, punându-se accent pe un cost de producție mic și un consum energetic redus, altfel spus pe optimizarea aplicației. Principala diferenţă dintre un microcontroller (µC) şi un microprocesor (µP) o constituie faptul că un µC integrează atât unitatea de procesare cât și memorie de program, memorie de date, interfeţe de intrare-ieşire și periferice.

Un µC operează la frecvenţe reduse, în general la zeci sau sute de MHz. Cu toate acestea, microcontroller-ele sunt utilizate într-o gamă largă de aplicaţii în diverse domenii. Dispozitivele unde µC sunt utilizate pot fi produse de uz general (mașini de spălat, frigidere, automate de cafea), sisteme industriale dedicate (relee de protecție, controllere industriale, sisteme de tip interfață om-mașină - HMI ), sisteme din industria aerospaţială și multe altele.

În funcție de tipul aplicațiilor pentru care sunt dedicate acestea, majoritatea µC nu au o magistrală externă de adrese sau date deoarece toate memoriile utilizate (volatile și nevolatile) sunt interne, ducând la integrarea acestora în capsule cu un număr mic de pini şi reduse ca dimensiuni, ceea ce reduce costurile de producţie și consumul energetic. Există desigur și microcontrollere care furnizează magistrale externe pentru date și adrese (multiplexate în timp sau magistrale dedicate) în scopul interfațării memoriilor externe (RAM, FLASH, ROM).

Cele mai întâlnite structuri din circuitul integrat al unui µC sunt următoarele:

  • Unitatea centrală de procesare (µP core) cu o arhitectură care poate fi pe 8, 16, 32 sau 64 de biţi.
  • Memorie de date volatilă (RAM) și/sau non-volatilă (Flash sau EEPROM)
  • Memorie de program non-volatilă (Flash sau EEPROM)
  • Porturi digitale de intrare-ieşire de uz general (GPIO - General Purpose Input Output)
  • Interfeţe seriale de comunicație (USART, SPI, I2C, PCM, USB, SDIO etc.)
  • Interfețe ethernet
  • Interfețe pentru afișaj grafic (LVDS, HDMI sau alte protocoale dedicate controlului afișajelor LCD)
  • Timere (cu rol intern, sau utilizate în generarea semnalelor periodice -ex: PWM-, sau ca watchdog)
  • Convertoare analog-digitale și digital-analogice, front-end-uri analogice și alte circuite dedicate semnalelor analogice
  • Sursă de tensiune integrată
  • Interfață pentru programare şi debugging

Un periferic reprezintă orice dispozitiv, intern sau extern, care se conectează la un sistem de calcul și îi extinde funcționalitatea de bază. În cazul microcontroller-ului, există o serie de astfel de periferice incluse direct în circuitul integrat (cele menționate mai sus). Deși nu seamănă cu perifericele unui PC (monitor, placă grafică, imprimantă, tastatură, mouse, etc.), fără ele microcontroller-ul nu ar putea interacționa cu mediul exterior. Mai mult, perifericele ne ajută să conectăm alte elemente mai performante la controller si sa îi putem oferi funcționalități similare unui sistem PC (conexiune la internet, linie de date USB, display grafic etc.)

Costul unui µC depinde în mare măsură de numărul de periferice integrate, frecvența de lucru a procesorului, cantitatea de memorie și alți parametri amintiți mai sus. Cu cât caracteristicile acestuia sunt mai avansate, cu atât crește prețul de producție. Arhitecturile de µC pe piaţă la ora actuală variază în limite largi, de la chip-uri cu doar 2 pini de I/O până la procesoare digitale de semnal (DSP) sau procesoare cu arhitecturi avansate pe 32 sau chiar 64 de biţi (ex: ARM, PIC32, STM32 sau AVR32).

În concluzie, un microcontroller este un circuit integrat ce reunește o unitate de procesare (CPU), memorii(volatile -RAM, nevolatile EEPROM, Flash, ROM) și diverse periferice ce îi permite acestuia să comunice cu mediul extern. Microcontrollerele sunt utilizate în diverse aplicații, din diverse domenii, de la aplicații casnice și industriale până la aviație și sateliți. Există o gamă largă de microcontrollere disponibile și acestea se aleg în funcție de aplicație, având în vedere mai ales optimizarea costului și a consumului energetic pentru dispozitivul unde µC urmează a fi folosit.

Ce pot face cu un microcontroller?

Alte exemple pot fi găsite în întreg Internetul, și lista poate continua. https://www.hackster.io/projects/tags/microcontroller

2. Atmel AVR

Famila AVR de la Atmel este formată din microcontroller-e cu arhitectură Harvard pe 8 biţi şi set redus de instrucţiuni (RISC). Arhitectura de bază AVR a fost concepută de doi studenţi de la Norwegian Institute of Technology (NTH), Alf-Egil Bogen şi Vegard Wollan. Ele au fost introduse pe piaţă în 1996, fiind printre primele controller-e care foloseau memoria Flash pentru program în locul memoriilor OTPROM sau EPROM, folosite de competiţie.

AVR-urile sunt clasificate în patru mari categorii:

tinyAVR megaAVR XMEGA Application Specific AVR
  • 1-8 KB memorie de program
  • capsulă de 8 până la 32 pini
  • set limitat de periferice
  • 4-256 KB memorie de program
  • capsulă de 28 până la 100 de pini
  • set extins de instrucţiuni (instrucţiuni pentru înmulţire şi adresare indirectă)
  • set extins de periferice
  • 16-256 KB memorie de program
  • capsulă de 44 până la 100 de pini
  • interfeţe performante extinse, ca DMA, “Event System”, şi support pentru criptografie
  • set extins se periferice
  • megaAVR cu funcţii speciale, care nu sunt prezente la familia AVR, cum ar fi controller de LCD, controller USB, CAN etc.
  • FPSLIC (Field Programmable System Level Integrated Circuit), un core AVR integrat cu un FPGA.

Memoriile Flash, EEPROM, şi SRAM sunt integrate în acelaşi chip, înlăturând nevoia de memorie externă. Programul este format din instrucţiuni de 16 biţi lungime care sunt stocate în memoria Flash non-volatilă. Mărimea memoriei de program este indicată de numele componentei respective. De exemplu, ATmega128 are 128kB de memorie Flash. Spaţiul de adresa este format din registrele generale, registrele de I/O şi memoria SRAM. Sunt în total 32 de registre generale a câte 8 biţi.

AVR au o unitate de execuţie în bandă de asamblare cu două niveluri, acest lucru permiţând că următoarea instrucţiune să fie adusă din memorie (fetch) în timp ce instrucţiunea curentă este în execuţie. Majoritatea instrucţiunilor se execută într-un singur ciclu de instrucţiune, acest lucru permiţând atingerea unui throughput de 1MIPS pe MHz.

2.1. ATmega324

Fig. 1: Un µC ATmega324 în capsulă PDIP

În cadrul laboratoarelor și a proiectului de PM, veți utiliza ATmega324, un microcontroller pe 8 biți din familia megaAVR. Registrele şi magistrala internă de date sunt pe 8 biţi.

Caracteristici generale

  • 32 KB Flash (determină dimensiunea maximă a programului care poate fi executat)
  • 1 KB EEPROM
  • 2 KB RAM
  • 20 MHz frecvența maximă de lucru
  • Tensiune de alimentare între 1.8V și 5.5 V
  • 6 canale de PWM
  • 8 canale de ADC, cu rezoluție de 10 biți
  • 4 porturi digitale de I/O (GPIO - General Purpose I/O) , fiecare cu 8 pini, în total 32 de pini de I/O
  • 3 timere (două pe 8 biți și unul pe 16 biți)
  • Interfeţe de comunicație seriale: USART, SPI, TWI
  • Interfaţe de programare ISP și debug JTAG

Perifericele interne chip-ului pot fi accesate din exterior prin intermediul pinilor. Capsula microcontroller-ului are 40 de pini (prezentați în figure 2), dintre care 8 sunt pentru alimentare sau funcții auxiliare, iar 32 sunt pentru I/O. GPIO-urile sunt împărțite în patru porturi (A, B, C și D) a câte 8 pini fiecare.

Pentru mai multe detalii, consultați datasheet-ul (documentația tehnică sumarizată) a microcontrollerului.

Fig. 2: Configurarea pinilor pentru ATmega324

3. Dezvoltarea programelor pentru AVR

3.1. Dezvoltare, compilare

Pentru dezvoltarea programelor putem folosi orice editor de text (notepad++, vim, emacs, etc), recomandat fiind Programmer's Notepad. Putem utiliza de asemenea şi IDE-ul Atmel Studio, oferit de Atmel.

În cadrul laboratoarelor vom scrie cod C, nu şi assembly (AVRASM), compilatorul fiind avr-gcc. Acesta poate fi folosit atât pe sisteme Linux/Unix cât şi pe sisteme Windows (WinAVR). WinAVR include şi biblioteca de C, avr-libc, pe sistemele Linux/Unix ea trebuie instalată separat.

Fig. 3: Procesul de compilare cu AVR-GCC

Deși microcontroller-ul nostru este pe 8 biți, în codul C putem folosi variabile întregi pe 16/32/64 de biţi şi chiar și în virgulă mobilă. Compilatorul este cel care se va ocupa de translatarea instrucţiunilor în cod asamblare ce lucrează pe 8 biţi.

3.2. Programarea microcontroller-ului

Pentru programarea unui microcontroller putem folosi diverse metode, cum ar fi programarea pe interfaţă serială, folosirea unui programator ISP (In-System Programming) ce foloseşte interfaţa serială SPI sau folosirea unui bootloader.

În cadrul acestui laborator vom folosi ultima variantă, un bootloader fiind un program încărcat (folosind, spre exemplu, un programator ISP) la sfârşitul memoriei de program a microcontroller-ului. Execuţia codului încărcat astfel va începe din zona de boot. Dezavantajul acestei metode este spaţiul ocupat de bootloader în memoria de program.

Fig. 4: Execuţia programelor folosind un bootloader

Folosirea bootloader-ului presupune îndeplinirea unei condiții la pornirea microcontroller-ului. Pe plăcile de laborator acesta este rulat la fiecare RESET, însă putem pune orice condiție pe care o poate deduce procesorul (spre exemplu, dacă un buton este apăsat sau nu). Unele microcontrollere au o memorie ROM dedicată bootloader-ului, cu care acestea sunt prevăzute din fabrică (ex: STM32).

Pentru a încărca programul pe controller trebuie să folosim un utilitar de pe calculator. Un exemplu de folosire a utilitarului avrdude pe Linux si pe Window găsiți mai jos.

$ avrdude -c arduino -P /dev/ttyUSB0 -b 57600 -p atmega324p -U flash:w:lab0.hex:a

C:/> avrdude -c arduino -P COM12 -b 57600 -p atmega324p -U flash:w:lab0.hex:a

4. Prima aplicație

4.1. Legătura cu mediul exterior: traductoare, senzori și actuatori

Pentru a putea intefața cu mediul exterior, sunt utilizate diferite componente electronice care au rol fie de actuator (modifică starea mediului exterior) sau de traductor/senzor (sunt influențate de mediul exterior și oferă informații microcontroller-ului despre diverși parametri).

Actuatorii sunt acele elemente care influențează mediul exterior. Putem spune că actuatorii sunt elementele ce pot fi comandate și care pot interacționadirect cu mediul pentru a influența diferiți parametri (ventilatoare, indicatoare sonore - buzzere , indicatoare luminoase, rezistențe de încălzire, etc). Uneori pentru a putea activa un actuator, este nevoie de un element de acționare. De exemplu, pentru a putea porni un motor(actuator) prin comanda dată de microcontroller, trebuie să folosim un element de acționare (care pornește ventilatorul) intermediar care să poată comanda curenți mari (prin ventilator) folosind curenți foarte mici de comandă (ieșirea microcontrollerului). În acest exemplu, soluția ar fi utilizarea unui tranzistor.

Traductoarele (sau traductorii) sunt acele elemente care iși modifică proprietățile electrice în funcție de parametrii mediului exterior și oferă microcontroller-ului informații despre mediul exterior. De exemplu: un fotorezistor va avea rezistența electrică mult mai mare în lipsa luminii, iar când aceasta este iluminată va avea o rezistență electrică de zeci sau chiar sute de ori mai mică. Un alt exemplu de traductor este butonul simplu normal deschis: când este apăsat el închide un contact, când nu este apăsat, contactul din interior este deschis. În funcție de tipul traductoarelor, acestea pot avea nevoie de prelucrarea semnalului înainte ca acesta să fie preluat de microcontroller (signal conditioning) - de exemplu fotorezistorul trebuie folosit într-un montaj cu divizor de tensiune sau cu sursă de curent -, sau pot fi conectate direct la microcontroller - de exemplu butonul.

În cadrul acestui laborator veți utiliza LED-uri pe post de indicator luminos de semnalizare a stării unui pin și butoane pentru a transmite informație către microcontroller.

Diode LED

LED-urile - Light Emitting Diode - numite și diode electroluminesciente emit lumină când ele sunt polarizate direct. În funcție de semiconductorii care sunt utilizați în construcție, și de plasticul ce are rol de lentilă de dispersie și filtru optic, LED-urile pot emite lumină în diferite culori. Nu confundați LED-urile cu becurile! În timp ce becurile emit lumina prin încalzirea unui filament de tungsten la temperaturi foarte mari, LED-urile emit lumina trecând un curent printr-un material semiconductor (joncțiune p-n). Spre deosebire de becuri, al căror randament este de sub 5% - majoritatea energiei primite de ele se pierde prin efect caloric -, LED-urile au un randament de transformare a energiei electrice în energie luminoasă mult mai mare.

LED-urile pot fi utilizate pe post de indicator luminos (adesea utilizate în diferite aparate pentru a semnaliza faptul că aparatul este pornit și realizează un anumit lucru), sau pentru iluminare, caz în care sunt utilizate LED-uri de putere.

În cadrul laboratorului LED-urile sunt utilizate pentru a indica starea unui pin.

LED-urile sunt diode, așadar, curentul prin acestea crește exponențial cu creșterea tensiunii aplicate. Pentru a utiliza un LED pentru indicarea stării unui pin (mai degrabă spus pentru a indica prezența de tensiune), curentul prin LED trebuie limitat. Aceasta se poate realiza în cel mai simplu mod prin înserierea unei rezistențe cu LED-ul.

Calculul rezistenței de limitare a curentului:

Un LED este proiectat să opereze la un curent nominal (ex: 10mA). Căderea de tensiune pe LED urile indicatoare, de mică putere, la acest curent, este dată de culoarea LED-ului, puterea acestuia și de temperatură. Ea poate varia de la 1.7V în cazul LED-urilor roșii, 2-2.3V pentru cele Orange, galbene și verzi și ajunge la 3.2-3.5V în cazul LED-urilor albastre și albe.

Schema utilizată este următoarea:

Fig. 5: Schema de lucru și calculul rezistenței de limitare a curentului prin LED

Întrucât microcontroller-ul are ieșirile comandate folosind stâlp Push-Pull CMOS, tensiunea Upin în starea de 1 logic va fi apropiată de tensiunea de alimentare a microcontroller-ului.

Exemplu:Dacă alimentarea microcontroller-ului este de 5V pentru un LED roșu ce dorim să îl folosim la 10mA, specificat de producător cu o cădere de tensiune de 1.7V, trebuie să folosim o rezistență de 330 de ohmi.

4.1.2. Butoanele

Cel mai simplu mod de interacțiune al utilizatorului cu un microcontroller îl constituie folosirea butoanelor. Modul de conectare al unui push-button este dat în figura de mai jos:

Fig. 6: Conectarea unui push-button: a) incorect, cu intrare flotantă, b) corect, cu rezistență de pull-up

figure 6.a) arată un buton conectat la pinul PD0 al µC. La apăsarea butonului, intrarea PD0 va fi legată la GND, deci va fi în starea logică “0”. Acest mod de legare este incorect deoarece atunci când butonul nu este apăsat intrarea se află într-o stare nedefinită (ca și cum ar fi lăsată în aer), ea nefiind conectată nici la GND, nici la Vcc. Această stare se numește stare de impedanță mărită și nu poate fi citită de către circuitele interne ale µC deoarece un bit dintr-un registru poate să ia doar valorile 0 sau 1. În practică, citirea unei intrări în stare de impedanță mărită va produce un rezultat de 1 sau 0 în funcție de condițiile de mediu. Spre exemplu, dacă apropiem degetul de acea intrare, citirea va fi 1, iar, daca îndepărtăm degetul, citirea va fi 0.

figure 6.b) arată modul corect de conectare al butonului, folosind o rezistență de pull-up între pinul de intrare și Vcc. Această rezistență are rolul de a aduce intrarea în starea “1” logic atunci când butonul este liber prin “ridicarea” potențialului liniei la Vcc. Alternativ, se poate folosi o rezistență de pull-down (conectată la GND), caz în care intrarea este ținută in starea logică “0” cât timpul butonul nu este apăsat.

Pentru a economisi spațiu exterior, în µC ATmega324, aceste rezistențe au fost incluse în interiorul circuitului integrat. Inițial ele sunt dezactivate iar activarea acestora se poate face prin software scriind o valoare în registrul de ieșire (PORTn) al unui port care a fost configurat drept intrare. Bitul PUD din registrul SFIOR este setat inițial pe 0, adică atunci când se scrie în PORTn se activează rezistențele de pull-up.

4.1.3. Avertizorul/indicatorul sonor

Un indicator sonor de tip buzzer/speakereste un dispozitiv electronic, capabil să producă sunet. Acesta poate fi mecanic, electromecanic sau piezoelectric. Se folosesc de regulă în sisteme de alarmă, ceasuri sau ca feedback sonor pentru anumite acțiuni.

Buzzerele sunt indicatoare sonore care folosesc semnale electric periodic pentru a genera sunetul. Buzzerele pasive sunt buzzerele care trebuie alimentate folosind impulsuri/semnal variabil. Există și buzzere, denumite active, care conțin circuitul oscilator, și pot fi alimentate în curent continuu. Acestea genereaza un sunet cât timp primesc alimentare.

Buzzerul utilizat în cadrul laboratorului este un buzzer pasiv, care este cuplat în alternativ prin înserierea cu o capacitate. Acest buzzer, pentru a funcționa trebuie să primească un semnal electric variabil, sunetul fiind emis în frecvența semnalului cu care acesta este atacat.

Pentru a genera un astfel de semnal trebuie urmați următorii pași:

  • setăm pinul ca fiind pin de ieșire
  • setăm pinul pe valoarea high
  • așteptăm jumătate din perioadă
  • setăm pinul pe valoarea low
  • așteptăm cealaltă jumătate din perioadă.

În funcție de durata perioadei, putem folosi macro-urile definite în biblioteca util/delay.h: _delay_ms sau _delay_us.

Fig. 7: Connectarea unui speaker

4.2. Lucrul cu registrele I/O

Microntroller-ul ATmega324 oferă 4 porturi I/O a câte 8 pini, iar intern, fiecare port are asociat trei registre a câte 8 biți prin care utilizatorul poate controla la nivel de pin fluxul datelor: poate scrie/citi date în/din portul respectiv. Aceste trei registre sunt:

  • DDRn - Data Direction Register
    • stabilește direcţia pinilor portului
    • dacă bitul x are valoarea 0 atunci pinul x este de intrare
    • dacă bitul x are valoarea 1 atunci pinul x este de ieșire
  • PORTn - Data Register
    • stabilește valorile de ieşire ale pinilor sau activează/dezactivează rezistenţele de pull-up
    • dacă bitul x are valoarea 0 atunci
      • dacă pinul x este de ieșire el va avea valoarea LOW
      • daca pinul x este de intrare rezistența de pull-up va fi dezactivată
    • data bitul x are valoarea 1 atunci
      • daca pinul x este de ieșire el va avea valoarea HIGH
      • dacă pinul x este de intrare rezistența de pull-up va fi activată
  • PINn - Input Pins Address
    • putem citi date de pe portul respectiv
    • dacă pinul x are valoarea LOW atunci bitul x va avea valoarea 0
    • daca pinul x are valoarea HIGH atunci bitul x va avea valoarea 1

n poate să fie A, B, C sau D în funcţie de portul selectat. x poate să fie între 0 și 7.

Descrierea detaliată a porturilor şi registrelor corespunzătoare acestora se găseşte în datasheet-ul ATmega324, în capitolul I/O Ports.

Registrele DDRn, PORTn, PINn, precum şi registrele perifericelor nu fac parte din cele 32 de registre de uz general. Ele nu sunt folosite pentru stocarea datelor, ci au ca rol comunicarea cu perifericul corespunzător. Pentru a face accesul la acestea mai simplu, ele sunt mapate în memorie. Astfel, pentru a scrie o valoare într-un registru este necesar să se scrie valoarea la adresa de memorie corespunzătoare registrului. Adresele lor se pot vedea în datasheet, în capitolul Register Summary, iar în avr-libc sunt definite macro-uri pentru fiecare dintre aceste adrese.

Pentru a scrie o valoare într-un registru se poate face direct atribuirea unei valori, însă acest mod nu face codul foarte ușor de înteles. Recomandarea noastră este de a folosi măștile pe biți ((1 << x)) sau macro-ul _BV(x). De asemenea recomandăm folosirea numelui pinului în loc de indexul acestuia, pentru că numele acestora sunt sugestive pentru funcția pe care o îndeplinesc (spre exemplu, în registrul ADCSRA al convertorului analog-digital, bitul ADEN este bitul de enable). Numele pinilor, ca și în cazul registrelor, sunt deja definite în avr-libc.

Operație Formă
Scriere bit pe 1
 register |= (1 << bit_index) 
Scriere bit pe 0
 register &= ~(1 << bit_index) 
Toggle bit
 register ^= (1 << bit_index) 
Citire bit
 register & (1 << bit_index) 
Tab. 1: Lucrul cu registre


DDRB = 8;               // AȘA NU.
DDRB = (1 << 3);        // Nici așa, este hardcodat indexul pinului.
DDRB |= (1 << PB3);     // AȘA DA.
DDRB |= _BV(PB3);       // AȘA DA.

Detalii despre lucru cu biții din registre puteți citi și la secțiunea tutoriale de pe wiki.

4.2.1. Exemplu de lucru cu ieșiri

Să presupunem că avem un LED legat la pinul 1 al portului B (numit PORTB1 sau PB1), asemănător cu figure ##. Pentru a aprinde sau stinge LED-ul trebuie să urmăm următorii pași:

  • Pinul PB1 trebuie configurat ca ieșire
    • Bitul 1 (PB1) din registrul DDRB va fi 1
  • Pentru a aprinde LED-ul trebuie ca pinul PB1 sa ia valoarea HIGH
    • Bitul 1 (PB1) din registrul PORTB va fi 1
  • Pentru a stinge LED-ul trebuie ca pinul PB1 sa ia valoarea LOW
    • Bitul 1 (PB1) din registrul PORTB va fi 0

4.2.2. Exemplu de lucru cu intrări

Să presupunem ca avem un buton legat la pinul 4 al portului D (numit PORTD4 sau PD4), asemănător cu figure 6.b). Pentru a determina starea butonului (apăsat sau liber) trebuie să urmăm următorii pași:

  • Pinul PD4 trebuie configurat ca intrare
    • Bitul 4 (PD4) din registrul DDRD va fi 0
  • Pentru a determina starea de apăsare a butonului trebuie să citim valoarea pinului la care este atașat. Acesta va fi 1 atunci când butonul este liber și 0 atunci când butonul este apăsat
    • Citim valoarea bitului 4 (PD4) din registrul PIND

4.3. Hello World

Vom scrie un program care stinge și aprinde un LED la intervale de 500 ms. Acest lucru se face modificând tensiunea unuia dintre pinii microcontroller-ului, în cazul nostru pinul 0 din portul C (PC0).

lab0.c
#include <avr/io.h>
#include <util/delay.h>
 
int main() {
	/* Setăm pinul 0 al portului C ca pin de ieșire. */
	DDRC |= (1 << PC0);
 
	while(1) {
                /* Inversăm starea pinului. */
		PORTC ^= (1 << PC0);
 
                _delay_ms(500);
	}
 
	return 0;
}

Pentru compilare folosim următorul Makefile:

Makefile
all: lab0.hex
 
lab0.hex: lab0.elf
	avr-objcopy  -j .text -j .data -O ihex $^ $@
	avr-size $@
 
lab0.elf: lab0.c	
	avr-g++ -mmcu=atmega324p -DF_CPU=16000000 -Os -Wall -o $@ $^
 
clean:
	rm -rf lab0.elf lab0.hex

Compilatorul folosit este avr-gcc. Flag-urile au următoarea semnificație:

  • -mmcu: Informează compilatorul despre tipul microcontroller-ului pentru care trebuie să genereze codul
  • -DF_CPU=16000000: Definește macro-ul F_CPU care indică frecvența de lucru la microcontroller-ului
  • -Os: Optimizează programul în privința memoriei ocupate (destul de folositor având în vedere memoria limitată prezentă pe chip)
  • -Wall: Activează toate mesajele de avertisment

Fișierul care poate fi programat pe plăcuță trebuie să fie în formatul ihex. De aceea este necesară extragerea secțiunilor de date și cod din fișierul elf obținut în urma compilării integrarea lor într-un fișier ihex. Acest pas folosește utilitarul avr-objcopy.

Comanda avr-size arată câtă memorie ocupă diverse secțiuni de cod ale programului. Trebuie avut în vedere că în memoria SRAM de date, în afară de secțiunea .data, o să fie pusă și stiva. Pentru a putea rula programul pe microcontroller-ul ATmega324 trebuie că secțiunea .text să fie <32 KB și secțiunea .data să fie <2 KB.

Pentru a compila programul de mai sus cu Programmer's Notepad se poate folosi shortcut-ul setat pentru comandă de make. Rezultatul compilării este fișierul lab0.hex care urmează a fi încărcat în microcontroller.

5. Exerciții

Capitole utile din Datasheet ATmega324

  • 1. Pin Configurations - pag. 2
  • 14. I/O-Ports - pag. 72

Task 0 (2p). Rulați exemplul din scheletul de laborator.

Task 1 (4p). Implementați, folosind cele 8 LED-uri de pe placa de laborator, o secvență simplă și o secvență avansată de aprindere dintre cele de mai jos. Selecția secvenței se face prin intermediul a două butoane conectate la uC pe pinii PB2 și PD6 (PB2 selectează secvența simplă, PD6 selectează secvența avansată).

       Simplu1          Simplu2          Simplu3 
  t00  *-------         *-------         *------*
  t01  -*------         **------         -*----*-
  t02  --*-----         ***-----         --*--*--
  t03  ---*----         ****----         ---**---
  t04  ----*---         *****---         ---**---
  t05  -----*--         ******--         --*--*--
  t06  ------*-         *******-         -*----*-
  t07  -------*         ********         *------*
       mergi la t00     mergi la t00     mergi la t00
  
  
       Avansat1         Avansat2         Avansat3
  t00  *-------         *-------         *-------
  t01  -*------         **------         -*------
  t02  --*-----         ***-----         --*-----
  t03  ---*----         ****----         *--*----
  t04  ----*---         *****---         -*--*---
  t05  -----*--         ******--         --*--*--
  t06  ------*-         *******-         *--*--*-
  t07  -------*         ********         -*--*--*
  t08  ------*-         -*******         --*--*--
  t09  -----*--         --******         mergi la t06
  t10  ----*---         ---*****         
  t11  ---*----         ----****         
  t12  --*-----         -----***         
  t13  -*------         ------**         
  t14  *-------         -------*      
       mergi la t00     mergi la t00                  
  

Task 2 (4p). Implemenţati codul Morse folosind speaker-ul. Veţi implementa 2 tonuri, unul scurt cu durata de 50 ms şi unul lung cu durata de 150 ms, cu pauză de 50 ms între 2 tonuri consecutive. Selecţia între tonuri se va face folosind cele 2 butoane de pe PB2 (produce un ton scurt) şi PD6 (produce un ton lung).

Task 3 (Bonus 2p). Scrieți un program care produce 8 note diferite folosind speaker-ul. Inițial speaker-ul va fi oprit. La apăsarea butonului de pe PD6 programul va trece la nota superioară. Dacă era la cea mai înaltă notă (a 8-a), programul nu va face nimic. La apăsarea butonului de pe PB2 programul va trece la nota inferioară. Dacă era la cea mai joasă notă notă (prima), programul va opri speaker-ul. Nota curentă va fi afișată prin intermediul celor 8 LED-uri, aprinzând LED-ul corespunzător notei produse (LED-ul 1 va fi aprins pentru nota 1, LED-ul 2 pentru nota 2, etc. Când speaker-ul este oprit niciun LED nu va fi aprins). Mai jos aveți funcționarea programului exemplificată în pseudocod.

frecventa_nota[8] = {262, 294, 320, 349, 392, 440, 494, 523};
 
init:
    nota_curenta = -1
    butoane.init()
    speaker.init()
    led.init()
 
bucla_principala:
    if (butoane.apasat(PD6) && nota_curenta < 7)
        ++nota_curenta;
 
    if (butoane.apasat(PB2) && nota_curenta > -1)
        --nota_curenta;
 
    if (nota_curenta != -1)
        speaker.reda(nota_curenta)
        led.aprinde(nota_curenta)
    else
        speaker.opreste()
        led.opreste()
 
    goto bucla_principala

6. Resurse

lab/lab0.txt · Last modified: 2019/04/06 16:55 by andrei [dot] voinescu [at] upb [dot] ro