Internet Windows Android

Exemple în C pentru microcontrolere Atmel AVR. Conectarea unui buton - Programare în CV AVR? Experimente cu butoane pe avr

Uneori trebuie să faci un dispozitiv foarte mic, de exemplu, un computer pentru ciclism. Sau designul nu permite plasarea multor butoane. În general, avem un buton de intrare și nimic mai mult.

Condiții spartane, dar chiar și aici puteți implementa funcționalități puternice, meniuri cu mai multe niveluri și alte delicii ale vieții. Acum voi arăta una dintre implementările unui astfel de control.

Deci, ce poate face butonul nostru?

  • Poate fi apăsat scurt
  • Puteți apăsa lung
  • Puteți face diferite combinații de prese
  • Poate fi lansat la momentul potrivit


Nu multe, dar destul de bine. Pentru un buton atunci. Principala problemă atunci când scrieți acest lucru este să nu irosești o mulțime de resurse de sistem (timp CPU, cronometre și mii) pentru a procesa acest buton nefericit. La urma urmei, va trebui să urmărim faptul de apăsare, faptul de a apăsa, timpul de apăsare, numărul de apăsări cu timpi diferiți. Am văzut implementări atât de infernale ale acestei interfețe, încât am fost pur și simplu uimit de cum a fost posibil să aduni atâtea lucruri stupide și încetiniri în acest singur pin și chiar să irosesc toate cronometrele :)

Deci, vom stabili specificațiile tehnice după cum urmează:

  • Nu există cronometre hardware, cu excepția cronometrului dispecerului.
  • Fără întârzieri, doar apelați-vă la un cronometru.
  • Fără așteptare pentru presă și lansare într-un ciclu. Au intrat, l-au verificat și au renunțat la control.
  • Să introducem un interval de timp mode_time, timp în care vom monitoriza combinația de clicuri. Să spunem 2s
  • Rezultatul va fi numărul de apăsări scurte și lungi pentru un interval dat

Algoritm
Să facem totul pe o mașină finită. Va avea trei stări:

  • Sus - butonul nu este apăsat
  • Dn - butonul apăsat
  • Buton Al - eliberat după apăsare lungă

Va exista și o procedură de service care, mode_time (2c) după prima acțiune cu butonul, va prelua toate rezultatele și va face ceva cu ele. Ce - nu mai contează. Depinde de program.
Și toate aceste gunoaie se vor învârti într-o buclă, apelându-se prin dispecer (sau într-un alt mod) o dată la 20 ms.

Sus
Să intrăm.
Să vedem dacă butonul este apăsat? Dacă nu, plecăm. Dacă este apăsat, atunci mutați mașina în poziția Dn
Este prima dată când verificăm acest interval? Dacă este primul, atunci vom seta procedura de service la o pornire întârziată (după 2 secunde) și vom ridica semnalizarea că procesul a început.
Să mergem afară.

Dn
Să intrăm.
Încă apăsat? Dacă nu, atunci butonul a fost deja eliberat, resetați la starea Sus și numărați o apăsare scurtă, crescând contorul de apăsare scurtă cnt_s. Dacă este încă apăsat, faceți clic pe contorul de timp pentru a măsura durata de apăsare a timpului. Măsurăm durata în iterații ale mașinii. O iterație este de 20 ms. Am stabilit limita de apăsare lungă la 20 de iterații, ceea ce dă aproximativ 400 ms. Orice lucru mai lung de 0,4 s este considerat o apăsare lungă. Când au trecut mai mult de 20 de iterații, numărăm o apăsare lungă și transferăm mașina în starea Al. Să mergem afară.

Al
Să intrăm.
Nu ai eliberat încă? Dacă nu, atunci plecăm. Dacă butonul este eliberat, atunci trecem la Sus, eliminând variabila Time.


