Procedura de programare a microcontrolerelor avr. Programare microcontrolere avr

Bună ziua, dragi radioamatori!
Bine ați venit pe site-ul „“

Ce este un microcontroler și pentru ce este folosit? Să ne uităm la definiția sa:

- un microcircuit conceput pentru a controla dispozitive electronice, sau cu alte cuvinte, un simplu computer (micro-computer) capabil să realizeze sarcini simple.

Adică, în esență, un microcontroler este un dispozitiv care ne permite să ne aducem ideile (chiar și pe cele nebunești) la viață, dar, firesc, în limitele capacităților sale. Și cel mai important, aducerea la viață a unei idei se realizează nu prin crearea unor structuri electronice sofisticate, ci doar, practic, prin puterea gândurilor noastre (vrei să devii vrăjitor?).
Cele mai populare printre radioamatorii sunt două tipuri de microcontrolere:
PIC– Tehnologia Microcipului
AVR- Atmel

Aș dori să fac imediat o mică digresiune și să clarific una dintre pozițiile mele. Nu am de gând, nici acum, nici mai târziu, să vorbesc despre meritele cutare sau cutare tip de microcontroler, cutare sau cutare software și, în general, tot ce ține de microcontrolere, să sfătuiesc ceva și cu atât mai mult să-l impun cititorilor. Totul este o chestiune de gust, preferințe personale și obiectivele finale în studierea microcontrolerelor. Ei bine, din moment ce „immensul nu poate fi înțeles”, îmi voi conduce toată povestea ulterioară în legătură cu microcontrolerele AVR și cu programul nu foarte comun, dar preferatul meu, „Algorithm Builder”. U tipuri diferite microcontrolere, programe, există, desigur, diferențe, dar au și multe în comun. Și vom explora lumea microcontrolerelor în așa fel încât mai târziu, cunoștințele dobândite să poată fi aplicate cu ușurință la PIC-uri și la orice software. Și permiteți-mi să vă reamintesc încă o dată că această serie de articole este încercarea mea de a-i ajuta pe cei care au auzit pentru prima dată despre existența microcontrolerelor și vor să învețe cum să lucreze cu ele.

Ce trebuie să înveți să lucrezi cu microcontrolere? Aș evidenția câteva, în opinia mea, condiții principale:
1. Dorinta si persistenta .
Totul este foarte simplu aici: dacă ai o dorință, totul se va rezolva. Și dorința cu perseverență este în general un lucru super.
2. Cunoașterea designului microcontrolerului.
Cunoașterea profundă nu este importantă aici (și poate nu este deloc necesară), dar este necesar să știți ce este „la bord” microcontrolerului. Doar știind în ce constă un microcontroler, ce dispozitive conține, capacitățile lor, cum funcționează - doar atunci vom putea folosi capacitățile microcontrolerului la maximum.
3. Cunoașterea limbajului de programare și a comenzilor de control al microcontrollerului.
Modul în care va funcționa microcontrolerul, ce sarcini îi atribuiți și modul în care le va îndeplini, este determinat de programul încorporat în el - un program pe care dvs. îl creați pentru microcontroler. Și în acest moment ne vom opri în detaliu pentru a lua în considerare problemele care pot apărea în viitor.

Program(tradus acest cuvânt înseamnă „prescripție”) – descriere preliminară evenimente sau acțiuni viitoare.

De exemplu, dorim ca microcontrolerul să clipească un LED. O sarcină simplă, dar totuși, pentru ca microcontrolerul să finalizeze această sarcină, trebuie mai întâi, pas cu pas, să descriem toate acțiunile microcontrolerului, să scriem un program pe care trebuie să-l execute pentru a obține rezultatul de care avem nevoie - un LED care clipește . Ceva de genul:
♦ Aprinde LED-ul:
- configurați ieșirea la care este conectat LED-ul să funcționeze ca ieșire de informații
- aplicați un nivel logic acestui pin, care va aprinde LED-ul
♦ Așteptați puțin:
- mergeți la subrutina care formează pauza (care trebuie și „mestecată”)
- după finalizarea subrutinei de pauză, reveniți la programul principal
♦ Opriți LED-ul:
- aplicați un nivel logic la ieșire, stingând LED-ul
și așa mai departe.
Cu termenul Program un alt termen este indisolubil legat - Algoritm(ca Lupul și Iepurele, Tom și Jerry).

Algoritm- un set de instrucțiuni care descriu procedura pentru a obține rezultatul dorit.

Daca in program detaliem prescriu acțiuni microcontroler, apoi în algoritmul noi determina cursul acțiunii microcontroler, pe baza căruia vom crea apoi un program. Similar cu exemplul de mai sus:
♦ Aprinde LED-ul
♦ Așteptați puțin
♦ Opriți LED-ul
și așa mai departe.
Prin urmare, un algoritm este un predecesor al unui program. Și cu cât algoritmul este creat mai atent și atent, cu atât va fi mai ușor să creați un program.

În total, un program pentru un microcontroler este o secvență de acțiuni ale microcontrolerului sub forma unui set de comenzi și instrucțiuni pe care trebuie să le execute pentru a ne atinge obiectivele.

Comenzile pentru microcontroler arată ca un set de unu și zero:
00110101 011000100
așa-zisul - coduri de comandă, iar codurile de comandă sunt limbajul pe care microcontrolerul îl înțelege. Și pentru a traduce algoritmul nostru din rusă în limba microcontrolerului - chiar în aceste seturi de zerouri și unități, există programe speciale.
Aceste programe vă permit să descrieți procedura de operare pentru microcontroler într-un limbaj mai mult sau mai puțin înțeles pentru noi și apoi să traduceți această ordine într-un limbaj pe care microcontrolerul poate înțelege, rezultând așa-numitul Codul mașinii– o secvență de comenzi și instrucțiuni (aceleași zerouri și unități) pe care doar microcontrolerul le înțelege. Este apelat textul programului scris de programator cod sursa. Programul este tradus din limbajul de programare (codul sursă) în limbajul microcontrolerului (codul mașină) radiodifuzorii. Traducătorul transformă textul programului în coduri de mașină, care sunt apoi scrise în memoria microcontrolerului.
În astfel de programe, procedura de operare a microcontrolerului este descrisă într-un limbaj special - un limbaj de programare. Un limbaj de programare este diferit de limbajul nostru uman. Dacă limbajul nostru de comunicare servește în principal la schimbul de informații, atunci:

Limbaj de programare - aceasta este o modalitate de transmitere a comenzilor, instrucțiunilor, instrucțiunilor clare de acțiune pentru microcontroler.

Există multe limbaje de programare și pot fi împărțite în două tipuri:
limbaje de programare nivel scăzut
limbaje de programare nivel inalt
Care este diferența. Și diferă prin apropierea de microcontroler.
În zorii tehnologiei microprocesoarelor, programele erau scrise în codul mașinii, adică întreg algoritmul de operare a fost scris secvențial sub formă de zerouri și unu. Cam asa a aratat programul:

01000110
10010011
01010010

