Forum: Mikrocontroller und Digitale Elektronik Serielle Verarbeitung von Strings mit Python


von VH (Gast)


Lesenswert?

Hallo,

ich arbeite gerade an einer Kombination aus Pythonskript und 
Mikrocontrollerprogrammierung in C bei der ich einen ARM Cortex-M3 vom 
Typ ADuCM301 über UART ansteuern möchte.
In diesem Skript soll erstmal ein String, später mehrere Strings, 
seriell an den µC gesendet werden. Hierzu verwende ich das Modul 
PySerial (http://pyserial.sourceforge.net/) dessen Funktion 
serial.Serial.write ich nutze um einen eingegeben Text seriell 
weiterzuleiten. Das C-Programm welches ich auf den Controller geflasht 
habe soll diesen Text nun verarbeiten und entsprechend Fälle abklären um 
dann einen String zurückzuschicken.
Beispiel: Ich schicke den String: "Hallo" und möchte, dass er eine 
Funktion ausführt wo dann "Hallo zurück" über UART gesendet wird und von 
meinem Pythonskript ausgegeben.
Mit einzelnen Zeichen funktionierte das ganz gut, aber ich kriege das 
irgendwie nicht hin, dass das auch mit einem String funktioniert. Ich 
habe schon versucht die gesendeten Zeichen in einen Array zu schreiben 
und jedesmal das Shift Register zu entleeren bis alle Zeichen durch 
sind, aber ich kriege nur eine fehlerhafte Ausgabe.
Bin dankbar für jede Antwort.

Gruß
VH

von Norbert (Gast)


Lesenswert?

VH schrieb:

> ich arbeite gerade an einer Kombination aus Pythonskript und
> Mikrocontrollerprogrammierung in C

> Mit einzelnen Zeichen funktionierte das ganz gut, aber ich kriege das
> irgendwie nicht hin, dass das auch mit einem String funktioniert. Ich
> habe schon versucht die gesendeten Zeichen in einen Array zu schreiben
> und jedesmal das Shift Register zu entleeren bis alle Zeichen durch
> sind, aber ich kriege nur eine fehlerhafte Ausgabe.

Also, das Problem liegt eindeutig im C-Code ODER im Python-Code.
Genaueres können wir von hier aus nicht erkennen.

> Bin dankbar für jede Antwort.

Gerne

von VH (Gast)


Lesenswert?

Also mein Pythonskript sieht wie folgt aus:
1
    eingabe = input('Send to UART: ') #Gib Zeichencode ein
2
    if eingabe == 'exit':
3
        ser.close() #Beende Programmdurchlauf
4
        exit()
5
    else:
6
        ser.write(eingabe.encode('latin1')) #Sende eingegebene Zeichenkette an Controller
7
        out = b''
8
        time.sleep(1)
9
        out = ser.readline() #Lese von Controller kommende Zeichenkette aus
10
        
11
        if out != b'':
12
            print(">>" + out.decode('latin1')) #Gib Ausgabe auf Bildschirm aus

Auf dem Controller läuft folgendes Programm:
1
  char eingabe[] = "";
2
  int a, b;
3
  
4
  WdtGo(T3CON_ENABLE_DIS); 
5
  DioCfg(pADI_GP4, 0x10);
6
  DioOen(pADI_GP4, BIT2); // P4.2  as output
7
                                                                           
8
  UrtLinCfg(0, 9600, COMLCR_WLS_8BITS, COMLCR_PEN_DIS);
9
  DioCfg(pADI_GP1,0x9); // port 1 configured for UART   
10
  
11
  printf ( "\nADuCM301 UART Program!\r\n"); 
12
  
13
  while(1)
14
  {      
15
      //Own Extensions
16
      //scanf("%s %i %i", eingabe, &a, &b);
17
      for(int i = 0; eingabe[i] != '\n'; i++){
18
        eingabe[i] = pADI_UART->COMRX;
19
        while((pADI_UART->COMLSR & (1 << 6))==0);
20
      }
21
      printf("\nYour input was: %s\n", eingabe);
22
      //uart_puts(eingabe);
23
      if(eingabe == "12")
24
        UART_test_function();
25
  }

Der Controller nimmt meine seriellen Eingaben schon an, verarbeitet das 
aber nicht wie gewünscht. Wenn ich "12" eingebe sollte eigentlich diese 
UART_test_function ausgeführt werden, stattdessen bekomme ich aber was 
kryptisches zurück:
1
Enter your commands below.
2
Insert 'exit' to leave
3
Send to UART: 12
4
>>B¿EðÀQñ
5
6
7
Send to UART: 12
8
>>
9
10
11
Send to UART: 12
12
>>Your input was: 444444444444444444444444444
13
14
15
Send to UART: 12
16
>>EêÁ!ɶñ 
17
Ýcúöë@†êB¿EðÀQñ
18
19
20
Send to UART: 12
21
>>
22
23
24
Send to UART: 12
25
>>Your input was: 444444444444444444444444444
26
27
28
Send to UART: 12
29
>>EêÁ!ɶñ 
30
Ýcúöë@†êB¿EðÀQñ

Hat das alles evtl. mit Laufzeitverzögerung zu tun? Anscheinend 
speichert er mir die gesendeten Werte nicht wie gewünscht in meinen 
Eingabepuffer hinein sondern kommt auf irgendwelche abstrusen Werte. Mit 
einzelnen Zeichen hat das ganz gut funktioniert, aber mit der 
Zeichenkette krieg ich es einfach nicht hin. Ich glaube, dass ich in der 
Verarbeitung im C-Code irgendwas falsch mache, weiß aber nicht genau 
was.

von Karl H. (kbuchegg)


Lesenswert?

VH schrieb:

> Auf dem Controller läuft folgendes Programm:
>   char eingabe[] = "";

Dieses Array hat eine Länge von 1.
Alle Strings die eine strlen größer als 0 haben, passen da nicht hinein.

Du wirst von Python gewohnt sein, dass du Strings einfach zuweisen 
kannst und dann wird die String-Variable ohne dein Zutun größer. Nur: C 
funktioniert nicht so.


>       //Own Extensions
>       //scanf("%s %i %i", eingabe, &a, &b);
>       for(int i = 0; eingabe[i] != '\n'; i++){
>         eingabe[i] = pADI_UART->COMRX;
>         while((pADI_UART->COMLSR & (1 << 6))==0);
>       }

Man mag mich engstirnig nennen. Aber ist die logische Reihenfolge nicht 
eigentlich:
   zuerst darauf warten, dass ein Zeichen vorliegt
   und erst dann dieses Zeichen abspeichern


>       if(eingabe == "12")

No.
Strings können nicht so verglichen werden

> Hat das alles evtl. mit Laufzeitverzögerung zu tun?

Nein,
Es hat damit zu tun, dass du kein C Buch hast.

Während du dir eines besorgst, kannst du das wichtigste erst mal hier 
nachlesen:
String-Verarbeitung in C

von Karl H. (kbuchegg)


Lesenswert?

Ach, ich hab noch vergessen:
Bedenke, dass in C ein korrekter String mit einem '\0' Zeichen aufhört. 
Von alleine kommt das aber nicht ans Stringende, wenn du den String 
Zeichen um Zeichen aufbaust. Das muss schon dein Programm machen!

von Norbert (Gast)


Lesenswert?

Zusätzlich zu Karl-Heinz Ausführungen:

-----------------------------
input([prompt])

    Equivalent to eval(raw_input(prompt)).

    Warning

    This function is not safe from user errors! It expects a valid 
Python expression as input; if the input is not syntactically valid, a 
SyntaxError will be raised. Other exceptions may be raised if there is 
an error during evaluation. (On the other hand, sometimes this is 
exactly what you need when writing a quick script for expert use.)

    If the readline module was loaded, then input() will use it to 
provide elaborate line editing and history features.



    Consider using the raw_input() function for general input from 
users.
-----------------------------

von Yalu X. (yalu) (Moderator)


Lesenswert?

Norbert schrieb:
> Zusätzlich zu Karl-Heinz Ausführungen:
>
> -----------------------------
> input([prompt])
>
>     Equivalent to eval(raw_input(prompt)).
>
>     Warning

So war das in Python 2. Vieles (wenn nicht alles) spricht aber dafür,
dass der Threaderöffner Python 3 verwendet.

von SNR (Gast)


Lesenswert?

Wie wurde die serielle Schnittstelle in Python initialisiert?
Baudrate stimmt?

von Norbert (Gast)


Lesenswert?

Yalu X. schrieb:

> So war das in Python 2. Vieles (wenn nicht alles) spricht aber dafür,
> dass der Threaderöffner Python 3 verwendet.

Das interessiert mich jetzt.

Hier war die Vorgabe:
1
eingabe = input('Send to UART: ')
2
if eingabe == 'exit':
3
    ser.close()
4
    exit()
5
else:
6
    ser.write(eingabe.encode('latin1'))
7
    out = b''
8
    time.sleep(1)
9
    out = ser.readline()
10
    if out != b'':
11
        print(">>" + out.decode('latin1'))

Wie bzw. woran erkennt man die Python Version?

PS. b'' gilt nicht, denn es ist absolut korrekt auch in 2.6.6 ;-)

