Forum: Mikrocontroller und Digitale Elektronik STM32L051 UART transmit


von Heinz K. (heinz_k960)


Lesenswert?

Hallo,

ich bin noch neu auf dem Gebiet der STM32 Programmierung. Aktuell habe 
ich ein kleines Evalboard mit dem STM32L051, welches ich bereits etwas 
Leben eingehaucht habe (Clock source auf externen Quarz und PLL 
umgestellt, GPIO initialisiert und UART initialisiert). Programmiert 
wird bare metal, sprich ohne jegliche HAL (nur die CMSIS) wird benutzt. 
Meine nächsten Schritte sind die anderen Peripherals (I2C und SPI) zum 
Laufen zu bringen.

Aktuelles Thema ist jedoch die UART. Die UART Übertragung funktioniert 
bei mir so, dass ich einen RX-Interrupt aktiviere und in der UART ISR 
dann auf bestimmte Zeichen abfrage und entsprechende Aktionen auslöse 
(z.B. LED ein/aus, String senden). Das funktioniert auch, sprich die 
Basisfunktionalität ist gegeben.
Anstatt des UART Tests möchte ich jedoch in serial.write Arduino-Manier 
einfach Log - Messages während der Entwicklung über die UART ausgeben 
und da komme ich irgendwie nicht weiter. Meine Idee war einfach printf 
zu benutzen, da man hier alle Formatierungsoptionen hat. Dazu muss man 
aber wohl eine Funktion (_write) umbiegen, damit printf die UART als 
standard output verwendet. Das scheint doch alles komplex zu werden - 
für eine "triviale" Aufgabe. Generell: ist es eine gute Idee mit UART RX 
/ TX Interrupts zu arbeiten oder ist einfaches Polling völlig in 
Ordnung?

Anbei der Code zur ISR und zur TX-Routine, der ursprünglich von R. Jesse 
stammt:

void USART2_IRQHandler(void)
{
    uint16_t received = 0;

    if (USART2->ISR & USART_ISR_RXNE)
    {
        received = USART2->RDR & 0x01FF;
        if (received == 'a')
        {
            LED_PC13_ON;
        }
        if (received == 'b')
        {
            LED_PC13_OFF;
        }

    /* Filter fuer zulaessige Zeichen */
        if (((received >= 'A') && (received <= 'Z')) || ((received >= 
'a') && (received <= 'z')) ||
             (received == ' ') || (received == CR) || (received == LF) 
|| (received == '*'))
        {
            USART2->RDR = received;
        }
        if (received == 'c')
        {
            USART2->CR1 |= USART_CR1_TXEIE;   // Tx-Interrupt nur 
aktivieren, wenn Daten gesendet werden sollen!
        }
    }

    if ((USART2->ISR & USART_ISR_TXE) && (USART2->CR1 & 
USART_CR1_TXEIE))
    {
        if (*outString != '\0')
        {
            USART2->TDR = *outString++;  // Text zeichenweise ausgeben, 
bis das 0-Byte (= Textende) erkannt wird
        }
        else                             // 0-Byte gefunden --> Text 
wurde vollstaendig ausgegeben
        {
            outString = saveString;      // Neu: Urspruenglichen Text 
wiederherstellen
          USART2->CR1 &= ~USART_CR1_TXEIE;
        }
    }

}

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
> Meine Idee war einfach printf zu benutzen, da man hier alle
> Formatierungsoptionen hat. Dazu muss man aber wohl eine Funktion
> (_write) umbiegen, damit printf die UART als standard output verwendet.
> Das scheint doch alles zu komplex zu sein.

Das ist gar nicht schwierig. Dort ist ein Beispiel (ohne Sendepuffer):

http://stefanfrings.de/stm32/stm32l0.html#usart

Wenn du das mit einem Puffer machen willst, würde ich aber nicht '\0' 
als Ende-Markierung benutzen, denn das funktioniert nur mit Strings in 
denen das Zeichen nicht vorkommt. Ich würde das eher mit einem 
Ringpuffer machen. Wie das geht, wurde hier schon ziemlich oft 
diskutiert. Du findest die Beiträge bestimmt mit der Suchfunktion.

