2013-05-08 9 views
10

Ich programmiere auf einem Arm Mikroprozessor und versuche zu debuggen mit Druckanweisungen über UART. Ich möchte nicht stdlibs nur zum Debuggen hinzufügen. Gibt es eine Möglichkeit, auf der Konsole ohne stdio.h/iostream.h zu drucken? Ist es mir möglich, meinen eigenen printf() zu schreiben?ist es möglich, ohne stdlibs in die Konsole zu schreiben? c/C++

Alternativ kann ich dies mit einem DMA-Controller und direkt auf den UART schreiben. Ich möchte jedoch vermeiden, dass dies möglich ist. Mit der eingebauten Testfunktion "echo" oder "remote loop-back" weiß ich, dass ich den UART richtig konfiguriert habe.

+2

Ja, es ist möglich - Sie können Ihre eigenen Ausgaberoutinen, finden Sie eine kleines Stand-alone Teil printf() Implementierung schreiben, oder schreiben Sie die notwendige Back-End-Unterstützung, um diese Funktionen von einem minimimal eingebettet libc zu ermöglichen (wahrscheinlich in Ihrer Toolchain enthalten), um auf Ihrer Plattform zu laufen. –

+0

Danke. Ich habe gehört, dass Newlib als eingebettete Libc in Ordnung ist. Ich werde jedoch zuerst nach einem partiellen printf() suchen. – Sam

+0

@ChrisStratton: Es hängt effektiv vom Betriebssystem ab. Es ist möglich, dass die systemeigenen OS-Routinen die Standardbibliothek sind. –

Antwort

9

Kurze Antwort: Ja, es ist durchaus möglich, beide Lösungen zu verwenden.

Die printf-Funktion ist sehr komplex, wenn Sie alle Datentypen und Formate unterstützen möchten. Aber es ist nicht so schwer, etwas zu schreiben, das einen String oder eine ganze Zahl in ein paar verschiedenen Basen ausgeben kann (die meisten Leute benötigen nur Dezimal und Hex, aber Oktal fügt wahrscheinlich nur noch 3-4 Zeilen Code hinzu, sobald du Dezimal und Hex hast).

int printf(const char *fmt, ...) 
{ 
    int ret; 
    va_list args; 

    va_start(args, fmt) 
    ret = do_xprintf(outputfunc, NULL, fmt, args); 
    va_end(args); 
    return ret; 
} 

Und dann do_xprintf() macht die ganze harte Arbeit für alle Varianten (printf, sprintf, fprintf, etc)

