Calculator aritmetic cu facilităţi avansate, implementat cu ATmega32.
Proiectul ales de mine este un calculator aritmetic cu funcţii avansate. Scopul în sine al proiectului este mai mult unul didactic, deoarece pe piaţă există foarte multe modele compacte de calculatoare de buzunar.
Ideea de la care am plecat este simplă: întotdeauna mi-am dorit să ştiu cum se construieşte un calculator de buzunar.
Deşi nu cred ca ar avea o utilitate reală practică, datorită suprasaturării acestei pieţe, totuşi consider că proiectul este un foarte bun proof of concept şi totodata îl consider a fi extrem de util pentru mine în mod special, prin acumularea de noi cunoştiinte în domenul embedded.
Nu în ultimul rând, să nu uităm totuşi că puterea dispozitivelor de calcul de pe vremea când Neil Armstrong a păşit pentru prima dată pe lună, este comparabilă cu cea a calculatorului construit de mine, astazi!
Schema bloc a proiectului este:
Calculatorul are 32 de taste. Ele sunt alocate astfel:
Am ales microcontrollerul ATmega32 datorită spaţiului mai mare de memorie pus la dispoziţie.
În momentul în care memorăm un număr (Memory Store), LED-ul se aprinde. Iar când eliberăm memoria (Memory Clear), LED-ul se stinge.
Pe lângă componentele pentru plăcuţa de baza, a mai fost necesar să cumpăr:
Pentru alimentarea ecranului LCD, am legat:
Datorită faptului că pinii 16 (PD2) şi 17 (PD3) ai microcontrollerului sunt legaţi atât la una din tastaturi, cât şi la pinii de date ai mufei USB, pentru o funcţionare corectă, trebuie ca alimentarea în timpul utilizării să se facă exclusiv utilizând un adaptor de curent continuu.
Am lucrat în Windows 7 Professional 32bit. Mi-am instalat Cygwin pentru simularea mediului Linux în line de comandă Windows şi WinAVR pentru compilarea bootloaderului şi a programului în sine. Am copiat executabilul avrusbboot.exe in directorul C:\Windows, pentru a îl putea accesa cu uşurinţă din orice alt director. Ca editor, am folosit Editra.
Codul scris de mine este bogat în comentarii, astfel încat să poată fi uşor de înteles. Practic, programul scris de mine simulează existenţa unor regiştrii.
Funcţia main() arată astfel:
int main() { char key; LCD_init(); init(); //initializari DDRB |= (1<<PB4); //setam portul ledului ca fiind de output PORTB &= ~(1<<PB4); //stingem LEDul //initializam tastatura 1 keyportddrD = 0xF0; //setam PD4 PD5 PD6 PD7 ca fiind de output si PD0 PD1 PD2 PD3 de input keyportD = 0x0F; //setam rezistenta de pullup pe PD0 PD1 PD2 PD3 //initializam tastatura 2 keyportddrC = 0xF0; //setam PC4 PC5 PC6 PC7 ca fiind de output si PC0 PC1 PC2 PC3 de input keyportC = 0x0F; //setam rezistenta de pullup pe PC0 PC1 PC2 PC3 _delay_ms(DEBOUNCETIME); afiseaza(); while (1) { key = get_key(); if (key != FALSE) { key = translate_key(key); proceseaza(key); // procesam caracterul afiseaza(); // afisam starea curenta } } return 0; }
API tastaturi:
char get_key(); //preia codul unei taste char translate_key(char key); //translateaza codul unei taste intr-un caracter de trimis functiei proceseaza.
Prototipurile funcţiilor utilizate de calculator sunt:
//Nota preliminara: o tasta genereaza un caracter ([A-P] sau [a-p]). void proceseaza(char input); //functia preia un caracter de la tastatura si apeleaza functia de procesare corespunzatoare void proceseazaCifra(char input); //functia trateaza caracterele cifre void proceseazaOperator(char input); //functia trateaza tastele + - * / si ridicare la putere void proceseazaEgal(); //functia trateaza caracterul = void marcheazaPunct(); //functia trateaza tasta . (punct zecimal) void C_CE(); //functia trateaza caracterul (tasta) C/CE void fTrigonometrice(char input); //functia trateaza tastele sinus, cosinus, tangenta, cotangenta, arcsinus, arccosinus, arctangenta void calculeazaRadical(); //functia trateaza tasta radical void schimbaSemn(); //functia trateaza tasta de schimbare de semn +/- void memorie(char input); //functia trateaza tastele "memory store", "memory read" si "memor clear" void paranteze(char input); //functia trateaza caracterele ( ) void init(); //functia se ocupa cu initializari de variabile void afiseaza(); //functia trimite un string catre API-ul ecranului double calculeaza(); //functia se ocupa de calculul a ceea ce se afla stocat in cei 3 registrii acum
În implementarea calculatorului, am utilizat variabile globale. Acestea sunt:
#define SIZESTIVA 20 //Nota preliminara: Programul simuleaza oarecum lucrul cu registri. double reg[2]; // Avem doi registrii pt operanzi. Registrul pentru operandul stang si cel pentru operandul drept. char regOp; // Avem un al treilea registru pentru stocarea operatorului. short int selReg; // Acest registru select ne spune cu care din cei 3 registrii de lucru operam la momentul respectiv. short int stareReg[3]; // Acest vector ne spune starea de incarcat/descarcat al celor 3 registrii short int vfStiva; // Ne indica varful stivei si implicit numarul de elemente din stiva. double rezStiva[SIZESTIVA]; // Aici stocam operandul stang (rezultatul calculabil pana in momentul respectiv), adica primul registru de lucru. char opStiva[SIZESTIVA]; // Aici stocam operatorul pasului respectiv, adica al treilea registru de lucru. double outputBuffer; // Valoarea afisata pe LCD. short int buff_repr_rez_egal; // Ne indica daca valoarea de pe ecran (deci din outputBuffer) a fost obtinuta in urma apasarii tastei egal. unsigned long int mascaZecimale; // Masca pentru zecimale: 1, 10, 100, etc. short int punctActivat; // Ne indica daca s-a introdus un punct (tasta punct zecimal) de la tastatura. short int contorInput; // Contorizeaza cate caractere au fost introduse, la introducerea unui numar. short int C_CE_contor; // Contorizeaza numarul de apasari ale tastei C/CE. double mem = 0; // Registru pentru memorare rezultat. short int stareMem = 0; // Indica starea registrului mem.
În mare, am realizat ceea ce mi-am propus: un calculator aritmetic cu funcţii avansate, complet funcţional.
Pentru o viitoare îmbunătăţire a proiectului, se poate monta o baterie de 9 volti, care să confere portabilitate calculatorului.
Descărcare arhivă zip cu codul sursă.
După ce se descarcă şi se despachetează arhiva zip, pentru compilare se foloseşte comanda make în consolă. Pentru programarea plăcuţei, se selectează din jumper alimentarea de la USB, se introduce cablul USB în plăcuţă, şi în maxim 30 de secunde cât rulează bootloaderul, se dă comanda avrusbboot calculator.hex.
După programare, se scoate cablul USB, se setează din jumper modul de alimentare pe adaptor, se introduce adaptorul şi se aşteaptă cele 30 de secunde până când bootloaderul predă execuţia programului nostru. În acest moment, cifra 0 ar trebui să apară pe ecranul LCD.
Pentru plăcuţa de bază:
Pentru partea a doua a proiectului:
Bootloader USB ATmega32:
Data: 2 Iunie 2010