Forum: Mikrocontroller und Digitale Elektronik extrem schnell Wandeln ASCII-Hex nach Binär auf einem uC


von Regatti (Gast)


Lesenswert?

Ich muss auf einem Microcontroller eine ganze Menge ASCI-Hex Daten nach 
Binär wandeln.
Leider dauert das alles noch zu lange. Mit Libary-Funktionen dauert es 
ewig.
Ich habe nun Zeichen für Zeichen genommen, geguckt ob es zwischen 0-9 
oder A-F liegt, entsprechend abgezogen und das High-Nibble um 4 
geschoben. Das geht schon viel schneller, ist aber immer noch zu 
langsam.

Kennt jemand eine tolle und extrem schnelle Methode (C-Code)? Mir fällt 
nicht mehr viel ein, auch in ASM würde ich es nicht anders machen als 
das, was der Compiler aus meinem C-Code macht.

Thx,

Regatti

von Matthias L. (Gast)


Lesenswert?

Also du willst aus nem String zB '4B' eine 8bit große Zahl machen???

von Εrnst B. (ernst)


Lesenswert?

Lookup-Tabelle im Flash, von "char"->"nibble" anlegen?
aber obs das wirklich schneller macht, musst du ausprobieren...

von Tcf K. (tcfkat)


Lesenswert?

Arithmetik und Fallunterscheidung je nach ASCII-Wert gar nicht mehr 
machen. Große Tabelle für ASCII-Werte von 0x00 bis 'f' resp. 'F' anlegen 
(Platz sollte nicht das Problem sein), und ASCII-Wert direkt als Offset 
für die Tabelle benutzen. Nicht benutzte Tabellenwerte interessieren 
dann nicht.
Tabelle doppelt anlegen für High- und Low-Nibble, dann musst Du nur noch 
die beiden Nibbles addieren.

von Matthias L. (Gast)


Lesenswert?

1
#include <avr/pgmspace.h>
2
uint8_t tabelle[] PROGMEM = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
3
                                   0, 0, 0, 0, 0, 0, 0, 
4
                                   10, 11, 12, 13, 14, 15   };
