Forum: Mikrocontroller und Digitale Elektronik 7 Segment Multiplexing und USART Kommunikation(Baudraten)


von Felix N. (felix_n888)


Lesenswert?

Guten Abend,

Ich habe mal eine Frage bzgl. des Bits U2X im Usart Register beim 328P. 
Ich habe für mein Flugsimulator ein VHF Radio gebaut welches mir 
ermöglicht die Frequenzen für die Flugsicherung einzudrehen und dann 
über USART zum PC zu senden, wo dann eine Software mit mein Flug 
Simulator interagiert und die Frequenz im Simulator eindreht.


Ich benutze für das darstellen der Frequenzen 7 Segment Anzeigen. 5 
Stück pro Frequenz also insgesamt 10. Diese tute ich per 74HC595 Shift 
Register Multiplexen um Pins zu sparen.


Angesteuert werden die 7 Segment Anzeigen mit 100Hz, so das kein 
Flackern zu erkennen ist. Jede 1ms setze ich ein Flag in einer ISR um im 
Hauptprogramm dann die Segmente anzusteuern. Das ganze funktioniert auch 
ohne Probleme.

Bis dann die Kommunikation mit dem USART kam. Ich arbeite normaler weiße 
nur mit 9600 Baud Rate. Da aber das Senden dann so lange gedauert hat 
das der Controller sich dort ein bisschen verzögert hat sich auch die 
ISR verzögert was dazu führt das die Multiplex Frequenz auf 15 Hz abfiel 
und ein Flackern erkennbar war.


Ich habe dann bei mir in Datenblatt geschaut unter "Table 20-7" UBBRn 
Settings für fosc=16.00000 MHz. Da nur relativ hohe Baudraten in frage 
kamen um den Sendevorgang vorher zu beenden bevor die ISR ins Stocken 
gerät.


Habe ich mir auf 230.4k, 250k, 0.5M Baudraten begrenzt. 230.4k hat zu 
viele Fehler bei 16 MHz. Bei 250k kann man immer noch ein flackern 
erkennen. Bei 0.5M ist das flackern weg. In meiner Initialisierung des 
Usart ist eine abfrage mit USE_2X drin.

Wenn ich das richtig verstanden habe wird dort die Teiler von 16 auf 8 
gesetzt und somit die Baudrate verdoppelt? Also bei 250k Baudraten wären 
es dann 500k?
Was wäre dann besser 500K ohne U2X oder 250K mit U2X oder macht das am 
Ende immer noch kein Unterschied weil die gleiche Menge an Daten mit der 
gleichen Geschwindigkeit durchs Kabel jagen?

Ich habe ein bisschen Sorgen das, wenn ich 0,5M Baudrate dauerhaft nehme 
das "Daten-Pakete" auf der 45cm langen Kabelstrecke zum USB Wandler 
aufgrund der hohen Baudrate verloren gehen, oder kann man das getrost 
vergessen, den laut Datenblatt sind es ja immerhin 0% Fehler @ 16 MHz.

Oder sollte man lieber den 328P über ein anderes Interface mit einem 
anderen Prozessor kommunizieren lassen(bzp. Attiny) der dann als USART 
dient? Was aber wiederum auch Verschwendung wäre, weil der 328P das auch 
selbst erledigen könnte.

Mfg

von H.Joachim S. (crazyhorse)


Lesenswert?

Sende und empfange via Interrupt und all deine Probleme sind vergessen.

von Felix N. (felix_n888)


Lesenswert?

H.Joachim S. schrieb:
> Sende und empfange via Interrupt und all deine Probleme sind
> vergessen.

Hallo Joachim!

Im UCRS0B Register habe ich im Moment nur das RXCIE0 Bit gesetzt. Alle 
Daten die vom PC aus an meinen Controller gesendet werden in der USART 
RX ISR wieder zu einem String zusammengesetzt und dann von einer 
Funktion weiterverarbeitet bzw. darauf entsprechend reagiert.

Ich weiß das es auch das TXCIE0 Bit für das UCRS0B Register existiert. 
Wenn ich das richtig verstanden habe wird das USART TX vect nachdem 
erfolgreich inkl. Stopp Bit erfolgreich gesendet wurde, ausgelöst.

Also bringt Sie mir nix? Oder verstehe ich da was falsch?