Este puțin probabil ca cineva să poată da seama de un astfel de set de combinații de două cifre, iar munca primilor programatori a fost foarte laborioasă. Pentru a le ușura viața, programatorii au început să creeze primele limbaje de programare. Deci, cu cât un limbaj de programare este mai aproape de un astfel de set de zerouri și unu, cu atât este mai „nivel scăzut” și cu cât este mai departe de ele, cu atât este mai „nivel înalt”.
Cele mai comune limbaje de programare pentru microcontrolere:
- limbaj de nivel scăzut - Asamblator
– limbaj de nivel înalt – C (Si)
Să ne uităm la un exemplu al diferențelor lor (aceste exemple sunt abstracte).
Să presupunem că trebuie să adunăm două numere: 25 și 35.
În codul mașinii, această comandă ar putea arăta astfel:
00000101 1101001
În limbaj de nivel scăzut:
ADD Rd, Rr
În limbaj de nivel înalt:
25+35
Diferențele dintre limbile de nivel scăzut și înalt sunt vizibile cu ochiul liber; comentariile, după cum se spune, sunt inutile.
Dar să pătrundem mai adânc în aceste exemple. Nu vom analiza exemplul de cod mașină, deoarece este identic cu exemplul din Asamblare. În esență, instrucțiunile de asamblare sunt aceleași coduri de mașină (comenzi) cărora li se atribuie abrevieri alfabetice, pentru a nu se pierde în zerouri și unu. Cu comanda de asamblare ADD Rd, Rr, setăm microcontrolerului sarcina de a adăuga două numere care se găsesc (și pentru aceasta trebuie mai întâi să le scriem acolo) - primul în Rd, al doilea în Rr și plasăm rezultatul adaos în Rd. După cum puteți vedea, am stabilit o sarcină foarte specifică pentru microcontroler: de unde să-l obțineți, ce să faceți cu el și unde să puneți rezultatul. În acest caz, lucrăm direct cu microcontrolerul.
Comandă într-un limbaj de nivel înalt: 25+35, o notație matematică familiară nouă, plăcută ochilor noștri. Dar în acest caz, nu lucrăm direct cu microcontrolerul, pur și simplu îi dăm sarcina de a adăuga două numere. Rezultatul și succesiunea acțiunilor în în acest caz, va fi la fel ca la executarea unei comenzi de asamblare: mai întâi aceste două numere vor fi scrise undeva, apoi adăugate și rezultatul va fi plasat undeva.
Și aici constă principala diferență dintre limbile de nivel înalt și de nivel scăzut. Dacă în Assembly controlăm întregul proces (fie că ne place sau nu): știm unde sunt scrise aceste două numere și știm unde va fi rezultatul, atunci într-un limbaj de nivel înalt nu controlăm procesul. Programul însuși decide unde să scrie în prealabil numerele și unde să plaseze rezultatul. În cele mai multe cazuri, nu trebuie să știm acest lucru, deoarece pentru noi rezultatul principal este numărul 60 la ieșire. Drept urmare, programele în limbaje de nivel înalt sunt mai lizibile, plăcute ochiului și mai mici ca dimensiune - pentru că nu trebuie să „intrim în toate găurile” și să scriem fiecare pas al microcontrolerului; programul apoi face acest lucru pentru noi când îl compilează - îl traduce în coduri de mașină. Dar există și un minus. Doi algoritmi identici scrisi in Assembly si C, dupa convertirea lor in coduri de masina, vor avea dimensiuni diferite: un program scris in Assembly va fi cu 20-40% mai scurt decat un program scris in C - cine stie ce cale parcurge C pentru a obtine rezultatul avem nevoie. Și există cazuri când nu există încredere în limbajul de nivel înalt și codul scris în Assembly este inserat într-un program C.
Programatorii profesioniști, de regulă, cunosc mai multe limbaje de programare (sau lucrează într-o echipă care include specialiști în diferite limbi), combinând creativ capacitățile și avantajele lor într-un singur program. Ei bine, noi, amatorii, trebuie să cunoaștem cel puțin o limbă (în primul rând), și trebuie să începem (și sunt ferm convins de asta, și nimeni nu mă va convinge de contrariu) cu un limbaj de nivel scăzut - Adunarea.

Ei bine, cred, și aici totul este clar pentru noi - trebuie să învățăm un limbaj de programare, nu există altă cale.

Comenzi și instrucțiuni pentru controlul microcontrolerului.
Microcontrolerele AVR au peste 130 de comenzi diferite care îi permit să realizeze toate capabilitățile inerente. Dar voi spune imediat că puțini amatori le știu pe toate, cu atât mai puțin le folosesc pe toate. De obicei, în practica amatorilor, cunoașterea a jumătate din comenzi, sau chiar mai puțin, este suficientă. Dar trebuie să studiezi comenzile. Cu cât cunoști mai multe comenzi, cu atât vei obține programe mai sofisticate (în sensul bun al cuvântului) și mai elegante.

Unitate aritmetico-logică și organizare a memoriei - memorie de programe, memorie de date, memorie nevolatilă



În acest curs de instruire avr, am încercat să descriu toate elementele de bază pentru începătorii în programarea microcontrolerelor avr. Toate exemplele sunt construite pe un microcontroler atmega8. Aceasta înseamnă că pentru a repeta toate lecțiile veți avea nevoie de un singur MK. Ca emulator circuite electronice Proteus este folosit - în opinia mea - cea mai bună opțiune pentru incepatori. Programele din toate exemplele sunt scrise în compilatorul C pentru avr CodeVision AVR. De ce nu într-un asambler? Pentru că un începător este deja încărcat cu informații, iar un program care înmulțește două numere necesită aproximativ o sută de linii în asamblator, iar în proiecte complexe, îndrăznețe, folosește C. Compilatorul CodeVision AVR este adaptat pentru microcontrolere atmel, are un generator de cod convenabil, o interfață bună și direct de la Puteți flash microcontrolerul cu el.

Acest curs de formare va explica și arăta cu exemple simple cum să:

  • Începeți să programați microcontrolere, de unde să începeți, de ce aveți nevoie pentru asta.
  • Ce programe să utilizați pentru scrierea firmware-ului pentru avr, pentru simularea și depanarea codului pe un computer,
  • Ce dispozitive periferice sunt în interiorul MK, cum să le controlați folosind programul dvs
  • Cum să scrieți firmware-ul terminat pe un microcontroler și cum să îl depanați
  • Cum se face placă de circuit imprimat pentru dispozitivul dvs
Pentru a face primii pași către programarea MK, veți avea nevoie doar de două programe:
  • Proteus este un program emulator (în el puteți dezvolta un circuit fără a apela la lipire reală și apoi testați programul nostru pe acest circuit). Vom lansa mai întâi toate proiectele în Proteus, iar apoi putem lipi dispozitivul real.
  • CodeVisionAVR este un compilator de limbaj de programare C pentru AVR. În el vom dezvolta programe pentru microcontroler și direct din acesta va fi posibil să flashăm un MK adevărat.
După instalarea Proteus, lansați-l
Ne invită să ne uităm la proiectele care vin cu el, refuzăm politicos. Acum să creăm cel mai mult schema simpla. Pentru a face acest lucru, faceți clic pe pictogramă și vizual nu se întâmplă nimic. Acum trebuie să faceți clic pe litera mică P (selectați din bibliotecă)în panoul cu liste de componente, se va deschide fereastra de selecție a componentelor
în câmpul mască, introduceți numele componentei pe care dorim să o găsim în bibliotecă. De exemplu, trebuie să adăugăm un microcontroler mega8
în lista de rezultate, indicați spre mega8 și apăsați butonul Bine. Microcontrolerul mega8 apare în lista noastră de componente
Astfel, adăugăm un alt rezistor la lista de componente introducând cuvântul mască în câmp resși LED LED

Pentru a plasa piese pe diagramă, faceți clic pe piesă, apoi faceți clic pe câmpul diagramă, selectați locația componentei și faceți clic din nou. Pentru a adăuga teren sau minus generalÎn diagrama din stânga, faceți clic pe „Terminal” și selectați Ground. Astfel, adăugând toate componentele și conectându-le, obținem acest circuit simplu
Gata, acum prima noastră schemă este gata! Dar te-ai putea întreba, ce poate face? Nimic. Nimic, pentru că pentru ca microcontrolerul să funcționeze, trebuie să scrieți un program pentru el. Un program este o listă de comenzi pe care microcontrolerul le va executa. Avem nevoie ca microcontrolerul să fie montat pe un picior PC0 0 logic (0 volți) și 1 logic (5 volți).