int do_xprintf(void (*outputfunc)(void *extra, char c), void *extra, const char *fmt, va_list args) 
{ 
    char *ptr = fmt; 
    while(1) 
    { 
     char c = *ptr++; 

     if (c == '%') 
     { 
      c = *ptr++; // Get next character from format string. 
      switch(c) 
      { 
       case 's': 
        char *str = va_arg(args, const char *); 
        while(*str) 
        { 
         count++; 
         outputfunc(extra, *str); 
         str++; 
        } 
        break; 
       case 'x': 
        base = 16; 
        goto output_number; 

       case 'd': 
        base = 10; 
     output_number: 
        int i = va_arg(args, int); 
        // magical code to output 'i' in 'base'. 
        break; 

       default: 
        count++; 
        outputfunc(extra, c); 
        break; 
     } 
     else 
      count++; 
      outputfunc(extra, c); 
    } 
    return count; 
}     

, alle jetzt:

Typischerweise wird printf wie folgt geschrieben Sie müssen nur ein paar Bits des obigen Codes ausfüllen und eine outputfunc() schreiben, die an Ihren seriellen Port ausgegeben wird.

Beachten Sie, dass dies eine grobe Skizze ist, und ich bin sicher, dass es einige Bugs im Code gibt - und wenn Sie Fließkomma oder "Breiten" unterstützen wollen, müssen Sie ein bisschen mehr daran arbeiten ...

(Hinweis auf den zusätzlichen Parameter - für die Ausgabe auf einem FILE *, dass der Dateizeiger für sprintf sein würde, Sie eine Struktur für den Puffer und die Position in dem Puffer, oder so etwas passieren kann)

+0

Vielen Dank. Ich bin einer der wenigen Menschen, die nur Dezimal/Hex benötigen. Ich verwende Festkomma-Notation und möchte meine Ergebnisse überprüfen. Dies wird die Dinge vereinfachen. Ich schätze Ihre Hilfe! – Sam

+0

Irgendein Grund, warum 'output_number' eine Beschriftung in einer case-Anweisung anstelle einer einfachen Funktion ist. – Lundin

+0

@Lundin: Nur, dass es kürzer zu tippen ist. Es wird wahrscheinlich auch einen kürzeren Code erzeugen, es sei denn, der Compiler ist WIRKLICH clever und erkennt, dass der einzige Unterschied zwischen den beiden Funktionsaufrufen der Eingabeparameter ist. –

2

das Konzept von "Konsole" hat außerhalb des Kontexts des von Ihnen verwendeten Systems keine große Bedeutung. Im Allgemeinen gibt es im eingebetteten Programm kein echtes Konzept einer Konsole.

Was Sie suchen, ist eine Möglichkeit, Daten aus Ihrem System zu bekommen. Wenn Sie den UART verwenden möchten und Sie kein High-Level-Betriebssystem wie GNU/Linux verwenden, müssen Sie Ihre eigenen E/A-Treiber schreiben. In der Regel bedeutet dies, zuerst den UART für die gewünschte Buttrate/Parität/Flusskontrolle über Registerschreibvorgänge zu konfigurieren. Für jede Art von robuster E/A-Schnittstelle müssen Sie sicherstellen, dass sie interruptgesteuert ist. Daher müssen Sie ISRs für tx und rx schreiben, die Ringpuffer verwenden.

Nachdem Sie das getan haben, können Sie Ihren eigenen Printf wie Mats angegeben schreiben.

+0

Danke! Ich habe das erkannt. Ich versuchte ursprünglich herauszufinden, wie ich das umgehen könnte, weil ich Probleme hatte. Aber ich habe herausgefunden, dass ich mit meinem Prozessor nach jedem Schreiben auf den TX-Puffer auf das Statusregister zugreifen muss. Wenn ich es nicht stalle. Ich beschäftige mich jetzt mit Baud-Rate-Problemen, aber ich werde auf jeden Fall Mats-Zeug benutzen, sobald ich die Knicke herausgefunden habe. Danke für Ihre Hilfe – Sam

1

Ich habe für das Hintergrund-Debugging gefunden, Zeichen in einen Ringpuffer einzuordnen, der dann durch eine Abfrageroutine auf dem uart Übertragungsregister abgelassen wird, ist meine Methode der Wahl.

Die Warteschlangenroutinen basieren auf einem Zeichen, einem String und der Variablengröße (entweder auf hexadezimale oder auf feste Breite). Und eine Deluxe-Pufferroutine könnte einen Überlauf mit einem reservierten Zeichen anzeigen.

Der Ansatz hat den geringsten Overhead/Einfluss auf die Zieloperation, kann (mit Vorsicht) in Interruptroutine verwendet werden und die Idee ist leicht übertragbar, daher habe ich den Debugger auf allen Systemen ignoriert, die ich verwendet habe.

1

Da das Ausdrucken von Informationen über eine serielle Schnittstelle in einem eingebetteten System das Timing des Hauptprogramms ändert, ist die beste Lösung, die ich gefunden habe, eine kleine Nachricht in 2 Bytes (manchmal 1 Byte funktioniert gut) zu senden ein Programm im PC, um diese Nachrichten zu dekodieren und die notwendigen Informationen zu liefern, die Statistiken und alles, was Sie brauchen, enthalten können. Auf diese Weise füge ich dem Hauptprogramm nur ein wenig Overhead hinzu und lasse den PC die Arbeit für die Verarbeitung der Nachrichten leisten. Vielleicht so etwas wie folgt aus:

  • 1 Byte Nachricht: Bits 7: 4 = Modul-ID, Bits 3: 0 = Debug-Informationen.

  • 2 Bytes Nachricht: Bits 15:12 = Modul-ID, Bits 11: 8 = Debug-Info, Bits 7: 0 = Daten.

Dann in der PC-Software, müssen Sie eine Tabelle mit den Nachrichten deklarieren, die zu einem bestimmten Modul-ID/Debug-Info Paar Karten, und verwenden Sie sie auf dem Bildschirm gedruckt werden.

Vielleicht ist es nicht so flexibel wie die Pseudo-printf-Funktion, da Sie eine feste Reihe von Nachrichten auf dem PC dekodieren müssen, aber es fügt nicht zu viel Overhead hinzu, wie ich zuvor erwähnt habe.

Ich hoffe, es hilft.

Fernando

Verwandte Themen