Oder auch dort: https://www.youtube.com/watch?v=teCQ8dosFts

von STK500-Besitzer (Gast)


Lesenswert?

Heinz K. schrieb:
> Generell: ist es eine gute
> Idee mit UART RX / TX Interrupts zu arbeiten oder ist einfaches Polling
> völlig in Ordnung?

Interrupts haben den Vorteil, dass das restliche Programm quasi 
weiterläuft und nicht blockiert wird.

Boards von ST (NUCLEO, Discovery etc) haben doch den ST-Link zum 
Programmieren drauf. Damit kann man auch debuggen.

von Peter (Gast)


Lesenswert?

Heinz K. schrieb:
> Generell: ist es eine gute
> Idee mit UART RX / TX Interrupts zu arbeiten oder ist einfaches Polling
> vällig in Ordnung?

Also generell ist es schonmal gut, wenn Du mit Bare Metal anfängst, um 
Dir einen etwas tieferen Einblick in den Microcontroller zu verschaffen.

Allerdings haben wir 'zu meiner Zeit' immer erstmal versucht einen 8 Bit 
Controller (ATmega8) zum laufen zu bringen. Das ist dann doch etwas 
einfacher.

Was den UART bzw. die Interrupts betrifft ist es keine gute Idee, darin 
die Daten auszuwerten. Du sollstest einfach einen globulen Byte Array 
nutzen und diesen mit den empfangenen Daten füllen (z.B. uartDataRx[i] = 
...). Dann kannst du noch ein (globales ) Flag sezten, z.B. uartReceived 
= true.

In der main() wird das ganze dann ausgewertet und ggf. auch was 
zurückgeschickt.

Gruß Peter

von W.S. (Gast)


Lesenswert?

Heinz K. schrieb:
> ich bin noch neu auf dem Gebiet der STM32 Programmierung. Aktuell habe
> ich ein kleines Evalboard mit dem STM32L051, welches ich bereits etwas
> Leben eingehaucht habe (Clock source auf externen Quarz und PLL
> umgestellt, GPIO initialisiert und UART initialisiert).

Du bist ein Held!
UNd wenn du mit dem Benutzen des UART (Universeller Asynchroner 
Empfänger und Sender) nicht allein zu Potte kommst, dann lade dir mal 
das:
http://www.mikrocontroller.net/attachment/316790/STM32F103C8T6.ZIP
herunter und lies darin. Oder schaue dir den folgenden Thread an:
Beitrag "Einstieg in STM32 : STM32F103C8T6 --> Kompilieren und Flashen?"

Das Zeug im Zipfile ist zwar für den STM32F103C8T6, aber zum einen ist 
vieles darin plattformunabhängig (läuft also auch auf z.B. 
Fujitsu-Controllern) und zum anderen sind die Peripheriecores bei den 
diversen STM32 oftmals ziemlich ähnlich, sodaß sich auch 
Lowlevel-Treiber ohne allzu große Hürden auf einen anderen Chip 
umschreiben lassen.

W.S.

von Heinz K. (heinz_k960)


Lesenswert?

Stefan F. schrieb:
> Das ist gar nicht schwierig. Dort ist ein Beispiel (ohne Sendepuffer):
>
> http://stefanfrings.de/stm32/stm32l0.html#usart

Vielen Dank Stefan für den Link. Ich kenne die Seite eigentlich, mmh. 
scheinbar habe ich den Artikel nicht gefunden ...

In der _write Funktion nutzt Du normales Polling, wenn ich das richtig 
sehe (Abfrage Bit USART_ISR_TXE).

In der UART Initialisierungsroutine werden sowohl TX und RX Interrupt 
gleichzeitig aktiviert. Ist das gut so? Sollte TX Interrupt nicht erst 
frigegeben werden, wenn etwas im TX Sendebuffer steht?

von Heinz K. (heinz_k960)


Lesenswert?

Peter schrieb:
> Allerdings haben wir 'zu meiner Zeit' immer erstmal versucht einen 8 Bit
> Controller (ATmega8) zum laufen zu bringen. Das ist dann doch etwas
> einfacher.