von VH (Gast)


Lesenswert?

@ Karl Heinz Buchegger:
Danke für die konstruktive Hilfe und für den Tipp, der Link vor allem 
war sehr hilfreich. Ich denke ich muss aber noch einen Weg finden den 
String richtig zu verarbeiten, aber damit bin ich schon auf dem 
entscheidenenden Weg.

@Norbert:
Ich nutze Python 3, da funktioniert raw_input() nicht mehr.

@SNR:
Ja, ich arbeite mit einer Baudrate von 9600. Das hat soweit auch alles 
geklappt, folgendes Codeschnipsel verwende ich für die Initialisierung:
1
ser = serial.Serial(
2
    port = 3,
3
    baudrate = 9600,
4
)

von VH (Gast)


Lesenswert?

Ich hab das ganze jetzt noch ein wenig angepasst und folgende Funktion 
geschrieben zum Auslesen eines gesendeten Bytes via UART:
1
int main (void)
2
{
3
  char eingabe[255] = "12";
4
  
5
  WdtGo(T3CON_ENABLE_DIS);  // Disable Watchdog 
6
  DioCfg(pADI_GP4, 0x10);   // Enable P4.2
7
  DioOen(pADI_GP4, BIT2);   // P4.2  as output
8
                                                                           
9
  UrtLinCfg(0, 9600, COMLCR_WLS_8BITS, COMLCR_PEN_DIS); // UART Config, 8 Bit wordlength, no parity, 1 Stop Bit, Baudrate = 9600
10
  DioCfg(pADI_GP1,0x9); // port 1 configured for UART   
11
  
12
  printf ( "\nADuCM301 UART Program!\r\n"); 
13
  
14
  while(1)
15
  {      
16
      uart_readline(eingabe);
17
      printf("\nYour input was: %s\n", eingabe);
18
      if(eingabe[0] == '1' && eingabe[1] == '2')  //Call Testfunction if String is "12"
19
        UART_test_function();
20
      
21
      DioTgl(pADI_GP4,BIT2);
22
      Delay();
23
  }
24
}
25
26
void uart_readline(char* str){
27
  char c;
28
  unsigned int index = 0;
29
30
  while (1){
31
    while((pADI_UART->COMLSR & (1 << 6))==0);
32
    c = pADI_UART->COMRX;  
33
    if (c != -1){
34
      if (c == '\n'){ /* ASCII: NewLine */
35
  str[index] = '\0';
36
  return;
37
      }
38
      else{
39
        str[index] = c;
40
        index++;
41
      }
42
    }
43
  }
44
}

