Limitní časování procesů pomocí obvodu UART

pozn.: Článek byl 10.7.2017 změněn a doplněn o poznatky, které vyplynuly z komentářů k článku. Tímto děkuji zejména Zdeňkovi za podněty a ověření celé řady informací a nalezení chyb v původním článku a Romanovi za informace, které k danému tématu nashromáždil již dávno přede mnou.

Co si pod pojmem „limitní časování“ představuji? Informaci o tom, že nastavený čas vypršel. Nevím sice, kolik uplynulo dalšího času, ale ten nastavený čas, ten zaručeně vypršel. Takže takto definuji tento pojem.

V praxi to vypadá tak, že zapíšu libovolný znak (bajt) do vysílacího buferu obvodu 8251 a čekám (nebo průběžně testuji), kdy UART dokončí vyslání tohoto znaku. Ta doba je odvislá od počtu vysílaných datových bitů (5-8), přítomnosti paritního bitu, délky STOP-bitu (i když tady je to trochu složitější, viz komentáře) a násobku vzorkování (1x/16x/64x). Celkem to dává (při taktování 1200Hz) celou řadu časů s rozpětím 6,7ms – 693ms.

Přesnost takového časování je samozřejmě z výše uvedeného důvodu omezená. Záleží také na zpoždění, které následně způsobí můj program, který neumí s časovačem regulérně pracovat a „ignoruje“ jeho informace. Pro synchronizaci například rámce hry či intervalu nějaké signalizace to však bohatě stačí. Prozatím všechny mé hry mimo Arkanoid takové časování používají. Arkanoid ale dělá to samé, používá však pouze časovač 8253, který testuje softwarově.

A nyní k věci. Níže uvedený výpis programu v assembleru ukazuje příklad, jak odměřovat interval ca jedné sekundy. Měří se základní interval 100ms a ten se desetkrát zopakuje. Bez ohledu na to, co program dělá (a jak dlouho) v základním intervalu 100ms, tak ten základní rámec trvá vždy stejných 100ms, které jsou odměřovány hardwarem (obvodem UART 8251). Pokud během odměřování těch základních 100ms bude můj program trvat řekněme 150ms, pak tento základní interval prošvihnu a ztratím informaci o těch 100ms, které jsem chtěl měřit. Ale jak jsem řekl v úvodu – obvod UART mi dává pouze informaci, že jím odměřených 100ms někdy v minulosti uplynulo. A je jen na mně, jak dlouhé kousky kódu z hlediska času vkládám do základních měřicích intervalů UARTu, zda kódu dovolím opravdu hrubě přesáhnout dobu základního intervalu. Pokud je kód příliš dlouhý, mohu jej rozdělit na více částí, které proložím „hlídáním“ UARTu, zda už nastavený čas uplynul.

Ale co když nechci 10x 100ms? Není nic snazšího. Pokud se nezatěžujete kompatibilitou napříč všemi verzemi PMD-85, prostě změníte dělicí poměr časovače 8253 a tím i taktovací frekvenci pro UART 8251 a to s velice jemným krokem. Verze 1 počítače PMD-85 má taktování UARTu hardwarově nastaveno na fixních 1200Hz a proto stejnou frekvenci nastavuji i na kanálu 1 časovače 8253, který u verzí 2 a 3 vyrábí proměnnou taktovací frekvenci UARTu 8251 namísto té fixní 1200Hz u verze 1. Takže změna výstupní frekvence kanálu 1 časovače 8253 je sice řešením, ale pokud chci zachovat kompatibilitu opravdu pro všechny verze PMD-85, musím použít jiné řešení. A tím je změna formátu vysílání znaku, což bylo popsáno hned ve druhém odstavci tohoto článku.

Doba, kterou odměřuje UART, je stanovena takto:

T = N x V / f

kde T je doba v sekundách, N je počet vysílaných bitů (započítává se START bit o délce 1 bitu, počet samotných datových bitů v počtu 5-8, případný paritní bit a STOP bit o nastavitelné délce 0-2 bity), V je koeficient převzorkování (1x, 16x, 64x) a f je taktovací frekvence (již zmíněných 1200Hz). Nyní k tomu STOP bitu a jeho délce. To, co nastavujeme jako délku STOP bitu, je zřejmě pouze jeho nominální doba. Ovšem z hlediska časového okna na konci vysílaného znaku je skutečná doba vysílání STOP bitu a ochranného „jedničkového“ intervalu za ním buď stejná nebo o něco málo delší. Opět odkazuji na komentáře za článkem.