ich kenne die 8bit AVR Controller recht gut und habe schon einiges 
programmiert. Nun wollte ich einfach den Schritt gehen und auf die STM32 
umschwenken. Mein Ziel ist wirklich ein harter Wechsel - sonst 
beschäftige ich mich nur halb mit dem STM32 ;-)

von Heinz K. (heinz_k960)


Lesenswert?

W.S. schrieb:
> Das Zeug im Zipfile ist zwar für den STM32F103C8T6, aber zum einen ist
> vieles darin plattformunabhängig (läuft also auch auf z.B.
> Fujitsu-Controllern) und zum anderen sind die Peripheriecores bei den
> diversen STM32 oftmals ziemlich ähnlich, sodaß sich auch
> Lowlevel-Treiber ohne allzu große Hürden auf einen anderen Chip
> umschreiben lassen.

Danke für den Link! Das werde ich mir bei Gelegenheit zu Gemüte führen. 
Zu den Peripherie-Cores und der Ähnlichkeit kann ich nur sagen - es 
stimmt nur bedingt. Die Familien sind doch sehr unterschiedlich und man 
kommt um das Studium des Referenzmanuals nicht herum. Beispiel: ich habe 
das Buch von R. Jesse "STM32 ARM Mikrocontroller programmieren - das 
umfassende Handbuch" (keine Werbung!!). Hier wird ein STM32F446 
verwendet und die Bare Metal Beispiele muss man häufig auf den STM32L0x1 
umschreiben, da die Registernamen anders sind. ImPrinzip sind diese aber 
schon sehr ähnlich, da gebe ich Dir völlig Recht.

von W.S. (Gast)


Lesenswert?

Heinz K. schrieb:
> Sollte TX Interrupt nicht erst
> frigegeben werden, wenn etwas im TX Sendebuffer steht?

Ähem... naja. Also bei vielen Chips ist es so, daß der Interrupt 
statisch ist, also solange ansteht, bis entweder etwas zum Senden in den 
Sender geschrieben wurde oder bis man ihn sperrt. Das bedeutet, daß der 
Sendeinterrupt immer dann von der ISR gesperrt werden muß, wenn sie 
gerade nix zum Senden vorfindet. Und von der Routine, die ein Zeichen 
zum Senden entgegennimmt und in einen Ringpuffer stopft, wird der 
Interrupt freigegeben, damit die ISR loslegen oder ihn wieder sperren 
kann (wenn es nix zu senden gibt).

W.S.

von Heinz K. (heinz_k960)


Lesenswert?

Heinz K. schrieb:
> In der UART Initialisierungsroutine werden sowohl TX und RX Interrupt
> gleichzeitig aktiviert. Ist das gut so? Sollte TX Interrupt nicht erst
> frigegeben werden, wenn etwas im TX Sendebuffer steht?

Sorry Stefan, da habe ich Deinen Code auf Deiner Seite falsch gelesen.

// Enable transmitter, receiver and receive-interrupt of USART1
    USART1->CR1 = USART_CR1_UE + USART_CR1_TE + USART_CR1_RE + 
USART_CR1_RXNEIE;

Du schaltest NUR den RX Interrupt frei. TX benutzt keinen Interrupt und 
damit nur Polling, richtig? Ist das so die "gängige" Methode?

von Heinz K. (heinz_k960)


Lesenswert?

Stefan F. schrieb:
> http://stefanfrings.de/stm32/stm32l0.html#usart

Hallo Stefan, Dein Code gefällt mir! kurz und prägnant. Meine Fragen 
hierzu.

1) Du verwendet puts() statt printf(). Benötigt puts() weniger Speicher 
oder was ist der Hintergrund für die Verwendung von puts? Wenn ich das 
richtig verstanden habe, kennt puts() keine format specifiers.
In der Regel will man beim Loggen ja einen kleinen erklärenden Text und 
dann (Roh)Daten ausgeben - also eigentlich schon das was printf() 
bietet. Wäre eine Möglichkeit statt printf() die Kombination von 
sprintf() und puts() zu verwenden? Dann würde man erst einmal den String 
zusammensetzen und dann als String per puts() ausgeben.

By the way. mein Code wird bei Verwendung von printf() ca. 5 kB 
gegenüber der Nichtverwendung aufgebläht...

von STK500-Besitzer (Gast)


Lesenswert?

