2014 m. lapkričio 26 d., trečiadienis

HID, Wiegand26 ir ATtiny2313

Darbe tenka susidurti su praėjimo kontrolės sistemomis, ir tas susidūrimas dažniausiai nereikalauja intensyvesnio gilinimosi į tų sistemų bendravimo bei veikimo subtilybes. Tačiau kartais tų subtilybių žinojimas praverčia tiek diegiant naujas tokio tipo sistemas, tiek integruojant į esamas, tiek diagnozuojant gedimus. Veikimo principo išmanymas, manau, leidžia išvengti potencialių klaidų, ypač kai tokios galimos klaidos dažniausiai atima nemažai laiko jas taisant.


STRUKTŪRA
Dažniausiai tokio tipo sistema susideda iš keleto struktūrinių vienetų.

Pirma sudedamoji dalis, kuri dažnai labiausiai pastebima galutiniam vartotojui, yra duomenų įvedimo įrenginys. Šis įrenginys gali būti keleto tipų: nuo PIN kodo įvedimo ranka iki pirštų atspaudų skaitytuvo. Jo funkcija – leisti vartotojui identifikuoti save tuo būdu, kuriuo jam suteikta galimybė tai padaryti, ir šiuos identifikacinius duomenis vienaip ar kitaip nusiųsti valdikliui, kuris ir yra visos sistemos „smegenys“. Svarbu pastebėti, kad duomenų įvedimo įrenginys pats nieko nesprendžia, jis tik priima vieno tipo identifikacijos duomenis, paverčia juos į standartinį protokolą ir nusiunčia valdikliui.
Valdiklis savo ruožtu, gavęs identifikacinius duomenis, turi nuspręsti, ar suteikti norinčiajam tam tikras privilegijas, kaip, tarkim, vienų ar kitų durų atidarymas, ar ne. Valdiklis dažniausiai kreipiasi į duomenų bazę, kuri gali būti tiek pačiame valdiklyje, tiek ir atskirame serveryje, ieškodamas identifikatoriaus. Neradęs identifikatoriaus duomenų bazėje valdiklis arba nedaro nieko, arba iš anksto numatytu būdu siunčia klaidos pranešimą. Radęs atitikmenį duomenų bazėje, valdiklis suteikia duomenų bazėje numatytas privilegijas, praėjimo kontrolės atveju – atidaro duris, siųsdamas signalą paskutiniam įrenginiui, kuris gali būti, tarkim, elektrinė spyna.
Paskutinis signalas dažniausiai būna tiesiog įtampa yra arba įtampos nėra, nes spynos atveju daugiau signalų ir nereikia. Valdiklio ir duomenų bazės komunikacija gali būti įvairių tipų – vidinės duomenų bazės atveju valdiklis ieško įrašų savo atmintyje, o išorinės, pavyzdžiui, gali būti ir IP protokolu, jungiantis prie SQL serverio.
Šio straipsnio tikslas – apžvelgti vienos krypties komunikavimą tarp duomenų įvedimo įrenginio ir valdiklio.
WIEGAND PROTOKOLAS
Vienas iš dažniausiai naudojamų komunikacijos tarp duomenų įvedimo įrenginio ir valdiklio protokolų vadinamas Wiegand protokolu, kurio yra keletas modifikacijų. Populiariausias – 26 bitų protokolas, nors yra ir 34, 36 ir 40 bitų modifikacijos. Wiegand 26 bitų protokolas yra laikomas standartiniu, ir dauguma įrenginių turi galimybę juo siųsti ar gauti duomenis, todėl kitų protokolų čia neaprašinėsiu, juolab kad jie veikia tuo pačiu principu.
Fiziškai duomenų siuntimas šiuo protokolu realizuotas dviejų duomenų laidų pagalba, kaip trečią laidą naudojant GND kontaktą (dažniausiai bendrą įrenginio minusą). Duomenų laidai įvardinami D1 ir D0. Įtampų lygiai šiuose laiduose – nuo 0 iki 5V. D1 laidu siunčiami loginiai „1“, D0 laidu – loginiai „0“. Siuntimo būdas paprastas: ramybės būsenoje abiejų kontaktų įtampa yra 5V trečio laido, GND, atžvilgiu. Loginis vienetas siunčiamas kaip 0V įtampos lygis D1 kontakte, tuo tarpu loginis nulis siunčiamas kaip 0V įtampos lygis D0 kontakte. Laiko intervalai tarp signalų nėra labai standartizuoti, bent jau aš neradau griežto apibrėžimo. Dažniausiai laikoma, kad tarp dviejų impulsų turėtų būti iki 2ms laiko tarpas, impulso ilgis - 50μs. Tokiu atveju, jeigu valdiklis negauna impulsų daugiau nei 2ms, laikoma, kad duomenų siuntimas baigtas.

