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

Programinis USART siuntimas

Darant vieną nedidelį projektėlį nedideliame mikrovaldiklyje (Attiny13) prireikė keletą skaičiukų, besikeičiančių programos metu, persiųsti į kompiuterio nuoseklųjį prievadą (RS232). Paprastai kiek didesniuose mikrovaldikliuose, tarkim, mano daug naudotame Attiny2313, yra dedikuotas USART (Universal Synchronous Asynchronous Receiver Transmitter) prievadas, kuris valdomas registrų arba gyvenimą palengvinančių, bet programos kodą gerokai didinančių bibliotekų pagalba. Tokiu atveju žemiausiu protokolo lygmeniu, t.y. bitais rūpintis nereikia. Tačiau mano atveju naudojamas mikrovaldiklis neturi dedikuoto USART prievado, todėl šis kelias netinka. Yra ir kita galimybė – programinis USART prievado programavimas. Tokiu atveju jau reikia rūpintis bitais, signalo perdavimo trukmėmis ir kitais painiais dalykais. Žinoma, programiniam USART taip pat galima rasti paruoštų bibliotekų, tačiau tokia išeitis manęs netenkino – tuoj paaiškinsiu, kodėl.
Pirma – man reikėjo tik keleto skaičiukų siuntimo, be to, pakankamai aktuali užimama vieta, nes mikrovaldiklis teturi 1kB programuojamos atminties. O bibliotekos paprastai būna tiek siuntimui, tiek gavimui, be to, su visomis paruoštomis funkcijomis biblioteka tikrai užims daugiau vietos (kita vertus, nebandžiau, bet manau, kad taip turėtų būti).
Antra – norint rasti ir parsisiųsti tokią biblioteką reikalingas internetas. O aš šiuo metu esu tokioje vietoje, kur internetas labai ribotas. Turiu parsisiuntęs kompiuteryje keletą RS232 protokolą aprašančių dokumentų, todėl teko naudotis savo galva ir nagrinėti, kas kaip veikia.

RS232 protokolas
Pirmiausia trumpai aprašysiu RS232 protokolą, smulkesnės informacijos pilnas internetas, yra netgi knyga, nuodugniai nagrinėjantį šį duomenų perdavimo būdą (neapsiribojant RS232, knyga vadinasi „Serial Port Complete“).
RS232 protokolo ypatybės:
Tik vienas siuntėjas ir vienas gavėjas;
Naudojami du duomenų laidai, paprastai žymimi Tx (Transmit) ir Rx (Receive), ir vienas bendras laidas, prijungtas prie GND.  Iš tikro RS232 protokolas naudoja daugiau duomenų laidų, nei tik Tx ir Rx, tačiau kompiuterio bendravimui su mikrovaldikliu aktualūs tik šiedu, tai tik apie juos ir rašysiu.
Standartiniai kompiuterio RS232 prievado (COM port)  įtampų lygiai: loginį vienetą atitinka -15v; loginį nulį atitinka 15v. Įtampų lygiai gali svyruoti tam tikrose ribose, tačiau bendras principas lieka tas pats.
Mikrovaldiklyje USART prievade naudojami įtampų lygiai (TTL) – loginį vienetą atitinka 5v; loginį nulį atitinka 0v.
Norint prijungti mikrovaldiklio USART prievadą prie kompiuterio COM prievado, reikalinga speciali schema, suderinanti įtampų lygius, nes visa kita atitinka, t.y. naudojamas tas pats protokolas. Tokia mikroschema yra MAX232, ir su keletu papildomų detalių ją galima naudoti įtampos lygių suderinimui.
Laidų žymėjime yra šiek tiek painiavos, bet iš tikro viskas paprasta. Įsivaizduokime, kad RS232 kabeliu yra sujungti įrenginiai, kurių vienas yra pagrindinis, kitas – šalutinis. Vienu duomenų laidu  bus siunčiama informacija iš pagrindinio įrenginio į šalutinį, kitu – atvirkščiai, iš šalutinio į pagrindinį. Prievadai ant įrenginių visada žymimi taip: jungtis, per kurią siunčiami duomenys – Tx, jungtis, per kurią duomenys priimami – Rx. Laidas, einantis iš pagrindinio įrenginio Tx turi būti pajungtas prie šalutinio įrenginio Rx, lygiai taip laidas, einantis iš pagrindinio įrenginio Rx turi būti pajungtas prie šalutinio Tx.
RS232 (UART, USART, Serial, nuoseklusis) prievadas naudoja pakankamai paprastą protokolą. Nuoseklusis duomenų siuntimas tokio tipo prievadu yra asinchroninis, t.y. nėra CLOCK duomenų linijos siuntėjo ir gavėjo greičiams suderinti. Dėl to tiek siuntėjas, tiek gavėjas turi būti nustatyti vienodiems duomenų perdavimo greičiams, be to, visų bitų ilgis turi būti vienodas.
Protokolas susideda iš vieno pradžios bito (START BIT), keleto duomenų bitų (dažniausiai aštuonių, D0-D7), nebūtino parity (teisingo duomenų persiuntimo tikrinimui, čia neaprašinėsiu)  bito ir vieno arba keleto pabaigos bitų (STOP BIT).
Ramybės būsenoje, t.y. kai nesiunčiami jokie duomenys, duomenų linijoje yra laikomas loginis vienetas (mikrovaldiklio atveju – 5v). Įrenginys, norintis siųsti duomenis, duomenų liniją pakeičia į loginį nulį – siunčiamas pradžios bitas, kurio trukmė priklauso nuo nustatyto duomenų persiuntimo greičio. Po pradžios bito siunčiami duomenų bitai D0...D7, po kurių, jeigu nenaudojamas parity bitas, siunčiamas pabaigos bitas, kuris visada yra loginis vienetas, taip gražinant duomenų liniją į ramybės būseną.  Visi šie bitai kartu vadinami duomenų paketu (angl. frame), o siunčiamo duomenų paketo tipas taip pat turi būti nurodomas tiek gavėjui, tiek siuntėjui tas pats. Paketo tipas žymimas taip: 8-N-1, kas reiškia 8 duomenų bitai, nėra parity bito (N) ir 1 pabaigos bitas. Toks formatas naudojamas dažniausiai, tokį naudosiu ir aš šiame straipsnyje.