În timpul mode_time, în acele două secunde pe care avem timp să le lovim, totul este al nostru. Procedura de analiză a datelor colectate va începe și va curăța mizeria. Totul este deja simplu acolo. Folosim un caz banal pentru a crea acțiunea de care avem nevoie. De exemplu, am setat casete de selectare care sunt interceptate de o altă sarcină. Principalul lucru este să nu blocați această sarcină cu ceva greu, pentru a nu rata următoarea combinație.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 #include u08 bt1, bt2, bt3, bt4, bt5, bt_l, bt_l2, bt_al; // Variabilele sunt steagurile butoanelor apăsate. // Este mai eficient să le faci câmpuri de biți // Dar am fost leneș. Optimizeaza-te :) u16 bt_mode_time = 2000; // Durata secvenței de analiză // A făcut o variabilă, nu o constantă // ca să te poți schimba din mers. Reduce // unde nu trebuie să așteptați combinații lungi // care mărește foarte mult capacitatea de răspuns a interfeței u08 bt_cnt; // Semnalizează că secvența este analizată u08 bt_cnt_s; // Contor de prese scurte u08 bt_cnt_l; // Contor de apăsare lungă void bt_scan(void) // Această funcție este un scaner. Ar trebui apelat o dată la 20 ms { #define up 0 // Definește starea mașinii. 0 - implicit.#define dn 1 #define al 3 static u08 bt_state= 0 ; // Variabila de stare a mașinii static u08 bt_time = 0 ; // Timpul de funcționare al mașinii variabil comutați (bt_state) // Mașina reală( majuscule: ( dacă (Închide) // Dacă butonul este apăsat( bt_state = dn; // Stage in Down bt_time = 0 ; // Resetează contorul de timp dacă (bt_cnt== 0 ) // Dacă prima dată în combinație( SetTimerTask(bt_ok, bt_mode_time) ; // Începe procedura de analiză bt_cnt = 1 ; // Numărătoarea a început! ) ) pauză ; // Ieșire ) case dn: ( dacă (Închidere) // Încă apăsat?(dacă (20 > bt_time) // Apăsat mai puțin de 20*20 ms?( // Da bt_time++; //Măriți contorul de iterații) else ( bt_state = al; // Nu, deja mai mult! Da, avem o presă lungă! Să mergem la AL bt_time = 0 ; // Resetați timpul de măsurare a clicurilor bt_cnt_l++; // Numără o apăsare lungă) ) else ( bt_state = sus; // Butonul este eliberat? bt_time = 0 ; // Timpul de măsurare până la zero bt_cnt_s++; // Contor de prese scurte) pauză ; // Ieșire ) caz al: // Și iată-ne dacă a fost presă lungă( dacă (Deschis) // Eliberat? ( bt_state = sus; // Da! Etapa la Sus bt_time = 0 ; // Timp la 0 bt_al = 1 ; // Am înregistrat evenimentul „Release after long”.) pauză ; ) ) SetTimerTask(bt_scan, 20 ); // Bucla prin dispecer. } // Și aceasta este o funcție care va funcționa după 2 secunde și va selecta toate rezultatele calculului void bt_ok(void) // Prindem aici decriptarea evenimentelor(switch(bt_cnt_s) // Vezi câte prese scurte sunt( cazul 1 : bt1 = 1 ; break ; // Am pus acest steag cazul 2 : bt2 = 1 ; pauză ; cazul 3 : bt3 = 1 ; pauză ; cazul 4 : bt4 = 1 ; pauză ; cazul 5 : bt5 = 1 ; pauză ; implicit: break; ) comutator (bt_cnt_l) // Vezi câte apăsări lungi sunt(cazul 1: bt_l = 1; break; // Am pus acest steag cazul 2 : bt_l2 = 1 ; pauză ; implicit: break; ) bt_cnt = 0 ; // Resetează contoarele bt_cnt_s = 0 ; bt_cnt_l = 0 ; )

#include u08 bt1,bt2,bt3,bt4,bt5,bt_l,bt_l2,bt_al; // Variabilele sunt steagurile butoanelor apăsate. // Este mai eficient să le faci să biți câmpuri // Dar am fost prea leneș. Optimizați-vă :) u16 bt_mode_time = 2000; // Durata secvenței de analiză // A făcut o variabilă, nu o constantă // astfel încât să poată fi schimbată din mers. Reducerea // unde nu este nevoie să așteptați combinații lungi // ceea ce mărește foarte mult capacitatea de răspuns a interfeței u08 bt_cnt; // Semnalizează că secvența este analizată u08 bt_cnt_s; // Contor de apasări scurte u08 bt_cnt_l; // Contor de apăsări lungi void bt_scan(void) // Această funcție este un scaner. Ar trebui apelat o dată la 20ms ( #define up 0 // Starea definițiilor mașinii. 0 - implicit. #define dn 1 #define al 3 static u08 bt_state=0; // Variabila de stare a mașinii static u08 bt_time =0; // Timp de funcționare variabil al comutatorului mașinii(bt_state) // Mașina în sine (case sus: ( if(Close) // Dacă butonul este apăsat ( bt_state = dn; // Stage in Down bt_time = 0; // Resetați timpul counter if(bt_cnt==0 ) // Dacă prima dată într-o combinație ( SetTimerTask(bt_ok,bt_mode_time); // Începe procedura de analiză bt_cnt =1; // Numărarea a început! ) ) break // Ieșire ) case dn: ( if(Close) // Încă apăsat? ( if (20 > bt_time) // Apăsat mai puțin de 20*20ms? ( // Da bt_time++; // Mărește contorul de iterații ) else ( bt_state = al; / / Nu, deja mai mult! Da, avem o apăsare lungă Să mergem mai departe în AL bt_time = 0 // Resetează timpul de măsurare a apăsării bt_cnt_l++) else ( bt_state = up; butonul eliberat bt_time = 0 // Timp de măsurare la zero bt_cnt_s++; // Contor de prese scurte ) break; // Ieșire ) case al: // Și iată-ne dacă a existat o apăsare lungă ( if (Open) // Released? ( bt_state = up; // Da! Stage at Up bt_time = 0; // Time at 0 bt_al = 1; // A înregistrat evenimentul „Release after long” ) ) SetTimerTask(bt_scan,20); // Bucla prin dispecer. ) // Și aceasta este o funcție care va funcționa după 2 secunde și va selecta toate rezultatele numărării void bt_ok(void) // Prindem aici decriptarea evenimentelor ( switch(bt_cnt_s) // Să vedem câte apăsări scurte sunt ( case 1: bt1 = 1 // Acest flag este setat la cazul 2: bt2 = 1: break 5: break (case 1: bt_l; = 1; bifează caseta 2: bt_cnt = 0;