Heinz K. schrieb:
> By the way. mein Code wird bei Verwendung von printf() ca. 5 kB
> gegenüber der Nichtverwendung aufgebläht...

printf ist ja auch ziemlich mächtig. puts gibt doch nur Zeichen aus.
printf übersetzt auch (rationale) Zahlen in strings.

von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
> In der UART Initialisierungsroutine werden sowohl TX und RX Interrupt
> gleichzeitig aktiviert. Ist das gut so?

Falls du diese Zeilen meinst, schau nochmal genau hin:
1
// Enable transmitter, receiver and receive-interrupt of USART1
2
USART1->CR1 = USART_CR1_UE + USART_CR1_TE + USART_CR1_RE + USART_CR1_RXNEIE;

Das Senden läuft bei diesem Beispielprogramm wie gesagt ohne Puffer, 
also auch ohne Interrupt.

Mir ging es darum, dir ein einfaches verständliches Beispiel zu geben, 
wie man die _write() Funktion implementiert. Puffern ist ein davon 
unabhängiges Thema.

Heinz K. schrieb:
> Du verwendet puts() statt printf(). Benötigt puts() weniger Speicher
> oder was ist der Hintergrund für die Verwendung von puts?

Printf ist zum formatieren von Ausgaben gedacht. Puts ist für einfache 
Strings.

Zumindest der avr-gcc ist allerdings so schlau, ein printf("...\n") 
durch puts("...") zu ersetzen. Ob der arm-gcc auch so clever ist, weiß 
ich nicht.

> Wäre eine Möglichkeit statt printf() die Kombination von
> sprintf() und puts() zu verwenden?

Kannst du auch machen. Effizienter ist das am Ende aber vermutlich 
nicht, als gleich printf() zu verwenden.

> By the way. mein Code wird bei Verwendung von printf()
> ca. 5 kB gegenüber der Nichtverwendung aufgebläht...

Das steht auch auf meiner Seite: 
http://stefanfrings.de/stm32/stm32l0.html#gccopt
Die ganzen Formatier-Funktionen von printf bekommt man halt nicht 
umsonst. Und ja, ich weiß dass printf() beim AVR deutlich schlanker ist.

Beitrag #7323831 wurde von einem Moderator gelöscht.
von Heinz K. (heinz_k960)


Lesenswert?

Danke an alle für die Infos. Leider gibt mein printf auf dem Terminal 
nichts aus. Ich habe es so gemacht wie Stefan auf seiner Webseite 
implementiert hat:

// Redirect standard output to the serial port
int _write(int file, char *ptr, int len)
{
    for (int i=0; i<len; i++)
    {
        while(!(USART1->ISR & USART_ISR_TXE));
        USART1->TDR = *ptr++;
    }
    return len;
}

Hat jemand eine Idee woran es liegen kann?

von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
> Leider gibt mein printf auf dem Terminal nichts aus.
> Hat jemand eine Idee woran es liegen kann?

Funktioniert denn direkte (herkömmliche Ausgabe) ohne die 
C-Bibliotheksfunktionen zu verwenden?

Hast du die Zeile mit \n abgeschlossen? Das ist bei ARM wichtig, weil 
die Bibliothek Ausgaben bis zum Zeilenumbruch zurück hält.

Hast du Schnittstelle richtig initialisiert, insbesondere den 
Transmitter eingeschaltet und die Baudrate eingestellt?

Da du PLL verwendest: hast du die Flash Latency eingestellt?

Ich könnte noch 20 weitere Fragen stellen. Besser du zeigst mal deinen 
ganzen Code.

von Heinz K. (heinz_k960)


Lesenswert?

Stefan F. schrieb:
> Hast du die Zeile mit \n abgeschlossen? Das ist bei ARM wichtig, weil
> die Bibliothek Ausgaben bis zum Zeilenumbruch zurück hält.

Danke Stefan, das war die Lösung! Ich hatte das \n im printf vergessen. 
Dieses Verhalten war mir nicht bewusst. Der Unterschied zu Deiner 
Implementierung war, dass Du das printf("Hello World") in der 
Endlosschleife hattest und ich davor. Meine Endlosschleife war leer.
Interessant ist auch, dass OHNE abschliessendes \n die Code Size um gut 
1.5kByte grösser ist als mit dem \n.