Scrierea unui program pentru un microcontroler

Vom scrie programul în limbajul C folosind compilatorul CodeVisionAVR. După rularea CV-ului, ne întreabă ce vrem să creăm: Sursă sau Proiect Selectăm pe acesta din urmă și facem clic pe OK. În continuare, ni se va cere să lansăm vrăjitorul CVAVR CodeWizard (acesta este un instrument de neprețuit pentru un începător, deoarece poate genera scheletul principal al programului) alege da
Expertul începe cu fila Chip activă, aici putem selecta modelul MK-ului nostru - acesta este mega8 și frecvența la care va funcționa MK (în mod implicit, mega8 este setat la o frecvență de 1 megahertz), așa că setăm totul așa cum se arată în captura de ecran de mai sus. Accesați fila Porturi
Microcontrolerul atmega8 are 3 porturi: Port C, Port D, Port B. Fiecare port are 8 pini. Picioarele de port pot fi în două stări:
  • Ieșire
Folosind registrul DDRx.y putem seta pinul să fie de intrare sau de ieșire. Dacă în
  • DDRx.y = 0 - ieșirea funcționează ca INTRARE
  • DDRx.y = 1 pin care rulează IEȘIRE
Când pinul este configurat ca ieșire, îl putem seta la 1 logic (+5 volți) și la 0 logic (0 volți). Acest lucru se face prin scrierea în registrul PORTx.y. În continuare vom vorbi în detaliu despre porturile I/O. Acum setăm totul așa cum se arată în captura de ecran și facem clic pe Fișier->Generare, Salvare și Ieșire. Apoi, CodeWizard ne va cere să salvăm proiectul, îl salvăm și ne uităm la cod:

#include //Biblioteca pentru crearea de întârzieri void main(void) ( PORTB=0x00; DDRB=0x00; PORTC=0x00; DDRC=0x01; // faceți pin PC0 o ieșire PORTD=0x00; DDRD=0x00; // Timer/Counter 0 inițializare TCCR0=0x00; TCNT0=0x00; // Inițializarea temporizatorului/contorului 1 TCCR1A=0x00; TCCR1B=0x00; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x0B=0x00; OCR1L=0x0B; = 0 x00 ; OCR1BL=0x00; // Inițializarea temporizatorului/contorului 2 ASSR=0x00; TCCR2=0x00; TCNT2=0x00; OCR2=0x00; // Inițializarea întreruperilor externe MCUCR=0x00; // Temporizatorului/contorului (s ) Inițializarea întreruperii(e) TIMSK=0x00; // Inițializarea comparatorului analog ACSR=0x80; SFIOR=0x00; în timp ce (1) ( ); )


Totul aici ți se poate părea înfricoșător și necunoscut, dar în realitate totul nu este așa. Codul poate fi simplificat prin eliminarea inițializării celor pe care nu le folosim dispozitiv periferic MK. După simplificare, arată astfel:

#include //biblioteca pentru lucrul cu microcontrolerul mega8 #include //bibliotecă pentru crearea de întârzieri void main(void) ( DDRC=0x01; /* faceți pinul PC0 la ieșire Intrarea 0x01 vă poate părea necunoscută, iar acesta este doar numărul 1 în formă hexazecimală, această linie va fi echivalentă la 0b00000001 în binar, atunci o voi scrie exact așa.*/ while (1) ( ; )


Totul e bine. Dar pentru ca LED-ul să clipească, trebuie să schimbăm nivelul logic pe pinul PC0. Pentru a face acest lucru, trebuie să adăugați mai multe linii buclei principale:

#include //biblioteca pentru lucrul cu microcontrolerul mega8 #include //bibliotecă pentru crearea de întârzieri void main(void) ( DDRC=0x01; /* faceți pinul PC0 la ieșire Intrarea 0x01 vă poate părea necunoscută, iar acesta este doar numărul 1 în formă hexazecimală, această linie va fi echivalentă la 0b00000001 în binar, apoi îl voi scrie exact așa.*/ while (1) // bucla principală a programului (// deschide bracketul operator al buclei principale a programului PORTC.0=1; / / setați pinul 0 al portului C 1 la delay_ms(500); // faceți o întârziere de 500 de milisecunde PORTC.0=0; //setați portul C la pinul 0 delay_ms(500); //faceți o întârziere de 500 de milisecunde) ;//închide paranteza operatorului din bucla principală a programului)


Gata, codul este acum gata. Faceți clic pe pictograma Build all Project files pentru a compila (traduceți în instrucțiunile procesorului MK) programul nostru. În folderul Exe, care se află în proiectul nostru, ar trebui să apară un fișier cu o extensie hex, acesta este fișierul nostru de firmware pentru MK. Pentru a alimenta firmware-ul nostru microcontrolerului virtual din Proteus, trebuie să faceți dublu clic pe imaginea microcontrolerului din Proteus. Va apărea o fereastră ca aceasta
Faceți clic pe pictograma folderului din câmpul Fișier program, selectați fișierul hex al firmware-ului nostru și faceți clic pe OK. Acum putem rula o simulare a circuitului nostru. Pentru a face acest lucru, faceți clic pe butonul „Play” din colțul din stânga jos al ferestrei Proteus.

Am spus de mai multe ori sau de două ori că studiul MK ar trebui să înceapă cu assembler. Un întreg curs pe site a fost dedicat acestui lucru (deși nu este foarte consistent, dar treptat îl pieptăn până la un aspect adecvat). Da, este dificil, rezultatul nu va fi în prima zi, dar vei învăța să înțelegi ce se întâmplă în controlerul tău. Veți ști cum funcționează și nu veți copia sursele altora ca o maimuță și veți încerca să înțelegeți de ce a încetat brusc să funcționeze. În plus, este mult mai ușor pentru C să creeze cod redneck care va ieși cu o furcă în cel mai inoportun moment.

Din păcate, toată lumea își dorește rezultate imediat. Așa că m-am hotărât să merg pe altă direcție - să fac un tutorial pe C, dar arătându-i lenjeria. Un programator bun de încorporare își ține întotdeauna piesa hardware strâns de șurub, nepermițându-i să facă un singur pas fără permisiune. Deci mai întâi va fi codul C, apoi ce a produs compilatorul și cum funcționează de fapt totul :)

Pe de altă parte, Xi punct forte Aceasta este portabilitatea codului. Dacă, desigur, scrieți totul corect. Separarea algoritmilor de lucru și a implementărilor lor hardware în diferite părți ale proiectului. Apoi, pentru a transfera algoritmul pe alt microcontroler, va fi suficient să rescrieți doar stratul de interfață, unde sunt scrise toate apelurile către hardware și să lăsați tot codul de lucru așa cum este. Și, desigur, lizibilitate. Codul sursă C este mai ușor de înțeles la prima vedere (deși... de exemplu, nu-mi pasă spre ce să punct - fie C sau ASM :)), dar, din nou, dacă totul este scris corect. Voi fi atent și la aceste puncte.

Partea leului din toate exemplele va fi a mea ca piesa de testare pe care va fi instalată partea leului din toate exemplele. consiliu de dezvoltare.

Primul program C pentru AVR