Codul este scris în așa fel încât literalmente câteva linii sunt legate de AVR. Cel puțin în codul de gestionare a clicului butonului. Toate legăturile pentru hardware merg în antet și nu există nimic acolo:

1 2 3 4 5 6 7 8 9 10 11 #include #include #define Deschide (BTN_PIN și 1<#define Închidere (!Deschidere) extern void bt_scan(void ) ; void bt_ok(void) ; extern u08 bt1, bt2, bt3, bt4, bt5, bt_l, bt_l2, bt_al; extern u16 bt_mode_time;

#include #include #define Deschide (BTN_PIN și 1<

Așadar, portarea acesteia la o altă arhitectură este o chestiune de schimbare a două linii. Ei bine, poate fi necesar să schimbați mecanismul de rulare automată și să lansați funcția de gestionare. Dacă utilizați un fel de propriul dispecer, sistemul de operare sau alt sistem pentru organizarea firmware-ului. Dar acestea sunt două linii de cod de remediat.

Toată carnea descrisă în articol este conținută în două fișiere. butonul.cȘi butonul.h

Video cu munca

Sună
Nu mai este nevoie să te ocupi de zdrăngănit. Deoarece frecvența de scanare este scăzută, așa că chiar și butonul gol și puternic oxidat al modelului TM2 nu a scos un zgomot - s-a încheiat înainte de a începe următoarea scanare. Dar ceea ce puteți adăuga aici este protecția împotriva alarmelor false ca urmare a interferențelor. La urma urmei, de îndată ce interferența este împinsă prin linie în momentul citirii, operația unui singur clic va fi numărată. Acest lucru poate fi evitat prin verificarea stării mașinii. Să presupunem adăugarea unui contor de iterații la Up pentru a confirma în, de exemplu, două sau trei iterații că butonul este apăsat și abia apoi mergeți la Dn.

Opțiuni
Adevărat, în proiectul meu am schimbat ușor procesarea. Deoarece Nu am avut nevoie de mai multe apăsări lungi, așa că am setat indicatorul „Apăsare lungă” imediat în handlerul AL și, în același timp, am eliminat numărarea numărului de apăsări lungi. Acest lucru a făcut posibilă creșterea capacității de răspuns a interfeței dispozitivului, unde a fost folosită o apăsare lungă pentru a intra într-un element de meniu, iar combinațiile cu două apăsări lungi nu au fost folosite deloc.

Ei bine, desigur, această mașină cu stări finite poate fi folosită pentru a analiza orice. De exemplu, pentru a urmări alt semnal, de la o tastatură matrice sau un dispozitiv de semnalizare.

Sarcină: Să dezvoltăm un program pentru a controla un LED. Când butonul este apăsat, LED-ul se aprinde și când este eliberat, se stinge.

Mai întâi, să dezvoltăm o diagramă schematică a dispozitivului. Porturile I/O sunt folosite pentru a conecta orice dispozitiv extern la microcontroler. Fiecare dintre porturi este capabil să funcționeze atât ca intrare, cât și ca ieșire. Să conectăm LED-ul la unul dintre porturi și butonul la celălalt. Pentru acest experiment vom folosi un controler Atmega8. Acest cip conține 3 porturi I/O, are 2 timer/contor de opt biți și 1 de șaisprezece biți. De asemenea, la bord există un PWM cu 3 canale, un convertor analog-digital cu 6 canale pe 10 biți și multe altele. În opinia mea, un microcontroler este excelent pentru a învăța elementele de bază ale programării.

Pentru conectarea LED-ului vom folosi linia PB0, iar pentru a citi informațiile de pe buton vom folosi linia PD0. Diagrama este prezentată în Fig. 1.

Orez. 1

