Forum: Mikrocontroller und Digitale Elektronik Linux Bootvorgang via USART überwachen


von Thomas (Gast)


Lesenswert?

Hallo zusammen,

ich möchte gerne für ein Projekt mit einem Intel Edison die serielle 
Schnittstelle dieses Chips via USART mit einem ATTiny817 überwachen. Die 
Idee ist, aus der Bootloaderausgabe ein paar Zeichenketten zu 
identifizieren und einen Fortschrittsbalken auf einem kleinen Display 
anzuzeigen. Auf meinem Board habe ich bereits die benötigte Peripherie 
um den Intel-Chip korrekt an den Strom zu kriegen.

Sobald der Intel-Chip mit Strom und Spannung versorgt wird, fängt dieser 
direkt an ein Linux Poky zu booten. Die Konsole des Linux habe ich mir 
auf meinen UART Eingang des Mikrocontrollers gesetzt. Parallel dazu habe 
ich eine Leitung an meinen Rechner gelegt um die Konsolenausgabe zu 
sehen. Diesen Prozess kann ich via TeraTerm auf dem PC mit 115200 Baud 
überwachen.

Nach einem erfolgreichen Empfangen verschiebe ich einen zyklischen 
Buffer (volatile char uart_keyword[UART_KEYWORD_MAXLENGTH]) um eine 
Stelle nach links und hänge das gerade empfangene Zeichen hinten an. 
Dabei gehe ich nur bis zu vorletzten "richtigen" Stelle, da ich am Ende 
immer '\0' sicherstellen will (wird auch so initialisiert).
1
// Code within the USART Interrupt when word complete received
2
if((USART0.STATUS & USART_RXCIF_bm) == USART_RXCIF_bm)
3
  {
4
    static uint8_t keyword_char = 0;
5
    // do a whole left shift in the buffer
6
    for(keyword_char = 0; keyword_char < (UART_KEYWORD_MAXLENGTH-2); keyword_char++)
7
    {
8
      uart_keyword[keyword_char] = uart_keyword[keyword_char+1];
9
    }
10
    // load the new received value
11
    uart_keyword[(UART_KEYWORD_MAXLENGTH-2)] = USART0.RXDATAL;
12
  
13
    
14
    // say that something happened
15
    measurement_complete = true;
16
    
17
    // reset the interrupt counter
18
    interruptcounter = 0;
19
  
20
    rx_bytes++;
21
  
22
    // delete the Interrupt flag
23
    USART0.STATUS = USART_RXCIF_bm;
24
  }

Sobald der Empfang abgeschlossen ist und ich aus dem UART-Interrupt 
herausspringe möchte ich den Wert des Buffers mit einem vorher 
festgelegten Keyword vergleichen
1
if(strcmp(uart_keyword,"Ein Keyword") == 0)
2
{
3
    // inform the user that "Ein Keyword" is received
4
}
5
else if (strcmp(uart_keyword,"ZweiKeyword") == 0)
6
{
7
    // inform the user that "ZweiKeyword" is received
8
}
9
//...

Alternativ habe ich einen eigenen Compare geschrieben, dieser soll schon 
direkt beim ersten Mismatch ein false zurückgeben und ist wie folgt 
implementiert.
1
bool strcmp_fancy(char* cmp)
2
{
3
  for(uint8_t index = 0; index < (UART_KEYWORD_MAXLENGTH-2); index++)
4
  {
5
    if(uart_keyword[index] != cmp[index])
6
    {
7
      return false;
8
    }
9
  }
10
11
  return true;
12
}

Mein Problem ist, dass nicht alle Keywords korrekt identifiziert werden. 
Bisher konnte ich einige Codewörter erkennen, welche nicht das 
Linux-Typische [  OK  ] am Anfang der Zeile haben. Ansonsten ist das 
Verhalten erstmal nicht wirklich nachvollziehbar :(

Hat hier jemand eine Idee, wo der Fehler liegen könnte?

Sollte der Code nicht verständlich sein, liefere ich natürlich gerne 
nach ;)

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Thomas schrieb:
> Alternativ habe ich einen eigenen Compare geschrieben, dieser soll schon
> direkt beim ersten Mismatch ein false zurückgeben
Aber genau das macht doch strcmp (wenn auch kein false).