Loginis šio protokolo principas taip pat nėra sudėtingas. Kaip iš pavadinimo galima numanyti, vienas duomenų siuntimas (paketas) sudarytas iš 26 bitų. Pirmas, Bit0, ir paskutinis, Bit25 yra parity bitai, naudojami teisingo duomenų perdavimo patikrinimui. Bit01 – Bit08 (8 bitai) – vadinamasis facility, arba site kodas. Bit09 – Bit24 (16 bitų) – vartotojo identifikacinis kodas. Dėl tokios protokolo struktūros galimi 256 site kodo variantai ir 65536 vartotojo kodo variantai, todėl galimi kodų pasikartojimai – jeigu gresia tokia galimybė, tenka naudoti daugiau bitų naudojančias protokolo modifikacijas.


Pirmasis parity kodas skaičiuojamas iš 01 – 12 bitų, antrasis – iš 13 – 24 bitų. Skaičiuojami bitai, atitinkantys loginį „1“.
Pirmasis parity bitas (Bit0):
  • ·Lygus „0“, jeigu bitų, atitinkančių loginį „1“ skaičius yra nelyginis;
  • ·Lygus „1“, jeigu bitų, atitinkančių loginį „1“ skaičius yra lyginis.
Antrasis parity bitas (Bit25):
  • ·Lygus „0“, jeigu bitų, atitinkančių loginį „1“ skaičius yra lyginis;
  • ·Lygus „1“, jeigu bitų, atitinkančių loginį „1“ skaičius yra nelyginis.
Gavėjas, šiuo atveju valdiklis, pagal gauto paketo bitus apskaičiavęs parity bitus, turi sulyginti gautus ir apskaičiuotus parity bitus. Jeigu gautieji atitinka atsiųstus, duomenų paketas priimtas tinkamai.
Beje, tokio tipo įrenginiai taip pat naudoja RS232 bei kitokių tipų nuoseklaus duomenų perdavimo protokolus, tokiu atveju duomenų paketai gali atrodyti kitaip.
PAMĄSTYMAI
Manau, kad norint užtikrinti tinkamą tokios sistemos veikimą, reiktų prisilaikyti tam tikrų principų. Dėl žemo signalų įtampos lygio bei dėl jų perdavimo principo yra didelis duomenų sugadinimo pavojus, tad jeigu aplinkoje esama įrangos, spinduliuojančios stiprų elektromagnetinį lauką, arba duomenų kabeliai praeina netoli tokios įrangos – reiktų naudoti ekranuotus kabelius, jų ekraną tinkamai įžeminus. Taip pat ieškodamas informacijos apie šį formatą radau rekomendacijas tokio tipo įrenginius maitinti ne impulsiniais maitinimo šaltiniais, nes jie gali skleisti papildomus triukšmus, dėl ko gali sumažėti kortelių nuskaitymo atstumas arba siunčiami duomenys gali būti iškraipyti.
JUNGIMAS PRIE MIKROVALDIKLIO
Kaip duomenų įvedimo įrenginio pavyzdį čia naudosiu firmos „HID“ kortelių skaitytuvą – dauguma tokio tipo „HID“ įrenginių palaiko Wiegand 26 bit protokolą. Skaitmeninės klaviatūros stuomenis valdikliui siunčia kiek kitaip, todėl čia jų neaptarinėsiu. Tačiau „HID“ nėra vieninteliai, naudojantys šį protokolą – Wiegand protokolas yra atviras, ir jį naudoja daug gamintojų, gaminančių tokio tipo įrenginius.
Mano pirminis tikslas buvo susipažinti su protokolu, o tada prijungti kortelių skaitytuvą prie mikrovaldiklio, kuris, gavęs duomenis Wiegand protokolu, išsiųstų juos RS232 protokolu į kompiuterio COM prievadą.
Kadangi įtampų lygiai, naudojami skaitytuvo, atitinka TTL įtampų lygius mikrovaldiklyje, jokių papildomų schemų nereikia. Naudojau savo senąjį, visus bandymus iki šiol betveriantį mikrovaldiklį ATtiny2313. Šis mikrovaldiklis turi du išorinės pertraukties prievadus INT0 bei INT1, todėl nusprendžiau pasinaudoti jais. Žinoma, galima naudoti bet kuriuos prievadus, sukonfigūravus juos kaip įėjimus, tačiau tokiu atveju reiks visą laiką juos užklausinėti, ar nėra pasikeitę įtampų lygiai. Pertraukties (interrupt) atveju pasikeitus įtampos lygiui bus generuojama programos pertrauktis.
Jungimo principas – sekančiame paveikslėlyje, visos schemos nepiešiau, nes su Paint‘u šiek tiek nepatogu, o kitų programų naudoti neturiu galimybės. Manau, esmę suprasti pavyks.

