PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : atoi() Probleme - String in Integer wandeln



Hender
06.04.2006, 20:01
Hallo,

ich möchte eine Zeichenkette die in dem Char Array "pc_data" enthalten ist in eine Integer Zahl umwandeln, die ich beispielweise einem Register (in diesem Fall PortB=i_value) zuweisen möchte. Ich sende vom Pc aus einen String, der Zahlen von 0-255 enhält: z.B. "7" oder "132".

Für Zahlen von 0-9 funktioniert das auch ganz gut. Die Zahlen werden mit atoi() umgewandelt und PortB=i_value zugewiesen. Sind die Zahlen aber größer als 9 erzeugt atoi() irgendwie nix brauchbares. Bei der Zahl 10 wird scheinbar in 1 und 0 aufgeteilt. Die Zahl 123 wird scheinbar in 1 und 23 aufgeteilt.

Das Programm ist im Moment zunächst erst eine Vorstufe zu einer einfachen Protokollimplementation. Ich will zunächst erst einmal testen, wie ich denn aus Strings Integer-Werte erzeugen kann.

Wie müßte ich denn mein Porgramm verändern damit es richtig funktioniert. Es wäre schön wenn mir da jemand helfen könnte.

Hier mein Quelltext:


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

char pc_data[3];
char *pointer=pc_data;
unsigned char receive_char;
unsigned char sendflag;

// Initialisierung UART
void uart_init (void)
{
/* USART-Init 38400 Baud bei 8MHz für Mega32 */
UCSRB = ( (1<<RXCIE) | (1<<RXEN) | (1<<TXEN) );
/* Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit */
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UBRRH = 0; // Highbyte ist 0
UBRRL = 12; // Lowbyte ist 51 ( dezimal )
}

// Zeichen senden
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))); /* warten bis Senden moeglich */
UDR = c; /* sende Zeichen */
return 0;
}

// String senden
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}

//Zeichen empfangen
uint8_t uart_getc (void)
{
while (!(UCSRA & (1<<RXC))); // warten bis Zeichen verfuegbar
return UDR; // Zeichen aus UDR an Aufrufer zurueckgeben
}

SIGNAL (SIG_UART_RECV)
{
receive_char=1;
}

// Erzeugt ASCII-Werte und sendet diese and den PC
void toascii_8 (uint8_t uvalue)
{
char pos1=0;
char rest1=0;
char pos2=0;
char pos3=0;

pos1=uvalue/100;
uart_putc(pos1+48);
rest1=uvalue%100;
pos2=rest1/10;
uart_putc(pos2+48);
pos3=rest1%10;
uart_putc(pos3+48);
uart_putc(' ');
}


int main()
{

uint8_t var;
uint8_t i;
uint8_t i_value;

DDRB=255;
PORTB=0;

uart_init();
sei();
i=0;

while (1)
{
if(receive_char==1)
{
while (!(UCSRA & (1<<RXC)));
{
pc_data[i]=UDR;
i++;
if(i>2)
{
i=0;
}
}
receive_char=0;
sendflag=0;
}

if(receive_char==0&&sendflag==0)
{
//uart_puts(pc_data);
//i_value=atoi(pointer); //Funktioniert auch nicht richtig

i_value=atoi(pc_data);
PORTB=i_value;
toascii_8(i_value); //Ausgabe als ASCII
i=0;
sendflag=1;
}
}
}

skillii
06.04.2006, 21:34
Kann es sein, dass du im String pc_data die null-Terminierung vergessen hast??

Es kann auch sein, dass ich es überlesen habe, aber aufgefallen ist es mir eigentlich nicht ...

SprinterSB
06.04.2006, 22:58
pc_data ist auf jeden falls zu kurz mit 3 Elementen.
Die Zahlen können 3-Stellig sein und die Ende-Null muss reinpassen.

Was die Null angeht hat skilli schon gesagt.

receive_char muss hier als volatile (flüchtig) definiert werden.

Crash32
07.04.2006, 01:54
Hi

Also ich weiß nicht genau wie es auf einem microcontroller ist,
aber in "normalem" c ist atoi so implementiert:

string=atoi(zahl,string,int);

string : ist ein Pointer auf char
zahl: ist die Zahl die du umwandeln willst
int: ist das zahlensystem in das du wandeln willst, also in deinem fall 10


Mfg Reinhold Fischer

Hender
07.04.2006, 11:52
Hallo,

