Overview

ASCII for C and Embedded UARTs


"American Standard Code of Information Interchange" (ASCII) and common character set interpretations for embedded microcontroller communication with Universal Asynchronous Receiver/Transmitter (UART) units.

Remark, that RS232 is bit-inverse to ASCII. ASCII is the code, that is used by the UARTs of your microcontroller internally.




Use of Carriage Return and Line Feed


	#if OPSYS == Windows
		#define CR "\r\n"
	#elif Linux
		#define CR "\n"		// line feed
	#elif MacOS
		#define CR "\r"		// carriage return
	#endif
	
	#ifndef OPSYS 
		#define CR "\r\n"
	#endif


printf() Escape Sequences


	Name	        	printf	dec	hex
	___________________________________________________________________________

	null character     	\0	 0	0x00	string termination sign
	alert (beep, bell) 	\a	 7	0x07	
	double quote       	\"	34	0x22	terminated string (*strg)
	single quote       	\'	39	0x27	no termination (char-array)
	backslash          	\\	92	0x5c	
	backspace          	\b	 8	0x08	
	formfeed           	\f	12	0x0c	
	horizontal tab    	\t	 9	0x09	
	vertical tab      	\v	11	0x0b	
	question mark     	\?	63	0x3f	
	newline, line feed 	\n	10	0x0a	
	carriage return    	\r	13	0x0d	

     Flow-control characters: 

	Xon  (DC1)        	\021	17	0x11	Rx buffer empty
	Xoff (DC3)        	\023	19	0x13	Rx buffer full
	Escape            	\033	27	0x1b	break

     WinAVR character string syntax example:
	const char *text = "\023 example \n\n\r\021"; 	// octal !

printf() Format Specifiers


	%a Hexadecimal output 0xh.hhhhp+d (C99 only)
	%A Hexadecimal output 0Xh.hhhhP+d (C99 only)
	%c Character
	%d Signed decimal integers
	%i Signed decimal integers
	%e Scientific notation (lowercase e)
	%E Scientific notation (uppercase E)
	%f Decimal floating point, numbers before/behind dot for example: %5.2f %1.3f or %.3f 
	%F Decimal floating point (C99 only)
	%g Uses %e or %f, whichever is shorter
	%G Uses %E or %F, whichever is shorter
	%o Unsigned octal
	%s String of characters
	%u Unsigned decimal integers
	%x Unsigned hexadecimal (lowercase letters)
	%X Unsigned hexadecimal (uppercase letters)
	%p Displays a pointer
	%n Argument must be a pointer to an integer
	%% Prints a percent sign
 

ASCII Table (7 bit)