Alegerea unui compilator și configurarea mediului
Există multe compilatoare C diferite pentru AVR:
În primul rând asta IAR AVR C- este aproape sigur recunoscut ca cel mai bun compilator pentru AVR, deoarece controlorul în sine a fost creat în strânsă colaborare între Atmel și specialiștii de la IAR. Dar trebuie să plătești pentru tot. Și acest compilator nu este doar un software comercial scump, dar are și o mulțime de setări încât este nevoie de mult efort pentru a-l compila pur și simplu în el. Chiar nu am dezvoltat o prietenie cu el; proiectul putrezea din cauza unor erori ciudate la etapa de conectare (mai târziu am aflat că era un crack strâmb).

Al doilea vine WinAVR GCC- un compilator de optimizare puternic. Complet open source, cross-platform, în general, toate bucuriile vieții. De asemenea, se integrează perfect în AVR Studio, permițându-vă să depanați chiar acolo, ceea ce este al naibii de convenabil. În general, l-am ales.

De asemenea este si CodeVision AVR C este un compilator foarte popular. A devenit popular datorită simplității sale. Program de lucruÎl puteți obține în doar câteva minute - vrăjitorul de cod de pornire vă ajută foarte mult în acest sens, ștampilând standardele pentru inițializarea tuturor tipurilor de uart-uri. Sincer să fiu, sunt cam suspicios în privința asta - odată ce a trebuit să demontez un program scris de acest compilator, s-a dovedit a fi un fel de mizerie și nu cod. O cantitate teribilă de mișcări și operații inutile, care a dus la o cantitate considerabilă de cod și o performanță lentă. Cu toate acestea, poate a existat o eroare în ADN-ul persoanei care a scris firmware-ul original. Plus că vrea bani. Nu la fel de mult ca IAR, dar vizibil. Și în modul demo vă permite să scrieți nu mai mult de 2 kb de cod.
Bineînțeles că există un crack, dar dacă ai de gând să furi, este un milion, în sensul IAR :)

De asemenea este si Image Craft AVR CȘi MicroC din microelectronică. Nu a trebuit să folosesc niciunul, dar S.W.G. foarte laudator MicroPascal, spun ei, un mediu de programare și biblioteci teribil de convenabil. Cred că MicroC nu va fi mai rău, dar este și plătit.

După cum am spus, am ales WinAVR din trei motive: este gratuit, se integrează în AVR Studio și există doar o mulțime de coduri gata făcute scrise pentru el pentru toate ocaziile.

Deci, descărcați instalarea WinAVR cu AVR Studio. Apoi, studioul este instalat mai întâi, apoi WinAVR este rulat deasupra și atașat la studio sub forma unui plugin. Recomand cu tarie instalarea WinAVR pe o cale scurta, ceva de genul C:\WinAVR, astfel veti evita o multime de probleme cu caile.

Crearea unui proiect
Deci, studioul este instalat, C este înșurubat, este timpul să încercăm să programăm ceva. Să începem cu cel mai simplu, cel mai simplu. Lansează un studio, alege acolo proiect nou, ca compilator AVR GCC și introduceți numele proiectului.

Se deschide un câmp de lucru cu un fișier *.c gol.

Acum nu va strica să configurați afișarea căilor în marcajele studioului. Pentru a face acest lucru, accesați:
Meniu Instrumente - Opțiuni - General - FileTabs și selectați „Numai nume fișier” din lista verticală. În caz contrar, va fi imposibil să funcționeze - fila va conține calea completă a fișierului și nu vor fi mai mult de două sau trei file pe ecran.

Configurarea proiectului
În general, se consideră clasic să se creeze un fișier make în care sunt descrise toate dependențele. Și probabil așa este. Dar pentru mine, care am crescut cu IDE-uri complet integrate, cum ar fi uVision sau AVR Studio această abordare este profund străină. Prin urmare, o voi face în felul meu, totul folosind mijloace de studio.

Apasă butonul cu roata.


Acestea sunt setările pentru proiectul dvs. sau, mai degrabă, setările pentru generarea automată a unui fișier make. Pe prima pagină trebuie doar să introduceți frecvența la care va funcționa MK. Acest lucru depinde de biții de siguranță, așa că presupunem că frecvența noastră este de 8000000Hz.
Acordați atenție și liniei de optimizare. Acum există -Os - aceasta este optimizarea dimensiunii. Lăsați-l așa cum este pentru moment, apoi puteți încerca să jucați cu acest parametru. -O0 nu este deloc optimizare.

Următorul pas este configurarea căilor. În primul rând, adăugați directorul de proiect acolo - veți adăuga biblioteci terțe acolo. Calea „.\” va apărea în listă.

Fișierul Make a fost generat, îl puteți privi în folderul implicit din proiectul dvs., doar aruncați o privire și vedeți ce este acolo.


Asta este tot pentru acum. Faceți clic pe OK peste tot și mergeți la sursă.

Formularea problemei
O foaie goală de hârtie este tentantă să implementeze o idee vicleană, deoarece clipirea banală a unei diode nu mai funcționează. Să luăm imediat taurul de coarne și să implementăm conexiunea cu computerul - acesta este primul lucru pe care îl fac.

Va funcționa astfel:
Când sosește unul (cod 0x31) prin portul COM, vom aprinde dioda, iar când sosește un zero (cod 0x30) se stinge. Mai mult, totul se va face pe întreruperi, iar sarcina de fundal va fi clipirea unei alte diode. Simplu și semnificativ.

Asamblarea circuitului
Trebuie să conectăm modulul convertor USB-USART la pinii USART ai microcontrolerului. Pentru a face acest lucru, luați un jumper din două fire și plasați-l pe pini în cruce. Adică conectăm Rx-ul controlerului la Tx-ul convertorului, iar Tx-ul convertorului la Rx-ul controlerului.

La final, aceasta este diagrama:


Nu iau în considerare conectarea pinilor rămași, alimentarea sau resetarea, este standard.

Cod de scriere

Permiteți-mi să fac imediat o rezervare că nu voi aprofunda în mod specific în descrierea limbajului C în sine. Există pur și simplu o cantitate colosală de material pentru aceasta, variind de la clasicul „Limbaj de programare C” de la K&R la diverse manuale.

Am găsit o astfel de metodă în depozitul meu; am folosit-o odată pentru a studia această limbă. Totul acolo este scurt, clar și la obiect. Îl pun treptat împreună și îl trag pe site-ul meu.

Este adevărat că nu au fost transferate încă toate capitolele, dar cred că nu va dura mult timp.

Este puțin probabil să o pot descrie mai bine, deci de la curs de pregatire, în loc de o explicație detaliată a subtilităților, voi oferi pur și simplu link-uri directe către pagini individuale ale acestui manual.

Adăugarea de biblioteci.
În primul rând, adăugăm bibliotecile și anteturile necesare cu definiții. La urma urmei, C este un limbaj universal și trebuie să-i explicăm că lucrăm în mod specific cu AVR, așa că scrieți linia în codul sursă:

1 #include

#include

Acest fișier se află în folder WinAVRși conține o descriere a tuturor registrelor și porturilor controlerului. Mai mult decât atât, totul este viclean, cu legare la un controler specific, care este transmis de compilator prin intermediul face fișier în parametru MCUși pe baza acestei variabile, un fișier antet este conectat la proiectul dvs. cu o descriere a adreselor tuturor porturilor și registrelor pentru acest controler special. Wow! Fără el, este și posibil, dar atunci nu veți putea folosi nume de registru simbolic precum SREG sau UDR și va trebui să vă amintiți adresa fiecăruia, cum ar fi „0xC1”, ceea ce va fi o durere de cap.

Echipa în sine #include<имя файла> vă permite să adăugați conținutul oricărui fișier text la proiect, de exemplu, un fișier cu o descriere a funcțiilor sau o bucată de alt cod. Și pentru ca directiva să poată găsi acest fișier, am specificat calea către proiectul nostru (directorul WinAVR este deja înregistrat acolo implicit).

