Un controler industrial are nevoie de o tastatură pentru programarea unor mărimi sau executarea unor comenzi date de operator. Uneori sunt suficiente doar câteva comutatoare BCD, alteori este necesară şi citirea unor senzori cu ieşire digitală. Iată de ce un sistem cu microcontroler trebuie să fie dotat cu intrări digitale, rezolvate prin utilizarea porturilor în mod intrare.
Oricare din porturile P0, P1, P2 şi P3 poate fi programat în mod intrare, ba mai mult se pot programa doar câteva linii ale unui port în mod intrare, celelalte linii putând fi folosite în continuare în mod ieşire. Deși avem la dispozitie un număr maxim de 32 de linii intrare/ieşire, de cele mai multe ori (vezi articolele precedente) sunt disponibile maxim 12: portul P1 şi jumătate din portul P3. Cum diferențele între porturi sunt nesemnificative, exemplificările se vor referi la portul P1, putând fi extinse şi asupra celorlalte porturi.
La conectarea alimentării sau după activarea RESET, registrele SFR asociate porturilor sunt iniţializate cu toţi biții pe ‘1’ logic, ceea ce are ca efect dezactivarea tranzistorului MOS din driverul de ieşire al liniei unui port (vezi fig.1 din articolul precedent). Aceasta este echivalentă cu tragerea la ‘1’ logic a tuturor liniilor de către rezistența internă de pull-up, însă datorită tragerii slabe (rezistența internă având valori mari), un circuit extern microcontrolerului poate trage linia portului la ‘0’ logic. În această situaţie, circuitul extern va trebui să preia curentul furnizat de linia portului, datorat rezistenţei de pull-up interne, a cărui valori sunt de ordinul 50 µA pentru familia CMOS şi 500 µA pentru NMOS.
Putem avea următoarele situaţii:
a) circuitul extern în ‘1’ logic şi linia portului trage la ’1′ logic, fără îndoială citirea pinului portului va indica ’1’ logic;
b) circuitul extern în ’0’ logic şi linia portului trage la ’1’ logic, citirea pinului portului va indica ’0’ logic cu condiţia ca circuitul extern să preia curentul generat de linia portului, lucru perfect posibil de către oricare din familiile de circuite logice.
În concluzie, funcţionarea porturilor microcontrolerului în mod intrare necesită înscrierea în latch-ul asociat a valorii ’1’ logic şi apoi citirea pinului portului, cu respectarea condiţiilor prezentate mai înainte.
Portul P0 diferă de cele prezentate prin faptul că nu are trageri la ’1’ interne decât în timpul accesării memoriei externe. Astfel, dacă este folosit ca port de intrare, portul P0 va avea linii flotante, acestea putând fi folosite ca intrări de impedanţă ridicată. Iată de ce P0 este considerat cu adevărat ca port bidirecțional, pe când P1, P2 şi P3, deoarece au trageri la ‘1’ logic fixe, sunt numite cvasi-bidirecționale.
Am văzut că, la punerea sub tensiune, toate porturile sunt configurate ca intrări, după aceea ele putând fi reconfigurate scriind ‘0’ la pinii de ieșire unde se doreşte a scoate ‘0’ logic. Când se scrie o nouă valoare la pinii de ieșire ai portului, trebuie rescris ’1’ la toţi pinii care trebuie să rămână intrări. Bineînțeles că se poate lucra și la nivel de bit individual, cu fiecare linie.
Instrucţiunile folosite cu precădere pentru configurare/reconfigurare sunt cele de tipul citeşte-modifică-scrie. Să presupunem că bistabilul asociat unei linii de port conţine valoarea ‘1’ logic, ceea ce implică tragerea sus a liniei portului, echivalentă cu a emite ’1’ logic în mod ieşire sau cu a permite citirea pinului portului în mod intrare. Instrucțiunile de citire-modifică-scrie, deoarece citesc bistabilul, vor lăsa nemodificați pinii configurați ca intrări, cand se execută scrierea (dacă nu se doreşte altfel prin instrucţiune). Astfel plecând din starea cu toţi biţii în ‘1’ logic, executând un ‘ȘI’ între ‘1’ şi biţii care să rămână intrări, pe de o parte, şi între ’0’ şi biţii care să devină ieşiri, pe de altă parte, se va schimba această configuraţie.
De exemplu ANL P1,# 11110000B configurează P1.0 ÷ P1.3 ca ieşiri emițând ’0’ logic și lasă neschimbați P1.4 ÷ P1.7 ca intrări. Dacă mai târziu se doreşte ca P1.2 să devină intrare şi P1.7 ieşire se execută succesiunea:
ORL P1,# 00000100B ; face P1.2 intrare
ANL P1,# 01111111B ; face P1.7 ieşire
sau acelaşi lucru se putea obține cu instrucțiuni la nivel de bit:
SETB P1.2 ; face P1.2 intrare
CLR P1.7 ; face P1.7 ieşire.
Se impune o observație cu privire la portul P3, unde trebuie avut în vedere faptul că funcțiile alternate ale liniilor portului sunt generate doar dacă pinii respectivi sunt configurați ca intrări.
Citirea porturilor (a informaţiei prezente la pinii porturilor) se face simplu prin instrucţiuni la care portul sau pinul sunt operanzi sursă, de exemplu:
MOV A, P1 ; citeşte portul P1
; și transferă
; rezuItatul
; citirii în
; acumulator
MOV C, P1.6 ; citeste pinul
; P1.6 şi transferă
; rezultatul
; citirii în
; indicatorul
; carry.
În cazul în care la intrări sunt conectate circuite externe care nu forţează la ’1’ logic intrarea (de exemplu open-colector), singura tragere la ’1’ este formată din rezistenţa de pull-up internă a portului, ceea ce face ca timpul tranzitoriu de trecere de ’0’ la ’1’ să fie destul de mare şi uneori să deranjeze. Se recomandă conectarea în acest caz a unor rezistenţe externe de pull-up cu valori de ordinul 1K ÷ 10K depinzând de aplicaţie.
În figura 1 s-a exemplificat conectarea la portul P1 a unui comutator BCD, a unui comutator DIP – SWITCH dublu, a unui senzor tip capăt de cursă realizat cu optocuplor şi a unui inversor 74LS14.
Citirea informaţiei furnizate de comutatorul BCD se realizează astfel:
MOV A, P1 ; citeşte portul
; P1, transferă în
; acumulator
ANL A, # 0FH ; maschează biţii
; 4 ÷ 7 punându-i pe
; 0,
obţinând în acumulator exact valoarea în cod BCD pe 4 biţi a poziţiei comutatorului. Prezenţa reţelei rezistive RN1 se recomandă în cazul conectării la distanţă a comutatorului faţă de microcontroler sau la utilizarea unui microcontroler CMOS.
Testarea celorlalte intrări se pretează a fi realizată prin instrucţiuni la nivel de bit:
JB P1.4, ETICHETA1 ; testează bitul P1.4 şi dacă este setat
; (‘1' logic - comutator DIP1.1 OFF) salt la
; 'ETICHETA1', altfel continuă
JNB P1.5, ETICHETA2 ; testează bitul P1.5 şi dacă nu este setat
; (‘0′ logic – comutator DIP1.2 ON) salt la
; ’ETICHETA2′, altfel continuă
JB P1.6, ETICHETA3 ; testează bitul P1.6 și dacă este setat
; (‘1′ logic = fototranzistor blocat ~ flux
; luminos întrerupt = capăt cursă) salt la
; ’ETICHETA3′, altfel continuă
JNB P1.7, ETICHETA4 ; testează bitul P1.7 şi dacă nu este setat
: (’0’ logic = ’1’ logic la intrarea 74LS14) salt
; la ‘ETICHETA4′, altfel continuă
ETICHETA5: …. ; secvenţă executată în ipoteza DIP1.1- OFF,
; DIP1.2 – ON, nu este capăt de cursă și la
; intrarea 74L514 este ’0’ logic.
Bineînteles că între două teste se puteau intercala și alte secvențe de program, după necesități. Se remarcă, în cazul instrucţiunilor la nivel de bit, realizarea într-o singură instrucţiune a citirii pinului portului, a testării sale şi a saltului conform rezultatului testării, fără a fi nevoie de nicio mascare suplimentară.
Un alt exemplu tipic de utilizare a porturilor în mod intrare este dat în figura 2, unde la portul P1 şi 1/2 din portul P3 s-a conectat o tastatură ’Iiniară’ gen telefon cu 12 taste, fiecare având conectat la masă unul din capete.
Programul pentru scanarea unei asemenea tastaturi, scris sub forma unei subrutine este:
TAST: MOV A, P1 ; citeşte portul P1, primele 8 taste şi depune
; rezultatul citirii în acumulator
CPL ACC ; complementează acumulatorul, tastă apăsată
; echivalent cu '1' logic
JZ TAST1 ; testează acumulatorul, dacă este zero (nici o
; tastă apăsată din primele 8) salt la TAST1,
; altfel continuă
MOV R0, # 00H ; una din primele 8 taste apăsată, încarcă
; contor cu valoarea 00h
SJMP SCAN ; salt la algoritm scanare
TAST1: MOV A, P3 ; citeşte portul P3, ultimele 4 taste şi depune
; rezultatul citirii în acumulator
ORL A, # 11000011B ; maschează liniile portului P3 care nu
; au legate taste, punându-le în ‘1’ logic
CPL ACC ; complementează acumulatorul, tastă apăsată
: echivalent cu ‘1’ logic
JZ TAST ; testează acumulatorul, dacă este zero (nici o
; tastă apăsată din ultimele 4) salt la TAST,
; reluând citirea stării tastelor până când se
; apasă o tastă
MOV R0, # 08H ; una din ultimele 4 taste apăsată, încarcă
; contor cu valoarea 08h
RR A ; roteşte acumulatorul la dreapta cu 2 poziții,
; aducând starea tastelor în poziția biţilor
; 0.3
RR A
SCAN: JB ACC.0, COD ; testează bitul 0 al acumulatorului, dacă
; este setat (tastă apăsată) salt la COD, altfel
; continuă
RR A ; roteşte la dreapta acumulatorul cu o poziţie
INC R0 ; incrementează contorul cu 1
SJMP SCAN ; reia scanare
COD: MOV A, R0 ; încarcă contorul în acumulator
MOV DPTR, # CODURI ; încarcă registrul DPTR cu adresa de
; început a tabelei de coduri asociate tastelor
MOVC A,@A+DPTR ; citeşte codul tastei din tabelul ‘CODURI’ cu
; deplasamentul dat de valoarea iniţială a
; acumulatorului şi pune codul citit în
; acumulator
RET ; revenire din subrutină
CODURI: DB 31H, 32H, 33H
DB 34H, 35H, 36H
DB 37H, 38H, 39H
DB 2AH, 30H, 23H
Se impun câteva observaţii:
– subrutina, odată apelată, aşteaptă apăsarea unei taste şi nu revine în programul principal decât după apăsarea oricăreia din cele 12 taste;
– după decodificarea unei taste nu se aşteaptă eliberarea tastei, ci se revine în programul principal, făcând posibilă citirea repetată a aceleiaşi taste apăsate prelungit;
– algoritmul de scanare se opreşte la prima tastă apăsată, ignorând alte taste de rang inferior apăsate simultan; scanarea începe întotdeauna cu tastele conectate la portul P1, în ordinea P1.0 la P1.7 şi apoi P3.2 la P3.5;
– codurile din tabel pot fi alese după dorinţă, însă ordinea din tabel trebuie pusă în corespondenţă cu ordinea de scanare; se poate renunţa la tabelul de coduri, folosind ca rezultat la ieşirea din subrutină contorul scanării, a cărui valoare este în corespondenţă directă cu ordinea de scanare de la 00H la 0BH;
– dezavantajul unei asemenea tastaturi liniare rămâne, fără îndoială, ocuparea unui număr destul de mare de linii de port, dar în ipoteza utilizării unei tastaturi telefonice gata fabricată, având tastele conectate în acest mod, este soluţia care se impune.
În cazul în care se dispune de taste individuale şi se construieşte o tastatură, se recomandă organizarea acesteia sub forma unei matrici cu linii şi coloane, la intersecţia dintre acestea fiind plasată câte o tastă. Astfel dacă se alege, de exemplu, o matrice 4×4, rezultă folosirea a 16 taste şi sunt necesare doar 8 linii de port (P1), faţă de 16 linii cât ar fi solicitat varianta liniară.
Algoritmul cel mai des folosit pentru scanarea unei tastaturi de tip matrice presupune că liniile tastaturii sunt conectate la un port de intrare, iar coloanele sunt comandate de un port de ieşire. Aplicând ‘0’ logic la toate coloanele, se citesc liniile pentru a determina momentul când se apasă o tastă. Dacă nu s-a apăsat nici o tastă, se va citi ’1’ logic peste tot. În cazul în care s-a apăsat cel puţin o tastă, una dintre linii va fi conectată cu o coloană şi deci va fi citită în ’0’ logic.
După detectarea situaţiei tastă apăsată, se aplică ’0’ logic doar la prima coloană şi ’1’ logic la celelalte. Se citesc liniile, iar dacă toate sunt în ’1’ logic înseamnă că nu avem tastă apăsată pe prima coloană şi se trece la a doua coloană aplicând ‘0’ logic , celelalte fiind în ’1’ logic. Se va repeta scanarea coloanelor până când la activarea unei coloane cu ’0’ logic se va citi una dintre linii diferită de ‘1’ logic. Determinând care linie este în ’0’ logic, rezultă perechea ( coloană, linie ) la intersecţia cărora se află tasta apăsată.
O soluţie ingenioasă care pune în evidenţă facilităţile oferite de portul P1 este prezentată în figura 3, unde s-a implementat conectarea unei tastaturi 8×3 prin intermediul unui multiplexor, simultan cu conectarea unui afişaj de tipul celui prezentat în articolul precedent.
Se remarcă folosirea pentru comanda afişajului şi a liniei P1.3 pe post de linie de validare: ’0’ logic validează lucrul cu afisajul; ’1’ logic permite prin inversorul U8F trecerea intrării INH a U9 4051 în ‘0’ logic şi deci ieşirea din înaltă impedanță a ieşirii OUT a U9, permiţând citirea liniilor tastaturii prin P1.7.
Diodele D2, D3, D4 s-au introdus pentru ca apăsarea simultană a două taste de pe aceiaşi linie să nu perturbe înscrierea datelor în afişaj. Adresa liniei care este conectată prin multiplexor la P1.7 se obţine prin comanda intrărilor A, B, C ale U9 de către P1.0, P1.1, P1.2 şi cu condiţia ca P1.3 să fie ‘1’ logic, deci adrese de la ’8’ la ‘F’.
Coloanele sunt baleiate prin comanda dată de P1.4, P1.5, P1.6 prin tragere la ’0’ logic cu ajutorul diodei corespunzătoare.
P1.7 este singura linie a portului P1 care funcţionează în acest caz în mod intrare, asigurând citirea cu ajutorul multiplexorului U9 a celor 8 linii ale tastaturii.
Transpunerea algoritmului de scanare într-o subrutină de scanare se prezintă astfel:
TAST: MOV P1,# 10001000B ; P1.7 în mod intrare, toate coloanele
; pe ’0’, linia 0 aşteaptă apăsarea unei
; taste
WAIT JNB P1.7, TAST1 ; tastă apăsată = ‘0’ logic, salt la
; TAST1, altfel continuă
INC P1 ; incrementează adresă linie
MOV A, P1
CJNE A,# 8FH, WAIT ; testează dacă s-a ajuns la adresa
; ultimei linii
JNB P1.7, TAST1 ; testează ultima linie
SJMP TAST ; reia de la linia 0
TAST1: ACALL TEMP ; temporizare cca. 30ms pentru
; eliminarea vibrațiilor tastelor
; scanare coloane, lasă nemodificată adresa linie, pune pe ‘1’ toate
; coloanele, pune pe ‘0’ doar o coloană
SCANC1: ORL P1, # 01110000B ; scanare prima coloană
ANL P1, # 11101111B
JB P1.7 , SCANC2 ; tastă neapăsată = ‘1’ logic, salt la
; SCANC2, altfel continuă
MOV R0, # 00H ; contor = 0
SJMP COD ; salt la COD
SCANC2: ORL P1, # 01110000B ; scanare a doua coloana
ANL P1, # 11011111B
JB P1.7, SCANC3 ; tastă neapăsată = ’1’ logic, salt la
; SCANC3, altfel continuă
MOV R0, # 08H ; contor = 8
SJMP COD ; salt la COD
SCANC3: ORL P1, # 01110000B ; scanare a treia coloană
ANL P1, # 10111111B
JB P1.7, TAST ; tastă neapăsată = ’1’ logic, slt la
; TAST, altfel continuă
MOV R0, # 10H ; contor = 16
COD: MOV A, P1
ANL A, # 07H ; refă adresa linie
ORL A, R0 ; adună valoarea coloană
PUSH ACC ; salvează indexul tastei în stivă
;aşteaptă eliberare tastă
MOV P1, # 10001000B
WAIT1: JNB P1.7, WAIT1
INC P1
XCH A, P1
CJNE A, # 8FH, WAIT1
WAIT2: JNB P1.7, WAIT2
ACALL TEMP ; temporizare la eliberare tastă
MOV P1, # 07H ; P1.7 în mod ieşire, data = 0000 şi
; adresa = 7 penţru afişaj
POP ACC ; restaurează indexul taste;
INC ACC ; incrementează index cu 1
MOVC A, @A+PC ; citește cod tastă din tabela coduri
RET
CODURI: DB 1BH, 17H, 13H, 08H, 04H, 01H, 14H, 09H
DB 1AH 16H, 12H, 07H, 03H, 00H, 05H, 1EH
DB 19H, 15H, 11H, 06H, 02H, 1DH, 1CH, 18H
Bibliografie:
1. Philips Semiconductors – 80C51-based 8-bit Microcontrollers, 1994;
2. MHS — 8 bit Microcontrollers, 1989;
3. Microelectronica — Data Book. Mos Integrated Circuits, Bucureşti 1989;
Articol publicat in revista RET nr, 19