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
@ 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.
Die uint16_t kann aber nur max 16Bit speichern und das ist nur 0-65535.
Mehr oder weniger geht nicht.
Dein array oben ist nicht initialisiert. Kann es daran liegen?
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)
@ 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>
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_tPCValue=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.
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.
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.
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.
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.
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!
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.
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.
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...
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.
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!
Stefan D. schrieb:> void Uart_Send_16Bit(uint16_t data)> {> char snuma[5]; // Mit 6 tut es!> itoa (data,snuma,10);
und auch hier wieder: falsche Funktion.
Für unsigned gibt es die Funktion utoa.
Und nein, das ist kein Kavaliersdelikt, denn für -32750 wäre dein Array
immer noch um 1 Stelle zu klein.
Stefan D. schrieb:> Aus> irgendeinem Grund müssen es für eine 5-stellige Zahl mindestens 6 sein.
wenn du das immer noch nicht verstehst, solltest du dir dringend ein
Grundlagenbuch über C zulegen...
Stefan D. schrieb:> Aus irgendeinem Grund müssen es für eine 5-stellige Zahl mindestens 6 sein.
Das da:
> char snuma[5];
enthält ja keine Zahl, sondern Zeichen. Genau genommen soll es eine
Zeichenkette sein. Und wie markiert man in der Programmiersprache C das
Ende einer solchen? Dies liest Du bitte nach.
Stefan D. schrieb:> <c>> ....> </c>
Kleiner Tipp: probiers mal wie in der Anleitung direkt über dem
Eingabefeld beschrieben nicht mit spitzen sondern mit eckigen Klammern:
[ ]