Functie principala.
Un program C constă în întregime din funcții. Ele pot fi imbricate și chemate unul de celălalt în orice ordine și în moduri diferite. Fiecare funcție are trei parametri necesari:

  • Valoarea returnată este de ex. sin(x) returnează valoarea sinusului lui x. Ca la matematică, pe scurt.
  • Parametrii transmisi sunt același X.
  • Corpul funcției.

Toate valorile transmise și returnate trebuie să fie de un anumit tip, în funcție de date.

Orice program C trebuie să conțină o funcție principal ca punct de intrare în programul principal, altfel nu este deloc C :). Prin prezența principalului în codul sursă al altcuiva dintr-un milion de fișiere, puteți înțelege că aceasta este partea principală a programului, unde începe totul. Deci hai sa intrebam:

1 2 3 4 5 int main(void) ( return 0 ; )

int main(void) ( returnează 0; )

Gata, a fost scris primul program cel mai simplu, nu contează că nu face nimic, tocmai am început.

Să ne dăm seama ce am făcut.
int Acesta este tipul de date pe care îl returnează funcția principală.

Desigur, într-un microcontroler principalîn principiu nimic nu poate fi returnat și în teorie ar trebui să existe void main(void), dar GCC este proiectat inițial pentru PC și acolo programul poate returna valoarea sistemului de operare după finalizare. Prin urmare, GCC pe void main(void) jură pe Avertisment.

Aceasta nu este o eroare, va funcționa, dar nu-mi plac avertismentele.

gol acesta este tipul de date pe care le transmitem funcției, în acest caz principal de asemenea, nu pot accepta nimic din exterior, prin urmare gol- un manechin. Un stub este folosit atunci când nu este nevoie să transmiteți sau să returnați nimic.

Aici sunt ei { } acoladele sunt un bloc de program, în acest caz corpul unei funcții principal, codul va fi localizat acolo.

întoarcere- aceasta este valoarea returnată pe care funcția principală o va returna la finalizare, deoarece avem un int, adică un număr, atunci trebuie să returnăm un număr. Deși acest lucru încă nu are sens, pentru că... pe microcontroler, nu putem merge decât nicăieri de la principal. revin nul. Pentru că nu contează. Dar compilatorul este de obicei inteligent și nu generează cod pentru acest caz.
Deși, dacă este pervertit, atunci din principal Puteți merge la MK - de exemplu, intrați în secțiunea bootloader și executați-o, dar acest lucru va necesita reparații la nivel scăzut cu firmware-ul pentru a corecta adresele de tranziție. Mai jos vei vedea singur și vei înțelege cum să o faci. Pentru ce? Aceasta este o altă întrebare, în 99,999% din cazuri acest lucru nu este necesar :)

Am făcut-o și am mers mai departe. Să adăugăm o variabilă, nu prea avem nevoie de ea și nu are rost să introducem variabile fără ea, dar învățăm. Dacă variabilele sunt adăugate în corpul unei funcții, atunci ele sunt locale și există numai în această funcție. Când ieși din funcție, aceste variabile sunt șterse, iar memoria RAM este alocată pentru nevoi mai importante. .

1 2 3 4 5 6 int main(void) (unsigned char i; return 0;)

int main(void) (unsigned char i; return 0; )

nesemnatînseamnă nesemnat. Faptul este că în reprezentarea binară, cel mai semnificativ bit este alocat semnului, ceea ce înseamnă că numărul +127/-128 se încadrează într-un octet (car), dar dacă semnul este aruncat, acesta se va potrivi de la 0 la 255. De obicei semnul nu este necesar. Asa de nesemnat.
i este doar un nume de variabilă. Nu mai.

Acum trebuie să inițializam porturile și UART. Desigur, puteți lua și conecta biblioteca și puteți apela un fel de UartInit(9600); dar atunci nu vei ști ce s-a întâmplat cu adevărat.

Facem asta:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main(void ) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1)#define HI(x) ((x)>>8) #define LO(x) ((x)& 0xFF) UBRRL = LO(bauddivider) ; UBRRH = HI(divizor de baud); UCSRA = 0; UCSRB = 1<< RXEN| 1 << TXEN| 1 << RXCIE| 0 << TXCIE; UCSRC = 1 << URSEL| 1 << UCSZ0| 1 << UCSZ1; }

int main(void) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1) #define HI(x) ((x)>>8) #define LO( x) ((x)& 0xFF) UBRRL = LO(divizor de baud); UBRRH = HI (divizor de baud); UCSRA = 0; UCSRB = 1<

Infricosator? De fapt, există doar cinci ultimele linii de cod real. Tot ce #defini este un limbaj macro preprocesor. Aproape aceleași lucruri ca în Assembly, dar sintaxa este ușor diferită.

Acestea vă vor facilita operațiunile de rutină de calculare a coeficienților necesari. În primul rând spunem că în schimb XTAL puteți înlocui în siguranță 8000000 și L- indicarea tipului, spunând lung este frecvența de ceas a procesorului. Aceeași baudrate— frecvența transmiterii datelor prin UART.

bauddivider deja mai complicată, în locul ei se va înlocui expresia calculată folosind formula din cele două anterioare.
Bine si L.O.Și BUNĂ octeții mici și înalți vor fi preluați din acest rezultat, deoarece Evident, s-ar putea să nu se potrivească într-un octet. ÎN BUNĂ X (parametrul de intrare macro) este deplasat de opt ori spre dreapta, rezultând doar cel mai semnificativ octet rămas. Si in L.O. facem un SI pe biți cu numărul 00FF, ca rezultat va rămâne doar octetul mic.

Deci tot ce s-a făcut este ca #definiîl puteți arunca în siguranță și calcula numerele necesare pe un calculator și le introduceți imediat în liniile UBBRL = …. și UBBRH = …..

Poate sa. Dar! Fa asta ABSOLUT IMPOSIBIL!

Va funcționa în acest fel sau în altul, dar veți avea așa-numitul numere magice- valori luate de nicăieri și din motive necunoscute, iar dacă deschideți un astfel de proiect în câțiva ani, va fi al naibii de greu de înțeles care sunt aceste valori. Chiar și acum, dacă doriți să schimbați viteza sau frecvența cuarțului, totul va trebui să fie recalculat din nou, dar ați schimbat câteva numere în cod și gata. În general, dacă nu doriți să fiți marcat ca codificator, atunci creați-vă codul astfel încât să fie ușor de citit, de înțeles și ușor de modificat.

Atunci totul este simplu:
Toate aceste „UBRL și Co” sunt registre de configurare ale transmițătorului UART cu ajutorul cărora vom comunica cu lumea. Și acum le-am atribuit valorile necesare, setându-le la viteza și modul dorit.

Tip de înregistrare 1<Înseamnă următoarele: luați 1 și puneți-l la loc RXENîn octet. RXEN acesta este al 4-lea bit al registrului UCSRB, Asa de 1< formează numărul binar 00010000, TXEN- acesta este al 3-lea bit și 1< va da 00001000. Single „|” este pe biți SAU, deci 00010000 | 00001000 = 00011000. În același mod, biții de configurare necesari rămași sunt setați și adăugați la heap-ul general. Ca urmare, numărul colectat este înregistrat în UCSRB. Mai multe detalii sunt descrise în fișa de date despre MK în secțiunea USART. Deci, să nu ne lăsăm distrași de detaliile tehnice.

Gata, este timpul să vedem ce s-a întâmplat. Faceți clic pe compilare și începeți emularea (Ctrl+F7).