http://www.cplusplus.com/reference/cstring/strcmp/
1
...it continues with the following pairs until the characters differ...

Wenn du unbedingt einen bool haben willst mach es doch so:
1
bool strcmp_fancy(char* cmp) {
2
    if (strcmp(...) != 0) {
3
        return false;
4
    }
5
6
    return true;
7
}

Thomas schrieb:
> dass nicht alle Keywords korrekt identifiziert werden.
Wie genau sehen denn die Keywords aus?
Auf was genau pruefst du?
Pruefst du auf "[  OK  ]" oder nur auf "OK"?

von Jim M. (turboj)


Lesenswert?

Thomas schrieb:
> Hat hier jemand eine Idee, wo der Fehler liegen könnte?

Da sehe ich gleich mehrere:
 - Zugriff im Hauptprogramm nicht atomar - der Interrupt kann das Array 
verändern während strcmp() darauf zugreift

 - Interrupt Routine ist wegen der Kopiererei - die einem memmove() 
entspricht - unnötig lang. Außerdem können da bei schnellem UART oder 
weiteren Interrupts theoretisch Bytes verloren gehen, da das RX Flag 
sehr spät gelöscht wird.


Abhilfe wäre eine gewöhnliche Rimgpuffer Implementierung für den UART 
Interrupt, und das Umkopieren der Bytes macht das Hauptprogramm in einem 
eigenen Puffer, damit da nix dazwischen kommen kann.

von Thomas (Gast)


Lesenswert?

Dankeschön für die wirklich hilfreichen Antworten! Ich habe einige 
Schritte machen können.

Jim M. schrieb:
> Thomas schrieb:
> Da sehe ich gleich mehrere:
>  - Zugriff im Hauptprogramm nicht atomar - der Interrupt kann das Array
> verändern während strcmp() darauf zugreift
Das ist ein wirklich wichtiger Punkt. Habe ich nicht dran gedacht und 
schreibt jetzt immer in einen festen buffer rein, der seinen Index 
erhöht. So muss sich die ISR nicht um jegliche shifts o.ä. kümmern. 
Bisher dachte ich die Zeit reicht aus, tut sie aber nicht!

>  - Interrupt Routine ist wegen der Kopiererei - die einem memmove()
> entspricht - unnötig lang. Außerdem können da bei schnellem UART oder
> weiteren Interrupts theoretisch Bytes verloren gehen, da das RX Flag
> sehr spät gelöscht wird.
>
> Abhilfe wäre eine gewöhnliche Rimgpuffer Implementierung für den UART
> Interrupt, und das Umkopieren der Bytes macht das Hauptprogramm in einem
> eigenen Puffer, damit da nix dazwischen kommen kann.

Done. Funktioniert! Der maßgebliche Faktor hier ist nur, wie groß der 
Buffer ist. Ist dieser zu klein, so laufe ich auch hier schnell in einen 
bufferOverflow rein und habe das gleiche Problem wie vorher.
Bei knapp 60-80 Byte Speicher kann ich aber wunderbar die Wrter 
detektieren.

von Thomas (Gast)


Lesenswert?

Kaj G. schrieb:
> Wie genau sehen denn die Keywords aus?
> Auf was genau pruefst du?
> Pruefst du auf "[  OK  ]" oder nur auf "OK"?

Ich prüfe einzelne Zeichenketten aus dem Bootloader wie z.B. "Starting 
kernel ...", indem ich den Buffer mit der strcmp vergleiche. Über den 
ganzen Bootprozess hinweg habe ich mir ein paar Wörter rausgeschrieben, 
welche mir sinnvoll (eineindeutig) erscheinen.

von Markus F. (mfro)


Lesenswert?

Ehrlich, ich finde die Vorgehensweise einigermaßen seltsam.

Der Linux-Bootprozeß ist(schon immer und immer noch) scriptgesteuert. 
Egal ob mit init oder systemd.

Was liegt näher, als in diese Skripte eine zusätzliche Zeile einzubauen, 
die den Fortschritt des Bootprozesses an den Monitor meldet?

Genauso macht(e) das splashutils.

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.