5
6
7
uint8_t  ascii2hex ( uint8_t data[2] )
8
{
9
  // -- local var's ---------------
10
   uint8_t   high; low, result;
11
  // -- Großbuchstaben ------------
12
   data[0] &= ~(0x20);
13
   data[1] &= ~(0x20);
14
  // -- Offset abziehen -----------
15
   high = data[0] - 0x30;
16
   low  = data[1] - 0x30;
17
  // -- wert aus tabelle holen ----
18
   result  = ( pgm_read_byte ( &tabelle[ data[ high ] )  << 4 );
19
   result |= ( pgm_read_byte ( &tabelle[ data[ low] )         );
20
  // -- rückgabe ------------------
21
   return result;
22
}

Vielleicht so? Habs auf die Schnelle geschrieben, ist also nicht 
getestet...
Der Compiler macht das draus:
1
@00000071: ascii2hex
2
---- cluster.c ------------------------------------------------------------------------------------
3
47:       {
4
+00000071:   01FC        MOVW    R30,R24          Copy register pair
5
51:          data[0] &= ~(0x20);
6
+00000072:   8180        LDD     R24,Z+0          Load indirect with displacement
7
+00000073:   7D8F        ANDI    R24,0xDF         Logical AND with immediate
8
+00000074:   8380        STD     Z+0,R24          Store indirect with displacement
9
52:          data[1] &= ~(0x20);
10
+00000075:   8191        LDD     R25,Z+1          Load indirect with displacement
11
+00000076:   7D9F        ANDI    R25,0xDF         Logical AND with immediate
12
+00000077:   8391        STD     Z+1,R25          Store indirect with displacement
13
57:          result  = ( pgm_read_byte ( &tabelle[ high ] )  << 4 );
14
+00000078:   5380        SUBI    R24,0x30         Subtract immediate
15
+00000079:   E82C        LDI     R18,0x8C         Load immediate
16
+0000007A:   E030        LDI     R19,0x00         Load immediate
17
+0000007B:   01F9        MOVW    R30,R18          Copy register pair
18
+0000007C:   0FE8        ADD     R30,R24          Add without carry
19
+0000007D:   1DF1        ADC     R31,R1           Add with carry
20
+0000007E:   9144        LPM     R20,Z            Load program memory
21
+0000007F:   9542        SWAP    R20              Swap nibbles
22
+00000080:   7F40        ANDI    R20,0xF0         Logical AND with immediate
23
58:          result |= ( pgm_read_byte ( &tabelle[ low  ] )       );
24
+00000081:   5390        SUBI    R25,0x30         Subtract immediate
25
+00000082:   0F29        ADD     R18,R25          Add without carry
26
+00000083:   1D31        ADC     R19,R1           Add with carry
27
+00000084:   01F9        MOVW    R30,R18          Copy register pair
28
+00000085:   9184        LPM     R24,Z            Load program memory
29
+00000086:   2B48        OR      R20,R24          Logical OR
30
61:       }
31
+00000087:   2F84        MOV     R24,R20          Copy register
32
+00000088:   2799        CLR     R25              Clear Register
33
+00000089:   9508        RET                      Subroutine return

Wenn ich richtig gezählt habe, sind das 25Befehle, bei 2Takten je Befehl 
und 16MHz sollten das etwa ~3,1µsek...

von Matthias L. (Gast)


Lesenswert?

Ich seh grad, es ist wohl besser (spart paar befehle), wenn dus so 
machst:
1
...
2
 // -- local var's ---------------
3
   uint8_t   high; low, result;
4
  // -- Großbuchstaben & Offset ------------
5
   high    =  ( data[0] &= ~(0x20) ) - 0x30;
6
   result  =  ( pgm_read_byte ( &tabelle[ data[ high ] )  << 4 );
7
8
   low     =  ( data[1] &= ~(0x20) ) - 0x30;
9
   result  |= ( pgm_read_byte ( &tabelle[ data[ low  ] )       );
10
  // -- rückgabe ------------------
11
   return result;

von Peter D. (peda)


Lesenswert?

Wo kommen die Daten her, wo gehen sie hin, was für ein MC ?

Ich kann mir nicht vorstellen, daß das Wandeln gegenüber den weiteren 
Tasks merkbar viel Zeit beansprucht. Es sind ja nur wenige Vergleiche, 
Additionen.

Ob die Tabellenmethode wirklich ein klein wenig schneller ist, hängt vom 
MC ab, erheblich aufwendiger ist sie auf jeden Fall.


Peter

von Tcf K. (tcfkat)


Lesenswert?

Aufwändiger ist eine Tabellenmethode nicht, aber schneller. Wenn man 
"dünn besiedelte" Tabellen zulässt, spart man jede 
Indizierungsarithmetik... schneller geht es dann nicht mehr.

von Matthias L. (Gast)


Lesenswert?

Was ist denn eine "dünn besiedelte" Tabelle?

;-)

von holger (Gast)


Lesenswert?

@ Matthias

Schnell und Tabelle im Flash dürfte sich widersprechen ;)
Pack sie ins RAM.

von Karl H. (kbuchegg)


Lesenswert?

Matthias L. wrote:

> Wenn ich richtig gezählt habe, sind das 25Befehle, bei 2Takten je Befehl
> und 16MHz sollten das etwa ~3,1µsek...

Das ist aber reichlich viel Holz
1
uint8_t  ascii2hex ( uint8_t* data )
2
{
3
  uint8_t Nibble1, Nibble2, Result;
4
5
  Nibble1 = *data - '0';
6
  if( Nibble1 > 9 )
7
    Nibble1 -= 'A' - '0';
8
9
  Nibble2 = *(data+1) - '0';
10
  if( Nibble2 > 9 )
11
    Nibble2 -= 'A' - '0';
12
13
  Result = Nibble1 << 4;
14
  Result |= Nibble2;  
15
  return Result;
16
}

1
uint8_t  ascii2hex ( uint8_t* data )
2
{
3
  8e:  fc 01         movw  r30, r24
4
  uint8_t Nibble1, Nibble2, Result;
5
6
  Nibble1 = *data - '0';
7
  90:  90 81         ld  r25, Z
8
  92:  89 2f         mov  r24, r25
9
  94:  80 53         subi  r24, 0x30  ; 48
10
  if( Nibble1 > 9 )
11
  96:  8a 30         cpi  r24, 0x0A  ; 10
12
  98:  08 f0         brcs  .+2        ; 0x9c <ascii2hex+0xe>
13
    Nibble1 -= 'A' - '0';
14
  9a:  81 51         subi  r24, 0x11  ; 17
15
16
  Nibble2 = *(data+1) - '0';
17
  9c:  91 81         ldd  r25, Z+1  ; 0x01
18
  9e:  29 2f         mov  r18, r25
19
  a0:  20 53         subi  r18, 0x30  ; 48
20
  if( Nibble2 > 9 )
21
  a2:  2a 30         cpi  r18, 0x0A  ; 10
22
  a4:  08 f0         brcs  .+2        ; 0xa8 <ascii2hex+0x1a>
23
    Nibble2 -= 'A' - '0';
24
  a6:  21 51         subi  r18, 0x11  ; 17
25
26
  Result = Nibble1 << 4;
27
  a8:  82 95         swap  r24
28
  aa:  80 7f         andi  r24, 0xF0  ; 240
29
  Result |= Nibble2;  
30
  ac:  82 2b         or  r24, r18
31
  return Result;
32
}
33
  ae:  99 27         eor  r25, r25
34
  b0:  08 95         ret

 = 18 Befehle

von Matthias L. (Gast)


Lesenswert?

@Holger:

Aus
1
pgm_read_byte( *uint8_t );

wird eh ein
1
LPM
und dieser Befehl dauert drei Takte, statt zwei wie ein RAM-Zugriff.
Das würde bedeuten, der aus den ~3,1µsek werden dann vielleicht 
2,9µsek..
Ich glaube nicht, dass es das bringt..

von W. B. (wb1)


Lesenswert?

Hilft das eventuell?
Hier gibt es jedoch eine Einschränkung, A-F müssen als Großbuchstaben 
vorhanden sein. Wenn sie als Kleinbuchstaben vorhanden sind, ist 
entweder zusätzlich ein bit zu löschen oder "subi register, 0x61-0x3a" 
zuverwenden

; zu wandelnder Wert '4F'
anfang:
        ldi r16,'4'  ;r16 enthält 0x34
        ldi r17,'F'  ;r17 enthält 0x46
        call beginn_ascii_nach_binaer
        nop
        rjmp anfang

beginn_ascii_nach_binaer:
  sbrs r16,5
  subi r16,0x41-0x3a
  subi r16,0x30
  swap r16
  sbrs r17,5
  subi r17,0x41-0x3a
  subi r17,0x30
  add r16,r17  ;Ergebnis in r16
        ret

Im ungünstigsten Fall sind 8 Befehle abzuarbeiten im günstigsten Fall 
nur 6

;Ende
MfG
wb1

von Falk B. (falk)


Lesenswert?

Die Vorschläge sind ja alle ganz nett, aber vielleicht sollte man den 
Hintergrund des Problems erstmal beleuchten.

Wieviele Daten sollen in welcher Zeit gewandelt werden? Wo kommen die 
her? Etc.
Dann kann man sicher noch ganz andere Lösungen in Betracht ziehen.

Think global, act local.

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

1
uint8_t  ascii2hex ( uint8_t* data )
2
{
3
  static uint8_t Hex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
4
                           0, 0, 0, 0, 0, 0, 0, 
5
                           10, 11, 12, 13, 14, 15   };
6
7
8
9
  uint8_t Result;
10
11
  Result = Hex[ *data - '0' ];
12
  Result |= Hex[*(data+1) - '0' ];
13
14
  return Result;
15
}
1
uint8_t  ascii2hex ( uint8_t* data )
2
{
3
  8e:  9c 01         movw  r18, r24
4
  static uint8_t Hex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
5
                           0, 0, 0, 0, 0, 0, 0, 
6
                           10, 11, 12, 13, 14, 15   };
7
8
9
10
  uint8_t Result;
11
12
  Result = Hex[ *data - '0' ];
13
  90:  fc 01         movw  r30, r24
14
  92:  80 81         ld  r24, Z
15
  94:  a0 e6         ldi  r26, 0x60  ; 96
16
  96:  b0 e0         ldi  r27, 0x00  ; 0
17
  98:  fd 01         movw  r30, r26
18
  9a:  e8 0f         add  r30, r24
19
  9c:  f1 1d         adc  r31, r1
20
  9e:  f0 97         sbiw  r30, 0x30  ; 48
21
  a0:  90 81         ld  r25, Z
22
  Result |= Hex[*(data+1) - '0' ];
23
  a2:  f9 01         movw  r30, r18
24
  a4:  81 81         ldd  r24, Z+1  ; 0x01
25
  a6:  a8 0f         add  r26, r24
26
  a8:  b1 1d         adc  r27, r1
27
  aa:  d0 97         sbiw  r26, 0x30  ; 48
28
  ac:  8c 91         ld  r24, X
29
  ae:  98 2b         or  r25, r24
30
31
  return Result;
32
}
33
  b0:  89 2f         mov  r24, r25
34
  b2:  99 27         eor  r25, r25
35
  b4:  08 95         ret

= 20 Befehle
Das Problem mit der Tabelle ist, dass die Indizierung selbst auch
einige Befehle braucht. Die Berechnungsmethode ist da schneller.

PS: Ich geh von der Annahme aus, dass nur Grossbuchstaben vorkommen

von holger (Gast)


Lesenswert?

@ Karl Heinz

uint8_t  ascii2hex ( uint8_t* data )
{
  uint8_t Nibble1, Nibble2, Result;

  Nibble1 = *data - '0';
  if( Nibble1 > 9 )
    Nibble1 -= 7;

  Nibble2 = *(data+1) - '0';
  if( Nibble2 > 9 )
    Nibble2 -= 7;

  Result = Nibble1 << 4;
  Result |= Nibble2;
  return Result;
}

von Karl H. (kbuchegg)


Lesenswert?

Wenn die Character nicht als Array übergeben werden sondern
als 2 Character, kommt man runter
1
uint8_t  ascii2hex ( uint8_t Byte1, uint8_t Byte2 )
2
{
3
  static uint8_t Hex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
4
                           0, 0, 0, 0, 0, 0, 0, 
5
                           10, 11, 12, 13, 14, 15   };
6
7
0000008e <ascii2hex>:
8
9
10
  uint8_t Result;
11
12
  Result = Hex[ Byte1 - '0' ];
13
  8e:  a0 e6         ldi  r26, 0x60  ; 96
14
  90:  b0 e0         ldi  r27, 0x00  ; 0
15
  92:  fd 01         movw  r30, r26
16
  94:  e8 0f         add  r30, r24
17
  96:  f1 1d         adc  r31, r1
18
  98:  f0 97         sbiw  r30, 0x30  ; 48
19
  9a:  90 81         ld  r25, Z
20
  Result |= Hex[Byte2 - '0' ];
21
  9c:  a6 0f         add  r26, r22
22
  9e:  b1 1d         adc  r27, r1
23
  a0:  d0 97         sbiw  r26, 0x30  ; 48
24
  a2:  8c 91         ld  r24, X
25
  a4:  98 2b         or  r25, r24
26
27
  return Result;
28
}
29
  a6:  89 2f         mov  r24, r25
30
  a8:  99 27         eor  r25, r25
31
  aa:  08 95         ret

von Karl H. (kbuchegg)


Lesenswert?

Runter auf 13
1
uint8_t  ascii2hex ( uint8_t Byte1, uint8_t Byte2 )
2
{
3
  uint8_t Nibble2, Result;
4
5
  Result = Byte1 - '0';
6
  8e:  80 53         subi  r24, 0x30  ; 48
7
  if( Result > 9 )
8
  90:  8a 30         cpi  r24, 0x0A  ; 10
9
  92:  08 f0         brcs  .+2        ; 0x96 <ascii2hex+0x8>
10
    Result -= 'A' - '0';
11
  94:  81 51         subi  r24, 0x11  ; 17
12
  Result = Result << 4;
13
  96:  82 95         swap  r24
14
  98:  80 7f         andi  r24, 0xF0  ; 240
15
16
  Nibble2 = Byte2 - '0';
17
  9a:  96 2f         mov  r25, r22
18
  9c:  90 53         subi  r25, 0x30  ; 48
19
  if( Nibble2 > 9 )
20
  9e:  9a 30         cpi  r25, 0x0A  ; 10
21
  a0:  08 f0         brcs  .+2        ; 0xa4 <ascii2hex+0x16>
22
    Nibble2 -= 'A' - '0';
23
  a2:  91 51         subi  r25, 0x11  ; 17
24
25
  Result |= Nibble2;  
26
  a4:  89 2b         or  r24, r25
27
  return Result;
28
}
29
  a6:  99 27         eor  r25, r25
30
  a8:  08 95         ret

Ich wuesste nicht, wo da jetzt noch was gehen würde.

von Simon K. (simon) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:
> Runter auf 13

Bei so trivialen Aufgaben ist es wirklich besser ohne Tabelle. Hätte ich 
nicht gedacht.

Hier mal eine (fast) vollständig auf Tabelle basierende Lösung:
1
static const uint8_t g_Ascii2Hex[23][23] PROGMEM = 
2
{                                                                                                                  
3
  {  0  ,  1  ,  2  ,  3  ,  4  ,  5  ,  6  ,  7  ,  8  ,  9  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  10  ,  11  ,  12  ,  13  ,  14  ,  15  },                    
4
  {  16  ,  17  ,  18  ,  19  ,  20  ,  21  ,  22  ,  23  ,  24  ,  25  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  26  ,  27  ,  28  ,  29  ,  30  ,  31  },                    
5
  {  32  ,  33  ,  34  ,  35  ,  36  ,  37  ,  38  ,  39  ,  40  ,  41  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  42  ,  43  ,  44  ,  45  ,  46  ,  47  },                    
6
  {  48  ,  49  ,  50  ,  51  ,  52  ,  53  ,  54  ,  55  ,  56  ,  57  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  58  ,  59  ,  60  ,  61  ,  62  ,  63  },                    
7
  {  64  ,  65  ,  66  ,  67  ,  68  ,  69  ,  70  ,  71  ,  72  ,  73  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  74  ,  75  ,  76  ,  77  ,  78  ,  79  },                    
8
  {  80  ,  81  ,  82  ,  83  ,  84  ,  85  ,  86  ,  87  ,  88  ,  89  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  90  ,  91  ,  92  ,  93  ,  94  ,  95  },                    
9
  {  96  ,  97  ,  98  ,  99  ,  100  ,  101  ,  102  ,  103  ,  104  ,  105  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  106  ,  107  ,  108  ,  109  ,  110  ,  111  },                    
10
  {  112  ,  113  ,  114  ,  115  ,  116  ,  117  ,  118  ,  119  ,  120  ,  121  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  122  ,  123  ,  124  ,  125  ,  126  ,  127  },                    
11
  {  128  ,  129  ,  130  ,  131  ,  132  ,  133  ,  134  ,  135  ,  136  ,  137  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  138  ,  139  ,  140  ,  141  ,  142  ,  143  },                    
12
  {  144  ,  145  ,  146  ,  147  ,  148  ,  149  ,  150  ,  151  ,  152  ,  153  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  154  ,  155  ,  156  ,  157  ,  158  ,  159  },                    
13
  {  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  },                    
14
  {  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  },                    
15
  {  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  },                    
16
  {  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  },                    
17
  {  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  },                    
18
  {  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  },                    
19
  {  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  },                    
20
  {  160  ,  161  ,  162  ,  163  ,  164  ,  165  ,  166  ,  167  ,  168  ,  169  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  170  ,  171  ,  172  ,  173  ,  174  ,  175  },                    
21
  {  176  ,  177  ,  178  ,  179  ,  180  ,  181  ,  182  ,  183  ,  184  ,  185  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  186  ,  187  ,  188  ,  189  ,  190  ,  191  },                    
22
  {  192  ,  193  ,  194  ,  195  ,  196  ,  197  ,  198  ,  199  ,  200  ,  201  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  202  ,  203  ,  204  ,  205  ,  206  ,  207  },                    
23
  {  208  ,  209  ,  210  ,  211  ,  212  ,  213  ,  214  ,  215  ,  216  ,  217  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  218  ,  219  ,  220  ,  221  ,  222  ,  223  },                    
24
  {  224  ,  225  ,  226  ,  227  ,  228  ,  229  ,  230  ,  231  ,  232  ,  233  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  234  ,  235  ,  236  ,  237  ,  238  ,  239  },                    
25
  {  240  ,  241  ,  242  ,  243  ,  244  ,  245  ,  246  ,  247  ,  248  ,  249  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  0  ,  250  ,  251  ,  252  ,  253  ,  254  ,  255  }                    
26
};
27
                    
28
uint8_t Ascii2Hex(char c1, char c2)
29
{
30
  return pgm_read_byte(&g_Ascii2Hex[c1 - '0'][c2 - '0']);
31
}
1
uint8_t Ascii2Hex(char c1, char c2)
2
{
3
 2a0:  e8 2f         mov  r30, r24
4
  return pgm_read_byte(&g_Ascii2Hex[c1 - '0'][c2 - '0']);
5
 2a2:  87 e1         ldi  r24, 0x17  ; 23
6
 2a4:  e8 02         muls  r30, r24
7
 2a6:  f0 01         movw  r30, r0
8
 2a8:  11 24         eor  r1, r1
9
 2aa:  77 27         eor  r23, r23
10
 2ac:  67 fd         sbrc  r22, 7
11
 2ae:  70 95         com  r23
12
 2b0:  e6 0f         add  r30, r22
13
 2b2:  f7 1f         adc  r31, r23
14
 2b4:  ec 52         subi  r30, 0x2C  ; 44
15
 2b6:  f4 40         sbci  r31, 0x04  ; 4
16
 2b8:  e4 91         lpm  r30, Z
17
}
18
 2ba:  8e 2f         mov  r24, r30
19
 2bc:  99 27         eor  r25, r25
20
 2be:  08 95         ret

PS: Man könnte die Tabelle mit 48 Nullen in x und y Richtung am Anfang 
erstellen, dann könnte man sich die Subtraktion mit '0' sparen. Dann 
wird die Tabelle aber 5kb groß :D

von Regatti (Gast)


Lesenswert?

Also vielen Dank für die vielen tollen Antworten, ich werde das alles 
mal testen.

von Karl H. (kbuchegg)


Lesenswert?

Simon Küppers wrote:
> Karl heinz Buchegger wrote:
>> Runter auf 13
>
> Bei so trivialen Aufgaben ist es wirklich besser ohne Tabelle. Hätte ich
> nicht gedacht.

Die Assembler Lösung von weiter oben hängt uns immer noch kräftig
ab. Das wurmt :-)

von holger (Gast)


Lesenswert?

Man ignoriert mich hier scheinbar :(

  Nibble1 = *data - '0';
  if( Nibble1 > 9 )
    Nibble1 -= 'A' - '0';


Angenommen data = 'A'
Nibble1 = 'A'- '0' = 0x11

  if( Nibble1 > 9 ) // gegeben weil Nibble1 bei 'A' => 0x11
    Nibble1 -= 'A' - '0';

Nibble1 = 0 !

Gute Nacht.

von Simon K. (simon) Benutzerseite


Lesenswert?

holger wrote:
> Man ignoriert mich hier scheinbar :(

Du hast nur keinen Beweis für die Länge des Assemblercodes geliefert. 
Ich denke darum ging's ja hier

von Karl H. (kbuchegg)


Lesenswert?

holger wrote:
> Man ignoriert mich hier scheinbar :(
>
>   Nibble1 = *data - '0';
>   if( Nibble1 > 9 )
>     Nibble1 -= 'A' - '0';
>
>
> Angenommen data = 'A'
> Nibble1 = 'A'- '0' = 0x11
>
>   if( Nibble1 > 9 ) // gegeben weil Nibble1 bei 'A' => 0x11
>     Nibble1 -= 'A' - '0';
>
> Nibble1 = 0 !
>

Oops. Du hast recht.
Muss natürlich
     Nibble1 -= 'A' - '0' - 10;
heissen. (Ditto für Nibble2)

Ändert aber nichts am Code. Kommt nur eine andere Konstante beim
subi raus.

von Karl H. (kbuchegg)


Lesenswert?

Die Assembler Lösung in C umgesetzt
1
uint8_t  ascii2hex ( uint8_t Byte1, uint8_t Byte2 )
2
{
3
  if( Byte1 & 0b00100000 )
4
  8e:  85 fd         sbrc  r24, 5
5
    Byte1 -= 'A' - '0' - 10;
6
  90:  8b 51         subi  r24, 0x07  ; 27
7
  Byte1 -= '0';
8
  Byte1 <<= 4;
9
  92:  82 95         swap  r24
10
  94:  80 7f         andi  r24, 0xF0  ; 240
11
12
  if( Byte2 & 0b00100000 )
13
  96:  65 fd         sbrc  r22, 5
14
    Byte2 -= 'A' - '0' - 10;
15
  98:  6b 51         subi  r22, 0x07  ; 27
16
  Byte2 -= '0';
17
  9a:  60 53         subi  r22, 0x30  ; 48
18
19
  Byte1 |= Byte2;  
20
  9c:  86 2b         or  r24, r22
21
  return Byte1;
22
}
23
  9e:  99 27         eor  r25, r25
24
  a0:  08 95         ret

gibt die beste Lösung. Nicht ganz so kurz wie das Original,
aber dafür werden die gcc Konventionen eingehalten :-)

Interessant finde ich, dass der Compiler die Subtraktion von '0'
vom Byte 1 rausoptimiert, da der andi etwas weiter unten das
überflüssig macht.

Nochmal in 'schöner' C-Form
1
uint8_t  ascii2hex ( uint8_t Byte1, uint8_t Byte2 )
2
{
3
  if( Byte1 & 0b00100000 )
4
    Byte1 -= 'A' - '0' - 10;
5
  Byte1 -= '0';
6
  Byte1 <<= 4;
7
8
  if( Byte2 & 0b00100000 )
9
    Byte2 -= 'A' - '0' - 10;
10
  Byte2 -= '0';
11
12
  Byte1 |= Byte2;  
13
  return Byte1;
14
}

von holger (Gast)


Lesenswert?

>Oops. Du hast recht.

Danke !

Und wenn man jetzt bedenkt das er für die Parameterübergabe
in zwei Bytes in einer übergeordneten Funktion schieben muss
könnte das Ergebnis auch noch anders aussehen.

von Karl H. (kbuchegg)


Lesenswert?

holger wrote:
>
> Und wenn man jetzt bedenkt das er für die Parameterübergabe
> in zwei Bytes in einer übergeordneten Funktion schieben muss
> könnte das Ergebnis auch noch anders aussehen.

Ja klar.
Kommt allerdings darauf an, wo die Zeichen herkommen.

von Peter D. (peda)


Lesenswert?

Ich sehe da im meiner Glaskugel:

Er liest ein Hexfile von ner Speicherkarte, wandelt in Bytes und brennt 
damit den Flash.

Das Brennen dürfte mit Abstand die meiste Zeit verbraten. Es ist also 
völlig unsinnig um ein paar popelige Zyklen beim Byte wandeln zu 
feilschen.


Peter

von holger (Gast)


Lesenswert?

Da war doch noch was :

uint8_t  ascii2hex ( uint8_t Byte1, uint8_t Byte2 )
{
  if( Byte1 & 0b01000000 ) // & 0x40, nicht 0x20
    Byte1 -= 'A' - '0' - 10;
  Byte1 -= '0';
  Byte1 <<= 4;

  if( Byte2 & 0b01000000 ) // & 0x40, nicht 0x20
    Byte2 -= 'A' - '0' - 10;
  Byte2 -= '0';

  Byte1 |= Byte2;
  return Byte1;
}

Jetzt kann ich in Ruhe schlafen ;)

von Regatti (Gast)


Lesenswert?

@Peter

Deine Glaskugel hat recht.
Aber mit deiner Einschätzung liegst du falsch. Das flashen geht im 
moment wesentlich schneller als das Wandeln (Faktor 4).

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

@Karl Heinz, hast Du hier nicht was vergessen?
1
uint8_t  ascii2hex ( uint8_t* data )
2
{
3
  static uint8_t Hex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
4
                           0, 0, 0, 0, 0, 0, 0, 
5
                           10, 11, 12, 13, 14, 15   };
6
  uint8_t Result;
7
8
  Result = Hex[ *data - '0' ];
9
  Result |= Hex[*(data+1) - '0' ];
10
11
  return Result;
12
}

Wird dieser Funktion "22" übergeben, ist das Ergebnis 2. Warum wohl?

von Matthias L. (Gast)


Lesenswert?

1
Result =  ( Hex[ *data     - '0' ] << 4 );
2
Result |=   Hex[ *(data+1) - '0' ]       ;

von Karl H. (kbuchegg)


Lesenswert?

Es war schon spät, und ich brauchte das G....

Ich sollte mir wieder angewöhnen, Programme zumindest einmal
durchlaufen zu lassen, egal wie einfach sie sind :-)

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Die von Matthias vorgeschlagene Schiebeoperation kann man sich auf 
Kosten einer zweiten Tabelle übrigens sparen:
1
uint8_t ascii2hex(uint8_t* data)
2
{
3
  static uint8_t Hex0x[] = 
4
  { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
5
    0, 0, 0, 0, 0, 0, 0, 
6
    0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F 
7
  };
8
  static uint8_t Hexx0[] = 
9
  { 0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90,
10
    0, 0, 0, 0, 0, 0, 0, 
11
    0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0 
12
  };
13
14
  uint8_t Result;
15
16
  Result = Hexx0[*data - '0'];
17
  Result |= Hex0x[*(data+1) - '0'];
18
19
  return Result;
20
}

... manche Prozessoren sind beim Schieben langsam. Wenn aber ein 
Swap-Befehl existiert (der beide Nibbles vertauscht), dann ist der 
Aufwand natürlich geringer, sofern der Optimizer des Compilers den auch 
nutzt.


Interessant ist allerdings, wozu der OP die hier geforderte 
Funktionalität überhaupt braucht.

von Karl H. (kbuchegg)


Lesenswert?

Rufus t. Firefly wrote:
>
> Interessant ist allerdings, wozu der OP die hier geforderte
> Funktionalität überhaupt braucht.

Hast du das mal im Listfile begutachtet? Würde mich interessieren
wieviele Takte da drauf gehen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das muss leider jemand anderes machen, ich nutze derzeit keine AVRs.

von Falk B. (falk)


Lesenswert?

@ Regatti (Gast)

>Aber mit deiner Einschätzung liegst du falsch. Das flashen geht im
>moment wesentlich schneller als das Wandeln (Faktor 4).

Sicher? Richtig gemessen? Bei einer Page Size von 512 Byte und einer 
Brenndauer von 10ms sind das ~20us pro Byte. Macht bei 16 MHz 320 Takte. 
huh, wenn DAS nicht reicht?

MFg
Falk

von Tcf K. (tcfkat)


Lesenswert?

Eigentlich gelesen, was ich zu Anfang schrieb?
Große, dünn besiedelte Tabelle verwenden, die das ganze verwendete 
ASCII-Alphabet abdeckt - Platzbedarf dürfte hier interessieren. Tabelle 
für Low- und High-Nibble anlegen. Von 0x0 bis klein 'f' sind das 103 
Werte, mal zwei = 206 Bytes, keine 5kB! Weil in der Tabelle Groß- und 
Kleinbuchstaben drin sind, braucht es keine Fallunterscheidung. Nicht 
benutzte Tabelleneinträge setzt man zu 0.
Dann DIREKT indizieren, ohne jede Arithmetik... das schlägt alles an 
Geschwindigkeit.
1
tab1 [103] = {Initialisierung für Lownibble};
2
tab2 [103] = {Initialisierung für Highnibble); /* Wert wie für tab1 aber * 16 */
3
4
val = tab1[low_nibble] + tab2[high_nibble];

Geht es noch schneller?

von Karl H. (kbuchegg)


Lesenswert?

Tcf Kat wrote:
> Dann DIREKT indizieren, ohne jede Arithmetik... das schlägt alles an
> Geschwindigkeit.
>
1
> tab1 [103] = {Initialisierung für Lownibble};
2
> tab2 [103] = {Initialisierung für Highnibble); /* Wert wie für tab1 aber
3
> * 16 */
4
> 
5
> val = tab1[low_nibble] + tab2[high_nibble];
6
>
>
> Geht es noch schneller?

Hast du das ausprobiert, durch den Compiler gejagt und das List-
File studiert? Du wirst dich wundern.

Array-Indizierung kriegst du nicht gratis.
Auf einem AVR heist das:
   Z Pointer mit der Startadresse laden    2 Befehle
   Index dazuaddieren                      2 Befehle
   Byte holen                              1 Befehl

macht für einen Array Zugriff 5 Befehle. Das ganze dann ein
2-tes mal für den 2. Zugriff, macht 10 Befehle. Noch ein bischen
drumherum für die Addition, Registerkonventionen für den
Funktionsaufruf und du bist ganz schnell bei 13 oder 14.
Die beste Lösung die es bisher gibt, liegt aber bei 10 und
die beruht nicht auf Array Indizierung.
Dazu dann noch der Speicherplatz für die Tabelle. Auch wenn
das nur 206 Bytes sind, so ist das zb. für einen Mega8 schon
reichlich viel.

von Simon K. (simon) Benutzerseite


Lesenswert?

Tcf Kat wrote:
> Von 0x0 bis klein 'f' sind das 103
> Werte, mal zwei = 206 Bytes, keine 5kB!

Richtig, aber diese Methode ist verschieden von meiner oben genannten. 
Da sind es 5kB. Dafür fällt aber eine Addition flach (Ok, es kommen ein 
paar Befehle für die 2D-Array Indizierung hinzu)

von Simon K. (simon) Benutzerseite


Lesenswert?

> Die beste Lösung die es bisher gibt, liegt aber bei 10 und
> die beruht nicht auf Array Indizierung.

Immernoch erstaunlich, wo doch meistens Tabellen benutzt werden um
Berechnungen im Voraus zu machen und nicht während der Laufzeit.
Hier aber eben wirkt es sich gegenteilig aus.

von Karl H. (kbuchegg)


Lesenswert?

> Immernoch erstaunlich, wo doch meistens Tabellen benutzt werden um
> Berechnungen im Voraus zu machen und nicht während der Laufzeit.
> Hier aber eben wirkt es sich gegenteilig aus.

Yep.
Liegt daran, dass ein Nibble mit 3 Befehlen abgehandelt
werden kann. Da kommt eine Array-Indizierung einfach nicht
mit.

Wobei ich der Fairness halber auch sagen muss, dass ich
zunächst auch auf die Array-Lösung geschworen hätte, bis
mir dann der Assembler Code weiter oben das Stichwort geliefert
hat.

von Tcf K. (tcfkat)


Lesenswert?

@kbuchegg; Nein, ich gebe zu, ich habe das nicht ausprobiert, und ich 
kenne die Adressierungsmodi der AVR nicht. Hätte aber gedacht, dass die 
Indizierung effizienter ist... Regatti hat aber immer noch nicht gesagt, 
um welchen µC es sich handelt. Anstatt also von AVR auszugehen, sollte 
man sich die Möglichkeiten der verwendeten Kiste angucken.
Den Funktionsaufruf würde ich mir sparen, sondern das direkt in den Code 
schreiben.

@simon: Die Idee ist ja richtig, durch das zweidimensionale Array wird 
das aber aufgebläht; und die Indexierung wird aufwändiger. Ist hier 
nicht nötig, da die beiden Nibble getrennt betrachtet werden können.

von Karl H. (kbuchegg)


Lesenswert?

Tcf Kat wrote:

> Den Funktionsaufruf würde ich mir sparen, sondern das direkt in den Code
> schreiben.

Genau das würde ich wieder nicht tun :-)
Compiler beherrschen schon seit langer Zeit das Funktions-inlining.

von Tcf K. (tcfkat)


Lesenswert?

Ok, ich bin halt "old school", ich schreibe den Code so, dass er auch 
mit -Od gut ist... der Rest ist dann Kür... ;)

von FPGA-Fan (Gast)


Lesenswert?

Am schnellsten geht es sowieso mit einem Dual-Port-RAM und einem FPGA.
Ach ja, der gemeine AVR hat ja kein RAM-Interface.

Schade.

von Falk B. (falk)


Lesenswert?

@ FPGA-Fan (Gast)

>Am schnellsten geht es sowieso mit einem Dual-Port-RAM und einem FPGA.

Warum nicht gleich einen Quantencomputr nehmen?
;-)

MfG
Falk

von FPGA-Fan (Gast)


Lesenswert?

Wozu Quantencomputer?

Ein N-Port-RAM und N FPGAs tun es parallel und fast genausoschnell.
Und dabei ist das ganze wenigstens noch Goedelisierbar.

von W. B. (wb1)


Lesenswert?

Hier nochmal was in Assembler.
Der Tabellenzeiger wird aus dem umzurechnenden Wert und dem Highteil der 
Tabelle gebildet.
Die eigentliche Umrechnung sind noch vier Befehle.
Es geht auch mit drei Befehlen, wenn man je eine Tabelle für den high 
und lowteil benutzt. (zb ldi yh, high(highnibbletabelle), ldi xh, 
high(lownibbletabelle, wobei highnibbletabelle auf zb 0x330 liegt). Der 
swap-Befehl kann dann entfallen.
Schneller kann es dann nur noch gehen, wenn man Prozessoren benutzt, die 
einen gezeigerten Wert direckt zu einem Registerwert addieren können, 
ich glaube der Z80 konnte sowas.

tabelle1:
  ldi xh,high(tabelle)
  mov yh,xh
tabelle1a:
  ldi xl,'4'
  ldi yl,'F'
  call beginn
  rjmp tabelle1a
beginn:
  ld r16,x
  ld r17,y
  swap r16
  add r16,r17
  ret

.dseg
.org 0x230
tabelle:
.db 0,1,2,3,4,5,6,7,8,9
.org 0x241
.db 10,11,12,13,14,15
.org 0x261
.db 10,11,12,14,14,15

MfG
wb1

von Tcf K. (tcfkat)


Lesenswert?

@wb1: Wenn ich das richtig sehe, ist das nach meinem Vorschlag von 
16:00?
Fehlt da aber nicht ein *16 für das highnibble?

von Karl H. (kbuchegg)


Lesenswert?

Tcf Kat wrote:
> @wb1: Wenn ich das richtig sehe, ist das nach meinem Vorschlag von
> 16:00?
> Fehlt da aber nicht ein *16 für das highnibble?

Nein. Das erledigt der swap

Allerdings hat der Code natürlich ein paar Annahmen, die man
in C so nicht treffen kann :-)
* die Indexregister x und y werden vorgeladen und verändern
  ihren Wert während der Wandlung mehrere Werte nicht
* die Tabelle ist sorgfältig so in den Speicher positioniert
  worden, dass kein Offset berechnet werden muss

In Assembler kann man das natürlich machen, aber in C ist
das so afaik nicht möglich :-)

von W. B. (wb1)


Lesenswert?

@tcf Ja
Die multiplikation mit 16 ist in dem Fall das gleiche wie swap
Im Highnibble des umzurechnenden ascii-codes (hier 4F) steht ja hex34
im lownibble steht hex46.
Aus der Tabelle auf Platz hightabelle (hex02) lowtabelle (hex34), also 
hex0234 wird 4 gelesen.
Also im Register r16 0b00000100, swap des Registers = 0b01000000.
Dann braucht man nur den lownibbleteil den man aus der tabelle liest zu 
addieren.
Wenn du eine zweite Tabelle aufmachst, z.B ab 0x300 und legst das 
Ergebnis dort bereits mit 16 multipliziert ab, brauchst du kein swap 
mehr und der Programmschnipsel verkürzt sich um einen weiteren Befehl.
Also an der Tabellenstelle 0x334 würde dann der Wert 0b01000000 
hinterlegt sein.

MfG
wb1

von Tcf K. (tcfkat)


Lesenswert?

Karl heinz Buchegger wrote:
> Nein. Das erledigt der swap

patsch Ja, wie dumm von mir... :(

> Allerdings hat der Code natürlich ein paar Annahmen, die man
> in C so nicht treffen kann :-)
> * die Indexregister x und y werden vorgeladen und verändern
>   ihren Wert während der Wandlung mehrere Werte nicht
> * die Tabelle ist sorgfältig so in den Speicher positioniert
>   worden, dass kein Offset berechnet werden muss
>
> In Assembler kann man das natürlich machen, aber in C ist
> das so afaik nicht möglich :-)

Egal, das erscheint mir die kürzeste Lösung... wie ich sagte, dünn 
besetzte Tabelle, und damit keine Fallunterscheidung und/oder 
Indizierungsarithmetik... ;)))

Richtigen schnellen Code muss man also immer noch von Hand 
programmieren, allen tollen Compiler-Optimierungen zum Trotze... grins

Edit @ wb1: Ok, alles klar!

von W. B. (wb1)


Lesenswert?

Ich weiß nicht was du mit offset meinst.
Wenn du damit meinst, das die tabelle bei C in jedem Bereich liegen kann 
und bei assembler nicht, ist das nicht richtig.
Es wird nur eine Startadresse benötigt. Das ich das hier auf eine 
geraden Wert positioniert habe, ist meiner Faulheit zu verdanken und 
natürlich dem bestreben nach kompaktem code.
Angenommen ich lege die Startadresse der Tabelle auf hex123
Dann ändert sich der code folgendermaßen:

  ldi yh,high(0x123)
  mov xh,yh
neuwert_holen:
  ldi xl,'4'
  ldi yl, 'F'
  addiw x,low(0x123)
  addiw y,low(0x123)
  call beginn
  rjmp neuwert_holen

An der eigentlichen Rechenroutine ändert sich nichts.

MfG
wb1

von Ralph (Gast)


Lesenswert?

Warum dieser Aufwand ??????

Schreib die Datei doch direkt im Binärformat auf die SD Karte.

Dein Pc macht die Wandlung gerne.

von Karl H. (kbuchegg)


Lesenswert?

W. Bl wrote:

>   ldi yh,high(0x123)
>   mov xh,yh
> neuwert_holen:
>   ldi xl,'4'
>   ldi yl, 'F'
>   addiw x,low(0x123)
>   addiw y,low(0x123)
>   call beginn
>   rjmp neuwert_holen
>
> An der eigentlichen Rechenroutine ändert sich nichts.

ok. du benutzt einen addiw

Aber: jetzt ändere das ganze mal so ab, dass du auch 2 Umwandlungen
hintereinander machen kannst. 2 register enthalten die Character

die Aufrufe sollen also so aussehen

    ldi  r16, '4'
    ldi  r17, 'F'
    call ToHex

    ....

    ldi  r16, '3'
    ldi  r17, '2'
    call ToHex

    ....


Wie muss jetzt ToHex aussehen.
Denk aber daran, zwischen den Aufrufen könnte sich X und Y
verändern :-)
Wenn ich das jetzt richtig überschlagen habe, dann kommst du
mit 7 Befehlen durch. Nicht schlecht, aber der adiw benötigt
ja 2 Taktzyklen, damit sind wir bei 9 : 10

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger wrote:

> Wenn ich das jetzt richtig überschlagen habe, dann kommst du
> mit 7 Befehlen durch. Nicht schlecht, aber der adiw benötigt
> ja 2 Taktzyklen, damit sind wir bei 9 : 10

Ich glaube ich hab mich verzählt. Das müssten 8 Befehle
sein. Mit den 2 zusätzlichen Takten für die beiden adiw
steht es wieder 10:10, wobei du noch zusätzlich Speicher
für eine Tabelle verbrauchst.

Wenn man davon ausgehen kann, dass die Register X und Y
unangetastet bleiben, dann gewinnst du 8:10. Nur ist
diese Annahme in einem C-Programm eine schlechte Annahme :-)

von Karl H. (kbuchegg)


Lesenswert?

W. Bl wrote:

> Angenommen ich lege die Startadresse der Tabelle auf hex123
> Dann ändert sich der code folgendermaßen:
>
>   ldi yh,high(0x123)
>   mov xh,yh
> neuwert_holen:
>   ldi xl,'4'
>   ldi yl, 'F'
>   addiw x,low(0x123)
>   addiw y,low(0x123)
>   call beginn
>   rjmp neuwert_holen
>
> An der eigentlichen Rechenroutine ändert sich nichts.

Da fällt mir gerade auf:
Da sollte sich aber was ändern. Leg die Startadresse der Tabelle
mal auf 0x01FF

(du kriegst dann einen Überlauf inach xh, was sich beim
nächsten Durchlauf als fatal erweisen wird)

von W. B. (wb1)


Lesenswert?

Irgendwo müssen die Daten ja herkommen, eventuell als stream von einer 
UART, oder sie stehen im speicher.
Wie auch immer, wir betrachteten bisher nicht die Datenquelle.
Ich mach es mir jetzt einfach:
Die register xl und yl kommen mit dem geladenen, umzuwandelden Wert 
angeschwirrt und der Rückgabewert ist in r16.
yh und xh sind vorgeladen
Das ganze wird als subroutine ausgeführt.
Mit dem Überlauf des highteils hast du recht, ich muß im ungünstigem 
fall noch ein register verwenden oder den stack bemühen. Hab ich mal in 
Klammern beim ersten aufruf eingefügt.
Eventuell bekommst du es in C auch hin, wenn du vereinfachende 
Festlegungen triffst, oder geht das nicht?
Noch was, ich kann C nur ansatzweise, es war mir immer suspekt weil ich 
nicht genau wußte was da passiert. Ich find Assembler einfach besser, 
bilde mir ein, dadurch die volle Kontrolle zu haben. Allerdings muß ich 
zugeben bekommt man dabei leicht Kopfschmerzen und graue Haare. Aber was 
solls

    ldi xh,high(0x123)
    mov yh, yh



   ldi  xl, '4'
    ldi  yl, 'F'

    (push xh)

    call ToHex

    (pop yh
     mov xh,yh)

    ....

    ldi  xl, '3'
    ldi  yl, '2'

    (hier mit register
     ld r18,xh)

    call ToHex

    (ld xh,r18
     ld yh,r18)


    ....


ToHex:
  addiw x,low(0x123)
  addiw y,low(0x123)
  ld r16,x
  ld r17,y
  swap r16
  add r16,r17
  ret

von Karl H. (kbuchegg)


Lesenswert?

W. Bl wrote:
> Mit dem Überlauf des highteils hast du recht, ich muß im ungünstigem
> fall noch ein register verwenden oder den stack bemühen. Hab ich mal in
> Klammern beim ersten aufruf eingefügt.

Oder du lässt die Annahme der vorgeladenen xh, yh auf. Ist in Summe
schneller als push/pop

Aber:
> ToHex:
>     ldi xh,high(0x123)
>     mov yh, yh
>   addiw x,low(0x123)
>   addiw y,low(0x123)
>   ld r16,x
>   ld r17,y
>   swap r16
>   add r16,r17
>   ret

aber damit bist du bereits auf 11 Takten und der Vorteil ist
dahin :-)

> Eventuell bekommst du es in C auch hin, wenn du vereinfachende
> Festlegungen triffst, oder geht das nicht?

Nein, das geht nicht mehr.
In einer Hochsprache muss man gewisse Dinge aufgeben. Einer
davon ist: Der Compiler hat die Kontrolle über die Register
und entscheidet welches Register er wofür nimmt. Dies unter
anderem auch deshalb, weil es vernünftig ist ein Register-
benutzungsschema zu verwenden. Der Nachteil dabei ist, dass
dieses Schema für alle Programme immer gleich ist. Hier
hast du in Assembler den Vorteil, dass du das Registerschema
individuell an jedes Programm anpassen kannst. Du kannst daher
ein Register für nur einen Zweck reservieren, in C geht das
dann so nicht mehr.

> Noch was, ich kann C nur ansatzweise, es war mir immer suspekt weil ich
> nicht genau wußte was da passiert. Ich find Assembler einfach besser,
> bilde mir ein, dadurch die volle Kontrolle zu haben. Allerdings muß ich
> zugeben bekommt man dabei leicht Kopfschmerzen und graue Haare. Aber was
> solls

:-)
Etwas Kontrolle muss man in jeder Hochsprache abtreten. Dafür
befreit einen der Compiler allerdings aber auch von vielen
Detail- und Routineaufgaben.
Das Augenmerk bei der Programmierung in einer Hochsprache liegt
daher auch weniger in den Details wie Registerbelegung oder wo
genau im Speicher eine Variable liegt. In einer Hochsprache
musst du dich um solche Dinge nicht mehr kümmern und kannst dich
mehr auf den Problemaspekt und Algorithmen konzentrieren.
Bei einem µC, wie einem Mega8 oder Mega16 mag das noch nicht so
sehr die Rolle spielen, aber bei Programmen die in die 100-erte
Kilobytes oder Megabytes gehen spielt das dann die überragende
Rolle. Entwicklungszeit ist teuer!

Ein guter Assemblerprogrammierer kann einen Compiler schlagen,
allerdings meist nicht um sehr viel.

von W. B. (wb1)


Lesenswert?

Bevor gezetert wird
nicht mov yh,yh sondern mov yh,xh

von W. B. (wb1)


Lesenswert?

In einer Hochsprache muss man gewisse Dinge aufgeben. Einer
davon ist: Der Compiler hat die Kontrolle über die Register
und entscheidet welches Register er wofür nimmt. Dies unter
anderem auch deshalb, weil es vernünftig ist ein Register-
benutzungsschema zu verwenden. Der Nachteil dabei ist, dass
dieses Schema für alle Programme immer gleich ist.