Meine Funktion die ich im Moment fürs Senden nutze sieht so aus:
1
void sendToSerial(char c[]) {
2
  for (int i = 0; i < strlen(c); i++){
3
    if(c[i] != 0) {
4
      while (( UCSR0A & (1<<UDRE0))  == 0){};
5
      UDR0 = c[i];
6
    }else{
7
      break;
8
    }
9
  }
10
}

Mfg

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Felix N. schrieb:
> Meine Funktion die ich im Moment fürs Senden nutze sieht so aus
Diese Zeilen enthalten nicht das Problem. Denn prinzipiell ist diese 
Routine unterbrechbar.

> Also bringt Sie mir nix? Oder verstehe ich da was falsch?
Wenn Interruptquellen nichts bringen würden, dann hätte man sie nicht 
eingebaut.

Der Trick ist dann letztendlich, eine Interruptroutine so kurz wie 
irgend möglich zu halten. Die Empfangsroutine empfängt nur, sie wertet 
aber nicht aus.
Eine Anzeigeroutine zeigt nur das an, was in der Hauptschleife berechnet 
und schon passend "vorverdaut" und zurecht gerechnet wurde.
Die ganze Arbeit wird letztlich in der Hauptschleife erledigt. Und die 
wird aus diesem Grund auch nicht mit einem "while" angehalten. Sondern 
es wird nur abgefragt, ob das Senderegister frei ist. Falls ja, dann 
wird das nächste Zeichen gesendet, falls nein, wird mit dem 
Hauptprogramm weitergemacht. Irgendwas gibt's immer zu tun.

Aber häng doch einfach mal deine C Dateien hier an...

von Felix N. (felix_n888)


Angehängte Dateien:

Lesenswert?

Lothar M. schrieb:
> Diese Zeilen enthalten nicht das Problem. Denn prinzipiell ist diese
> Routine unterbrechbar.

Das würde ich nicht so sagen. Denn der Code enthält ja eine while 
abfrage:
1
while (( UCSR0A & (1<<UDRE0))  == 0){};
in der er wartet bis er neue Daten senden kann. Und dort wird sich der 
Controller dann verzögern, oder nicht?

Lothar M. schrieb:
> Wenn Interruptquellen nichts bringen würden, dann hätte man sie nicht
> eingebaut.

Ja aber laut Datenblatt wird USART_TX_vect erst ausgelöst wenn alle 
Daten inkl. das Stopp Bit gesendet wurden. Dann muss ich ja trotzdem die 
Daten mit der while Schleife senden und bekomme so gesehen nur das "ok" 
später in der ISR.

Lothar M. schrieb:
> Der Trick ist dann letztendlich, eine Interruptroutine so kurz wie
> irgend möglich zu halten.

Hab ich schon öfters gehört. Ich versuche dort nur mit Flags zu 
arbeiten, und Integer zu inkrementieren(Präzises Timing).

Lothar M. schrieb:
> Aber häng doch einfach mal deine C Dateien hier an...

Klar mache ich gerne.(Und ja ich weiß ich habe viele Globale Variablen, 
die brauche ich später noch, mein Programm ist noch nicht mal zur Hälfte 
fertig)

RotaryEncoder.c/h kommen hier aus dem Forum, sind nur so abgeändert das 
ich zwei Encoders ansteuern kann. ADC.c/h ist nur zum init des ADCs und 
zum auslesen. Usart.c/h erklärt sich glaubig selbst :)

Mfg

von Stefan F. (Gast)


Lesenswert?

Ich stimme H.Joachim S. zu.
Du brauchst einen Sende-Puffer, der Interruptgesteuert abgearbeitet 
wird.

von chris (Gast)


Lesenswert?

Felix N. schrieb:
> Dann muss ich ja trotzdem die
> Daten mit der while Schleife senden und bekomme so gesehen nur das "ok"
> später in der ISR.

Nein, du schreibst alle Daten in einen Puffer und die ISR sendet im 
Hintergrund Byte für Byte raus.
Der Puffer sollte wenn möglich so groß sein, dass deine längste zu 
sendende Nachricht reinpasst.
Weit verbreitet und gut ist die UART-Lib von Peter Fleury:
http://homepage.hispeed.ch/peterfleury/avr-software.html


lg
Chris

von chris (Gast)


Lesenswert?

Felix N. schrieb:
> Diese tute ich per 74HC595 Shift
> Register Multiplexen um Pins zu sparen.