Ich rufe das ganze in der main-Funktion auf indem ich als String die 
Texteingabe an die Funktion sende. Diese soll dann schrittweise ein Byte 
auslesen immer wenn COMTX/COMRX und das Shift Register leer ist.
Wenn die Funktion aufgerufen wird wird mein Array jedoch leider nicht 
inkrementiert obwohl ich die Indexvariable hochzähle. Somit schreibt er 
auch nichts relevantes in meinen Stringbuffer rein.
Woran kann das liegen?

von Karl H. (kbuchegg)


Lesenswert?

VH schrieb:

> Wenn die Funktion aufgerufen wird wird mein Array jedoch leider nicht
> inkrementiert

Die Aussage ergibt keinen Sinn.
Ein Array kann sowieso nicht inkrementiert werden.

> obwohl ich die Indexvariable hochzähle.

Na, dann passt es doch. Zumindest der Teil.

> Somit schreibt er
> auch nichts relevantes in meinen Stringbuffer rein.

Na, irgendwas muss er aber 'reinschreiben'. Was ist das? Wo kommt es her 
und ist es dort vielleicht schon falsch?

Bist du sicher, dass dieser Teil hier
1
    while((pADI_UART->COMLSR & (1 << 6))==0);
2
    c = pADI_UART->COMRX;