Duomenų greitis nurodomas bitais per sekundę (bps). Yra standartiniai greičiai, kurių reikėtų laikytis, nes ne visi įrenginiai gali dirbti nestandartiniais greičiais. Kadangi mano Attiny13 dirba nuo vidinio RC generatoriaus (1,2 Mhz), aš naudojau mažiausią greitį, t.y. 2400 bps.
Žinant per sekundę siunčiamų bitų skaičių nesunku apskaičiuoti vieno bito trukmę:

Gauname, kad vienas bitas turi trukti 417μs.
Rašome programą:
#include <avr/io.h>#include <util/delay.h> // laiko skaičiavimo biblioteka // naudojau #define, kad nereikėtų kaskart rašinėti komandų:#define wait _delay_us(417)#define sbit PORTB|=1<<PB2#define cbit PORTB&=~(1<<PB2) // duomenų siuntimo funkcijavoid sendChar( char simbolis) {  cbit; // siunčiamas pradžios bitas  wait; // pradžios bitas laikomas nustatytą laiką   for (int b=1; b<=128; b*=2) { // tikrinami siunčiamo simbolio bitai    if (!(simbolis&b)) cbit; // jeigu bitas=0, duomenų linijoje – loginis 0      else sbit; // jeigu bitas=1, duomenų linijoje – loginis 1  wait; // bitas laikomas nustatytą laiką  }  sbit; // siunčiamas pabaigos bitas  wait; // palaukiama nustatytą laiką} int main(void){DDRB=1<<PB2;  // duomenų siuntimui naudojamas PB2 prievadas,   //kuris nustatomas kaip išėjimassbit; // duomenų linija nustatoma loginiam 1while(1)  { // amžinas ciklas  sendChar(‘A’); // siunčiamas simbolis  sendChar(10); // siunčiama komanda “new line” (\\n)  sendChar(13);// siunčiama komanda “carriage return” (/r)  _delay_ms(1000); // palaukia sekundę  }}


Ši programa kas sekundę siųs simbolį „A“, bei perkels gaunančiojo terminalo kursorių į sekančios eilutės pradžią („new line“ ir „carriage return“).
Rezultatą galima pamatyti prijungus mikrovaldiklį prie kompiuterio COM prievado per suderinimo mikroschemą MAX232 (mikroschemos aprašyme (datasheet) yra reikalingos schemos, o ir šiaip jų pilnas internetas). Programinę įrangą naudoti galima bet kokią, kuri leis naudoti čia suprogramuotą protokolą 2400 bps greičiu, tačiau rekomenduoju naudoti PuTTY – nemokama ne tik nuosekliojo prievado terminalo programa, bet ir telnet bei ssh klientas.
Programa gal ir nėra labai optimizuota, tačiau pademonstruoti USART protokolo  veikimą jos užtenka.  Pastaba: jeigu programoje naudojami pertraukimai, funkcijos vykdymo metu juos reikia išjungti komanda cli() (globalūs pertraukimai neleidžiami) bei išsiuntus simbolį įjungti komanda sei() (globalūs pertraukimai leidžiami).

Komentarų nėra:

Rašyti komentarą