Anbei der jetzt so funktionierende (Minimal-)Code:

int main(void)
{
    SystemCoreClockInit(); // PLL using HSE with 32MHz clock out
    GpioInit(); // GPIO configuration
    Usart2Init();
    // GPIOC->ODR |= GPIO_ODR_OD13; // GPIOC: PC13 -> 1
    // GPIOC->BSRR = GPIO_BSRR_BS_13; // GPIOC: PC13 -> 1; w/o 
read-modify-write
    printf("Hello World\n");
    LED_PC13_ON;

    while(1)
    {

    }
}


ISR (IRQ Handler) und _write Funktion sind identisch zu Deiner 
Implementierung, ausser dass ich USART1 durch USART2 ersetzt habe.

von Stefan F. (Gast)


Lesenswert?

Stefan F. schrieb:
>> Hast du die Zeile mit \n abgeschlossen? Das ist bei ARM wichtig, weil
>> die Bibliothek Ausgaben bis zum Zeilenumbruch zurück hält.

Heinz K. schrieb:
> Danke Stefan, das war die Lösung!

Das steht übrigens auch auf der Webseite. Am besten liest du sie mal 
komplett durch. Ich habe da alle Knackpunkte aufgeschrieben, auf die ich 
beim Wechsel von AVR nach STM32 gestoßen war.

von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
> Interessant ist auch, dass OHNE abschliessendes \n die Code Size um gut
> 1.5kByte grösser ist als mit dem \n.

Wohl deswegen:

Stefan F. schrieb:
> Zumindest der avr-gcc ist allerdings so schlau, ein printf("...\n")
> durch puts("...") zu ersetzen. Ob der arm-gcc auch so clever ist, weiß
> ich nicht.

Heinz K. schrieb:
> dass Du das printf("Hello World") in der Endlosschleife hattest

Wo hast du das gesehen? Ich würde das dann korrigieren.

von Heinz K. (heinz_k960)


Lesenswert?

Hi Stefan, ich will so höflich sein, Deine anderen Fragen auch zu 
beantworten.

Stefan F. schrieb:
> Funktioniert denn direkte (herkömmliche Ausgabe) ohne die
> C-Bibliotheksfunktionen zu verwenden?

Ja, funzt

> Hast du die Zeile mit \n abgeschlossen? Das ist bei ARM wichtig, weil
> die Bibliothek Ausgaben bis zum Zeilenumbruch zurück hält.

siehe Antwort von gerade eben. (nein, hatte ich nicht)

> Hast du Schnittstelle richtig initialisiert, insbesondere den
> Transmitter eingeschaltet und die Baudrate eingestellt?

Ja, eingestellt und getestet mit meinem Minimal-Code

>
> Da du PLL verwendest: hast du die Flash Latency eingestellt?

Ja, auch das habe ich korrekt gemacht.

von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
> ich will so höflich sein, Deine anderen Fragen auch zu
> beantworten.

Ach, ist doch gar nicht mehr nötig, nachdem der Fehler gefunden wurde.

von Heinz K. (heinz_k960)


Lesenswert?

Stefan F. schrieb:
> Wo hast du das gesehen? Ich würde das dann korrigieren.

http://stefanfrings.de/stm32/stm32l0.html#usart


    while (1)
    {
        // LED on
        WRITE_REG(GPIOA->BSRR, GPIO_BSRR_BS_5);
        delay(500);

        puts("Hello");

        // LED off
        WRITE_REG(GPIOA->BSRR, GPIO_BSRR_BR_5);
        delay(500);
    }

Du schreibst selbst auch über dem Quelltext: "Das folgende 
Beispielprogramm sendet regemäßig "Hello World!" aus." -> Beschreibung 
passt zum Code für mich.

von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
>         puts("Hello");

Da habe ich puts() verwendet, nicht printf(). Puts hängt immer einen 
Zeilenumbruch an die Ausgabe an.

Heinz K. schrieb:
> Du schreibst selbst auch über dem Quelltext: "Das folgende
> Beispielprogramm sendet regemäßig "Hello World!" aus." -> Beschreibung
> passt zum Code für mich.