Iš tikro USART RxD prievado nereikės, nes duomenys bus tik siunčiami.
Algoritmas paprastas: jeigu sugeneruota INT0 arba INT1 pertrauktis, pridėti gaunamą bitą prie kintamųjų, jeigu po paskutinio gavimo praeina daugiau, nei 2ms – laikyti, kad gavimas baigtas, ir per USART siųsti arba kodus, arba klaidos pranešimą.

#include <avr/io.h>#include <util/delay.h>#include <avr/interrupt.h> // NUSTATYMAI #define DECIMAL 0 // ar rodyti desimtainius skaicius#define BINARY 1 // ar rodyti bin #define TARPAI 0 // ar daryti tarpus#define PARITY 0 // ar tikrinti parity #define spc if (TARPAI) USARTWriteChar('' '') // Tarpo siuntimas per USART#define Ten  TIMSK |= 1<<OCIE1A // Taimerio pertraukties ijungimas#define Tdis TIMSK &= ~(1<<OCIE1A) // Taimerio pertraukties isjungimas  volatile uint8_t bitcount=0; // gaunamu bitu skaitliukas  volatile uint8_t p1=0;volatile uint8_t sitecode=0; volatile uint16_t idcode=0;volatile uint8_t p2=0;volatile uint8_t parity1 = 0; //parity1 bitaivolatile uint8_t parity2 = 0; //parity2 bitai volatile uint8_t timer = 0;    void INTinit () {MCUCR |=(1<<ISC11)|(1<<ISC01); // Pertrauktis kai itampa ant INT krintaGIMSK = (1<<INT1)|(1<<INT0); // Isorines pertraukties ijungimas} void TIMER1init() {TCCR1B = (1<<WGM12)|(1<<CS11); // Taimerio CTC modeOCR1A = 50; // Taimerio pertrauktis generuojama kas 100usTCNT1=0;Tdis; // Taimeris isjungiamas}void USARTInit(uint16_t ubrr_value) // USART inicializacija{    UBRRL = ubrr_value;   UBRRH = (ubrr_value>>8);   UCSRC=(0<<UMSEL)|(3<<UCSZ0);   UCSRB=1<<TXEN;}  void USARTWriteChar(char data) // USART siuntimo funkcija{   while(!(UCSRA & (1<<UDRE)));   UDR=data;} void nl() // USART \\n \\r siuntimas{ USARTWriteChar(13); USARTWriteChar(10);}  void USARTWriteInt (uint16_t sk) // USART skaiciaus siuntimas{ char skaitmuo[5]={0,0,0,0,0}; int i=4,j=0; while(sk) { skaitmuo[i]=sk%10; sk=sk/10; i--; } for(i=j;i<5;i++) { USARTWriteChar(48+skaitmuo[i]); } }  void tx () { // USART duomenų siuntimas  if (BINARY) { // jeigu siusti dvejetaini koda USARTWriteChar(p1+48); // parity1 spc; for (int i=7; i>=0; i--) { // sitecode BINARY  uint8_t tmp=1<<i;  if (sitecode&tmp) USARTWriteChar(49);   else USARTWriteChar(48);  } if (!PARITY) spc;  for (int i=15; i>=0; i--) { //idcode BINARY  uint8_t tmp=1<<i;  if (idcode&tmp) USARTWriteChar(49);   else USARTWriteChar(48);  if ((PARITY)&&(i==12)) spc;  } spc; } if (DECIMAL) { // jeigu siusti desimtaini koda USARTWriteInt(sitecode); spc; USARTWriteInt(idcode); spc; }if (BINARY) USARTWriteChar(p2+48); // parity2 if (PARITY) { // Parity skaiciavimas USARTWriteChar('' ''); USARTWriteChar(''P''); USARTWriteChar(''a''); USARTWriteChar(''r'');USARTWriteChar(''i''); USARTWriteChar(''t''); USARTWriteChar(''y''); USARTWriteChar('' ''); if ((((p1)&&(parity1%2))||((!p1)&&(!(parity1%2))))&&   (((!p2)&&(parity2%2))||((p2)&&(!(parity2%2))))) {  USARTWriteChar(''O''); USARTWriteChar(''K'');  } else  {   USARTWriteChar(''F'');  USARTWriteChar(''A'');   USARTWriteChar(''I''); \\  USARTWriteChar(''L'');  } } nl(); sitecode=0;idcode=0;  } void gavimas (int bit) { // bitu gavimastimer=0;if (!bitcount) { //jeigu pirmas bitas Ten; // Taimerio pertraukties ijungimas p1=bit; // gaunamas pirmas PARITY bitas } else if ((bitcount>=1)&&(bitcount<=8)) { // gaunamas SITE kodas  sitecode|= bit<<((8-bitcount));    } else if ((bitcount>=9)&&(bitcount<=24)) { // gaunamas ID kodas   idcode|= bit<<(24-bitcount);   } else if (bitcount==25) { // gaunamas antras PARITY bitas    p2=bit;    }  if (PARITY) { // jeigu tikrinti parity - skaiciuoja vienetukus if ((bitcount>=1)&&(bitcount<=12)&&(bit)) parity1++; // pirmo parity vienetukai if ((bitcount>=13)&&(bitcount<=24)&&(bit)) parity2++; // antro parity vienetukai }    bitcount++;  }  int main(){ USARTInit(25);    //Nurodomas USART greitis, 9600bps @ 4MHz taktiniam dazniuiINTinit();TIMER1init();sei(); while(1); // pagrindine programa nieko neveikia }  ISR (INT0_vect) // D0 pertrauktis{gavimas(0);} ISR (INT1_vect) // D1 pertrauktis{gavimas(1);} ISR (TIMER1_COMPA_vect) {timer++; if (timer>20) { // jeigu praejo daugiau nei 2 ms Tdis; // Taimerio pertraukties ijungimas timer=0; // Taimerio nulinimas if (bitcount<26) { // jeigu per mazai bitu gauta  USARTWriteChar(69); USARTWriteChar(49); spc; USARTWriteInt(bitcount); nl();// E1  } else if (bitcount>26) { // jeigu per daug bitu gauta   USARTWriteChar(69); USARTWriteChar(50); spc; USARTWriteInt(bitcount); nl(); // E2   } else { // jeigu viskas gerai     tx();     }  bitcount=0; // bitu skaitpliuko nulinimas } } 