Depanare
Au trecut tot felul de bare de progres, studioul s-a schimbat și o săgeată galbenă a apărut lângă intrarea în funcția principală. Acesta este locul în care procesorul rulează în prezent și simularea este întreruptă.

Cert este că inițial, de fapt, era pe linia UBRRL = LO(bauddivider); La urma urmei, ceea ce avem în definire nu este cod, ci pur și simplu calcule preliminare, motiv pentru care simulatorul este puțin plictisitor. Dar acum și-a dat seama că prima instrucțiune a fost finalizată și dacă te urci în copac Vizualizare I/O, la secțiunea USART și uitați-vă la octetul UBBRL de acolo, veți vedea că valoarea este deja acolo! 0x33.

Faceți un pas mai departe. Vedeți cum se modifică conținutul celuilalt registru. Așa că parcurge-le pe toate, fii atent la faptul că toți biții indicați sunt setați așa cum ți-am spus și sunt setați simultan pentru întregul octet. Nu va merge mai departe decât Return - programul s-a terminat.

Deschidere
Acum resetați simularea la zero. Click acolo Resetare (Shift+F5). Deschideți lista dezasamblată, acum veți vedea ce se întâmplă de fapt în controler. Vizualizare -> Dezasamblator. Și nu YYAAAAAA!!! Asamblator!!! GROAZĂ!!! SI ESTE NECESAR. Pentru ca mai târziu, când ceva nu merge bine, să nu fii prost în cod și să nu pui întrebări prost pe forumuri, ci să intri imediat în curajul și să vezi unde ești blocat. Nu e nimic înfricoșător acolo.

Mai întâi vor fi topuri din serie:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 +00000000: 940C002A JMP 0x0000002A Salt +00000002: 940C0034 JMP 0x00000034 Salt +00000004: 940C0034 JMP 0x000MP 0x00000034 Salt +00000008: 940C0034 JMP 0x00000034 Salt +0000000A: 940C0034 JMP 0x00000034 Salt +0000000C: 940000000000034 JMP JMP 0x00000034 + 0000000E: 940C0034 JMP 0x00000034 Salt +00000010: 940C0034 JMP 0x00000034 Salt +00000012: 940C0034 JMP 0x000040400300404040400400404 J MP 0x00000034 Salt +00000016: 940C0034 JMP 0x00000034 Salt +00000018: 940C0034 JMP 0x00000034 Salt +0000001A: 94000000000001A: 940000000000000 JMP JMP 0x00000034 +000000 1C : 940C0034 JMP 0x00000034 Salt +0000001E: 940C0034 JMP 0x00000034 Salt +00000020: 940C0034 JMP 0x00040400300404004020034 JMP 0x0000 0034 Salt +00000024: 940C0034 JMP 0x00000034 Salt +00000026: 940C0034 JMP 0x00000034 Salt +00000028: 0x3040C0034 JMP A sari

00000000: 940C002A JMP 0x0000002A Salt +00000002: 940C0034 JMP 0x00000034 Salt +00000004: 940C0034 JMP 0x0000mp0400340 JMP +00000034 MP 0x00000034 Salt +00000008: 940C0034 JMP 0x00000034 Salt +0000000A: 940C0034 JMP 0x00000034 Salt +0000000C: 940000000x30 JMP +0034 JMP 0x00000034 000000 0E : 940C0034 JMP 0x00000034 Salt +00000010: 940C0034 JMP 0x00000034 Salt +00000012: 940C0034 JMP 0x000040400304040404004004004 JMP 0x0000 0034 Salt +00000016: 940C0034 JMP 0x00000034 Salt +00000018: 940C0034 JMP 0x00000034 Salt +0000001A: 9034 JMP Salt 0x00C0 +0000001C: 940C0034 JMP 0x00000034 Salt +0000001E: 940C0034 JMP 0x00000034 Salt +00000020: 940C0034 JMP 0x0000mp0400340040402034 JMP +00000034 MP 0x000000 34 Salt +00000024: 940C0034 JMP 0x00000034 Salt +00000026: 940C0034 JMP 0x00000034 Salt +00000028: 9000000000000000 JMP JMP 0x00000034

Acesta este tabelul vector de întrerupere. Vom reveni la el mai târziu, dar deocamdată doar uitați-vă și amintiți-vă că există. Prima coloană este adresa celulei flash în care se află comanda, a doua este codul comenzii, a treia este mnemonicul comenzii, aceeași instrucțiune de asamblare, a treia este operanzii comenzii. Ei bine, comentariu automat.
Deci, dacă te uiți, există tranziții continue. Și codul de comandă JMP este de patru octeți, conține adresa de salt scrisă invers - octetul mic la adresa inferioară și codul de comandă de salt 940C

0000002B: BE1F OUT 0x3F,R1 Ieșire către locația I/O

Înregistrarea acestui zero la adresa 0x3F Dacă vă uitați la coloana de vizualizare I/O, veți vedea că adresa 0x3F este adresa registrului SREG - registrul flag al controlerului. Acestea. resetăm SREG pentru a rula programul în condiții zero.

1 2 3 4 +0000002C: E5CF LDI R28,0x5F Încărcare imediată +0000002D: E0D4 LDI R29,0x04 Încărcare imediată +0000002E: BFDE OUT 0x3E,R29 Ieșire la locația I/O +000000D02F la locația I/O +00000002F: Ieșire BFC3002F: 8:20

0000002C: E5CF LDI R28,0x5F Încărcare imediată +0000002D: E0D4 LDI R29,0x04 Încărcare imediată +0000002E: BFDE OUT 0x3E,R29 Ieșire la locația I/O +000000D02F la locație Ieșire/Ieșire +00000002F: 8BFC3D02F:

Aceasta se încarcă indicatorul stivei. Nu puteți încărca direct în registrele I/O, doar printr-un registru intermediar. Prin urmare, mai întâi LDI la intermediar și apoi de acolo OUT la I/O. De asemenea, vă voi spune mai multe despre stiva mai târziu. Deocamdată, știți că aceasta este o zonă de memorie dinamică care se blochează la sfârșitul memoriei RAM și stochează adrese și variabile intermediare. Acum am indicat de unde va începe stiva noastră.

00000032: 940C0041 JMP 0x00000041 Salt

Treceți la sfârșitul programului și acolo avem o interdicție a întreruperilor și a buclei strânse pe sine:

1 2 +00000041: 94F8 CLI Global Interrupt Disable +00000042: CFFF RJMP PC-0x0000 Salt relativ

00000041: 94F8 CLI dezactivare întrerupere globală +00000042: CFFF RJMP PC-0x0000 Salt relativ

Acest lucru se întâmplă în cazul unor circumstanțe neprevăzute, cum ar fi părăsirea funcției principale. Controlerul poate fi scos dintr-o astfel de buclă fie printr-o resetare hardware, fie, mai probabil, printr-o resetare de la un watchdog. Ei bine, sau, după cum am spus mai sus, corectați acest lucru în editorul hexadecimal și mergeți în galop către oriunde dorește inima noastră. De asemenea, rețineți că există două tipuri de tranziții: JMP și RJMP; primul este o tranziție directă la o adresă. Ocupă patru octeți și poate sări direct prin întreaga zonă de memorie. Al doilea tip de tranziție este RJMP - relativ. Comanda lui durează doi octeți, dar se mișcă de la poziția (adresa) curentă cu 1024 de pași înainte sau înapoi. Și parametrii săi indică decalajul față de punctul curent. Este folosit mai des pentru că ocupă jumătate din spațiu într-o culoare, iar tranzițiile lungi sunt rareori necesare.

1 +00000034: 940C0000 JMP 0x00000000 Salt

00000034: 940C0000 JMP 0x00000000 Salt