richtig ist? Wenn du dir danach das c ansiehst, steht da das korrekte 
Zeichen drinnen? Es ist zb eine sehr sehr gute Idee, wenn du zu 
Debug-Zwecken dieses c über die UART gleich mal zum Sender 
zurückschickst. Auf einem Terminalprogramm muss dann das Zeichen 
auftauchen. Du drückst die Taste 'A' und wenn dann auf dem ganzen Weg 
zum µC und wieder zurück irgendwas unvorhergesehenes passiert, dann 
taucht bei dir im Terminal dann eben kein 'A' auf sondern was anderes 
(oder auch gar nichts). Sowas ist dann immer ein deutliches Indiz, dass 
etwas nicht stimmt.

von VH (Gast)


Lesenswert?

Hallo,

ich habe meinen Code nun soweit, dass ich diverse Daten sende und 
empfange.
Den Code im Mikrocontroller habe ich mal ganz einfach durch eine 
for-Schleife realisieren wollen um zu sehen ob überhaupt die Daten die 
ich aus dem Skript hinaus schicke überhaupt ankommen:
1
  
2
while(1){            
3
      for(int i = 0; eingabe[i] != '\n' && i < 10; i++){ //Empfange String aus maximal 10 Zeichen
4
        while((pADI_UART->COMLSR & (1 << 6))==0); //Leere Puffer
5
        eingabe[i] = pADI_UART->COMRX; //Empfange Byte
6
      }
7
8
      printf("\nYour input was: %s\n", eingabe);
9
      if(eingabe[0] == '1' && eingabe[1] == '2')  //Rufe Testfunktion auf wenn String "12" ist
10
        UART_test_function();
11
      else{
12
        printf("Please try something else!");
13
      }
14
      
15
      DioTgl(pADI_GP4,BIT2);
16
      Delay();
17
  }
18
}

Mein Pythonskript sieht aktuell wie folgt aus:
1
import time
2
import sys
3
import serial
4
import codecs
5
6
#configure the serial connections (the parameters differs on time)
7
ser = serial.Serial(
8
    port = 3,
9
    baudrate = 9600,
10
)
11
12
#ser.open()
13
ser.isOpen()
14
15
print("Enter your commands below.\nInsert 'exit' to leave")
16
17
def write(s):
18
    for c in s:
19
        ser.write(c.encode('ascii'))
20
        ser.flushInput()
21
        time.sleep(0.01)
22
        
23
while 1:
24
    #get keyboard input
25
    eingabe = input('Send to UART: ')
26
    if eingabe == 'exit':
27
        ser.close()
28
        exit()
29
    else:
30
        #send character to the device
31
        write(eingabe)
32
        out = b''
33
        time.sleep(1)
34
        ser.flush()
35
        while ser.inWaiting() > 0:
36
        #   out += ser.read(1)
37
            out = ser.readline()[:-2]
38
        if out != b'':
39
            print(">>" + out.decode('ascii'))
40
        else:
41
            print(">> could not read anything!")
42
        ser.flushOutput()

Ich sende also durch eine eingabe einen String an den Controllerport und 
je nachdem was der Controller damit macht möchte ich die richtige 
Ausgabe haben. Jetzt ist es aber so, dass wenn ich beispielsweise die 
Zeichen "12" sende, der Controller 10 mal die 2 empfängt auf Grund der 
for-Schleife. Meine Eingabe lautet für ihn also "2222222222". Wenn ich 
"12345" ist es 10 mal die 5 usw. also der Controller empfängt immer nur 
das letzte Zeichen und nimmt dieses als endgültig für alle anderen 
Zeichen an.
Eine typische Ein- und Ausgabe sieht wie folgt aus:
1
Enter your commands below.
2
Insert 'exit' to leave
3
Send to UART: 12
4
>>Please try something else!
5
Send to UART: 12
6
>>Your input was: 2222222222
7
Send to UART: 24
8
>>Please try something else!
9
Send to UART: 35
10
>>Please try something else!
11
Send to UART: 35
12
>>Please try something else!
13
Send to UART: 35
14
>>Please try something else!
15
Send to UART: 36
16
>>Your input was: 6666666666
17
Send to UART: 37
18
>>Your input was: 7777777777
19
Send to UART: 58
20
>>Please try something else!

