Möglichst kurze Routine für Hex-to-ASCII-Conversion

OP Persönliche Seite #3499070
Lesenswert?

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...
(Firma: matzetronics) #3499089
Lesenswert?

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
char hextable[16] = "0123456789ABCDEF";
4
// very simple output to hex 
5
static void printbyte(const unsigned char data)
6
{
7
  uart_put(hextable[data >> 4]);
8
  uart_put(hextable[data & 0x0f]);
9
}
10
static void printword(const unsigned int data)
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.
#3499405
Lesenswert?

Array ist viel zu lang!
1
/*
2
$ avr-gcc -mmcu=atmega8 -DHEX_FUN -c -Os h.c && avr-size h.o
3
   text     data      bss      dec      hex  filename
4
     52        0        0       52       34  h.o
5
$ avr-gcc -mmcu=atmega8 -DHEX_TABLE -c -Os h.c && avr-size h.o
6
   text     data      bss      dec      hex  filename
7
     60       16        0       76       4c  h.o
8
$ avr-gcc -mmcu=atmega8 -DHEX_PROGMEM -c -Os h.c && avr-size h.o
9
   text     data      bss      dec      hex  filename
10
     76        0        0       76       4c  h.o
11
*/
12
#include <stdint.h>
13
#include <avr/pgmspace.h>
14

15
extern void put(char c); 
16

17
#if defined(HEX_TABLE)
18

19
 char hextable[16] = "0123456789ABCDEF";
20
 #define N2C(X) (hextable[X])
21

22
#elif defined(HEX_FUN)
23

24
 char n2c(uint8_t n) { return (n<10?n+'0':n+('A'-10)); }
25
 #define N2C(X) (n2c(X))
26

27
#elif defined(HEX_PROGMEM)
28

29
 char hextable[16] PROGMEM = "0123456789ABCDEF";
30
 #define N2C(X) (hextable[X])
31

32
#else 
33
#error "define HEX_FUN or HEX_TABLE or HEX_PROGMEM"
34
#endif
35

36
void put8(uint8_t n)
37
{
38
  put(N2C(n>>4)); 
39
  put(N2C(n&0xf)); 
40
}
41
void put16(uint16_t n) 
42
{
43
  put8(n>>8);
44
  put8(n);
45
}
#3499449
Lesenswert?

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 :-)
#3499529
Lesenswert?

In vielen meiner Projekte findet sich diese kurze Funktion:

1
void hexout(uint8_t x)                                                          
2
{                                                                               
3
    register uint8_t d= (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
Gast #3499534
Lesenswert?

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.
#3499541
Lesenswert?

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:
1
#define STR "1245"
2

3
char y[sizeof(STR)-1] = STR;
#3499764
Lesenswert?

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 ;)
1
  pop r17
2
  rjmp putc


XL
Gast #3499773
Lesenswert?

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:
1
const char HexChars[17]   = {"0123456789ABCDEF"};
2
/* ein Hex-Zeichen ausgeben */
3
void Nibble_Out (int aHex)
4
{ V24_CharOut (HexChars[aHex & 0x0F]); }
5

6
/* ein Byte als Hexa ausgeben */
7
void HexB_Out (byte aHex)
8
{ Nibble_Out (aHex >> 4);
9
  Nibble_Out (aHex);
10
}
11

12
/* ein Word als Hexa ausgeben */
13
void HexW_Out (word aHex)
14
{ HexB_Out (aHex >> 8);
15
  HexB_Out (aHex);
16
}
17

18
/* ein DWORD als Hexa ausgeben */
19
void HexL_Out (long aHex)
20
{ HexW_Out (aHex >> 16);
21
  HexW_Out (aHex);
22
}
23

24
/* ein QWORD als Hexa ausgeben */
25
void HexQ_Out (int64* aHex)
26
{ HexL_Out (*aHex >> 32);
27
  HexL_Out (*aHex);
28
}
zitiert aus der Lernbetty..  ;-)

W.S.
#3500489
Lesenswert?

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.
#3500501
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren