ATOI nur bis 4 Stellen

OP #4384245
Lesenswert?

Hallo,

ich versuche jetzt schon seit 3 Stunden ein Problem mit ATOI zu lösen:

Ich schicke per Uart Zahlen vom PC im ASCII Format an einen ATMega328P.
Die Zahlen sind im Bereich von 0-65535 und NULL-terminiert.
Empfangen wird über Interrupt, gewandelt mit ATOI und über UART wieder 
zum PC zurückgeschickt.

Mein Problem: Sende ich Zahlen von 0-9999 läuft alles problemlos. Sobald 
ich allerdings die erste fünfstellige Zahl sende, wird diese noch wieder 
zurückgeschickt. Die nächste Zahl, ganz gleich welche wird dann 
allerdings immer zu 0. Wenn ich daraufhin weitere Zahlen sende verbleibt 
der Wert bei 0.

Beispiel:

PC TX -> PC RX
100 -> 100
1000 -> 1000
10000 -> 10000
12000 -> 0
1000 -> 0
150 -> 0
etc.

Es scheint, als ob ATOI nicht mit mehr als 5 Stellen umgehen kann und 
dann den Dienst verweigert...

<c>
char uart_buffer[8];
uint8_t NewValue=0, z=0;

ISR (USART_RX_vect)
{
  uart_buffer[z]=UDR0;
  z++;
  if (uart_buffer[z-1]==0x00) {z=0; NewValue=1;}
}

int main(void)
{
  Init_SPI_Master();
  Init_Uart();

  while(1)
  {
    if (NewValue==1)
    {
      NewValue=0;
      uint16_t PCValue = atoi(uart_buffer);
      //uint16_t PCValue = strtoul(uart_buffer,NULL,10);
      if (PCValue<65536)
      {
        SPI_Write_Reg(PCValue);
        Uart_puts("DAC MAX541 set to: ");
        Uart_Send_16Bit(PCValue);
        Uart_CR_LF();
      }
      else
      {
        Uart_puts("Value out of range!");
        Uart_CR_LF();
      }
    }
  }
}
</c>

Ich habe das auch schon mit "strtoul" versucht - gleiches Ergebnis.
Hat jemand eine Idee?

PS: Der Übersichtlichkeit halber habe ich die UART-Routinen und die 
Inits weggelassen.

Danke und Gruß Stefan
OP #4384267
Lesenswert?

@ Walter, prinzipiell gute Einwände, jedoch funktioniert es ja für die 
Werte von 0-9999 zuverlässig und reproduzierbar.

@Lothar, das ist lediglich ein Plausibilitätscheck, damit Werte >65536 
vom PC kommend ignoriert werden. Es sollen ja nur die Werte kleiner 
65536 verarbeitet werden.

Ich sende das "NULL" vom HTerm und wie gesagt bei Werten von 0-9999 tut 
es zuverlässig.
#4384281
Lesenswert?

Stefan D. schrieb:

> @Lothar, das ist lediglich ein Plausibilitätscheck, damit Werte >65536
> vom PC kommend ignoriert werden. Es sollen ja nur die Werte kleiner
> 65536 verarbeitet werden.

Da bist du aber schon viel zu spät drann.
Aus einem atoi kann per Definition nichts größeres als 65535 rauskommen. 
Eigentlich kann nichts größeres als 32737 rauskommen, denn das i in atoi 
steht für int. Und der hat auf deinem AVR nun mal einen Wertebereich von 
-32768 bis +32767.
Wenn du auf einen unsigned aus bist, dann wäre die richtige Funktion 
atou (u wie unsigned)
OP #4384283
Lesenswert?

@ Lothar, Diek, Stimmt das ist noch ein Fragment als ich noch maximal 
einen 12 Bit Wert übertragen hab. Für 16 Bit Werte macht das natürlich 
keinen Sinn - ändert aber erstmal nichts am Problem.

Hab das Array jetzt initialisiert - leider keine Veränderung.

<c>
char uart_buffer[8]={0};
</c>
OP #4384295
Lesenswert?

Karl H. schrieb:
> Dann würde ich vorschlagen, du fügst hier mal ein
>
1
>   if (NewValue==1)
2
>   {
3
>     NewValue=0;
4
> 
5
>     Uart_puts( uart_buffer );   // <-----
6
> 
7
>     uint16_t PCValue = atoi(uart_buffer);
8
> 
9
>     ....
10
>
> ein und siehst dir an, welchen String der AVR tatsächlich empfangen hat.

Hab ich getan:
Empfangene Daten aus HTerm:

100DAC MAX541 set to: 100;<\r><\n>
100DAC MAX541 set to: 100;<\r><\n>
10000DAC MAX541 set to: 10000;<\r><\n>
DAC MAX541 set to: 0;<\r><\n>

Gesendet hatte ich 100, 100, 10000, 12000

Wie man sieht fehlt die 12000 in der Rückmeldung :-(

Den Punkt mit dem Wertebereich sehe ich schon, allerdings sollte es ja 
bis 32767 funktionieren.
#4384313
Lesenswert?

Karl H. schrieb:
> Stefan D. schrieb:
>
>> Wie man sieht fehlt die 12000 in der Rückmeldung :-(
>
> Also lautet die eigentliche Frage nicht, was mit dem atoi los ist,
> sondern warum da kein gültiger String vorhanden ist.

Womit wir bei der nächsten Modifikation wären.

Die ISR, die Zeichen empfängt, soll doch bitte schön mal ein bischen 
auskunftsfreudiger sein, was denn da so alles eintrudelt.
1
ISR (USART_RX_vect)
2
{
3
  uart_buffer[z] = UDR0;
4

5
  {
6
    char tmp[20];
7
    sprintf( tmp, "'%c' - %02x\r\n", uart_buffer[z], uart_buffer[z] );
8
    Uart_puts( tmp );
9
  }
10
  
11
  z++;
12
  if (uart_buffer[z-1]==0x00)
13
  {
14
    z=0;
15
    NewValue=1;
16
  }
17
}

nicht nervös werden. Normalerweise ist ein Versenden per UART keine gute 
Idee aus einer ISR heraus. Aber da es hier um einen Test geht und du 
händisch am Terminal tippst, geht das schnell genug (ausser dein 
Versenden ist Interrupt getrieben, dann funktioniert das nicht)

Alte Volksweisheit
In der klassischen Verarbeitungskette
1
  Eingabe - Verarbeitung - Ausgabe
kann ein Fehler überall sitzen - auch in der Eingabe. Es ist daher eine 
gute Idee, wenn man im Fehlerfall erst einmal sicherstellt, dass das 
Programm auch wirklich mit den Daten arbeitet von denen man annimmt, das 
sie dort auftauchen. Ansonsten kann man sich nämlich im Schritt 
'Verarbeitung' einen Wolf suchen und doch nichts finden.
#4384316
Lesenswert?

Karl H. schrieb:

> Die ISR, die Zeichen empfängt, soll doch bitte schön mal ein bischen
> auskunftsfreudiger sein, was denn da so alles eintrudelt.

Und ich wette 2 zu 1, du wirst dort eine weitere nicht erwartete 
'Eingabe' auftauchen sehen, die dir den String noch ehe er verarbeitet 
wurde wieder zu einem 0 String zusammenkürzt. Eventuell wird dort auch 
ein Carriage Line Feed auftauchen.
OP #4384331
Lesenswert?

Stimmt, irgendwie empfange ich Mist:

Bin im ersten Schritt von 115000 auf 19200 Bau runter gegangen um den 
Fehler zu minimieren.

Bei deinem Code kommt jetzt folgendes bei raus:

'1' - 31<\r><\n>
'0' - 30<\r><\n>
'0' - 30<\r><\n>
'100DAC MAX541 set to: 100;<\r><\n>

Beim Senden von 1000

'5' - 35<\r><\n>
'0' - 30<\r><\n>
'0' - 30<\r><\n>
'500DAC MAX541 set to: 500;<\r><\n>

Beim Senden von 5000

Werde mir das mal mit dem Oszi anschauen, was da wirklich ankommt...
Vielen Dank erstmal für die gute und schnelle Hilfe!
#4384336
Lesenswert?

Stefan D. schrieb:
> Stimmt, irgendwie empfange ich Mist:
>
> Bin im ersten Schritt von 115000 auf 19200 Bau runter gegangen um den
> Fehler zu minimieren.

Das muss nichts heissen. Eine kleinere Baudrate bedeutet nicht 
automatisch, dass die Fehlerwahrscheinlichkeit sinkt. Entscheidend ist, 
mit wievielen % Fehler sich die Baudrate einstellen lässt. Das ist ein 
Zusammenspiel aus Prozessortaktfrequenz und Baudrate. Es kann durchaus 
sein, dass die höhere Baudrate einen kleineren prozentualen Fehler hat 
und damit zuverlässiger funktioniert.
#4384339
Lesenswert?

Dein hTerm ist so eingestellt, dass es Tastendrücke sofort weiterleitet?

Oder wartet es darauf, dass du Return drückst?
Denn dann ist alles klar. Auch das gedrückte Return wird gesendet und 
kommt dir in deiner Eingabe in die Quere.

Auch darf die Übertragung einzelner Zeichen jetzt nicht zuuuu schnell 
passieren. Denn der AVR braucht ja auch etwas Zeit um die Rückmeldung zu 
schicken. Beim händischen Tippen normalerweise kein Problem, sofern 
hTerm jeden Tastendruck sofort auf die Reise bringt.
OP #4384347
Lesenswert?

Karl H. schrieb:
> Dein hTerm ist so eingestellt, dass es Tastendrücke sofort weiterleitet?
>
> Oder wartet es darauf, dass du Return drückst?
> Denn dann ist alles klar. Auch das gedrückte Return wird gesendet und
> kommt dir in deiner Eingabe in die Quere.
>
> Auch darf die Übertragung einzelner Zeichen jetzt nicht zuuuu schnell
> passieren. Denn der AVR braucht ja auch etwas Zeit um die Rückmeldung zu
> schicken. Beim händischen Tippen normalerweise kein Problem, sofern
> hTerm jeden Tastendruck sofort auf die Reise bringt.

Ja erst bei Return wird alles gesendet. Wüsste auch nicht, wo ich es bei 
HTerm umstellen kann...

Ja das kann natürlich sein, dass es dann zu schnell geht...
#4384351
Lesenswert?

Stefan D. schrieb:

> Ja erst bei Return wird alles gesendet. Wüsste auch nicht, wo ich es bei
> HTerm umstellen kann...

Ich denke du hast recht, das kann man bei hTerm nicht wirklich 
einstellen

> Ja das kann natürlich sein, dass es dann zu schnell geht...

Plan B. Die Ausgabe in der ISR drastisch verkürzen. Dann muss es ein * 
als Rückmeldung auch tun, auch wenn es besser wäre, das Zeichen selbst 
zu sehen. Aber zumindest kann man mal sehen, ob die Anzahl der Zeichen 
stimmt.

Dann bleibt noch Plan C:
Innerhalb der ISR einen vollständigen String zur Seite kopieren, so dass 
der Buffer schon wieder zur Verfügung steht, damit weitere Zeichen 
empfangen werden können, während der erste abgearbeitet wird.

Dann gibts noch Plan D:
Einen Ringbuffer installieren, in dem eintrudelnde zeichen geparkt 
werden, die sich dann die Hauptschleife abholt und zu einem String 
zusammensetzt. Die Fleury UART Library macht das beispielsweise.
OP #4384369
Lesenswert?

Fehler gefunden!

Mit der Funktion Uart_Send_16Bit hatte ich 5 Zeichen fürs Array. Aus 
irgendeinem Grund müssen es für eine 5-stellige Zahl mindestens 6 sein.


<c>
void Uart_Send_16Bit(uint16_t data)
{
  char snuma[5];  // Mit 6 tut es!
  itoa (data,snuma,10);
  Uart_puts(snuma);
}
</c>

Jetzt tut es! Nix desto trotz, Karl Heinz, vielen Dank für Deine Mühe!

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