(Bit 8 always zero)


       Decimal   Octal   Hex    Binary      Value    Key		Meaning
                               76543210		  
       __________________________________________________________________________________

         000      000    0x00   00000000      NUL    CTL @		null character, string-end 
         001      001    0x01   00000001      SOH    CTL A		start of header
         002      002    0x02   00000010      STX    CTL B		start of text
         003      003    0x03   00000011      ETX    CTL C		end of text
         004      004    0x04   00000100      EOT    CTL D		end of transmission
         005      005    0x05   00000101      ENQ    CTL E		enquiry
         006      006    0x06   00000110      ACK    CTL F		acknowledgment
         007      007    0x07   00000111      BEL    CTL G		bell
         008      010    0x08   00001000       BS    CTL H		backspace
         009      011    0x09   00001001       HT    CTL I		horizontal tab
         010      012    0x0A   00001010       LF    CTL J		line feed/ new line \n
         011      013    0x0B   00001011       VT    CTL K		vertical tab
         012      014    0x0C   00001100       FF    CTL L		form feed
         013      015    0x0D   00001101       CR    CTL M		carriage return \r
         014      016    0x0E   00001110       SO    CTL N		shift out
         015      017    0x0F   00001111       SI    CTL O		shift in
         016      020    0x10   00010000      DLE    CTL P		data link escape
         017      021    0x11   00010001      DC1    CTL Q		device control 1 (XON)
         018      022    0x12   00010010      DC2    CTL R		device control 2
         019      023    0x13   00010011      DC3    CTL S		device control 3 (XOFF)
         020      024    0x14   00010100      DC4    CTL T		device control 4
         021      025    0x15   00010101      NAK    CTL U		negative acknowledgement
         022      026    0x16   00010110      SYN    CTL V		synchronous idle
         023      027    0x17   00010111      ETB    CTL W		end of transmission block
         024      030    0x18   00011000      CAN    CTL X		cancel
         025      031    0x19   00011001       EM    CTL Y		end of medium
         026      032    0x1A   00011010      SUB    CTL Z		substitute
         027      033    0x1B   00011011      ESC    CTL [		escape
         028      034    0x1C   00011100       FS    CTL \		file separator
         029      035    0x1D   00011101       GS    CTL ]		group separator
         030      036    0x1E   00011110       RS    CTL ^		request to send (record separator)
         031      037    0x1F   00011111       US    CTL _		unit separator
         032      040    0x20   00100000       SP    		space sign
         033      041    0x21   00100001        !    		exclamation mark
         034      042    0x22   00100010        "    		double quote
         035      043    0x23   00100011        #    		number sign
         036      044    0x24   00100100        $    		dollar sign
         037      045    0x25   00100101        %    		percent
         038      046    0x26   00100110        &    		ampersand
         039      047    0x27   00100111        '    		single quote
         040      050    0x28   00101000        (    		left/opening parenthesis
         041      051    0x29   00101001        )    		right/closing parenthesis
         042      052    0x2A   00101010        *    		asterisk
         043      053    0x2B   00101011        +    		plus
         044      054    0x2C   00101100        ,    		comma
         045      055    0x2D   00101101        -    		minus or dash
         046      056    0x2E   00101110        .    		dot
         047      057    0x2F   00101111        /    		forward slash
         048      060    0x30   00110000        0
         049      061    0x31   00110001        1
         050      062    0x32   00110010        2
         051      063    0x33   00110011        3
         052      064    0x34   00110100        4
         053      065    0x35   00110101        5
         054      066    0x36   00110110        6
         055      067    0x37   00110111        7
         056      070    0x38   00111000        8
         057      071    0x39   00111001        9
         058      072    0x3A   00111010        :    		colon
         059      073    0x3B   00111011        ;    		semi-colon
         060      074    0x3C   00111100        < 	 		less than
         061      075    0x3D   00111101        =    		equal sign
         062      076    0x3E   00111110        > 			greater than
         063      077    0x3F   00111111        ?    		question mark
         064      100    0x40   01000000        @    		at-sign
         065      101    0x41   01000001        A
         066      102    0x42   01000010        B
         067      103    0x43   01000011        C
         068      104    0x44   01000100        D
         069      105    0x45   01000101        E
         070      106    0x46   01000110        F
         071      107    0x47   01000111        G
         072      110    0x48   01001000        H
         073      111    0x49   01001001        I
         074      112    0x4A   01001010        J
         075      113    0x4B   01001011        K
         076      114    0x4C   01001100        L
         077      115    0x4D   01001101        M
         078      116    0x4E   01001110        N
         079      117    0x4F   01001111        O
         080      120    0x50   01010000        P
         081      121    0x51   01010001        Q
         082      122    0x52   01010010        R
         083      123    0x53   01010011        S
         084      124    0x54   01010100        T
         085      125    0x55   01010101        U
         086      126    0x56   01010110        V
         087      127    0x57   01010111        W
         088      130    0x58   01011000        X
         089      131    0x59   01011001        Y
         090      132    0x5A   01011010        Z
         091      133    0x5B   01011011        [    		left/opening bracket
         092      134    0x5C   01011100        \    		back slash
         093      135    0x5D   01011101        ]    		right/closing bracket
         094      136    0x5E   01011110        ^    		caret/circumflex
         095      137    0x5F   01011111        _    		underscore
         096      140    0x60   01100000        `    		single quote back
         097      141    0x61   01100001        a
         098      142    0x62   01100010        b
         099      143    0x63   01100011        c
         100      144    0x64   01100100        d
         101      145    0x65   01100101        e
         102      146    0x66   01100110        f
         103      147    0x67   01100111        g
         104      150    0x68   01101000        h
         105      151    0x69   01101001        i
         106      152    0x6A   01101010        j
         107      153    0x6B   01101011        k
         108      154    0x6C   01101100        l
         109      155    0x6D   01101101        m
         110      156    0x6E   01101110        n
         111      157    0x6F   01101111        o
         112      160    0x70   01110000        p
         113      161    0x71   01110001        q
         114      162    0x72   01110010        r
         115      163    0x73   01110011        s
         116      164    0x74   01110100        t
         117      165    0x75   01110101        u
         118      166    0x76   01110110        v
         119      167    0x77   01110111        w
         120      170    0x78   01111000        x
         121      171    0x79   01111001        y
         122      172    0x7A   01111010        z
         123      173    0x7B   01111011        {    		left/opening brace
         124      174    0x7C   01111100        |    		vertical bar
         125      175    0x7D   01111101        }    		right/closing brace
         126      176    0x7E   01111110        ~    		tilde
         127      177    0x7F   01111111      DEL    		delete






Woher ASCII kam

Bis in die 1970-er Jahre waren Lochkarten und Lochbänder die pupulärsten Massenspeicher. Programmentwicklungen erfolgten nicht auf einem Monitor (den hatte man noch nicht), sondern auf Lochkarten oder Lochbändern. Insbesondere Lochband war die erste Wahl bei der Entwicklung von Microcontrollern, weil Schreib- und Lesegeräte vergleichbar einfach und preiswert waren und an einem Orgautomat betrieben werden konnten. Im Bild sehen wir einen Auszug. Obwohl irgendwann um 1977 von mir selbst erzeugt, erschließt sich mir der Inhalt dieses Streifenstücks nicht mehr so recht. Egal.

Was dazu zu sagen wäre: Die achte Spur (Bit 7) wurde meist als Parität oder Steuerspur genutzt. Zwischen Bit 4 und 5 lag ein Markierungsloch, welches die Aufgabe der mechanischen Bitsynchronisation hatte.

Da ein Lochband am Anfang und am Ende meist ungelocht war, konnte die Binärzahl 0000_0000 (keine Löcher) nicht benutzt werden. Hatte man ein Zeichen falsch gelocht, gab es als Korrekturmöglichkeit nur den Handlocher: Mit dem wurden alle Bitstellen durchstoßen, damit hatte die Zahl 1111_1111 ebenfalls keine Bedeutung.

Was aber bedeutete das für die Übertragung binärer Zahlen? War es noch möglich, 8 Bit von 0x00 bis 0xFF (0...255) zu codieren? Nein, denn es fehlten die Ziffern 0x00 und 0xFF. Wie kam man heraus aus dem Schlamassel? Entweder war eine neunte Spur einzuführen, oder man verzichtete generell darauf, Binärzahlen darstellen zu wollen.

Und hier kommt ASCII ins Spiel: Man begnügte sich damit, pro Zeichen nur eine Ziffer 0...9 oder 0...F zu speichern.

ASCII heute

Bei den Mikrocontrollern ist es wie in der Politik. Ein Prozessor kann entweder denken/arbeiten, oder reden, d.h. Zeit und Lebensleistung für Kommunikation nutzen. Beides zusammen ist kontraproduktiv - das wird oft übersehen, wenn neue Monster-Protokolle für die Steuerung im automobilen Bereich oder in der Industrieautomation angewandt werden. 32-Bit Prozessoren mit zig Megahertz schaffen dann in wallender Hitze tatsächlich Datenraten von 9600 Baud (man denke an Linux-Derivate oder Raspi). Ob man will oder nicht - beim Anblick solcher Lösungen wird einem kuschelig warm!

Intelligente Sensoren nutzen oft den Parameter "Zeit" zur Messung. Oft sperrt man alle Interrupts, um zur Meßzeit auch ja nicht von außen vollgelabert zu werden, sich keine Lese-Unterbrechung einzufangen.

Gerade bei unseren Kleinsten kommt es darauf an, die notwendige Kommunikation (Laden von Parametern, Ausgabe der Ergebnisse) möglichst effizient und zeitsparend in das jeweilige Arbeitsprogramm zu integrieren. Hier hat ASCII ungebrochen eine hervorragende, in den letzten Jahren sogar wieder wachsende Bedeutung. Vorausgesetzt, die IC-Hersteller erkennen irgendwann, daß es Kunden gibt, die 9600 Baud nicht 16-fach abtasten brauchen, sondern die 64 MegaBaud einfach abgetastet wollen. Im Embedded-Bereich existieren jede Menge kurzer Punkt- zu Punkt- Verbindungen, für die Überabtastung oder Netzprotokolle nutzlos sind. Fernverbindungen laufen sowieso über ETH.

Werden Daten unbekannter Größe übertragen, so sind Steuerzeichen nötig, um z.B. das Datenende zu kennzeichnen. Das Steuerzeichen selbst darf aber nicht in den Daten vorkommen. Üblich sind zur Datentrennung Leerzeichen, NULL-Zeichen, Tabulator, Komma, Semikolon (Excel CSV-Format) sowie Zeilenvorschub oder Wagenrücklauf.

Die Verwendung eines einzigen Steuerzeichens verhindert aber nun, daß Daten hexadezimal übertragen werden können. Schließlich kann ein einziger hex-Wert auch den Code eines Steuerzeichens besitzen, wie zum Beispiel 0x0d.

Will man hexadezimal codierte ("blanke") Daten übertragen, so geht das nur in einer Kapselung, in einem Datenblock bekannter Größe. Diesen nannten wir früher "Rahmen", heute zumeist "Paket". Wir zählen die Bytes des Datenblocks mit, interessieren uns aber erst wieder für das nächste Steuerzeichen, wenn der Block beendet ist. Damit ist sichergestellt, daß wir nach der Übertragung des Datenpakets wieder zurückschalten können auf Befehlsdekodierung. Selbst die NULL-Terminierung von C hilft hier nicht weiter, schließlich ist die '\0' als Hex-Wert 0x00 mit dem Integer-Wert Null verwechselbar; die NULL ist (ASCII-) Steuerzeichen und Integer-Wert zugleich.

Will man Daten schnell übertragen, so ist Pufferung unumgänglich. Das Einlesen in den Puffer braucht wenig Rechenzeit, nachdem der String fertig gelesen ist, hat man alle Zeit der Welt für die Befehlsdekodierung. Problematisch wird es, wenn mit '\r' beendete ASCII-Befehle und ASCII-Befehle mit hex-Werten gemischt werden sollen. Unser Puffer funktioniert nicht mehr, da ein Befehlsende-Zeichen auch in einem hex-Wert vorkommen kann. Es ist unvermeidbar, Zeichen für Zeichen in Echtzeit zu dekodieren, um sofort das Paket rechtzeitig erkennen zu können. Das kostet Rechenzeit. Die übertragbare Datenrate vermindert sich drastisch.

Nun haben derzeitige Mikrocontroller unkomplizierte UART-Interfaces, die minimal mit einer Tx- und einer Rx-Leitung daherkommen. Abgesehen von Video-Kameras tut man sich in rechenintensiven Applikationen schwer, Paket-Protokolle zu implementieren. Diese blockieren viel Rechenzeit oder Interrupts oder beides. Flexibler ist oft eine Nutzung der ASCII-Codierung übertragener Zeichen, insbesondere, wenn Arbeits- und Kommunikationsprogramm nur alternativ arbeiten können.

Nachteil einer ASCII-Codierung ist eine Umformung von Integer- oder Float-Werten in ASCII-Zeichen verschiedener Länge. Aus der uint16-Zahl 0xfe63 wird z.B. die ASCII-Zeichenfolge '6', '5', '1', '2', '3'. Aus der hex-Zahl 0x0000 wird ASCII '0'. Das Leseprogramm wird kompliziert, es braucht viel Rechenzeit.

Einfacher ist die von printf() bekannte Nibble-Kodierung von hex-Werten (Nibble: 4 Bit). Das Format %x stellt jeden hex-Wert als zwei ASCII dar. Damit existiert eine klare und einfache Codierungs- und Decodierungsvorschrift bei Sender und Empfänger. Steuerzeichen sind nun mit hex-Werten kompatibel.

Beispiel: Aus uint16_t 0xfe63 wird die ASCII-Zeichenfolge 'f', 'e', '6', '3'. Aus zwei Byte werden vier. Jedes Nibble (Halbbyte) wird mit seinem hex-Wert als ASCII '0'...'f' übertragen.

Weil sie immer mal wieder gebraucht werden, liegen hier einige einfache Grundroutinen zur Byte- zu Nibble-Wandlung als C-Code und als Demo-Exe.

Arbeitet man mit 12-Bit ADC, so macht die Nibble-Variante nur ein Drittel Overhead. Für die Übertragung von 2 Byte werden nur drei Nibble gebraucht. Aus zwei Byte hex werden drei Byte ASCII.

Für eine Protokollimplementierung ist also abzuwägen zwischen Rechenzeit für ein Blockprotokoll oder Übertragungszeit für die ASCII-Konvertierung und die dabei größere, zu übertragende Datenmenge:

- Die ASCII-Variante erschließt die Möglichkeit, sprachlich ungebunden zu kommunizieren, d.h. unter Verzicht auf Frames irgendwelcher Art. Man denke an Telnet, FTP, Email oder SMS. Sie eignet sich zur Steuerung und zur Übertragung von Parametern.

- Die hex-Block-Variante steht hingegen für die Übertragung uniformer Rechenergebnisse, man denke an Pixel-Bilder oder Zeitfunktionen.

Als geschlossene hex-Alternative bleibt die Definition eines einheitlichen (hex-) Pakets für alle Kommunikationen. Dieses hat mindestens die Länge des größten Parameter- oder Datensatzes plus dem Befehlswort-Format. Jeder simpelste Befehl hat die Länge des Pakets. Der Vorteil liegt darin, daß sich insbesondere ein Parametersatz als C-struct unkompliziert in und aus dem Puffer schieben läßt. Pakete können hier mit geringster Intelligenz gelesen oder geschrieben werden, damit sind höchste Bandbreiten sichergestellt. Kleben wir schlußendlich noch eine Sende- und Empfangsadresse an das Paket, sind wir fast im Internet. Der Nachteil aller Paket-Lösungen ist deren Unflexibilität. Die Gegenseite muß das Protokoll exakt kennen, um irgendwas zu verstehen. Und damit wären wir wieder bei den Vorzügen einer ASCII-Kodierung...

Wäre da nicht ein zweites Problem. Neben den langsamen USARTs derzeitiger Controller ist auch PuTTY nicht das schnellste. Etwa bei 900 kBaud ist der Spaß vorbei.

Wäre ich Prozessorhersteller, würde ich eine UART mit einem 256 Byte Puffer anbieten, die mit doppelter oder vierfacher CPU-Frequenz läuft. Der Puffer ist in den RAM-Bereich gemappt. Und ich würde sofort ein Devkit damit anbieten, welches eine Gegenstelle für eigene Experimente darstellt.



Viel mehr dazu:

Page address: http://www.gheinz.de/techdocs/ASCII-table_7bit.htm


Note
No liability or responsibility for mistakes or misinterpretations of every kind.
Please send a comment, if you find something wrong or dangerous.