Vajalikud teadmised: [HW] Kasutajaliidese moodul, [AVR] Digitaalsed sisendid-väljundid, [LIB] Sisend-väljundviigud, [LIB] Viide, [PRT] Lüliti
Nii nagu lüliteid tutvustavas peatükis öeldud, eksisteerib mehhaaniliste lülititega kontaktide põrkumise probleem. Kuna kontaktid on tehtud metallist, millel on teatav elastsus, siis kontaktide ühendamise või lahti-ühendamise hetkel need põrkuvad ning tulemusena tekib hulga väärlülitusi. Lülituste arv ja kestus sõltub lülitist, kuid enamasti jääb see mõne millisekundi piiresse. Kui lülitit kasutatakse mõne elektriseadme käivitamiseks, siis pole see suur probleem, kuid kui lüliti on kasutusel seadme juhtimiseks, võib mitmekordne lülitumine probleemiks olla.
Põhiline kontaktide põrkumisest tekkinud väärlülitusi välistav meetod on filtreerimine. Filtreerida saab nii elektriliselt kui ka tarkvaraliselt. Elektriliseks filtreerimiseks tuleb lüliti ühendada läbi madalpääsu filtri – näiteks RC filter - mis silub pingemuutusi ja sellest tulenevalt ei omanda mikrokontrolleri viik hetkelisi väärtusi. Tarkvaraline filtreerimine toimub põhimõttel, et viigu, kuhu lüliti on ühendatud, väärtust loetakse mitmel korral ja kui kõikidel kordadel on viigu väärtus sama, siis sellest järeldatakse, et lüliti omab kindlat asendit ning tegu pole põrkumisega. Igasugusel filtreerimisel peab aga arvestama viitega, mis tekib oleku määratlemisel.
Kodulabori lülititel ei kasutada elektrilist filtreerimist, sest siis ei saaks harjutada tarkvaralist väärlülituste elimineerimise ülesannet. Harjutus koosneb kahest osast. Esimese ülesande eesmärk on demonstreerida Digitaalse sisend-väljundmooduli nuppude kontaktide põrkumist. Selleks on järgmine programm, mis iga nupuvajutuse peale paneb põlema järgmise LED-i. Väär-nupuvajutuste tõttu süttivad LED-id aga mitmeid kordi ja näib, et lausa suvaliselt.
// // Kodulabori digitaalse sisend-väljundmooduli // nuppude kontaktide põrkumise demonstreerimise programm. // #include <homelab/pin.h> // // LED-ide ja nupu viikude määramine // pin leds[3] = { PIN(C, 5), PIN(C, 4), PIN(C, 3) }; pin button = PIN(C, 0); // // Põhiprogramm // int main(void) { unsigned char new_value, old_value = 0; unsigned char index, counter = 0; // LED-ide viikude väljundiks seadmine for (index = 0; index < 3; index++) { pin_setup_output(leds[index]); } // Nupu viigu sisendiks määramine pin_setup_input(button); // Lõputu tsükkel while (true) { // Nupu oleku lugemine new_value = pin_get_value(button); // Kontroll, kas nupp vajutati alla, // ehk kas uus olek on 1 ja vana 0 if ((new_value) && (!old_value)) { // Loenduri suurendamine ja mooduli 3 võtmine counter = (counter + 1) % 3; // Loenduri väärtusele vastava LED-i süütamine for (index = 0; index < 3; index++) { pin_set_to(leds[index], index != counter); } } // Peame vana olekut meeles old_value = new_value; } }
Tarkvaraliseks filtreerimiseks on mitmeid meetodeid. Seda võib teha lihtsalt ja keeruliselt, mõlemal variandil on oma eelised ja puudused. Kui programm on selline, et nupuvajutusi oodatakse harva, võib pärast nupu allavajutamise registreerimist lisada pika pausi, mis välistab reageerimise põrkumisest tekkinud lisalülitustele. Samas tuleb sellise lahenduse puhul arvestada, et kui kasutaja hoiab nuppu pikalt all, reageerib programm ka nupu lahtilaskmisel tekkinud väärlülitustele.
Töökindlam on programm, mis kontrollib nupu olekut teatud aja jooksul mitu korda (mida rohkem ja mida pikema aja jooksul, seda kindlam). Järgnevalt on toodud Digitaalse sisend-väljundmooduli nupu filtreeritud väärtuse lugemise funktsioon:
// // Funktsioon mõne IO laiendusplaadi lüliti filtreeritud väärtuse lugemiseks // unsigned char pin_get_debounced_value(pin button) { unsigned char buffer = 0xAA; unsigned char timeout = 100; // Ootame, kuni nupu olek on selgunud või oleku selgitamine aegunud while (timeout-- > 0) { // 8-kohalise (bitise) olekupuhvri pidamine // Kõik eelmised olekud (bitid) nihutatakse vasakule // ja paremale lisatakse uus olek (bitt). buffer <<= 1; buffer |= (pin_get_value(button) ? 0x01 : 0x00); // Kui kõik 8 bitti on kõrged, siis // nupp on kindlasti alla vajutatud if (buffer == 0xFF) { return 1; } // Kui kõik 8 bitti on madalad, siis // nupp on kindlasti üleval if (buffer == 0x00) { return 0; } // Paus 1 millisekund // See funktsioon sisaldub Kodulabori teegis sw_delay_ms(1); } // Kui olekut ei õnnestunud välja selgitada, siis oletame, // et nuppu ei vajutatud return 0; }
See funktsioon kasutab viite tekitamise funktsiooni, millest räägib lähemalt vastav harjutusülesanne. Siinkohal pole viite funktsiooni kohta vaja suurt midagi teada peale selle, et see tekitab 1-millisekundilise viite iga nupu oleku lugemise tsükli lõpus. Kui nupp on kaheksa lugemise korral samas asendis, tagastab see loetud asendi. Kogu protseduur võib nupu ebastabiilsuse korral kesta kuni 100 ms. Funktsioon sisaldub juba viikude teegis, nii et näite läbimiseks ei pea seda oma programmi lisama. Selle proovimiseks tuleb ülesande esimest poolt natukese muuta – kaasata programmi viite tekitamise teek ja kohas, kus nupu väärtust otse loeti, kasutada filtriga funktsiooni. Tulemus on järgmine:
// // Kodulabori digitaalse sisend-väljundmooduli // nuppude kontaktide põrkumise filtreerimise programm. // #include <homelab/delay.h> #include <homelab/pin.h> // // LED-ide ja nupu viikude määramine // pin leds[3] = { PIN(C, 5), PIN(C, 4), PIN(C, 3) }; pin button = PIN(C, 0); // // Põhiprogramm // int main(void) { unsigned char new_value, old_value = 0; unsigned char index, counter = 0; // LED-ide viikude väljundiks seadmine for (index = 0; index < 3; index++) { pin_setup_output(leds[index]); } // Nupu viigu sisendiks määramine pin_setup_input(button); // Lõputu tsükkel while (true) { // Nupu oleku lugemine new_value = pin_get_debounced_value(button); // Kontroll, kas nupp vajutati alla, // ehk kas uus olek on 1 ja vana 0 if ((!new_value) && (old_value)) { // Loenduri suurendamine ja mooduli 3 võtmine counter = (counter + 1) % 3; // Loenduri väärtusele vastava LED süütamine for (index = 0; index < 3; index++) { pin_set_to(leds[index], index != counter); } } // Peame vana olekut meeles old_value = new_value; } }
Kui nüüd programmi proovida, siis LED-id süttivad täpselt sellises järjekorras nagu kasutaja nupplülitit vajutab.