Dazu frage ich mich allerdings: Wenn du schon Schieberegister einsetzt, 
warum dann nicht gleich für jede 7-Segmentanzeige ein separates? Dann 
musst du nicht multiplexen, und sparst außerdem noch mehr Pins, da du 
die Schieberegister kaskadieren kannst.
Die Anzeigehelligkeit kannst du dann auch noch komfortabel mit PWM am 
OE-Pin der 595er steuern.

von Stefan F. (Gast)


Lesenswert?

Wenn du ein Schieberegister aus der CD40xxBE Reihe verwendest und sie 
mit maximal 5V betreibst, kannst du sogar auf die Vorwiderstände 
verzichten, wei die IC's den Strom schon passend begrenzen.

von Felix N. (felix_n888)


Lesenswert?

chris schrieb:
> Wenn du schon Schieberegister einsetzt,
> warum dann nicht gleich für jede 7-Segmentanzeige ein separates?

Hi. Wenn ich für jedes Segment ein Schieberegister nehmen würde bräuchte 
ich 10 Stücke dafür, das wäre wiederum viel Arbeit die auf einer Platine 
platzsparend unterzubringen.

Deswegen nutze ich 2x 74HC595 und 2x Pins vom PortC Register. Die 
Ausgänge des ersten Registers liegen über 510 Ohm Widerstände an den 
Anode der Segment LED an. Q0 bis Q6 sind dabei für die einzeln Segmente 
und Q7 ist für den Dot Punkt.

Das zweite Schieberegister bekommt die Daten vom Q7S vom ersten. Und 
steuert die Transistoren an. Da mir am zweiten Register aber 2 Pins 
fehlen und ich noch ein paar frei hatte an meinem Mikrocontroller nutze 
ich diese einfach so spare ich mir ein dritten 74HC595.

chris schrieb:
> Die Anzeigehelligkeit kannst du dann auch noch komfortabel mit PWM am
> OE-Pin der 595er steuern

Bin mir gerade nicht ganz sicher ob das geht. Sollte aber am zweiten 
Register möglich sein, so das dann der Transistor als Spannungsregler 
wirkt.

chris schrieb:
> Nein, du schreibst alle Daten in einen Puffer und die ISR sendet im
> Hintergrund Byte für Byte raus.

Könnte das was mit dem USART UDRE ISR zutun haben? Wenn ich das richtig 
lese ist das das Datenregister des USART wenn ich dort Daten rein 
schiebe und dann das Interrupt Bit für UDRE setze müsste er ja dort rein 
gehen und kann könnte ich die Daten doch mit UDR0 = ... rüber senden?

mfg

von Falk B. (falk)


Lesenswert?

Felix N. schrieb:

> Bis dann die Kommunikation mit dem USART kam. Ich arbeite normaler weiße
> nur mit 9600 Baud Rate. Da aber das Senden dann so lange gedauert

Man darf eben NICHT auf das Ende der Übertragung.

> hat
> das der Controller sich dort ein bisschen verzögert hat sich auch die
> ISR verzögert was dazu führt das die Multiplex Frequenz auf 15 Hz abfiel
> und ein Flackern erkennbar war.

Konzeptfehler.

> Ich habe dann bei mir in Datenblatt geschaut unter "Table 20-7" UBBRn
> Settings für fosc=16.00000 MHz. Da nur relativ hohe Baudraten in frage
> kamen um den Sendevorgang vorher zu beenden bevor die ISR ins Stocken
> gerät.

Konzeptfehler.

> Oder sollte man lieber den 328P über ein anderes Interface mit einem
> anderen Prozessor kommunizieren lassen(bzp. Attiny)

Nein. du mußt dich mit dem Thema Multitasking befassen, dann klappt 
das auch.

von Stefan F. (Gast)


Lesenswert?

Felix N. schrieb:
> Könnte das was mit dem USART UDRE ISR zutun haben? Wenn ich das richtig
> lese ist das das Datenregister des USART wenn ich dort Daten rein
> schiebe und dann das Interrupt Bit für UDRE setze müsste er ja dort rein
> gehen und kann könnte ich die Daten doch mit UDR0 = ... rüber senden?

Ich weiss nicht, ob man auf diese Art einen Interrupt auslösen kann. 
Normalerweise macht man das so:

Wenn das UDRE leer ist, schreibt man das erste Byte hinein und den Rest 
in den Puffer. Ansonsten kommt alles in den Puffer, welcher von der ISR 
abgearbeitet wird.

von Karl M. (Gast)


Lesenswert?