Prin rezistorul R2, plus tensiunea de alimentare este furnizată la intrarea PD0, care corespunde unui semnal logic. Când butonul este închis, tensiunea scade la zero, ceea ce corespunde unui zero logic. În viitor, R2 poate fi exclus din circuit, înlocuindu-l cu un rezistor de sarcină intern, introducând setările necesare în program. LED-ul este conectat la ieșirea portului PB0 prin rezistența de limitare a curentului R3. Pentru a aprinde LED-ul, trebuie să aplicați un semnal logic liniei PB0. Vom folosi un generator intern de ceas master la 4 MHz, deoarece dispozitivul nu are cerințe ridicate pentru stabilitatea frecvenței.

Acum scriem programul. Folosesc mediul de programare pentru a scrie programe AVR StudioȘi WinAvr. Deschideți AVR Studio, apare o fereastră de bun venit, faceți clic pe butonul „Creați un nou proiect”, apoi selectați tipul de proiect - AVR GCC, scrieți numele proiectului, de exemplu „cod1”, bifați atât „Creare folder project” cât și „Create” "Fișier de inițializare", faceți clic pe butonul „Următorul”, selectați „AVR Simulator” în fereastra din stânga și tipul de microcontroler „Atmega8” în fereastra din dreapta, faceți clic pe butonul „Finish”, editorul și arborele de categorii de proiecte se deschide - setările inițiale sunt finalizate.

Mai întâi, să adăugăm text de descriere standard pentru Atmega8 folosind operatorul pentru atașarea fișierelor externe: #include

sintaxa directivei #include

#include<имя_файла.h>
#include „filename.h”

Paranteze unghiulare< и >indicați compilatorului că fișierele incluse trebuie mai întâi căutate în folderul standard WinAvr numit include. Ghilimelele „ și „ îi spun compilatorului să înceapă căutarea în directorul în care este stocat proiectul.

Fiecare tip de microcontroler are propriul său fișier antet. Pentru ATMega8 acest fișier se numește iom8.h, pentru ATtiny2313 - iotn2313.h. La începutul fiecărui program, trebuie să includem fișierul antet al microcontrolerului pe care îl folosim. Dar există și un fișier antet comun io.h. Preprocesorul procesează acest fișier și, în funcție de setările proiectului, include fișierul antet necesar în programul nostru.

Pentru noi, prima linie a programului va arăta astfel:

#include

Orice program C trebuie să conțină o funcție principală. Se numește principal. Execuția programului începe întotdeauna cu execuția funcției principale. Funcția are un antet - int main(void) și un corp - este delimitată de acolade ().

int main(void)
{
organismul funcțional
}

Vom adăuga codul nostru în corpul funcției. Tipul de returnare este indicat înaintea numelui funcției. Dacă funcția nu returnează o valoare, se folosește cheia gol.

int- acesta este un număr întreg de 2 octeți, intervalul de valori este de la - 32768 la 32767

După numele funcției, parametrii care sunt transferați funcției atunci când este apelată sunt indicați între paranteze (). Dacă funcția nu are parametri, se folosește cuvântul cheie gol. Funcţie principal conține un set de comenzi, setări de sistem și bucla principală a programului.

Apoi configuram portul D la intrare. Modul de funcționare a portului este determinat de conținutul registrului DDRD(registru de direcție de transfer de informații). Scriem numărul „0x00” (0b0000000 - în formă binară) în acest registru, nimic nu este conectat la acest port, cu excepția butonului, așa că configurem întregul port D ca intrare; Puteți configura portul bit cu bit scriind numerele 0 sau 1 în fiecare bit al registrului (0-intrare, 1-ieșire), de exemplu DDRD = 0x81 (0b10000001) - prima și ultima linie a portului D funcționează ca ieșire, restul ca intrare. Rezistorul de sarcină intern trebuie de asemenea conectat. Registrul PORTx controlează dacă rezistențele interne sunt pornite sau oprite atunci când portul este în modul de intrare. Să scriem unități acolo.

Configurarea portului B spre ieșire. Modul de funcționare a portului este determinat de conținutul registrului DDRB. Nimic decât un LED la port B nu este conectat, astfel încât întregul port poate fi configurat ca ieșire. Acest lucru se face scriind la registru DDRB numerele „0xFF”. Pentru a preveni aprinderea LED-ului la prima pornire, scrieți în port B zerouri logice. Acest lucru se face prin înregistrare PORTB= 0x00;

Pentru a atribui valori se folosește simbolul „=" și se numește operator de atribuire, a nu se confunda cu semnul „egal”.

Configurația portului va arăta astfel:

DDRD = 0x00;
PORTD = 0xFF;
DDRB = 0xFF;
PORTB = 0x00;