Da sehe ich jetzt aber einen Fehler. Die Ausgabe lautet "Hello", nicht 
"Hello World!".

von Heinz K. (heinz_k960)


Lesenswert?

Peter schrieb:
> Was den UART bzw. die Interrupts betrifft ist es keine gute Idee, darin
> die Daten auszuwerten. Du sollstest einfach einen globulen Byte Array
> nutzen und diesen mit den empfangenen Daten füllen (z.B. uartDataRx[i] =
> ...). Dann kannst du noch ein (globales ) Flag sezten, z.B. uartReceived
> = true.

Das finde ich eine gute Idee. Frage meinerseits: wie gross dimensioniert 
man so ein Array? Hast Du Code-Beispiele für mich (oder einen Link) für 
eine entsprechende Implementierung? Mir geht es nur um den Ansatz und 
die grobe Struktur.

Beim Thema Buffer bin ich ehrlich gesagt auch Newbie. Das Prinzip des 
Ring-Buffers habe ich verstanden, habe aber im Moment keine Idee, das 
kompakt in C umzusetzen ...

von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
> wie gross dimensioniert man so ein Array

Das kommt ganz auf deine Anwendung an.

Beim Empfangen musst du dir überlegen, wie viele Bytes sich maximal im 
Puffer aufstauen, während dein µC gerade nicht empfangsbereit ist weil 
er anderweitig beschäftigt ist. Das hängt auch von der Quelle der Daten 
ab.

Beim Senden musst du dir überlegen, ob du überhaupt puffern willst. Wenn 
ja, dann würde ich mir überlegen, wie viele Daten dein Programm quasi am 
Stück erzeugt, die dann aber später nach und nach gesendet werden.

Viele Programme warten nach dem Senden, bis das letzte Byte raus ist, 
bevor sie (mit was auch immer) weiter machen. In diesem Fall mach ein 
Puffer die Sache bloß unnötig kompliziert.

In anderen Programm kann ein Sendepuffer aber hilfreich sein, damit dein 
Programm nach dem Erzeugen einer Ausgabe sofort weiter arbeiten kann, 
ohne auf die relativ langsame serielle Kommunikation warten zu müssen.

von blubdidup (Gast)


Lesenswert?

> Programmiert wird bare metal

Debugausgaben sollte man gar nicht puffern oder dabei Funktionen
benutzen die das doch tun. Warum das so ist, da solltest du
selber drauf kommen.

Debugausgaben sollten ohne Interrupt auskommen.
Global den Interrupt sperren ist dann natuerlich noetig.
Es kann lohnenswert sein, Debugausgaben mit einem Soft-UART
auszugeben. Der kann u.U. ein mehrfaches der Geschwindigkeit
eines HW-UART erreichen. Warum das fuers Debugging gut ist,
auch darauf solltest du selber kommen.

printf hat nun gar nichts von "bare metal".

Mehr als Zeichen, (kurze) Zeichenketten, und Hex und Dezimal in
Byte, 16/32 bit-Word braucht man da eigentlich nicht.


Ich kannte mal jemanden, der versuchte eine *zeitkritische"
Software mit solchem "printf"-Germurkse zum Laufen zu bringen.
Dabei haben schon die Debugausgaben sein Programm zeitlich
so ruiniert, dass das nicht mehr funktionierte.

von W.S. (Gast)


Lesenswert?

STK500-Besitzer schrieb:
> printf ist ja auch ziemlich mächtig.

Sagen wir's mal direkt: mit printf wird Übersetzungsarbeit vom 
Übersetzungszeitpunkt in die Laufzeit des µC verlagert. Das printf ist 
im Wesentlichen ein Textinterpreter, der zum Übersetzen des 
Formatstrings gebraucht wird und da er nicht ahnen kann, was der 
jeweilige Programmierer dort hineingeschrieben hat, muß er obendrein 
auch noch alle möglichen (und unmöglichen) Ausgabe-Konverter vorhalten. 
Das macht ihn so dick. Aber als Programmierer der Firmware weiß man 
eigentlich, was man an Ausgabekonvertern braucht und kann diese dann 
auch selbst aufrufen, ohne dafür einen Textinterpreter zu benötigen.