Hallo TO,

ich verwende die von Peter Dannegger (peda) mal veröffentliche Routine, 
als Vorlage für weitere Implementierungen. Auch außerhalb von C.

Beitrag "AVR-GCC: UART mit FIFO"

von Felix N. (felix_n888)


Lesenswert?

Falk B. schrieb:
> Man darf eben NICHT auf das Ende der Übertragung.

Weil der Controller auf das Ende Wartet, im Moment wie ich es habe.

Falk B. schrieb:
> Nein. du mußt dich mit dem Thema Multitasking befassen, dann klappt
> das auch.

Ich habe mir den Betrag mal durchgelesen, auf Delays aus der Delay.h 
verzichte ich ja schon komplett, und wenn ich doch ein Delay brauche 
löse ich das über ein ISR Timer Overflow der zb. alle 1ms sich erhöht 
und ein uin16_t hoch zählt und dann halt was auslöst ... Im Moment ist 
nur ein delay aus der delay.h enthalten das ist aber nur zum testen und 
wird wieder entfernt.


Wie oben bereits geschrieben von Joachim S, kann man das ja per 
Interrupt beheben das Problem ich bin hier auf diesen Artikel gestoßen: 
https://www.mikrocontroller.net/articles/Interrupt#UART_mit_Interrupts

Da erklärt sich auch wie ich mir das vorgestellt habe mit dem UDRE ISR 
Event.

Das werde ich nachher mal ausprobieren.

Mal was anderes was mir so aufgefallen ist:

Warum müssen eigentlich manche Wert die in einer ISR vorkommen vom Typ 
"static" sein? Zb:
1
static uint8_t uart_rx_counter = 0;
Eben habe ich ein paar variablen erst mit dem Wort "static" im 
Variablennamen erstellt und habe dann später mit der Such und 
Ersetzenfunktion der IDE das "static" wieder entfernt. Dadurch hat er 
natürlich auch das static vor dem uart_rx_counter entfernt. Nachdem das 
static weg war funktionierte die Kommunikation beim Empfangen von Daten 
vom PC nicht mehr richtig. Erst wieder als ich das static wieder vor das 
uint8_t schreibe. Wieso?

Mfg

von Stefan F. (Gast)


Lesenswert?

static Variablen innerhalb einer Funktion verlieren ihren Wert zwischen 
zwei Funktionsaufrufen nicht.

von Karl M. (Gast)


Lesenswert?

Hallo,

das sind doch C Grundlagen!

static können Variable oder Funktionen sein.

Bei static innerhalb von Funktionen wird der Speicherplatz einer 
Variable nicht auf dem Stack angelegt und anschließen - bei beenden der 
Funktion - wieder freigegeben und somit das Datum ungültig/ gelöscht.

Man kann also Daten über Funktionsaufrufe hinweg immer wieder nutzen, 
hier der Index auf einen Speicherbereich (Array).

 Felix N. schrieb:
> Warum müssen eigentlich manche Wert die in einer ISR vorkommen vom Typ
> "static" sein? Zb:

von Felix N. (felix_n888)


Angehängte Dateien:

Lesenswert?

Karl M. schrieb:
> Bei static innerhalb von Funktionen wird der Speicherplatz einer
> Variable nicht auf dem Stack angelegt und anschließen - bei beenden der
> Funktion - wieder freigegeben und somit das Datum ungültig/ gelöscht.

Ah ja stimmt ohne Static wäre die Variable in der Funktion sinnlos weil 
sie jedesmal beim neuen Durchgang wieder mit 0 neu initialisiert werden 
würde. Danke für die Erklärung!

Felix N. schrieb:
> Das werde ich nachher mal ausprobieren.

Ich habe jetzt mal alle "sendToSerial" durch "sendToISR" ersetzt, wie in 
dem Artikel beschrieben, werden die Daten nun über die ISR(UDRE) zum PC 
gesendet, das klappt schon mal wunderbar! Empfangen wird auch über eine 
ISR also sollte der Kommunikation zwischen PC - AVR ohne Störungen 
laufen.

Eins ist mir aufgefallen wenn ich in der Main Funktion ohne Delay 
dazwischen dauerhaft Sende(hier die Spannung die der ADC Misst um die 
Knöpfe zu erkennen) dann fällt die Multiplex Frequenz von 100Hz auf 
circa. 96-98 Hz ab also eigentlich kaum ein Unterschied.

