Worum es geht:
auf einem AVR controller einen empfangenen 16 Bit Hexwert (hier vom ADC)
zB. 0xA2FD in 4 ASCII Werte umwandeln: "A" "2" "F" "D" (zum Senden über
UART).
Gibt es da EINFACHE KURZE routinen? Wenn ich jetzt anfange das
umzusetzen wird es sicher wieder komplizierter als es sein muss...
Außerdem noch eine nebenfrage:
Ich schicke alles per ASCII, damit ich es dann einfach im CSV format
speichern kann.
Schicke ich dann am ende jeder Zeile ein
- Carriage Return oder
- Line Feed oder
- Carriage Return + Line Feed?
micha schrieb:> Du nimmst ein 16 char Array mit [0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F] und> nutzt jeweils ein Nibble des Datenwortes als index. Ist nicht schwer
Hier mal die praktische Umsetzung:
1
// main conversion routine
2
// table unfortunately needs to reside in RAM for now
3
charhextable[16]="0123456789ABCDEF";
4
// very simple output to hex
5
staticvoidprintbyte(constunsignedchardata)
6
{
7
uart_put(hextable[data>>4]);
8
uart_put(hextable[data&0x0f]);
9
}
10
staticvoidprintword(constunsignedintdata)
11
{
12
printbyte(data>>8);
13
printbyte(data);
14
}
Es wäre auch möglich, die Hextabelle ins Flash zu setzen, und über
pgm_read_byte drauf zuzugreifen. So kostet der Spass 16 Bytes im RAM für
die Tabelle.
Alex v. L. schrieb:> Und wie ist es konventionell mit den CR /LF am ende?
Das hängt vom Betriebssystem ab.
Bei Windows Systemen ist es zum Beispiel CR + LF, bei Unixioden nur der
LF.
Falk Brunner schrieb:> CR + LF, das ist das Standardformat aus DOS-Zeiten. Unix macht nur CR.
CR LF auch Windows (auch neue Apple?)
Nur CR hatte Apple früher
IBM (vor allem Host, aber auch AIX teilweise) unterscheidet zwischen
Line Feed (LF) und New Line (NL)
cyblord ---- schrieb:> innerand innerand schrieb:>> bei Unixioden nur der LF>> Typisch, und wie kommt dann der Cursor wieder an den Anfang der Zeile?
Stimmt schon, was "innerand" sagt.
Bei Unix: LF
Bei Mac: CR
Bei DOS/Windoofs: CR+LF
Luther Blissett schrieb:> char hextable[16] = "0123456789ABCDEF";
Sollte das nicht char hextable[17] = "0123456789ABCDEF";
^
--------------------------------|
sein? Im String "0123456789ABCEDF" wird ja wohl noch die '/0' als
Terminierung dazu gepackt
Cheers
LTC1043 schrieb im Beitrag #3499442:
> Luther Blissett schrieb:>> char hextable[16] = "0123456789ABCDEF";>> Sollte das nicht char hextable[17] = "0123456789ABCDEF";> ^> --------------------------------|>> sein? Im String "0123456789ABCEDF" wird ja wohl noch die '/0' als> Terminierung dazu gepackt
Die allerdings keiner braucht.
Das Teil wird hier als ein Array von Charactern benutzt und nicht als
String.
(Und es gibt eine Sonderregel in C: Ist die Initialisierung eines
Char-Arrays genau so groß, das alle Zeichen in das Array passen, dann
hängt der COmpiler kein \0 hinten drann. Jetzt hab ich endlich mal ein
Beispiel dafür, wo dieses Verhalten nützlich ist :-)
In vielen meiner Projekte findet sich diese kurze Funktion:
1
voidhexout(uint8_tx)
2
{
3
registeruint8_td=(x>>4)&0x0F;
4
putc(d+(d>9?'A'-10:'0'));
5
d=x&0x0F;
6
putc(d+(d>9?'A'-10:'0'));
7
}
Da man einen längeren Integer i.d.R. problemlos in Bytes zerlegen kann,
ruft man die Funktion einfach für jedes Byte einmal auf, beginnend mit
dem höchstwertigen.
Selbstverständlich kann man statt putc() auch sendRS232() o.ö.
verwenden. Und wenn man Klein- statt Großbuchstaben will, tauscht man
das 'A' gegen ein 'a'.
XL
Axel Schwenke schrieb:> In vielen meiner Projekte findet sich diese kurze Funktion:> register uint8_t d= (x>>4) & 0x0F;
hast du dir mal den erzeugten ASM code angeschaut? Das Register hatte
bei mir mehr negative Auswirkungen, weil es dann dafür extra ein neues
Register verwendet hat. Dabei steht der übergebene wert ja bereits in
einem Register.
Schneller wird es damit auf jeden Fall nicht.
Karl Heinz schrieb:> (Und es gibt eine Sonderregel in C: Ist die Initialisierung eines> Char-Arrays genau so groß, das alle Zeichen in das Array passen, dann> hängt der COmpiler kein \0 hinten drann.
Ich bin immer zu faul zum Zählen.
Ich lass daher die Klammer leer und opfere das Byte.
Aber es ginge auch so:
Peter II schrieb:> Axel Schwenke schrieb:>> In vielen meiner Projekte findet sich diese kurze Funktion:>> register uint8_t d= (x>>4) & 0x0F;>> hast du dir mal den erzeugten ASM code angeschaut? Das Register hatte> bei mir mehr negative Auswirkungen, weil es dann dafür extra ein neues> Register verwendet hat. Dabei steht der übergebene wert ja bereits in> einem Register.
Der übergebene Wert darf aber nicht direkt verändert werden, weil er ja
zweimal gebraucht wird. Unten wird er daher nach r17 gerettet und die
eigentliche Rechenarbeit wird in r24/r25 gemacht. Was sinnvoll ist, weil
r24 ja gleich das Argument für den Aufruf von putc() enthalten muß. Ein
weiterer Grund, das hexout()-Argument nach r17 zu retten.
> Schneller wird es damit auf jeden Fall nicht.
Nein. Es schadet aber auch nicht. avr-gcc erzeugt bei mir mit oder oder
ohne register den gleichen Code. Interessant ist aber, daß man einen
Maschinenbefehl sparen kann, wenn man den Ausdruck
1
putc(d+(d>9?'A'-10:'0'))
ausführlicher (aber nicht unbedingt lesbarer) so schreibt:
1
d+='0';
2
if(d>'9')
3
d+='A'-10-'0';
4
putc(d)
Hier ist was avr-gcc im Optimierungslevel -Os erzeugt:
1
void hexout(uint8_t x)
2
{
3
3e4: 1f 93 push r17
4
3e6: 18 2f mov r17, r24
5
register uint8_t d= (x>>4) & 0x0F;
6
3e8: 98 2f mov r25, r24
7
3ea: 92 95 swap r25
8
3ec: 9f 70 andi r25, 0x0F ; 15
9
lcd_putc(d + (d>9 ? 'A'-10 : '0'));
10
3ee: 9a 30 cpi r25, 0x0A ; 10
11
3f0: 10 f4 brcc .+4 ; 0x3f6 <hexout+0x12>
12
3f2: 80 e3 ldi r24, 0x30 ; 48
13
3f4: 01 c0 rjmp .+2 ; 0x3f8 <hexout+0x14>
14
3f6: 87 e3 ldi r24, 0x37 ; 55
15
3f8: 89 0f add r24, r25
16
3fa: fe d2 rcall .+1532 ; 0x9f8 <lcd_putc>
17
d= x & 0x0F;
18
3fc: 91 2f mov r25, r17
19
3fe: 9f 70 andi r25, 0x0F ; 15
20
d+= '0';
21
400: 89 2f mov r24, r25
22
402: 80 5d subi r24, 0xD0 ; 208
23
if (d>'9') d+= 'A'-10-'0';
24
404: 8a 33 cpi r24, 0x3A ; 58
25
406: 08 f0 brcs .+2 ; 0x40a <hexout+0x26>
26
408: 89 5f subi r24, 0xF9 ; 249
27
lcd_putc(d);
28
40a: f6 d2 rcall .+1516 ; 0x9f8 <lcd_putc>
29
}
30
40c: 1f 91 pop r17
31
40e: 08 95 ret
In Assembler hätte ich den Teil übrigens anders geschrieben:
1
andi r24, 0x0F
2
ldi r25, '0'
3
cpi r24, 10
4
brcs +2
5
ldi r25, 'A'-10
6
add r24, r25
7
rcall putc
genauso kurz wie die kürzere Variante oben, aber IMHO lesbarer. Und am
Ende der Funktion hätte ich das ret noch weggespart ;)
Peter Dannegger schrieb:> sprintf( buff, "%04X\r\n", val );
Tja Pete, Thema verfehlt ürde ich sagen. Es ging nicht um eine
minimalistische Quellzeile, sondern um eine "EINFACHE KURZE" Routine.
Ich denke, der TO meinte sowas:
W.S. schrieb:> Peter Dannegger schrieb:>> sprintf( buff, "%04X\r\n", val );>> Tja Pete, Thema verfehlt ürde ich sagen. Es ging nicht um eine> minimalistische Quellzeile, sondern um eine "EINFACHE KURZE" Routine.> Ich denke, der TO meinte sowas:>> const char HexChars[17] = {"0123456789ABCDEF"};
Lies den Thread noch mal durch. Die Kovertierung über einen Arraylookup
ist schon diskutiert worden. Du findest dort auch einen Post der
erläutert, warum du für den HexChar Array keine 17 bytes brauchst.
W.S. schrieb:> Tja Pete, Thema verfehlt ürde ich sagen. Es ging nicht um eine> minimalistische Quellzeile, sondern um eine "EINFACHE KURZE" Routine.
Nö, es ging ihm genau um den Quelltext, daß der nicht zu kompliziert
ist:
Alex v. L. schrieb:> Wenn ich jetzt anfange das> umzusetzen wird es sicher wieder komplizierter als es sein muss...
Das mit "wenig Flash" haben nur andere fälschlich hinein interpretiert.