2013-02-02
A soros vonal régi perifériája/tartozéka a számítógépeknek. A nyolcvanas években még dolgoztam olyan számítógéppel, amelyikben a soros portot megvalósító modul csaknem éjjeliszekrény méretű volt. Persze már akkor is általános volt az integráltabb kivitel, a Zilog Z80 SIO, vagy az Intel i8251. A soros vonalat megvalósító áramkör (USART) megtalálható a legtöbb AVR mikrovezérlőben is. Nagy túlélő, mondhatnánk erre. De valójában arról van szó, hogy ez egy olyan jól bevált megoldás, ami lehetővé tette a kommunikációt anno és ma is a számítógéppel. Mről is van szó tulajdonképpen?
Egy számítógének nem természetes tartozéka a képernyő, a video kártya, ellentétben mondjuk a mai PC-kkel, tabletekkel, vagy akár mobil telefonokkal. A számítógépek a perifériáik segítségével tudnak kommunikálni a környezetükkel. Fejlesztéseinkben általában mi is követjük az evolúciót. Előszőr voltak a lámpácskák (nem LED-ek:) és a kapcsolók. Ennél jobb eredményt adtak a nyomtatók, az adatokat meg lyukkártyák, meg lyukszallag formájában lehetett a gépbe tölteni. Persze az interaktívabb munkához sokkal megfelelőbbek voltak a terminálok. Egy terminál állt egy display-ből, meg egy billentyűzetből. A terminál soros vonallal volt a számítógéphez kötve. A bebillentyűzött kódolt jeleket (általában ASCII), a soros vonalon keresztül megkapta a számítógép. Ha a számítógépen futott a megfelelő program (mondjuk operációs rendszer), ami fogadta a küldött kódokat, általában a soros vonalon keresztül visszaküldte a terminálnak/displaynek, amin megjelenek azok. Ha a számítógép mondjuk nem volt bekapcsolva, vagy nem klappolt minden, akkor hiába nyomkodtuk a billentyűzetet, a képernyőn nem jelent meg semmi. Ha sikerült olyan dolgokat beírnunk, amit a számítógép értelmezni is tudott, akkor még egyébb dolgokat is kiírt jutalmul. A soros vonal minimál megvalósítása 2 vezetéket igényel, egy föld és egy adat eret. Ez az úgynevezett half duplex megoldás. Ilyenkor az adat vezetéken mindkét irányban mennek adatok, kezelni kell, hogy a számítógép vagy a terminál a saját adását ne vegye figyelembe, illetve ütközések is felléphetnek, mint az életben, ha a vonal mindkét végén egyszerre akarnak beszélni. Általánosabb megoldás a ful duplex, amikor 3 vezetéket alkalmaznak, a föld vezetéken kívül külön adat vazaték van a számítógéptől a terminál felé és a terminál felől a számítógép felé. Egy teljes kiépítésű soros interface tartalmaz még több handshake vezetéket is, vagyis olyan jeleket, amelyekkel a két beszélgető fél jelezni tud egymásnak, hogy adás kész meg hasonlók. Ezeket nem részletezném, ne bonyolítsuk a dolgot, ha kevesebb vezetékkel is meg tudjuk oldani. Annyit érdemes megjegyezni, hogy a plusz jelek segítségével lehet megvalósítani a szinkron kommunikációt, ezek hiányában pedig asszinkron tudunk kommunikálni.
Tehát ma is talán a legegyszerűbb lehetőség, hogy kommunikáljunk a számítógépünkkel/mikro vezérlőnkkel a soros vonal. Manapság általában az ember rendelkezésére áll egy PC, így nincs szükségünk terminálra, csak egy terminál emulátor programra. Annak idején világviszonylatban vezető cég volt a Digital Equipment Corporation (versenyben az IBM-mel). A DEC nevét olyan számítógép endszerek fémjelezték mint a PDP, VAX, Alpha, és olyan teminálok mint a VT52, VT100, VT220... régi idők dicsősége. Csak azért említettem meg, mert az ember könnyen belefuthat, hogy milyen terminál emulációt szeretne, általában a VT100 szerintem kielégít minden igényt. Azért vannak különböző típusok, mert a mérnökök egyre újabb vezérlő kódok kezelését építettek be a terminálokba (ugrás sor elejére, képernyő törlés...).
A kommunikáció tehát három ér segítségével valósítható meg. Az RS232 szabvány +/- 12V-os feszültség szintek alkalmazását írja elő, ezért néhol V24-nek is nevezik. Ennek az volt az oka, hogy a soros vonalat zajos környezetben is lehetett használni (gépterem), nagyobb távolság (20-30m) áthidalására. Az mikrovezérlőnk általában 5V, vagy alacsonyabb tápfeszültségről működik, ezért kiegészítő illesztő áramkörrel csatlakozhatunk a PC szabványos soros portjára. A ténylegesen megvalósított illesztő áramkörök (pl. SN75154) általában 3V-os szintnél komparálnak, ezt használtam ki az APH2 fejben lévő szintilesztő kapcsolásnál is. Korrektebb megoldás a MAXIM MAX232-es szintillesztő használata. Ez az IC az 5V tápfeszből, kondenzátorok segítségével +/-10V körüli feszültséget csinál, és ezekre a szintekre konvertálja a jelet. Hátránya, hogy alkalmazása 20-30mA többletfogyasztással jár, előnye, hogy nem kell +/-12V tápfeszt biztosítanunk. Ipari környezetben soros kommunikációt az RS485 interface, vagy optikailag leválasztott áramhurkos interface segítségével valósítják meg (ezek áramkörileg térnek el az RS232-től, de ugyanaz a kódolásuk), amelyek alkalmasak a jel több száz méteres távolságra való továbbítására is. Ha már a szintillesztésről írok, meg kell említeni, hogy léteznek USB/soros átalakító áramkörök (pl. FT232RL), vagy Bluetooth/soros modulok is, amivel vezeték nélküli kapcsolat is megvalósítható.
Kiollóztam az ATmega8 doksijából ezt az ábrát, amely a soros adatátvitelt mutatja. Tehát alapban 1-ben van az adatvezeték. Az adatátvitel úgy kezdődik, hogy az adó egység 0-ba lehúzza, ez a start bit. Na most egy kicsit részletezzük, hogy mennyi is ez az egy bit? Mivel az órajel nem kerül az adatjellel párhuzamosan átvitelre, ezért fontos, hogy mind az adó, mind a vevő egyforma sebességgel dolgozzon. Elég régre nyúlik vissza a szabványos sebességek kialakulása. Utóbb nagyon tréfás, de minden lépcsőnél azt állították, hogy ez a vége, egy telefon vonalon képtelenség nagyobb átviteli sebességet megvalósítani. Az én internet emlékeimben előszőr nagyon örültem a 14,4k-s modemnek, azután kijött a 33.6k-s és végül az 56k-s. A telexeket nem igazán ismerem, de volt amikor úgy gondolták, hogy 300 bit/sec átvitele az egy szép teljesítmény. És tényleg, ha gépelni kellene ilyen tempóban... Ezt nevezzük 300 Baud-nak. A szabványos értékek ennek a többszöresei: 600, 1200, 2400, 4800, 9600, 19200, 38400... Az átvitt adatmennyiség valójában kicsit kevesebb ennél, ezt mindjárt látni fogjuk a fenti diagram további elemzéséből. Tehát a start bit után az adat egyes bitjeinek megfelelően állítja az adó a vezetéket 1-be vagy 0-ba. Valaha használtak különböző hosszúságú adatokat, de gyakorlatilag már csak a 8 bites adathossz van használatban. Az soros adatunkat kiegészíthetjük paritás bit-tel, de el is hagyhatjuk. A paritás bitet egy ellenörző összeg, amit az áramkör az adatbitek összeadásából képez. Ebből a vevő oldal meg tudja állapítani, ha az átvitt adatban 1 bitnyi hiba van. Ilyenkor a vevő kérheti az adót az adat megismétlésére. Klassz ez a dolog, lehet, hogy valakinek szüksége lesz rá, de a gyakorlatban nem szoktuk használni, hanem olyan összeköttetést csinálunk amelyiken nem jellemzőek az ilyen hibák. Ezután következik a stop bit, amikor a vezeték 1-be kerül. Választhatunk két stop bitet is. Ez egy elválasztó jel, nyilván ha pont vessző nélkül nyomnánk az adatokat, nem tudná a vevő, hogy hol végződik az egyik adat, és mikor következik már a másik. Én jellemzően 9600 Baud, no parity, 1 stop bit beállítást használok. Röviden 9600N1-nek szoktam jelezni. Szép emlékeimben ezen a sebességen már egészen real-time-ban (valós időben) lehetett szöveget szerkeszteni VT100 terminálon, sőt jól futott PDP architektúrán a Tetris nevü orosz játék is!
A fenti kapcsolást építettem meg a dugdosós panelemen. Én azt tapasztaltam, hogy soros port használata esetén célszerűbb kvarcot, vagy kerámi szűrőt használni, mert a kalibrált belső R-C oszcillátornál pontosabb órajelre van szükség. A szintillesztést az APH2 fej végzi.
Az első program másodpercenként egy 'U' betüt küld. Ez arra jó, hogy az adó oldalt ellenőrizni tudjuk. Az 'U' betű nsem véletlen. Ha megnézzük a kódját, ez binárisan 01010101, amelyik legalkalmasabb az adatviteli sebesség tesztelésére. A programban megtalálható néhány makró, amelyekkel átekinthetően tudjuk az USART-ot konfigurálni. Két álmatlan éjszakát okozott az ATmega8 tökkelütött regiszter kiosztása. Az UBRRH és az UCSRC regiszter ugyanazon a címen találhatók, és a legfelső bittől függ, hogy éppen melyikbe írunk. Ilyet én még nem pipáltam. A programban megtalálhatók az adat adását és vételét kezelő függvények is. Adásnál meg kell néznünk, hogy az előző adat adásával már elkészült-e az USART, ha igen, egyszerűen csak bele kell írnunk az adatregiszterbe az új adatot. A vétel egy pici megszakítás kezeléssel bonyolultabb csak. Ha az USART vett egy adatot, akkor megszakítást (IT) kezdeményez. A megszakítást kezelő függvényben csupán annyi a dolgunk, hogy kiolvassuk az USART adatregiszteréből a vett adatot. Én itt most szimplán átmásolom az usart_input változóba. Ezt később lehet saját szájíz szerint cizellálni, buffer kezelés, miegymás
/*******************************************************************************
* Author - Kiraly Tibor
* http://www.tkiraaly.hu
* Date - 2013.01.31.
* Chip - ATmega8, APH2
* Compiler - avr-gcc ( WinAVR)
*
* A program 9600 Baud 8N1 mellett folyamatosan "U" betuket kuld.
* Adas ellenorzesehez.
*
********************************************************************************
*
* CKSEL3, CKSEL2, CKSEL1, CKSEL0
* P P P P 0000 External Clock
*
* P P P - 0001 Calibrated Internal RC Oscillator 1MHz
* P P - P 0010 Calibrated Internal RC Oscillator 2MHz
* P P - - 0011 Calibrated Internal RC Oscillator 4MHz
* P - P P 0100 Calibrated Internal RC Oscillator 8MHz
*
* P - P - 0101 External RC Oscillator 0.1-0.9MHz
* P - - P 0110 External RC Oscillator 0.9-3MHz
* P - - - 0111 External RC Oscillator 3-8MHz
* - P P P 1000 External RC Oscillator 8MHz-
*
* - P P - 1001 External Low-frequency Crystal
*
* - P - P 1010 Ceramic resonator 0.4-0.9MHz
* - P - - 1011 Crystal Oscillator 0.4-0.9MHz
* - - P P 1100 Ceramic resonator 0.9-3MHz
* - - P - 1101 Crystal Oscillator 0.9-3MHz
* - - - P 1110 Ceramic resonator 3-8MHz
* - - - P 1110 Ceramic resonator 8MHz-, +CKOPT P
* - - - - 1111 Crystal Oscillator 3-8MHz
* - - - - 1111 Crystal Oscillator 8MHz-, +CKOPT P
*
* Calibrated Internal RC Oscillator
* SUT1 SUT0
* P P BOD enabled
* P - Fast rising power
* - P Slowly rising power
*
********************************************************************************
*
* PB0 (14) - LED - 470R
* TXD (2)
* RXD (3)
*
*******************************************************************************/
#define F_CPU 4000000 // orajel 4MHz
#define BAUDRATE 9600
#define MYUBRR F_CPU/ 16UL/ BAUDRATE - 1
#include "tkiraaly_atmega8.h"
#include
#include
#define LED 0
#define LED_ENABLE BS( DDRB, LED)
#define LED_BE BC( PORTB, LED)
#define LED_KI BS( PORTB, LED)
// UCSRA beallitasai
#define DOUBLE_SPEED 0B00000010 // ketszeres sebesseg
// UCSRB beallitasai
#define IT_RX_ENABLE 0B10000000 // vetel kesz megszakitas engedelyezese
#define IT_TX_ENABLE 0B01000000 // adas kesz megszakitas engedelyezese
#define IT_UDR_EMPTY_ENABLE 0B00100000 // adat buffer ures megszakitas engedelyezese
#define RX_ENABLE 0B00010000 // vetel engedelyezese
#define TX_ENABLE 0B00001000 // adas engedelyezese
// UCSRC beallitasai
#define SET_UCSRC 0B10000000 // UCSRC kivalasztasa
#define SET_UBRRH 0 // UBRRB kivalasztasa
#define ASYNCRON 0 // asszinkron mukodes
#define SYNCRON 0B01000000 // szinkron mukodes
#define PARITY_NO 0 // nincs paritas bit
#define PARITY_EVEN 0B00100000 // paros paritas
#define PARITY_ODD 0B00110000 // paratlan paritas
#define STOPBIT_1 0 // 1 stopbit
#define STOPBIT_2 0B00001000 // 2 stopbit
#define BIT_5 0 // 5 bit-es karakterhossz
#define BIT_6 0B00000010 // 6 bit-es karakterhossz
#define BIT_7 0B00000100 // 7 bit-es karakterhossz
#define BIT_8 0B00000110 // 8 bit-es karakterhossz
UC volatile usart_input= 0; // soros portrol bejovo betu
void usart_putc( UC); // USART egy karakter kuldese
ISR( USART_RXC_vect) // USART megszakitas kezelese
{
usart_input= UDR;
}
int main( void)
{
UBRRL= MYUBRR; // USART inicializalasa
UBRRH= ( MYUBRR)>> 8;
UCSRC= SET_UCSRC+ ASYNCRON+ BIT_8+ PARITY_NO+ STOPBIT_1;
UCSRB= RX_ENABLE+ TX_ENABLE+ IT_RX_ENABLE;
IT_ENABLE;
LED_ENABLE;
LED_BE;
for(;;)
{
usart_putc( 'U'); // 0B01010101
_delay_ms( 1000);
}
}
void usart_putc( UC c) // USART egy karakter kuldese
{
while( BTC( UCSRA, UDRE)); // 1, ha kesz az adat fogadasara
UDR= c;
}
Ez a második program echo-za a bejövő karaktereket, vagyis visszaküldi a terminálnak. Itt következik a második programból a main(), amiben a kettő program különbözik.
/*******************************************************************************
* Author - Kiraly Tibor
* http://www.tkiraaly.hu
* Date - 2013.01.31.
* Chip - ATmega8, APH2
* Compiler - avr-gcc ( WinAVR)
*
* A program 9600 Baud 8N1 mellett a berkezo karaktereket echo-zza (visszakuldi).
* Adas/vetel ellenorzesehez.
*
********************************************************************************
*
* CKSEL3, CKSEL2, CKSEL1, CKSEL0
* P P P P 0000 External Clock
*
* P P P - 0001 Calibrated Internal RC Oscillator 1MHz
* P P - P 0010 Calibrated Internal RC Oscillator 2MHz
* P P - - 0011 Calibrated Internal RC Oscillator 4MHz
* P - P P 0100 Calibrated Internal RC Oscillator 8MHz
*
* P - P - 0101 External RC Oscillator 0.1-0.9MHz
* P - - P 0110 External RC Oscillator 0.9-3MHz
* P - - - 0111 External RC Oscillator 3-8MHz
* - P P P 1000 External RC Oscillator 8MHz-
*
* - P P - 1001 External Low-frequency Crystal
*
* - P - P 1010 Ceramic resonator 0.4-0.9MHz
* - P - - 1011 Crystal Oscillator 0.4-0.9MHz
* - - P P 1100 Ceramic resonator 0.9-3MHz
* - - P - 1101 Crystal Oscillator 0.9-3MHz
* - - - P 1110 Ceramic resonator 3-8MHz
* - - - P 1110 Ceramic resonator 8MHz-, +CKOPT P
* - - - - 1111 Crystal Oscillator 3-8MHz
* - - - - 1111 Crystal Oscillator 8MHz-, +CKOPT P
*
* Calibrated Internal RC Oscillator
* SUT1 SUT0
* P P BOD enabled
* P - Fast rising power
* - P Slowly rising power
*
********************************************************************************
*
* PB0 (14) - LED - 470R
* TXD (2)
* RXD (3)
*
*******************************************************************************/
#define F_CPU 4000000 // orajel 4MHz
#define BAUDRATE 9600
#define MYUBRR F_CPU/ 16UL/ BAUDRATE - 1
#include "tkiraaly_atmega8.h"
#include
#include
#define LED 0
#define LED_ENABLE BS( DDRB, LED)
#define LED_BE BC( PORTB, LED)
#define LED_KI BS( PORTB, LED)
// UCSRA beallitasai
#define DOUBLE_SPEED 0B00000010 // ketszeres sebesseg
// UCSRB beallitasai
#define IT_RX_ENABLE 0B10000000 // vetel kesz megszakitas engedelyezese
#define IT_TX_ENABLE 0B01000000 // adas kesz megszakitas engedelyezese
#define IT_UDR_EMPTY_ENABLE 0B00100000 // adat buffer ures megszakitas engedelyezese
#define RX_ENABLE 0B00010000 // vetel engedelyezese
#define TX_ENABLE 0B00001000 // adas engedelyezese
// UCSRC beallitasai
#define SET_UCSRC 0B10000000 // UCSRC kivalasztasa
#define SET_UBRRH 0 // UBRRB kivalasztasa
#define ASYNCRON 0 // asszinkron mukodes
#define SYNCRON 0B01000000 // szinkron mukodes
#define PARITY_NO 0 // nincs paritas bit
#define PARITY_EVEN 0B00100000 // paros paritas
#define PARITY_ODD 0B00110000 // paratlan paritas
#define STOPBIT_1 0 // 1 stopbit
#define STOPBIT_2 0B00001000 // 2 stopbit
#define BIT_5 0 // 5 bit-es karakterhossz
#define BIT_6 0B00000010 // 6 bit-es karakterhossz
#define BIT_7 0B00000100 // 7 bit-es karakterhossz
#define BIT_8 0B00000110 // 8 bit-es karakterhossz
UC volatile usart_input= 0; // soros portrol bejovo betu
void usart_putc( UC); // USART egy karakter kuldese
ISR( USART_RXC_vect) // USART megszakitas kezelese
{
usart_input= UDR;
}
int main( void)
{
UBRRL= MYUBRR; // USART inicializalasa
UBRRH= ( MYUBRR)>> 8;
UCSRC= SET_UCSRC+ ASYNCRON+ BIT_8+ PARITY_NO+ STOPBIT_1;
UCSRB= RX_ENABLE+ TX_ENABLE+ IT_RX_ENABLE;
IT_ENABLE;
LED_ENABLE;
LED_BE;
for(;;)
{
while( usart_input == 0 );
usart_putc( usart_input);
usart_input= 0;
}
}
void usart_putc( UC c) // USART egy karakter kuldese
{
while( BTC( UCSRA, UDRE)); // 1, ha kesz az adat fogadasara
UDR= c;
}
Itt a vége, fuss el véle, legytek az én vendégeim, innen letölthetitek a hozzávalókat összecsomagolva.