Wenn man sich aber die Multiplexen Anzeigen ganz genau anschaut, sieht 
man das ein leichtes flackern zu erkennen ist. Bzw. wenn man sich an 
einen Transistor dran klemmt mit dem Oszilloskop dann sieht man zwar das 
es kein so schönes Rechteck ist aber das es während des schnellen Senden 
an Daten leicht ins Schwanken nach rechtes und linkes kommt(+/-3Hz).

Müsste ich in diesem Fall die Baudrate erhöhen, oder ist so dauerhaftes 
Senden eh er Problematisch?

Mfg

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Wenn du dauerhaft schneller sendest, als es die serielle Schnittstelle 
zulässt, ist irgendwann der Puffer voll und dann muss doch gewartet 
werden. Hast du das bedacht?

Falls das nicht die Problemursache ist, musst du herausfinden, welchen 
Programmteil du optimieren kannst, damit deine Hauptschleife innerhalb 
der gewünschten Intervalle ausgeführt werden kann.

Wenn ich mir deine bisherigen Beiträge so anschaue würde ich Dir raten, 
das Debugging und Profiling zu üben. In diesem Fall geht es um die 
Frage, welcher Programmteil wie viel Zeit benötigt und warum das so ist.

Du fragst uns und bekommst Hilfe. besser wird es aber sein, wenn du 
weniger auf unsere Erfahrungswerte aufbaust, sondern es selbst durch 
Messungen heraus findest. Wir kann man das messbar machen? Überlege Dir 
das.

Kleiner Tipp: Ein Oszilloskop oder ein Logic Analyzer kann dabei serh 
hilfreich sein. Die billigen Produkte unter 50€ sind hier immer noch 
hilfreicher, als gar keins zu haben.

von Peter D. (peda)


Lesenswert?

Felix N. schrieb:
> Jede 1ms setze ich ein Flag in einer ISR um im
> Hauptprogramm dann die Segmente anzusteuern. Das ganze funktioniert auch
> ohne Probleme.

Genau das schreit geradezu nach Problemen und Du hast sie ja schon. 
Multiplexen macht man immer im Timerinerrupt. Du legst ein 10 Byte Array 
an, in dem speicherst Du die 10 Digitmuster und der Timerinterrupt gibt 
sie aus.

Felix N. schrieb:
> 230.4k hat zu viele Fehler bei 16 MHz.

Na dann nimm doch einfach nen 14,7456MHz Quarz.
Für alle anderen Sachen ist der exakte Wert vollkommen egal. Die 
Frequenzberechnungen macht man eh in float, d.h. Kommazahlen sind 
erlaubt.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Felix N. schrieb:
> Eins ist mir aufgefallen wenn ich in der Main Funktion ohne Delay
> dazwischen dauerhaft Sende(hier die Spannung die der ADC Misst um die
> Knöpfe zu erkennen) dann fällt die Multiplex Frequenz von 100Hz auf
> circa. 96-98 Hz ab also eigentlich kaum ein Unterschied.

> Müsste ich in diesem Fall die Baudrate erhöhen, oder ist so dauerhaftes
> Senden eh er Problematisch?

 Nein.
 100Hz bedeutet alle 10ms ein Refresh.
 Das sind bei 16MHz 160.000 (Hundertsechzig Tausend) Instruktionen
 dazwischen.

 Wenn dir diese 160.000 Instruktionen nicht reichen, dann machst du
 irgendetwas falsch.

von Peter D. (peda)


Lesenswert?

Marc V. schrieb:
> Wenn dir diese 160.000 Instruktionen nicht reichen, dann machst du
>  irgendetwas falsch.

Projekte haben es an sich, daß sie wachsen und wachsen. Man sollte daher 
vorausschauend programmieren. Zeitkritische Sachen haben in der Mainloop 
nichts verloren.

von m.n. (Gast)


Lesenswert?

Felix N. schrieb:
> Hi. Wenn ich für jedes Segment ein Schieberegister nehmen würde bräuchte
> ich 10 Stücke dafür, das wäre wiederum viel Arbeit die auf einer Platine
> platzsparend unterzubringen.

Ab 13 mm Ziffernhöhe kann man beide Seiten effektiv nutzen. Ob es dann 
drei Stellen oder 20 Stellen sind - man kann einfach kaskadieren.
Beitrag "7-Segm.-LED-Anzeige, 6-stellig, statische Ansteuerung mit (74HC)4094"

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.