Scriem bucla principală a programului. in timp ce(„while” din engleză) - această comandă organizează o buclă, repetând corpul buclei de mai multe ori până când condiția este îndeplinită, adică în timp ce expresia dintre paranteze este adevărată. În C, o expresie este considerată adevărată dacă nu este egală cu zero și falsă dacă este.

Comanda arată astfel:

în timp ce (condiție)
{
corpul buclei
}

În cazul nostru, bucla principală va consta dintr-o singură comandă. Această comandă atribuie registrul PORTB valoarea registrului care trebuie inversată PORTD.

PORTB = ~PIND; // ia valoarea de la portul D, o inversează și o atribuie la PORTB (scrie la PORTB)

// Expresiile C sunt citite de la dreapta la stânga

PIND registrul de introducere a informațiilor. Pentru a citi informațiile de la ieșirea externă a controlerului, trebuie mai întâi să comutați bitul dorit al portului în modul de intrare. Adică, scrieți în bitul corespunzător al registrului DDRx zero. Numai după aceasta poate fi furnizat un semnal digital de la un dispozitiv extern acestui pin. Apoi, microcontrolerul va citi octetul din registru PINx. Conținutul bitului corespunzător corespunde semnalului de pe pinul extern al portului. Programul nostru este gata și arată astfel:

#include int main (void) ( DDRD = 0x00; //port D - intrare PORTD = 0xFF; //conectați rezistența de sarcină DDRB = 0xFF; //port B - ieșire PORTB = 0x00; //setați ieșirea la 0 while(1) ) ( PORTB = ~ PIND; //~ semn de inversare pe biți ) )

Comentariile sunt utilizate pe scară largă în limbajul C. Există două moduri de a scrie.

/*Un comentariu*/
//Un comentariu

În acest caz, compilatorul nu va acorda atenție celor scrise în comentariu.

Dacă utilizați același program și conectați 8 butoane și 8 LED-uri la microcontroler, așa cum se arată în Figura 2, atunci va fi clar că fiecare bit al portului D se potrivește cu bitul său de port B. Prin apăsarea butonului SB1, HL1 se aprinde, prin apăsarea butonului SB2, HL2 se aprinde etc.

Figura 2

Articolul a folosit materiale din cartea lui A.V Belov. „Tutorial pentru dezvoltatorii de dispozitive AVR”

Aproape niciun produs cu microcontroler nu este complet fără butoane. Acest subiect este deja stricat și cunoscut în mare măsură. Scriind acest articol, nu încerc să reinventez roata. Tocmai am decis să adun toate informațiile despre circuite împreună. Cred că materialul va fi util pentru începători Pentru a nu vă încurca, figurile de mai jos nu arată circuitele de alimentare, resetare și ceas pentru microcontrolere.

Metoda unu - tradițională

fig1a fig1b

Dacă sunt puține butoane și nu lipsesc pinii MC, folosim metoda tradițională de conectare.

Când butonul este eliberat, ieșirea μ este conectată printr-un rezistor la „plusul” sursei de alimentare (Fig. 1a). Când butonul este apăsat, pinul μ este conectat la masă. Rezistorul de tragere R1 limitează curentul în circuitul comutatorului. Dacă nu ar fi acolo, atunci când apăsăm butonul pur și simplu ne-am scurtcircuita sursa de alimentare.

Cele mai multe microcontrolere moderne au rezistențe pull-up încorporate, deci nu trebuie să instalați altele externe (Fig. 1b). În programul de microcontroler, va trebui să configurați pinul pentru a fi utilizat ca intrare și să activați rezistența internă de pull-up.

Ce se întâmplă dacă pinul microcontrolerului este în modul de ieșire? Acest lucru va depinde de starea acestui pin. Dacă există un „zero logic” la ieșire, nu se va întâmpla nimic rău, deoarece în primul caz (Fig. 1a) cantitatea de curent de intrare este limitată de rezistența R1, iar în al doilea caz (Fig. 1b) nu există curent. va curge deloc. Când apăsați butonul, nici nu se va întâmpla nimic, deoarece diferența de potențial dintre ieșire și „împământare” în acest caz va fi zero.

Dacă există „unul logic” la ieșire și butonul este apăsat, atunci un curent de câteva zeci de miliamperi va curge prin ieșirea microcontrolerului la pământ și ieșirea portului se poate „arde”. Curentul maxim admisibil pentru ieșirea microcontrolerului AVR conform documentației este de 40 mA. Prin urmare, uneori este util să plasați un rezistor cu o valoare de câteva sute de ohmi, de exemplu 330, între pinul μ și buton (Figura 1c). Așa se conectează, de exemplu, butoanele de pe placa de depanare STK500. Acest lucru se face ca o plasă de siguranță, astfel încât utilizatorul să nu ardă accidental microcontrolerul în timpul experimentelor sale.