Počet vysílaných bitů i koeficient převzorkování je  určen režimem, ve kterém UART pracuje. Režim se nastavuje zápisem vhodné hodnoty do COMMAND registru na adrese portu 1Fh (adresa 1Fh platí ovšem jen pro PMD-85). Před začátkem používání UARTu 8251 je nutné jej inicializovat. Datasheet Intelu k 8251A nabízí jako vhodný začátek inicializační sekvence:

  • 00h-00h-00h-40h (synchronizace sekvenceru a následný povel RESET)

Datasheet fy NEC k tomuto obvodu uvádí i mírně kratší začátek inicializační sekvence:

  • 80h-80h-40h

V obou případech musí následovat bajt, určující režim UARTu, následovaný vhodnou povelovou instrukcí pro povolení vysílání a nastavení komunikačních signálů modemu. Zkoušel jsem tedy tuto inicializační sekvenci, kterou jsem podrobil všem možným výchozím stavům zápisového sekvenceru obvodu 8251.

  • 80h – formální synchronní režim nebo 1. sync znak nebo 2. sync znak nebo jalový povel
  • 80h – 1. sync znak nebo 2. sync znak nebo jalový povel
  • 40h – povel RESET, za kterým následuje nastavení režimu (módu)
  • 82h – nastavení asynchronního režimu s dobou vysílání znaku 100ms
  • 23h – povolení vysílače + definice signálů modemu (RTS a DTR)

Vzhledem k dosti značnému počtu možností (verze UARTu, typ RESETu, výchozí stav sekvenceru, cílová platforma) budu tuto sekvenci testovat i nadále. Prozatím jede, tak ji tady dávám. Ale nevylučuji, že tato sekvence může při nějaké variantě býti nefunkční.

Vše je ukázáno v přiloženém demonstračním programu. Je použita „časovací“ doba 100ms, které odpovídá hodnota konfiguračního bajtu režimu UARTu o hodnotě 82h. A právě změna této hodnoty mění i „časovací“ dobu. Je přiložena rovněž tabulka v Excelu, která pro každou hodnotu konfiguračního bajtu režimu UARTu uvádí dosaženou „časovací“ dobu.

Možná ještě poznámka, proč přiložená tabulka neobsahuje hodnoty, odpovídající synchronním režimům UARTu. Prostě proto, že mají jinou inicializační sekvenci. A protože „nabídka“ časů pro asynchronní režimy je mnohem větší než případná „nabídka“ časů režimů synchronních, zvolil jsem „asynchronní sadu“.

Následující tabulka udává přehled dostupných časů dle nastaveného režimu UARTu.

 

 

 

 

Vzorový program pro časování procesů pomocí UARTu 8251

Tabulka hodnot konfiguračního bajtu režimu UARTu dle požadovaného času