W.S.

von Heinz K. (heinz_k960)


Lesenswert?

blubdidup schrieb:
> Es kann lohnenswert sein, Debugausgaben mit einem Soft-UART
> auszugeben. Der kann u.U. ein mehrfaches der Geschwindigkeit
> eines HW-UART erreichen.

Ok, gibt es dafür bereits Beispiel-Implementierungen? So ganz ist mir 
auch nicht klar, warum eine Soft-UART viel schneller ist als eine HW 
UART sein sollte.

Ich stimme zu, das printf nicht unbedingt zu bare metal passt. Wie 
bewerkstelligt man denn nun konkret die Ausgaben auf die serielle 
Schnittstelle zu Debug-Zwecken?

Was mich etwas irritiert ist, dass es für ein solches Standardthema (Log 
Ausgaben via UART) scheinbar keine vernünftige Lösung gibt.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Heinz K. schrieb:
> Was mich etwas irritiert ist, dass es für ein solches Standardthema (Log
> Ausgaben via UART) scheinbar keine vernünftige Lösung gibt.

Weil da jeder eine andere Vorstellung davon hat, wie man das richtig 
macht.

von W.S. (Gast)


Lesenswert?

Heinz K. schrieb:
> Ok, gibt es dafür bereits Beispiel-Implementierungen? So ganz ist mir
> auch nicht klar, warum eine Soft-UART viel schneller ist als eine HW
> UART sein sollte.

Löse dich einfach mal von der Vorstellung, daß man für alles, was 
irgendwie 'debug' genannt werden kann, dieselbe Signalisierung und dann 
noch per genormten Parametern machen kann. Ich hab selbe auch schon 
Einpin-Ausgaben gemacht, die dazu da sind, per Oszi angeschaut zu 
werden. Sowas ist sinnvoll an Stellen, wo noch kein definierter Takt 
vorliegt, also bei der Grundkonfiguration mittendrin, wo die PLL noch 
nicht fertig meldet. Ich hab auch schon eine zweistufige Debug-Ausgabe 
gemacht, zum Beispiel in der ISR des USB. Zuerst alles in einen 
RAM-Puffer und später dann von dort aus gemütlich per UART auf die Reise 
geschickt. Es kommt eben immer darauf an, was man wo machen will.


> Ich stimme zu, das printf nicht unbedingt zu bare metal passt. Wie
> bewerkstelligt man denn nun konkret die Ausgaben auf die serielle
> Schnittstelle zu Debug-Zwecken?

Es sind ja nicht nur Debug-Zwecke, sondern die Kommunikation insgesamt. 
Dazu sollte man sich eine vernünftige für den µC geeignete Schnittstelle 
zwischen Lowlevel-Treiber und den anderen Teilen der Firmware machen. 
Sowas wie printf gehört nicht dazu. Ich halte es so, daß der Treiber 
sowas wie char_out(char C) anbietet, wo dann je nach Bedarf ein 
String_Out(...) oder ein Long_Out(...) oder ein Float_Out(...) aufsetzen 
können. Eben je nachdem, was man tatsächlich braucht.


> Was mich etwas irritiert ist, dass es für ein solches Standardthema (Log
> Ausgaben via UART) scheinbar keine vernünftige Lösung gibt.

Es gibt (s.o.) keine Standard-Methode für's Debuggen und folglich auch 
keine Standard-Mitteilungs-Methode. Und für ein langsames (da durch den 
Menschen am Terminalprogramm auf dem PC zu beobachten) Verfahren reichen 
die bereits genannten auf Char_Out(...) aufsetzenden Methoden völlig 
aus. Aber die Programmier-Kinder, die nix anderes als printf gezeigt 
bekommen haben und selbst keine Lust oder keine Fähigkeit haben, sich 
etwas effizienteres selbst auszudenken oder gar von der Hochmut-Kuh 
gebissen sind (neulich: "ich programmiere NICHT auf Registerebene"), 
schielen eher darauf, als Ersatz für printf irgendwas aus der C++ Kiste 
nehmen zu wollen. Ohne auch nur einen einzigen Gedanken darauf zu 
verwenden, was sie sich damit einbrocken. Hauptsache, es gibt da eine 
fertige Lib für.