Și acesta este un salt la începutul codului. Un fel de repornire. Puteți verifica dacă toți vectorii sar aici. Concluzia de aici este că, dacă acum activați întreruperile (sunt dezactivate implicit) și întreruperea dvs. are loc, dar nu există un handler, atunci va exista o resetare software - programul va fi aruncat înapoi la început.

Funcția principală. Totul este similar, nici măcar nu trebuie să-l descrii. Uită-te doar la numărul deja calculat care este introdus în registre. Preprocesorul compilatorului este foarte bine!!! Deci fără numere „magice”!

1 2 3 4 5 6 7 8 9 10 11 12 <

00000036: E383 LDI R24,0x33 Încărcare imediată +00000037: B989 OUT 0x09,R24 Ieșire la locația I/O 15: UBRRH = HI(bauddivider); +00000038: BC10 OUT 0x20,R1 Ieșire către locația I/O 16: UCSRA = 0; +00000039: B81B OUT 0x0B,R1 Ieșire către locația I/O 17: UCSRB = 1<

Și iată bug-ul:

1 2 3 +0000003E: E080 LDI R24.0x00 Încărcare imediată +0000003F: E090 LDI R25.0x00 Încărcare imediată +00000040: 9508 RET Retur subprogram

0000003E: E080 LDI R24.0x00 Încărcare imediată +0000003F: E090 LDI R25.0x00 Încărcare imediată +00000040: 9508 RET Retur subprogram

Întrebarea este de ce adaugă compilatorul astfel de topuri? Și acesta nu este altceva decât Return 0, am definit funcția ca int main(void) și așa am irosit încă patru octeți pentru nimic :) Și dacă faceți void main(void), atunci va rămâne doar RET, dar va apărea un avertisment , că funcția noastră principală nu returnează nimic. În general, fă cum vrei :)

Dificil? Aparent nu. Faceți clic pe execuție pas cu pas în modul dezasamblare și vedeți cum procesorul execută instrucțiuni individuale, ce se întâmplă cu registrele. Cum se produce mișcarea prin comenzi și bucla finală?

De continuat în câteva zile...

Offtop:
Alexei78 Am creat un plugin pentru Firefox care ușurează navigarea pe site-ul și forumul meu.
Discuție și descărcare,

Există diferite limbaje de programare pentru microcontrolerele AVR, dar poate cele mai potrivite sunt asamblarea și C, deoarece aceste limbaje implementează cel mai bine toate capabilitățile necesare pentru gestionarea hardware-ului microcontrolerului.

Limbajul de asamblare este un limbaj de programare de nivel scăzut care utilizează setul de instrucțiuni directe al microcontrolerului. Crearea unui program în acest limbaj necesită o bună cunoaștere a sistemului de comandă al cipului programabil și suficient timp pentru a dezvolta programul. Limbajul de asamblare este inferior C în viteza și ușurința dezvoltării programului, dar are avantaje notabile în dimensiunea codului executabil final și, în consecință, în viteza de execuție a acestuia.

C vă permite să creați programe cu un confort mult mai mare, oferind dezvoltatorului toate beneficiile unui limbaj de nivel înalt.
Trebuie remarcat încă o dată că arhitectura și sistemul de comandă al AVR au fost create cu participarea directă a dezvoltatorilor compilatorului limbajului C și ia în considerare caracteristicile acestui limbaj. Compilarea codului sursă C este rapidă și produce cod compact și eficient.

Principalele avantaje ale C față de assembler: viteza mare de dezvoltare a programului; universalitate care nu necesită un studiu amănunțit al arhitecturii microcontrolerului; o mai bună documentare și lizibilitate a algoritmului; disponibilitatea bibliotecilor de funcții; suport pentru calcule în virgulă mobilă.

Limbajul C combină armonios capabilitățile de programare de nivel scăzut cu proprietățile unui limbaj de nivel înalt. Capacitatea de programare la nivel scăzut vă permite să operați cu ușurință direct pe hardware, iar proprietățile limbajului de nivel înalt vă permit să creați cod de program ușor de citit și modificabil. În plus, aproape toate compilatoarele C au capacitatea de a folosi inserții de asamblare pentru a scrie secțiuni de program care sunt critice în ceea ce privește timpul de execuție și consumul de resurse.

Într-un cuvânt, C este cel mai convenabil limbaj atât pentru începătorii care se familiarizează cu microcontrolerele AVR, cât și pentru dezvoltatorii serioși.

Compilatoarele sunt folosite pentru a converti codul sursă al unui program într-un fișier firmware al microcontrolerului.

Atmel oferă un compilator de asamblare puternic care este inclus în mediul de dezvoltare Atmel Studio care rulează pe Windows. Alături de compilator, mediul de dezvoltare conține un depanator și un emulator.
Atmel Studio este complet gratuit și disponibil pe site-ul web Atmel.

În prezent, există destul de multe compilatoare C pentru AVR. Cel mai puternic dintre ei este considerat a fi compilatorul de la IAR Systems din Stockholm. La mijlocul anilor '90, angajații săi au participat la dezvoltarea sistemului de comandă AVR. IAR C Compiler are capabilități extinse de optimizare a codului și vine ca parte a mediului de dezvoltare integrat IAR Embedded Workbench (EWB), care include, de asemenea, un compilator de asamblare, linker, manager de proiect și bibliotecă și depanator. Prețul versiunii complete a pachetului este de 2820 EUR. Pe site-ul companiei puteți descărca o versiune de evaluare gratuită pentru 30 de zile sau o versiune nelimitată cu o limită de dimensiune a codului de 4 KB.

Compania americană Image Craft din Palo Alto, California produce un compilator în limbaj C care a câștigat o popularitate destul de mare. JumpStart C pentru AVR are o optimizare de cod acceptabilă și un preț nu prea mare (de la 50 USD la 499 USD în funcție de versiune). Versiunea demo a JumpStart C pentru AVR este complet funcțională timp de 45 de zile.

Compilatorul Român Code Vision AVR C a câștigat nu mai puțin popularitate; prețul versiunii complete a acestui compilator este relativ mic și se ridică la 150 EUR. Compilatorul vine cu un mediu de dezvoltare integrat, care, pe lângă caracteristicile standard, include o caracteristică destul de interesantă - CodeWizardAVR Automatic Program Generator. Prezența unui terminal serial în mediul de dezvoltare vă permite să depanați programe folosind portul serial al microcontrolerului. Puteți descărca o versiune de evaluare gratuită de la dezvoltatori cu o limită de dimensiune a codului de 4 KB și salvarea dezactivată a codului sursă generat în C.

Compania MikroElektronika, situată în orașul sârb Belgrad, produce o întreagă familie de compilatoare pentru microcontrolere AVR. Un compilator pentru limbajul C numit mikroC PRO pentru AVR costă 249 USD. Există și mikroBasic și mikroPascal la același preț. Există versiuni demo pe site-ul web al dezvoltatorilor cu o limită de dimensiune a codului de 4096 de octeți. Avantajul acestei familii de compilatoare este o singură platformă și o singură ideologie, care poate oferi o tranziție ușoară nu numai între limbi, ci și între microcontrolere (există versiuni de compilator pentru PIC, STM32, 8051...).

Mediul de dezvoltare integrat a devenit cu adevărat iconic. Include compilatoare C și asamblare puternice, programatorul AVRDUDE, un depanator, un simulator și multe alte programe și utilitare suport. WinAVR se integrează perfect cu mediul de dezvoltare AVR Studio de la Atmel. Asamblatorul este identic ca cod de intrare cu asamblatorul AVR Studio. Compilatoarele C și assembler au capacitatea de a crea fișiere de depanare în format COFF, ceea ce vă permite să utilizați nu numai instrumente încorporate, ci și să utilizați puternicul simulator AVR Studio. Un alt avantaj important este că WinAVR este distribuit gratuit, fără restricții (producătorii acceptă Licența Publică Generală GNU).

