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
1) volatile verwenden 2) das ganze noch solange verriegeln bis die Daten verarbeitet sind damit nicht der Interrupt in den Daten rumpfuscht
Stefan D. schrieb: > if (PCValue<65536) PCValue kann niemals grösser als 65535 werden... Sendest du tatsächlich eine richtige 0 als Ende einer Zahl?
@ 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?
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 | ....
|
ein und siehst dir an, welchen String der AVR tatsächlich empfangen hat.
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_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.
:
Bearbeitet durch User
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.
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.
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.
:
Bearbeitet durch User
Das Oszi sagt, kein Fehler im Sender! Gemessen am RX-Pin des ATMega 328P. So langsam wird es doch wirklich merkwürdig....
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.
Allerdings denke ich, dass im Input Buffer immer noch ein Problem auf dich wartet. Das ist noch nicht erledigt.
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: [ ]
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.