Wie schaffe ich es, dass meine Strings richtig erkannt werden?
Mein Eingabestring stimmt soweit, wenn ich das in Python überprüfe, 
anscheinend kann mein Controllerprogramm die Daten nur nicht richtig 
empfangen.

von Krapao (Gast)


Lesenswert?

Dein Controllerprogramm arbeitet mit uart_readline(eingabe). Aber dein 
Pythonprogramm sendet keine "ASCII: NewLine" sondern nur einzelne 
Zeichen aus dem String. Versuche mal in def write(s) nach der for 
Schleife ein "ASCII: NewLine" zu senden, um den Strom von Zeichen 
abzuschliessen.

Wieso verwendest du input (d.h. mit Auswertung der Eingabe durch 
Python) statt raw_input zu benutzen?

von VH (Gast)


Lesenswert?

Krapao schrieb:
> Dein Controllerprogramm arbeitet mit uart_readline(eingabe). Aber dein
>
> Pythonprogramm sendet keine "ASCII: NewLine" sondern nur einzelne
>
> Zeichen aus dem String. Versuche mal in def write(s) nach der for
>
> Schleife ein "ASCII: NewLine" zu senden, um den Strom von Zeichen
>
> abzuschliessen.

Ok, wenn ich die Zeile
1
ser.write('\n'.encode('ascii'))
am Ende meiner write-Funktion im Pythonskript mit einfüge, so dass noch 
ein NewLine als letztes Zeichen gesendet wird, landet mein Skript im 
else-Zweig
1
print(">> could not read anything!")
 weil anscheinend der Fall der gleiche bleibt, dass nur das letzte 
eingegebene Zeichen vom Mikrocontrollerprogramm gelesen wird. Somit hab 
ich keine Ausgabe.

> Wieso verwendest du input (d.h. mit Auswertung der Eingabe durch
>
> Python) statt raw_input zu benutzen?

Bei Python 3 funktioniert raw_input nicht mehr, da spuckt er mir ständig 
den Fehler aus, dass es diese Funktion nicht gibt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

VH schrieb:
>> Wieso verwendest du input (d.h. mit Auswertung der Eingabe durch
>>
>> Python) statt raw_input zu benutzen?
>
> Bei Python 3 funktioniert raw_input nicht mehr, da spuckt er mir ständig
> den Fehler aus, dass es diese Funktion nicht gibt.

Da die Verwendung von input() etwas Verwirrung zu stiften scheint:

  http://docs.python.org/py3k/whatsnew/3.0.html

  "PEP 3111: raw_input() was renamed to input(). That is, the new
  input() function reads a line from sys.stdin and returns it with the
  trailing newline stripped. It raises EOFError if the input is
  terminated prematurely. To get the old behavior of input(), use
  eval(input())."

von Krapao (Gast)


Lesenswert?

Sorry, bin noch mit Python 2 verbandelt.

@ VH

Ich an deiner Stelle würde µC Programm und Python Skript trennen. Es 
bringt IMHO nix an zwei (vier!) Fronten zu kämpfen: Python-Send, 
µC-Empfang, µC-Send und Python-Empfang.

Essentiell ist ein Mitschnitt aller Daten, die tatsächlich über die 
serielle Leitung flitzen und die Zuordnung wer was macht. DAS kann man 
mit einem "neutralen Beobachter", d.h. einem Terminalprogramm und ggf. 
einem realen oder virtuellen Loopback herausfinden.

In beiden Codeschnippseln finde ich Sachen, die mir komisch vorkommen:

Im µC Code fehlt mir eine saubere, d.h. vollständige Initialisierung des 
eingabe-Puffers sowie der saubere Nullbyte-Abschluss nach dem Empfangen. 
Ohne ist es nicht verwunderlich, wenn Murks gesendet wird. Dann fehlt 
mir die Funktion der µC Senderoutine - schickt die ein Newline mit (die 
Pyhthonseite erwartet es) oder nicht?

Im Python-Code verwundern mich die Positionen flush-Routinen. Den 
Eingangspuffer flushen nach jedem gesendeten Zeichen und den 
Ausgangspuffer nach einer empfangenen Zeile? Und dass du bei einer 
empfangenen Zeile zwei Zeichen am Ende abschnippelst ist OK? Passt das 
zu der unbekannten µC-Sendefunktion?

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.