W.S.

von Heinz K. (heinz_k960)


Lesenswert?

W.S. schrieb:
> Ich hab selbe auch schon
> Einpin-Ausgaben gemacht, die dazu da sind, per Oszi angeschaut zu
> werden. Sowas ist sinnvoll an Stellen, wo noch kein definierter Takt
> vorliegt, also bei der Grundkonfiguration mittendrin, wo die PLL noch
> nicht fertig meldet.

Den ersten Teil kenne ich als HW Entwickler, das ist absolut Low-Level 
und geht, ist aber mühsam.

> Ich hab auch schon eine zweistufige Debug-Ausgabe
> gemacht, zum Beispiel in der ISR des USB. Zuerst alles in einen
> RAM-Puffer und später dann von dort aus gemütlich per UART auf die Reise
> geschickt.

Das finde ich eine gute Idee. Dazu fehlt mir allerdings die Erfahrung so 
etwas zu implementieren.

> Es sind ja nicht nur Debug-Zwecke, sondern die Kommunikation insgesamt.
> Dazu sollte man sich eine vernünftige für den µC geeignete Schnittstelle
> zwischen Lowlevel-Treiber und den anderen Teilen der Firmware machen.
> Sowas wie printf gehört nicht dazu. Ich halte es so, daß der Treiber
> sowas wie char_out(char C) anbietet, wo dann je nach Bedarf ein
> String_Out(...) oder ein Long_Out(...) oder ein Float_Out(...) aufsetzen
> können. Eben je nachdem, was man tatsächlich braucht.

Mit dem Ansatz gehe ich durchaus mit. printf() hat halt den "Charme", 
das man alles mit einer Funktion erschlägt. Ich habe mir auch schon 
andere Tools / Instrumentierungen angeschaut, wie z.B. Trice 
(https://github.com/rokath/trice). Leider habe ich das nicht zum Laufen 
bekommen ...

Ein letzter Aspekt: Debug via UART ist für mich Mittel zum Zweck - ich 
will z.B. ADC RAW Daten sehen. Ich möchte da ungern ein eigenes Projekt 
daraus machen.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Wir haben einen Artikel mit Codebeispielen zur Implementierung von 
Ring-Puffern
https://www.mikrocontroller.net/articles/FIFO#2n-Ringpuffer_-_die_schnellste_L%C3%B6sung

von blubdidup (Gast)


Lesenswert?

> Ok, gibt es dafür bereits Beispiel-Implementierungen? So ganz ist mir
> auch nicht klar, warum eine Soft-UART viel schneller ist als eine HW
> UART sein sollte.

Weil ein HW-UART das Signal gerne N-fach ueberabtasten will.
Das gilt zwar eigentlich nur fuer den RX-Teil, trifft aber
den TX des UARTs genauso.
Die Senderoutine eines Soft-UART in Assembler(!) zu schreiben,
ist eine gute Fingeruebung. Bereits 8-bitter schaffen bei
einem internen CPU-Takt von 2 MHz Geschwindigkeiten von 230 kbit.
Wer will, kann bei einem Soft-UART auch gleich Fast-IRDA
auf eine (IR-)LED ausgeben. Da ist dann Potentialtrennung gleich
inklusiv.
Sicher gibt es "Beispiel-Implementierungn" die aber fuer
diesen Zweck genau alle nicht taugen, weil sie interruptgesteuert
sind. Und natuerlich viel zu langsam.
Wenn man sich nicht an die Standardbaudraten halten will,
kann den seriellen Text ja auch im LA mitlesen.
Viele serielle USB-Adapter gestatten auch die Einstellung
arbitraerer Baudraten. Wenn es "ueberall" funtionieren soll,
sind die Standardbaudraten schon im Vorteil.

Ueber
> Debugausgaben sollte man gar nicht puffern
und
> Debugausgaben sollten ohne Interrupt auskommen.
scheinst du ja auch noch nicht nachgedacht zu haben.
Ein Debugprint ist auch noch etwas anderes als ein Debuglog.

> Ich möchte da ungern ein eigenes Projekt daraus machen.

Ein anderer schreibt dazu immer: "Keine Arme, keine Kekse."

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.