Pentru propriile tale prototipuri, totuși, poți face fără acest rezistor.

A doua metodă este utilizarea diodelor

Este folosit atunci când există mai mult de două butoane și doriți să economisiți bani la ieșirile MC. În acest caz, fiecare buton are propriul cod digital, iar numărul de butoane care pot fi atârnate în acest fel pe N pini μ = 2 N - 1. Adică 7 butoane pot fi atârnate pe trei pini, 15 pe patru pini , și așa mai departe... dar nu aș agăța mai mult de 7. Numărul de componente externe suplimentare crește, circuitul și programul microcontrolerului devin mai complicate. În plus, pentru un număr mare de butoane există și alte scheme de comutare. Rezistoarele de tragere nu sunt prezentate în diagramă, se presupune că sunt utilizate cele interne.

Apropo, prin diode puteți trimite și semnale de la butoane către ieșirea externă de întrerupere a controlerului (Fig. 3). Când este apăsat orice buton, ieșirea de întrerupere externă prin diodă va fi scurtcircuitată la masă și va provoca o întrerupere (desigur, cu condiția ca aceasta să fie configurată și activată). Astfel, controlerul nu va trebui să interogheze constant butoanele, această procedură va fi lansată numai atunci când are loc un eveniment de întrerupere extern.

Acest circuit nu este relevant pentru toate microcontrolerele AVR, deoarece la unele modele de microcontrolere poate apărea o întrerupere externă din cauza oricărei modificări pe orice pin. (de exemplu în ATmega164P)

A treia metodă este pentru o tastatură matricială

Această opțiune de conectare este de obicei utilizată pentru blocuri de mai multe butoane, care sunt combinate structural și conectate electric folosind un circuit matrice. Dar nimeni nu interzice utilizarea acestei scheme pentru a activa butoanele obișnuite, dar oferă economii reale în ceea ce privește numărul de butoane? 9.

Pinii PC0, PC1, PC2, PC3 sunt rândurile matricei, pinii PB0, PB1, PB2 sunt coloanele matricei. Butoanele pot fi interogate fie pe rând, fie pe coloană. Să presupunem că le interogăm coloană cu coloană. Procedura de sondaj va arăta astfel. Starea inițială a tuturor pinilor este o intrare cu rezistorul de pull-up pornit. Setați pinul PB0 în modul de ieșire și setați-l la zero. Acum apăsând butoanele S1, S2, S3, S4 se vor închide pinii PC0, PC1, PC2, PC3 la 0 sursă de alimentare. Interogăm aceste ieșiri și determinăm dacă vreun buton este apăsat în acest moment. Setați pinul PB0 în modul de ieșire și porniți rezistența de tragere. Setăm pinul PB1 în modul de ieșire și îl setăm la zero. Sondajăm din nou pinii PC0, PC1, PC2, PC3. Acum apăsând butoanele S5, S6, S7, S8 se vor închide pinii PC0, PC1, PC2, PC3. Interogăm ultima coloană de butoane în același mod.

Rândurile matricei pot fi conectate prin diode la pinul de întrerupere extern. Atunci logica programului ar putea fi construită astfel. Dacă tastatura nu este folosită timp de câteva minute, microcontrolerul intră în modul de putere redusă. În acest caz, pinii PB0, PB1, PB2 sunt configurați ca ieșiri cu un nivel logic zero. Când unul dintre butoane este apăsat, pinul de întrerupere este scurtcircuitat la zero prin diodă. Acest lucru provoacă o întrerupere externă, microcontrolerul se trezește și pornește un temporizator pe baza semnalelor cărora este scanată tastatura. În același timp, este pornit un numărător de timp, care este resetat atunci când oricare dintre butoane este apăsat. De îndată ce depășește, microcontrolerul intră din nou în modul de consum redus.

Așa că am ajuns la o parte integrantă a majorității proiectelor de microcontrolere - butoanele. Un buton este un dispozitiv destul de simplu care, de regulă, are doar două stări în limbajul de programare, aceasta este starea 1 logic (contacte închise) și 0 logic (contacte deschise); Să ne uităm la diagramă.

Avem în continuare aceeași schemă cu indicatori cu șapte segmente, dar au fost adăugate 4 butoane. Cu ajutorul butoanelor grupei A vom crește sau micșora valoarea afișată pe primii trei indicatori, iar folosind butoanele grupului B vom modifica valoarea pe ultimii doi indicatori.

Mai întâi, să vorbim pe scurt despre butoane. Butoanele pot fi folosite cu contacte normal deschise fără blocare. Conectăm un contact la masă, iar celălalt la pinii separați ai microcontrolerului. Nu vom instala un rezistor pull-up la pozitiv, deoarece unul este furnizat chiar în microcontroler. Tot ce rămâne este să scrieți un program pentru a interoga butoanele (stările pinii microcontrolerului) și să afișați rezultatul pe indicatori. Datorită simplității schemei și dificultăților cititorilor în înțelegerea programului C,Accentul principal în această secțiune va fi pe analiza programului.