danke für Eure schnell Hilfe. An eine Null-Terminierung habe ich natürlich nicht gedacht. Was muss ich denn da genau machen? Muss ich bei meinem String ein Terminierungszeichen mitsenden? Wenn ja, muss ich dieses Terminierungszeichen wieder entfernen, bevor ich atoi() anwende?
Also ich müßte auf jeden Fall das pc_data Feld mit mindestens 5 Elementen ausstatten.

@Crash32
Vielen Dank für Deine Hilfe, aber Du verwechselst die Syntax wahrscheinlich mit itao().

Crash32
07.04.2006, 13:08
Hi

Ja, hast recht, hab das was verwechselt, lag vielleicht an der Uhrzeit.
Ich mußte gestern mal wieder einen Marathon einlegen, ging noch bis halb 4 :)

Wegen der Null-Terminierung:

Ja, du mußt ein Terminierungszeichen mitsenden.

Oder du sendest jeden String ohne terminierungszeichen, und hängst es dann manuell an jeden String den du empfangen hast.Ist vielleicht besser, bei niedriger Bandbreite, man "spart" sich quasi bei jedem senden ein Zeichen.

Am besten ist es wenn du jeden String immer nullterminierst, z.b du hängst "\0" al letztes zeichen hintendran. So übergibst du den String auch an atoi.
Wenn du z.b itoa benutzt, bekommst du auch einen Null-terminierten String zurück.

Mfg Reinhold Fischer

Hender
07.04.2006, 13:58
Hallo,

ich habe die Nullterminierung jetzt meines Wissens nach richtig eingebaut, aber habe trotzdem weiterhin das gleiche Problem, dass die Zahlen ausgeteilt werden. Es wird am Ende immer nur die letzte Stelle zugewiesen. Schicke ich "10" so ist i_value=0, bei einer "11" ist i_value=1, bei einer "110" ist komischerweise i_value=10.
Ich bin nun echt ratlos woran das liegt und wie ich dsa Porblem beheben kann.

Meine Änderungen:
char pc_data[5];
volatile unsigned char receive_char;

Meine main() sieht jetzt so aus:


int main()
{

uint8_t var;
uint8_t i;
uint8_t i_value;

DDRB=255;
PORTB=0;

uart_init();
sei();
i=0;

while (1)
{
if(receive_char==1)
{
while (!(UCSRA & (1<<RXC)));
{
pc_data[i]=UDR;
i++;
if(i>2)
{
i=0;
}
}
//pc_data[3]='\0';
receive_char=0;
sendflag=0;
}

if(receive_char==0&&sendflag==0)
{
//uart_puts(pc_data);
//i_value=atoi(pointer); //Funktioniert auch nicht richtig
pc_data[3]='\0';
i_value=atoi(pc_data);
PORTB=i_value;
toascii_8(i_value); //Ausgabe als ASCII
i=0;
sendflag=1;
}
}
}

skillii
07.04.2006, 19:06
Ich glaube ich weiß jetzt was dein Problem ist ;)

[Code]
while (!(UCSRA & (1<<RXC)));
[\Code]
Ist der Strichpunkt am Ende der Zeile Absichtlich?


Mir kommt vor, dass dein Programm eher kompliziert/Umständlich geschriben ist... zumindest ist das meine Ansicht.

Wieso fragst du überhaupt das bit RXC aus??? das Bit braucht man doch normalerweise gar nicht wenn man den UART_RECV Interrupt verwendet, oder täusche ich mich da?

Wieso liest du das UDR nicht in der Interrupt-Routine aus(z.B. könntest du es in das Array pc_data speichern) und setzt dir ein Flag, wenn du 3 Zeichen empfangen hast.
Wenn dieses Flag gesetzt ist wertest du das Array pc_data im Hauptprogramm aus.(Null Terminierung hinzufügen und atoi() aufrufen)
So, würde ich das halt schreiben, ist eine Möglichkeit und muss nicht umbedingt die Beste sein ...

So, ich hoffe ich habe mich halbwegs verständlich ausgedrückt.

Hender
10.04.2006, 15:16
Hallo Skillii,
vielen Dank für Deine Hilfe. Ich habe jetzt Deine Hinweise beachtet und mein Programm umgbaut, sodass es jetzt bei einer festen Stringlänge funktioniert. Wahrscheinlich lag es doch an der Abfrage von "while (!(UCSRA & (1<<RXC)));". Die habe ich jetzt komplett weggelassen und das pc_data Array wird nun in der Interrupt-Routine mit Daten gefüllt.
Vielen vielen Dank nochmal.

Viele Grüße
Hendrik