13 komentářů u „Limitní časování procesů pomocí obvodu UART

  1. Zdeněk

    Zkoušel jsem na reálném PMD (verze 2A) a nefungovalo. Chvilku jsem dělal pokusy, rozjel jsem to a zde je pár poznámek:

    1) reset 8251 je lépe dělat až po trojnásobném zápisu 0 na port 1f, tj.:
    xra a
    out 1fh
    out 1fh
    out 1fh
    mvi a,40h
    out 1fh

    2) k povolení vysílání znaku se musí zapsat číslo 21h a to proto, že vysílání se povoluje přivedením log. 0 na vstup /CTS – ten je u PMD85 spojen se signálem /RTS. Číslo 21h nastavuje TxEN a RTS.

    3) test dokončení vysílání se provádí na prvním bitu stavového slova (maska 1) nikoliv třetím (maska 4)

    4) rychlost je delší o 10 procent prostě proto, že i 1,5 bitů dlouhý STOPBIT si vezme 2 clocky. Takže skutečná délka intervalu je 16×8/1200 = 106,67 ms

    1. Zdeněk

      Ještě poznámka k tomu resetu: při zápisu první 0 to 8251 vyhodnotí jako volbu synchronního režimu s dvěma SYNC znaky, proto následují další 2 nuly (to jsou ty znaky). Kratší (a ověřená funkčí verze) je:
      mvi a,81h
      out 1fh

      // pak reset
      mvi a,40h
      out 1fh

      1. Libor L.A.

        Opět máte pravdu. Místo té první nuly bych asi taky zapsal nějakou hodnotu, která má spodní dva LSB různé od hodnoty „00“. To by opravdu mohlo vést k nadefinování synchronního režimu a případnému čekání na dva synchronizační znaky. Holt, některé postupy se přebírají, protože fungují a evidentně jsem nezkoumal význam toho, co jsem považoval za tak triviální, že to ve mně nevzbudilo žádné podezření. Ale stále mi vrtá hlavou, proč mi to takto funguje (myslím s tou úvodní nulou)?

        1. Libor L.A.

          Tak už jsem našel zdroj, odkud jsem opsal tuto podmíněně funkční inicializaci UARTu. Je jím překvapivě sám BIOS PMD-85 V2.A. A je to na adrese 8B4Ah.

          Ale ne, když se nad tím zamyslím, tak je ta inicializace vlastně v pořádku. Kdy by mohla úvodní nula být interpretována jako definice synchronního režimu? Jedině po resetu (interním nebo externím). Ale to nemůže nastat, protože po externím resetu BIOS provede nastavení režimu UARTu a pak už prvním dalším zápisem nedefinuji režim UARTu. Zapisuji do COMMAND registru, jakkoli je na stejné adrese. A pokud vyvolám interní reset, tak pak už zapisuji správná data ve správném pořadí v rámci svého programu. Pokud Vám to dělalo neplechu, tak bude na vině „nedodělaná“ sekvence zápisu dat do UARTu z předchozího pokusu, kdy opravdu první bajt po resetu přišel nulový a bylo na problém zaděláno (tedy bylo zaděláno na synchronní režim).

    2. Libor L.A.

      ad 1) Pokud to fungovalo až po třech nulových bajtech, je pravděpodobnější, že máte pravdu Vy. Já mohu jen říci, že i na reálném PMD-85 mi to fungovalo a jedou mi tak zatím všechny hry (i na tom reálném PMD-85). Asi jsem předtím nepřivedl UART do stavu čekání na synchronizační bajt.

      ad 2) Máte pravdu, já tam taky dávám 21h (či přesněji řečeno 23h). Asi jsem se při opisování sekl.

      ad 3) Právě zde se asi neshodneme. Na emulátoru to je jedno, jestli testuji masku 1 nebo 4 (i když nevím, zda je to tak dobře). Na reálném PMD-85 musím testovat masku 4, jinak mi to nejede a UART trvale hlásí odeslaný znak. Jestli je tam double bufer, odhadoval bych to tak, že maska 1 indikuje prázdný zápisový registr a maska 4 indikuje vyprázdnění vysílacího posuvného registru.

      v katalogovém listu OKI MSM82C51A-2 je napsáno

      TXRDY (output terminal)
      This is an output terminal which indicates that the MSM82C51A-2 is ready to accept a
      transmitted data character
      . But the terminal is always at low level if CTS = high or the device
      was set in “TX disable status” by a command.
      Note: TXRDY status word indicates that transmit data character is receivable,
      regardless
      of CTS or command.
      If the CPU writes a data character, TXRDY will be reset by the leading edge or WR
      signal.

      TXEMPTY (Output terminal)
      This is an output terminal which indicates that the MSM82C51A-2 has transmitted all the
      characters and had no data character
      .
      In “synchronous mode,” the terminal is at high level, if transmit data characters are no longer
      remaining and sync characters are automatically transmitted. If the CPU writes a data
      character, TXEMPTY will be reset by the leading edge of WR signal.

      ad 4) Je to možné, tohleto jsem netestoval. Jestli Vás mohu poprosit, z jakého zdroje jste tuto informaci čerpal?

      PS: Děkuji za podnětné připomínky.

      1. Zdeněk

        Tak jsem to celé prozkoušel, promyslel, rozebral, vytáhl Saleae logic a zde je pár poznatků:

        Nejprve myslím, že si tady můžem tykat, už jsme si tu povídali (jsem z Třince).

        Proč mě inicializace nefungovala?
        – protože mám místo ROM modulu můj vlastní modul, ve kterém mám udělátko na načítání programu z PC přímo do paměti PMD. Díky tomu, že je samospouštěcí, předpokládám, že nedojde k inicializaci časovače/USARTu a proto je 8251 stále po resetu a čeká modovací slovo. Když nespouštím ROM modul, funguje to i se zápisem jedné nuly. Závěr tedy je, že programy spouštěné z ROM modulu by měli provést inicializaci trochu jinak.

        TxEMPTY versus TXRDY
        – ano, je to tak, jak si psal. Lepší je testovat TxEMPTY. Mě ukázkový příklad fungoval i s TxRDY, ale buď proto, že došlo rychle k zaplnění obou bufferů (smyčka je relativně krátká) a nebo proto, že v PMD mám 8251 a ne 8251A (nebo to Tesla nedoznačila). Univerzálnější je tedy TXEMPTY s maskou 4. V monitoru PMD se testuje maskou 1, to mě zmátlo.

        1,5 stop bitu
        – logickým analyzátorem jsem zjistil, že pro 16x a 64x mód je délka bitu opravdu 1,5x násobek. Pro mód 1x je dvojnásobná (což je celkem logické, když vysílání reaguje na každou sestupnou hranu TxC, tak ji nebude půlit ). Mám svůj vlastní emulátor a v něm je 1,5 bitu implementováno jako 2bity. Možná je to tak i v emulátoru bratrů Bórikových. Rozhodně si to teda doprogramuju.

        Tohle je jinak super, tak detailně poznat hardware se dá jedině vzájemnou konfrontací!

        1. Libor L.A.

          Se vším výše uvedeným souhlasím bez výhrad. Tvé poznámky mě vyprovokovaly k tomu, abych si nahrál wav kopie některých programů, které využívají časování UARTem a ještě jednou je nahrál na skutečné PMD-85. A došel jsem ještě k jednomu zajímavému poznatku. Odchylka od předpokládaného timeoutu 8251 je na emulátoru ca 10% ale na reálném PMD-85 klesá pod 3%. Rozhodně tedy upravím „knihovnu“ takto:

          A) Pro případ, že by program byl spouštěn z ROM modulu či jinak bez inicializace UARTu, zohledním synchronizaci zápisového sekvenceru. Teď nechci říkat, jaké hodnoty a kolik jich na UART pošlu, to musím raději promyslet (po prodělané zkušenosti).

          B) V inicializační sekvenci bude samozřejmě bajt 01h nahrazen bajtem 21h nebo 23h.

  2. RomBor

    O tom časovaní som pred nedávnom písal na OldCompe.
    http://www.oldcomp.cz/viewtopic.php?f=50&t=5334#p63122
    Libor: S tým TxEMPTY versus TXRDY máš asi pravdu, že to zrejme v Emulátore nefunguje správne. Pred odchodom na dovolenku som veľmi rýchlo pozrel do Datasheetu i8251 a skutočne sa má TxREADY zhodiť ihneď po presunutí zapísaného bytu do vysielacieho registra. Keď sa vrátim, tak sa na to pozriem.

    1. Libor L.A.

      Koukám, že jsem se opět vydal objevovat Ameriku. Alespoň její východní pobřeží..

      Na druhou stranu, více informačních zdrojů znamená větší šance na plné pokrytí problematiky. Přeji příjemnou dovolenou.

  3. Libor L.A.

    A další nečekaný poznatek. STOP bity, bez ohledu na jejich navolenou délku, mají vždy „délku“ 2 bity. To, co se navolí v UARTu jako délka STOP bitu je tedy jen doba, po kterou je hodnota STOP bitu nulová. Ale doba toho okna pro STOP bity je konstantní a trvá dvojnásobek délky jednoho vysílaného bitu. Jde to jednoduše zjistit tak, že pro hodnotu režimu 01h a C1h je doba vyprázdnění vysílacího registru stejná.. Tyto režimy se liší právě hodnotou délky STOP bitu (délky 0 a 2).

    Výše uvedená diskuse mě totiž přesvědčila, že není radno dělat myšlenkové extrapolace. A tak jsem začal měřit na skutečném PMD-85 doby vyprázdnění vysílacího registru UARTu 8251 a dávám dohromady tabulku časů pro každou hodnotu režimu, ve kterém UART jede. Samozřejmě při konstantním vnějším taktu 1200Hz.

    Jsem sám zvědav, co z toho ještě vyleze. Jinak, když započítám ten úlet s délkou STOP bitu, tak ty získané časy již nevykazují žádnou odchylku oproti teorii. Je neměřitelná, či spíše dost pod 1%.

  4. Zdeněk

    Tak jsem taky měřil – logickým analyzátorem Salea Logic, přímo na pinu 19 (TxD) obvodu MHB8251 (bez A). Při frekvenci 1200Hz mi vyšlo toto (mód… doba mezi dvěma startbity… přepočítaný počet „stop“ bitů)

    Dotaz na TxEMPTY (maska 4):

    01: (0 stop bitů) … 6,668ms … 8 bitů (1 start bit + 5 bitů + 2 bity trvající 1)
    81: (1,5 stop bitů) … 6,676ms … 8 bitů (1 start bit + 5 bitů + 2 bity trvající 1)
    C1: (2 stop bity) … 7,5ms … 9 bitů (1 start bit + 5 bitů + 3 bity trvající log. 1)

    02: (0 stop bitů) … 106ms … 8 bitů (1 start bit + 5 bitů + 2 bity)
    82: (1,5 stop bitů)… 100ms … 7,5 bitů (1 start bit + 5 bitů + 1,5 bitů trvající mezera)
    C2: (2 stop bity)… 120ms … 9 bitů (1 start bit + 5 bitů + 3 bity trvající log. 1

    03: (0 stop bitů) … 427ms … 8 bitů (1 start bit + 5 bitů + 2 bity)
    83: (1,5 stop bitů)… 400ms … 7,5 bitů (1 start bit + 5 bitů + 1,5 bitů trvající mezera)
    C3: (2 stop bity)… 480ms … 9 bitů (1 start bit + 5 bitů + 3 bity trvající log. 1

    Když jsem se však dotazoval na bit TxRDY (maska 1), vyšly tyto doby:

    01: (0 stop bitů) … 5,84ms … 7 bitů (1 start bit + 5 bitů + 1 bit trvající 1)
    81: (1,5 stop bitů) … 6,67ms … 8 bitů (1 start bit + 5 bitů + 2 bity trvající 1)
    C1: (2 stop bity) … 6,67ms … 8 bitů (1 start bit + 5 bitů + 2 bity trvající log. 1)

    02: (0 stop bitů) … 93,4ms … 7 bitů (1 start bit + 5 bitů + 2 bity)
    82: (1,5 stop bitů)… 100ms … 7,5 bitů (1 start bit + 5 bitů + 1,5 bitů trvající mezera)
    C2: (2 stop bity)… 107ms … 8 bitů (1 start bit + 5 bitů + 3 bity trvající log. 1

    (mód 64x už jsem nezkoušel).

    1. Zdeněk

      U toho posledního odstavečku mám chybu v závorkách, správně má být:

      02: (0 stop bitů) … 93,4ms … 7 bitů (1 start bit + 5 bitů + 1 bit)
      82: (1,5 stop bitů)… 100ms … 7,5 bitů (1 start bit + 5 bitů + 1,5 bitu)
      C2: (2 stop bity)… 107ms … 8 bitů (1 start bit + 5 bitů + 2 bity)

      1. Libor L.A.

        Je to tak. Ty drobné odchylky skutečných časů od teoretických pod 1% mi nedaly spát a tak jsem včera v noci měřil několik hodin všechny kombinace a je to tak, jak píšeš výše. Skutečná délka STOP bitu se mění od 1,5T do 3T. V režimu vzorkování 1x je pro nastavenou délku STOP bitu 0/1/1,5 skutečná délka 2T, pro nastavenou délku STOP bitu 2 je pak skutečná délka 3T. V režimech vzorkování 16x a 64x je to tak, že pro nastavené délky STOP bitu je ta skutečná délka vždy následující: 0>>2T, 1>>2T, 1,5>>1,5T!!!, 2>>3T. Nechápu význam ale je to tak.

        Měřil jsem vždy čas opakovaného vysílání znaku UARTem, tak aby tento čas byl ca 2-3 minuty. Pak už se odchylky počítaly na sekundy a byly pěkně měřitelné. Dle výše uvedeného ale již žádné další odchylky od teoretické předpovědi délky STOP bitu neeviduji. Takže anabáze kolem STOP bitu je (doufám) tímto vyřešena. Vzhledem k naměřenému množství dat (budu chtít ověřit opravdu každou „asynchronní“ hodnotu režimu a v měření stále pokračuji) dám ty údaje do tabulky v Excelu a připojím pod hlavní článek.

        To vše měřeno na čipu s označením TESLA MHB8251 (bez písmene „A“ na konci).

Napsat komentář

Vaše emailová adresa nebude zveřejněna.