Deci programul.

#include //include biblioteci #include #define SPI_SS PB2 //Ieșire SS #define SPI_MOSI PB3 //Ieșire MOSI #define SPI_MISO PB4 //Ieșire MISO #define SPI_SCK PB5 //Ieșire SCK #define BUTTON_AP PD4 //Ieșire butonul A+ #define BUTTON_AM PD5 //Ieșire buton A - #define BUTTON_BP PD6 //ieșire butonul B+ #define BUTTON_BM PD7 //ieșire butonul B- char di; void spi(char cmd,char data) //Funcție pentru transmiterea a două pachete de 8 biți prin protocolul SPI ( PORTB &= ~(1<999)a=999; //verifică dacă variabila a a atins valoarea maximă dacă (a<0)a=0; //проверка достижения минимального значения переменной a if(b>99)b=99; //verifică dacă valoarea maximă a variabilei b a fost atinsă dacă (b<0)b=0; //проверка достижения минимального значения переменной b } return 0; }

Să începem. programul CDe obicei, încep prin a conecta biblioteci externe. Două linii din program sunt responsabile pentru aceasta:

#include #include

avr/io.hAceasta este o bibliotecă I/O care va explica compilatorului ce porturi I/O are microcontrolerul, cum sunt etichetate și de ce sunt capabili. Și cel mai interesant lucru este că această bibliotecă în sine selectează din setările proiectului pentru care microcontroler trebuie aplicate descrierile, ceea ce vă permite să utilizați această bibliotecă pentru diferite microcontrolere. Această bibliotecă trebuie inclusă mai întâi.

A doua bibliotecă folosită frecvent este util/delay.hajută la crearea de întârzieri în execuția programului, ceea ce este destul de convenabil.

#define BUTTON_AP PD4

indică ceea ce este pe ieșirePD4 (registru cu 4 porturiB)vom conecta butonulA+ (vezi diagrama). Acest lucru este necesar, astfel încât, dacă decidem brusc să comutăm butonul la o altă ieșire, să nu fie nevoie să căutăm în tot programul, ci doar să îl schimbăm îndefininume de ieșirePD4la cea dorită și va rămâne în programBUTTON_AP.Dar în cazul concluziilor pentruSPInimic nu poate fi schimbat din cauza sprijinuluiSPIhardware și este legat rigid de pinii microcontrolerului de către producător.

Următoarea parte interesantă a programului este descrierea porturilor.