Das ist dann also der Grund für das ständige gepoppe bei C.
Ich dachte schon, ein sexist der gerne poppt hätte C erfunden.

Ich hab kein Schema, denk mir aus wie man es am besten machen könnte und 
los gehts.
Am liebsten sind mir interruptgesteuerte Programme, weil, ich hab mal 
früher Maschinensteuerungen gebastelt. Da kam es darauf an, das die 
Maschine sofort reagiert wenn eine Position erreicht oder ein Notaus 
betätigt wurde.
Besonders gefallen hat mir dabei, das der Interruptvektor nicht fest 
vorgegeben war, sondern im highteil in der cpu der lowteil in dem 
interruptfähigem peripheren Baustein hinterlegt wurde.Es mußte also 
nicht von einer vorgegebenen Interrupttabelle erst irgendwo 
hingesprungen werden, man konnte sofort an der Interruptvektoradresse 
mit der Bearbeitung beginnen
Damals war das Zeug auch etwas langsamer, der U880 (Z80) lief in meiner 
Jugendzeit mit 2,5 MHz maximalem Takt.

von holger (Gast)


Lesenswert?

@ Ralph

>Warum dieser Aufwand ??????
>Schreib die Datei doch direkt im Binärformat auf die SD Karte.
>Dein Pc macht die Wandlung gerne.

Mensch Ralph, schreib doch nicht so was.
Das könnte das Tempo ja glatt verdoppeln.
Solch einfache Lösungen sind hier nicht gefragt.
Gleich gibts Mecker vom Simon ;)

Duck, und wech

von Peter D. (peda)


Lesenswert?

holger wrote:

> Mensch Ralph, schreib doch nicht so was.
> Das könnte das Tempo ja glatt verdoppeln.

HEX ist sogar fast 3-mal größer.

Aber die 10ms Schreibzeit pro Page (256 Byte) dürften den Löwenanteil 
ausmachen, d.h. alle Optimierungen hier wirken sich bestenfalls mit 
wenigen Promille aus.


Peter

von holger (Gast)


Lesenswert?

>Aber die 10ms Schreibzeit pro Page (256 Byte) dürften den Löwenanteil
>ausmachen, d.h. alle Optimierungen hier wirken sich bestenfalls mit
>wenigen Promille aus.

Genau. Wahrscheinlich dauert die ASCII zu BIN Konvertierung
nur deshalb so lange weil die Daten extrem langsam
von der SD-Karte gelesen werden. Eine andere Erklärung
gibt es sonst eigentlich nicht.

von Ralph (Gast)


Lesenswert?

ach so;

Lieber kompliziert als einfach.

Sagt das doch direkt !!!

:-)


@ holger

Ich stimme dir mit der SD Karte zu.
Der Flaschenhals ist eher die Übertragung von der SD Karte als die 
Wandlung von ASCII in HEX.

von Peter D. (peda)


Lesenswert?

Ralph wrote:

> Ich stimme dir mit der SD Karte zu.
> Der Flaschenhals ist eher die Übertragung von der SD Karte als die
> Wandlung von ASCII in HEX.


Dann würde in der Tat, die binäre Speicherung die Lesegeschwindigkeit 
fast verdreifachen.

Aus dem gleichen Grund überträgt mein UART-Bootloader die Daten auch 
binär.


Peter

von Falk B. (falk)


Lesenswert?

@ W. Bl (wb1)

>Am liebsten sind mir interruptgesteuerte Programme, weil, ich hab mal
>früher Maschinensteuerungen gebastelt. Da kam es darauf an, das die
>Maschine sofort reagiert wenn eine Position erreicht oder ein Notaus
>betätigt wurde.

Notaus und anderes sicherheitskritische Dinge macht man prinzipiell 
nicht in Software sondern Hardwar.

>hingesprungen werden, man konnte sofort an der Interruptvektoradresse
>mit der Bearbeitung beginnen

Die zwei Takte mehr oder weniger sind selten wirklich entscheidend.

MFg
Falk

von Regatti (Gast)


Lesenswert?

Die Schreibzeit für 256Bytes beträgt "nur" 2ms. Die Pages sind übrigens 
64k groß.

An dem Format auf der SD-Karte kann ich nichts ändern, das ist so 
vorgegeben.

von Falk B. (falk)


Lesenswert?

@ Regatti (Gast)

>Die Schreibzeit für 256Bytes beträgt "nur" 2ms.

Macht immerhin noch 7,8 us/Byte, was ~125 Takten @16 MHz entspricht. 
Keinerlei Bedarf für eine Überschall Umwandlungsroutine.

> Die Pages sind übrigens 64k groß.

Und die kann man auf einmal schreiben? In 2ms?

MFG
Falk

von holger (Gast)


Lesenswert?

>An dem Format auf der SD-Karte kann ich nichts ändern, das ist so
>vorgegeben.

Vieleicht kannst du an der SPI Geschwindigkeit ja noch ein
bißchen drehen.

von wb1 (Gast)


Lesenswert?

@falk
Ich wollte es mit dem Notaus nur verdeutlichen.
Damals waren es Scheibenläufermotoren mit incrementellen Gebern.
Die Geber brachten 1024 Impulse/Umdrehung, die Motoren hatten eine 
Enddrehzahl von 3000U/min, das sind also rund 52000 Impulse/sec.
Die wurden von TTL-Gattern aufgefangen, die dann wiederum über die 
peripheren Bausteine Interrupts auslösten. Wenn das nicht schnell genug 
ging, zeigte der Roboterarm an die anderen des Werkstückes (ein bisschen 
übertrieben).

von Simon K. (simon) Benutzerseite


Lesenswert?

holger wrote:
> @ Ralph
>
>>Warum dieser Aufwand ??????
>>Schreib die Datei doch direkt im Binärformat auf die SD Karte.
>>Dein Pc macht die Wandlung gerne.
>
> Mensch Ralph, schreib doch nicht so was.
> Das könnte das Tempo ja glatt verdoppeln.
> Solch einfache Lösungen sind hier nicht gefragt.
> Gleich gibts Mecker vom Simon ;)
>

OT: Von mir?

von JanB (Gast)


Lesenswert?

Hallo,
hier noch ein Vorschlag der die Anfangs beschriebene
Assemblerroutine nochmals um 12,5% beschleunigt.
Die ASCII-nach-BIN Wandlung braucht dann nur noch 7 ASM-Befehle.
1
; zu wandelnder Wert '4F'
2
  ldi r16,'4'  ;r16 enthält 0x34
3
  ldi r17,'F'  ;r17 enthält 0x46
4
5
ascii2bin:
6
  sbrs r16,5
7
  subi r16,7
8
  sbrs r17,5
9
  subi r17,7
10
  swap r16
11
  add r16,r17
12
  subi r16,0x33 
13
14
; Ergebnis steht in r16
OK, ist trivial, aber der Thread heisst ja: extrem schnell wandeln...

Gruß Jan

von Simon K. (simon) Benutzerseite


Lesenswert?

Ich blick da so auf die schnelle nicht durch, aber die Routine 
funktioniert wirklich. Eventuell ein paar Erläuterungen? :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.