Programos funkcijų trumpas aprašymas:
INTInit()
Nustato, kad pertrauktis būtų generuojama, jeigu įtampos lygis INT0 arba INT1 prievade krinta (falling edge triggering), bei įjungiama išorinė pertrauktis.
TIMER1Init()
Nurodomas taimerio veikimo principas CTC (Clear Timer on Compare match), nurodomas dydis taimerio sulyginimui (OCR1A registras), nunulinamas taimerio skaitliukas (TCNT1), bei taimerio pertrauktis išjungiama.
USARTInit()
Nurodomas duomenų siuntimo greitis, paketo tipas bei įjungiamas USART siuntimas.
nl()
Per USART siunčiami du simboliai – naujos eilutės \\n bei grįžimo į eilutės pradžią \\r
USARTWriteInt()
Siunčia 5 skaitmenų skaičių (16 bitų) dešimtainiu formatu per USART
tx()
Siunčia gautus duomenis. Priklausomai nuo nustatymų, siunčia dvejetainiu ir/arba dešimtainiu formatu, taip pat, jei nustatyta, apskaičiuoja parity teisingumą.
gavimas(int bit)
Ši funkcija vykdoma iškart po pertraukties – priklausomai nuo bitų skaitliuko, gautas bitas pridedamas arba prie parity bitų, arba prie site kodo, arba prie ID kodo. Taip pat iškart skaičiuojami parity loginiai „1“, jeigu nustatyta.
Visi registrų nustatymai labai išsamiai aprašyti mikrovaldiklio aprašyme (datasheet), todėl rekomenduoju žvilgterėti ten. Kas dėl funkcijų kodo išbaigtumo – nesistengiau to padaryti, todėl programa vietomis gavosi visai gremėzdiška ir negraži.

Komentarų nėra:

Rašyti komentarą