Ca rezumat, merită să spunem că WinAVR este o alegere ideală pentru cei care încep să stăpânească microcontrolerele AVR. Acest mediu de dezvoltare este considerat ca fiind principalul în acest curs.

Operațiile pe biți se bazează pe operațiunile logice pe care le-am acoperit mai devreme. Ele joacă un rol cheie în programarea AVR și a altor tipuri de microcontrolere. Aproape niciun program nu poate face fără utilizarea operațiunilor pe biți. Înainte de aceasta, le-am evitat în mod deliberat pentru a facilita procesul de învățare a programării MK.

În toate articolele anterioare, am programat doar porturi I/O și nu am folosit componente suplimentare încorporate, de exemplu, cronometre, convertoare analog-digitale, întreruperi și alte dispozitive interne fără de care MK își pierde toată puterea.

Înainte de a trece la stăpânirea dispozitivelor încorporate ale MK, trebuie să învățați cum să controlați sau să verificați biții individuali ai registrelor AVR MK. Anterior, am efectuat o verificare sau am stabilit simultan cifrele întregului registru. Să ne dăm seama care este diferența și apoi să mergem mai departe.

Operații pe biți

Cel mai adesea, atunci când programam microcontrolere AVR, l-am folosit, deoarece este mai vizual în comparație cu și este bine înțeles pentru programatorii MK începători. De exemplu, trebuie să setăm doar al 3-lea bit al portului D. Pentru a face acest lucru, așa cum știm deja, putem folosi următorul cod binar:

PORTD = 0b00001000;

Cu toate acestea, cu această comandă setăm a 3-a cifră la unu și resetam toate celelalte (0, 1, 2, 4, 5, 6 și a 7-a) la zero. Acum să ne imaginăm o situație în care biții 6 și 7 sunt utilizați ca intrări ADC și în acest moment un semnal de la un dispozitiv este primit la pinii MK corespunzători și folosim comanda de mai sus pentru a reseta aceste semnale. Drept urmare, microcontrolerul nu le vede și crede că semnalele nu au ajuns. Prin urmare, în loc de o astfel de comandă, ar trebui să folosim alta care să seteze doar al 3-lea bit la unul, fără a afecta biții rămași. Pentru a face acest lucru, se utilizează de obicei următoarea operație pe biți:

PORTD |= (1<<3);

Vom discuta mai jos sintaxa acesteia în detaliu. Și acum încă un exemplu. Să presupunem că trebuie să verificăm starea celei de-a treia cifre a registrului PIND, verificând astfel starea butonului. Dacă acest bit este resetat la zero, atunci știm că butonul este apăsat și apoi este executat codul de comandă, care corespunde stării butonului apăsat. Anterior am fi folosit următoarea notație:

dacă (PIND == 0b00000000)

(orice cod)

Cu toate acestea, cu ajutorul său, verificăm nu doar al 3-lea bit, ci toți biții registrului PIND simultan. Prin urmare, chiar dacă butonul este apăsat și bitul dorit este resetat, dar în acest moment este primit un semnal la alt pin al portului D, valoarea corespunzătoare va fi setată la unu, iar condiția dintre paranteze va fi falsă. Ca urmare, codul din interiorul acoladelor nu va fi executat nici măcar atunci când butonul este apăsat. Prin urmare, pentru a verifica starea unui al 3-lea bit individual al registrului PIND, trebuie utilizată o operație pe biți:

dacă (~PIND & (1<<3))

(orice cod)

Pentru a lucra cu biți individuali ai microcontrolerului, limbajul de programare C are instrumente care pot fi utilizate pentru a schimba sau verifica starea unuia sau a mai multor biți individuali simultan.

Setarea unui singur bit

Pentru a seta un singur bit, cum ar fi portul D, se folosește o operație SAU pe biți. Acesta este ceea ce am folosit la începutul articolului.

PORTD = 0b00011100; // valoarea initiala

PORTD = PORTD | (1<<0); применяем побитовую ИЛИ

PORTD |= (1<<0); // сокращенная форма записи

PORTD == 0b00011101; // rezultat

Această comandă setează bitul zero și lasă restul neschimbat.

De exemplu, să instalăm încă al 6-lea bit al portului D.

PORTD = 0b00011100; // starea inițială a portului

PORTD |= (1<<6); //

PORTD == 0b01011100; // rezultat

Pentru a scrie unul la mai mulți biți separati simultan, de exemplu porturile zero, șase și șapte B Se aplică următoarea notație.

PORTB = 0b00011100; // valoarea initiala

PORTB |= (1<<0) | (1<<6) | (1<<7); //

PORTB == 0b1011101; // rezultat

Resetarea (reducerea la zero) biților individuali

Pentru a reseta un singur bit, sunt utilizate simultan trei comenzi discutate anterior: .

Să resetam al 3-lea bit al registrului PORTC și să lăsăm restul neschimbat.

PORTC = 0b00011100;

PORTC &= ~(1<<3);

PORTC == 0b00010100;

Să efectuăm acțiuni similare pentru a 2-a și a 4-a cifră:

PORTC = 0b00111110;

PORTC &= ~((1<<2) | (1<<4));

PORTC == 0b00101010;

Comutarea biților

Pe lângă setare și resetare, este folosită și o comandă utilă care comută un singur bit în starea opusă: unu la zero și invers. Această operațiune logică este utilizată pe scară largă în crearea diferitelor efecte de iluminare, de exemplu, o ghirlandă de Anul Nou. Să ne uităm la exemplul PORTA

PORTA = 0b00011111;

PORTA ^= (1<<2);

PORTA == 0b00011011;

Să schimbăm starea biților zero, al doilea și al șaselea:

PORTA = 0b00011111;

PORTA ^= (1<<0) | (1<<2) | (1<<6);

PORTA == 0b01011010;

Verificarea stării unui bit individual. Permiteți-mi să vă reamintesc că verificarea (spre deosebire de scriere) unui port I/O se realizează prin citirea datelor din registrul PIN.

Cel mai adesea, testarea este efectuată prin una dintre cele două instrucțiuni de buclă: if și while. Suntem deja familiarizați cu acești operatori mai devreme.

Verificarea bitului pentru prezența unui zero logic (resetare) cu dacă

dacă (0==(PIND & (1<<3)))

Dacă al treilea bit al portului D este șters, atunci Code1 este executat. În caz contrar, Code2 este executat.

Acțiuni similare sunt efectuate cu această formă de înregistrare:

dacă (~PIND & (1<<3))

Verificarea bitului pentru prezența unei unități logice (setare) cu dacă

dacă (0 != (PIND & (1<<3)))

dacă (PIND și (1<<3))

Cele două bucle de mai sus funcționează în mod similar, dar pot avea, datorită flexibilității limbajului de programare C, o formă diferită de notație. Operatorul != înseamnă nu este egal. Dacă al treilea bit al portului PD I/O este setat (unul), atunci Code1 este executat; dacă nu, Code2 este executat.

Se așteaptă ca bit să se reseteze in timp ce

în timp ce (PIND & (1<<5))

Code1 va fi executat atâta timp cât al 5-lea bit al registrului PIND este setat. Când îl resetați, Code2 va începe să se execute.

Se așteaptă ca bit să fie setat in timp ce

Aici, sintaxa C vă permite să scrieți cod în două dintre cele mai comune moduri. În practică, se folosesc ambele tipuri de înregistrare.