DDRB |= (1<

Acesta este modul în care biții listați ai portului au fost comutați la ieșireB(în mod implicit, toți biții din toate porturile sunt setați la intrare). EAcea linie poate fi scrisă după cum urmează, fără a utiliza definiția macro îndefini

DDRB |= (1<

Aceste înregistrări sunt echivalente și au scopul de a înlocui 1 în registruDDRBla categoria corespunzătoareBR3 (rangul 3), BR5 (rangul 5) ȘiBR2 (rangul 2). Alte părți ale registruluiDDRBramane neschimbat. De asemenea, puteți scrie această linie astfel

DDRB |= (1<<3)|(1<<5)|(1<<2);

ceea ce desigur este mai confuz.

Intrarea 1<<3 înseamnă că unitatea binară trebuie deplasată la stânga cu 3 poziții,pozițiile din dreapta sunt umplute cu zerouri.

1 <<3 ar însemna 1000 (un zero zero zero) în binar. Și la efectuarea operației(1<<3)|(1<<5) obținem 101000 în binar.

În următoarea linie a programului conectăm rezistențe de tragere pentru butoane. Să scriem unitățile în biții corespunzători ai registruluiPORTD

PORTD |= (1<

Aceste rezistențe sunt încorporate în microcontroler. Aceștia pot fi conectați la biți de port, cu condiția ca acești biți să fie definiți ca intrare. Rezistoarele trag pinul dorit al microcontrolerului la logica 1.

Setarile ramase se fac in acelasi mod folosind registrele corespunzatoare ale microcontrolerului.

Acum să ne uităm la partea principală a programului pe care microcontrolerul, după setările de bază, o execută la nesfârșit. Această parte a programului este închisă într-o buclă nesfârșită.

În timp ce(1) ( );

Toate liniile de program cuprinse între acolade vor fi executate într-un cerc. În interiorul acestei bucle fără sfârșit, numărul zecimal este defalcat pe biți pentru a fi afișat în cifre individuale ale indicatorului cu șapte segmente. După împărțire, trimitem date pe biți prinSPIpentru fiecare indicator individual.

Delay_ms(100);

pentru a preveni declanșarea falsă de la „saritul” contactelor butonului. Și pentru a doua oară interogăm butoanele despre apăsarea lor și verificăm steagurile setate anterior despre apăsare. Dacă sunt îndeplinite condițiile, schimbăm valorile afișate pe indicatori. Apoi resetam steagurile de apăsare a butonului la 0.După care ciclul se repetă.

În partea următoare - finală - se va lua în considerare ADC (convertorul analog-digital) și aplicarea a tot ceea ce s-a studiat anterior în practică.

Conectarea unui buton la o linie de port I/O

După ce ați studiat acest material, în care totul este descris în detaliu cu un număr mare de exemple, puteți stăpâni și programa cu ușurință porturile de intrare/ieșire ale microcontrolerelor AVR.

Vom lua în considerare un exemplu pe un microcontroler ATMega8 .

Vom scrie programul în Atmel Studio 6.0 .

Vom emula circuitul în Proteus 7 Professional .

Cea mai comună sarcină la crearea proiectelor de microcontroler este conectarea butoanelor. În ciuda simplității sale, această sarcină are caracteristici semnificative, poate neevidente.
Dacă conectați unul dintre contactele butonului, de exemplu, la firul comun („împământare”), iar al doilea la linia selectată a portului de intrare/ieșire al microcontrolerului, care este comutat în modul „Intrare”, va afla că această metodă nu funcționează. Când apăsați butonul, linia portului microcontrolerului este conectată la masă, iar programul va citi „0” logic din această linie portului I/O, dar când butonul este eliberat, pinul microcontrolerului nu va fi conectat la nimic, ceea ce este adesea numit „atârnat în aer”. În acest caz, programul va citi de la ieșire atât logica „0” cât și logica „1” aleatoriu, deoarece interferența vor fi induse pe o linie neconectată a portului I/O.
Conexiunea corectă presupune că, în stare deschisă, pinul microcontrolerului trebuie conectat printr-un rezistor, de exemplu, la magistrala de alimentare, iar în stare închisă - la masă sau invers. Rezistența rezistorului nu trebuie să fie prea mică, astfel încât curentul care circulă prin acesta atunci când contactele butonului sunt închise să nu fie prea mare. De obicei, se folosesc valori de ordinul 10-100 kOhm.

Fig: Conexiuni ale unui buton cu o magistrală de alimentare strânsă.

- când butonul este apăsat, este egal cu „1”;
- când butonul este apăsat, este egal cu „0”;

Fig: Conexiuni butoane cu masă conectată.
Cu această conexiune, starea liniei portului I/O va fi:
- când butonul este apăsat, este egal cu „0”;
- când butonul este apăsat, este egal cu „1”;

- conectarea la linia portului de intrare/ieșire a unui buton cu o magistrală de alimentare trasă în sus:

#include // Programul principal int main(void) ( // Configurați porturile de intrare/ieșire DDRB = 0b11111111; // Configurați toți biții portului B în modul „Ieșire” PORTB = 0b00000000; // Setați toți biții portului B în log „0” (Tensiunea la ieșirea portului este egală cu GND) DDRD = 0b00000000 //Setați toți biții portului D la modul „Intrare” PORTD = 0b11111111 //Setați toți biții portului D la „1”; (Tensiunea la ieșirea portului este egală cu Vcc) / / Buclă eternă while (1) ( //Verificați: dacă starea PD0 este logică „0” atunci butonul este apăsat dacă ((PIND&(1)<< PD0)) == 0) { //Состояние PB0 устанавливаем в лог.«1» PORTB |= (1 << PB0); } else { //Состояние PB0 устанавливаем в лог.«0» PORTB &= ~(1 << PB0); } } }

- conectarea la linia portului I/O a unui buton cu o masă conectată:

// Include biblioteci externe #include #include // Programul principal int main(void) ( // Configurați porturile de intrare/ieșire DDRB = 0b11111111; // Configurați toți biții portului B în modul „Ieșire” PORTB = 0b00000000; // Setați toți biții portului B în log „0” (Tensiunea la ieșirea portului este egală cu GND) DDRD = 0b00000000 //Setați toți biții portului D la modul „Intrare” PORTD = 0b11111111 //Setați toți biții portului D la „1”; (Tensiunea la ieșirea portului este egală cu Vcc) / / Buclă eternă while (1) ( //Verificați: dacă starea PD0 este logică „1” atunci butonul este apăsat dacă ((PIND&(1)<< PD0)) == 1) { //Состояние PB0 устанавливаем в лог.«1» PORTB |= (1 << PB0); } else { //Состояние PB0 устанавливаем в лог.«0» PORTB &= ~(1 << PB0); } } }