Forum: Mikrocontroller und Digitale Elektronik Architektur für "background" Uart-Transmission


von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Hallo,

ich bin an einem Flaschenhals angekommen:
Ich verwende einen ATSAM4S mit ST Bluetooth-Modul über UART im SPP 
Modus.
Die mögliche Baudrate ist in meinem Fall (auch wegen Schnittstelle zum 
PC) 115.2kBaud.

Nun ist es so, dass die Bandbreite an Daten, die ich an das BT-Modul 
übertrage, in der Nähe der Baudrate liegt - und der µC aber auch noch 
Daten vom ADC holen muss und Berechnungen darauf ausführen, bevor sie 
übertragen werden sollen.

Mein Problem nun ist die effiziente Übertragung: Wenn der µC sozusagen 
im Hintergrund immer wenn ein Byte im UART übertragen ist, das nächste 
shiften und dann zurück zur main routine kehren würde (wo neue daten 
generiert werden), müsste alles hin hauen. Genau diese Umsetzung gelingt 
mir bisher aber nicht.

Im Moment habe ich eine interrupt-Routine für den ADC, die immer, wenn 
ready, neue daten holt. Nach Berechnung in der mainroutine werden diese 
dort dann in einen transmit-ringbuffer steckt, der am ende der 
main-schleife dann stückweise geleert wird (d.h. so lange übertragen, 
bis er leer ist).
Das ist eindeutig nicht im Hintergrund!

Eine interrupt-gesteuerte Übertragung gelingt mir nicht mit den TXRDY / 
ENDTX interrupts: die übertragung bleibt immer stehen.

Wie würdet ihr dieses Problem lösen?

Momentan führt meine derzeitige Lösung dazu, dass Fehler während den 
Berechnungen passieren, weil zwischendurch in den ADC-Interrupt 
gesprungen wird - und zwar nur, weil alles nicht rechtzeitig fertig 
ist...

: Bearbeitet durch User
von Pandur S. (jetztnicht)


Lesenswert?

Der Controller hat genuegend Zeit ? Ich nehme an schon. Das kann 
geprueft werden, indem man im main() - while(1) zuoberst einen Pin 
toggelt. Dann sieht man die freie Zeit am Oszilloscope. Vorausgesetzt, 
keine Subroutine wartet irgendwo. Irgendwo zu warten ist sowieso 
schlechter Stil.

Dann ist was mit den Interupts nicht gut. Der TxRdy kommt immer wenn man 
einen naechstes byte nachfuellen kann. Wenn man keins mehr nachfuellen 
will, weils keins mehr hat wartet man auf den TxEnd. Wenn der kommt ist 
Schluss.

Ich wuerd also ohne ADC, per timer versuchen repetitiv Strings zu 
senden. zB alle 100ms 5 bytes. Also im main() auf TimerCame warten und 
dann den TXbuffer zu fuellen. Bis das geht. Dann erst den ADC Interrupt 
zu verwenden um auch im Main() dien ADC Resultate in den TxBuffer zu 
fuellen.

Was man nie machen sollte, ist in einem Interrupt einen Buffer eines 
anderen Interruptes zu bedienen.

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Oder D. schrieb:
> Der Controller hat genuegend Zeit ? Ich nehme an schon. Das kann
> geprueft werden, indem man im main() - while(1) zuoberst einen Pin
> toggelt. Dann sieht man die freie Zeit am Oszilloscope.

Das mache ich grade mal, Ergebnisse kommen.

>Vorausgesetzt, keine Subroutine wartet irgendwo. Irgendwo zu warten ist >sowieso 
schlechter Stil.

Weiß ich, den stil habe ich mir abgewöhnt ;) warten/delays gibts bei mir 
nicht.

> Was man nie machen sollte, ist in einem Interrupt einen Buffer eines
> anderen Interruptes zu bedienen.

Mache ich so nicht, der ADC interrupt füllt einen "Measurement Buffer", 
der dann in der main verarbeitet wird.


> Dann ist was mit den Interupts nicht gut. Der TxRdy kommt immer wenn man
> einen naechstes byte nachfuellen kann. Wenn man keins mehr nachfuellen
> will, weils keins mehr hat wartet man auf den TxEnd. Wenn der kommt ist
> Schluss.
>
> Ich wuerd also ohne ADC, per timer versuchen repetitiv Strings zu
> senden. zB alle 100ms 5 bytes. Also im main() auf TimerCame warten und
> dann den TXbuffer zu fuellen. Bis das geht. Dann erst den ADC Interrupt
> zu verwenden um auch im Main() dien ADC Resultate in den TxBuffer zu
> fuellen.

Danke, das schaue ich mir mal gleich an!

von c-hater (Gast)


Lesenswert?

Alex V. schrieb:

> ich bin an einem Flaschenhals angekommen:
> Ich verwende einen ATSAM4S mit ST Bluetooth-Modul über UART im SPP
> Modus.
> Die mögliche Baudrate ist in meinem Fall (auch wegen Schnittstelle zum
> PC) 115.2kBaud.
>
> Nun ist es so, dass die Bandbreite an Daten, die ich an das BT-Modul
> übertrage, in der Nähe der Baudrate liegt - und der µC aber auch noch
> Daten vom ADC holen muss und Berechnungen darauf ausführen, bevor sie
> übertragen werden sollen.
>
> Mein Problem nun ist die effiziente Übertragung

Nein, höchstwahrscheinlich ist das nicht dein Problem, sondern die 
BT-Latenzen. Bei BT passiert eben doch noch sehr viel mehr als bei einer 
einfachen UART über eine Strippe Draht.

Inbesondere ist der Übertragungskanal Funk ein "shared medium". Du 
kannst keinesfalls erwarten, daß du darüber konstant die maximale 
Nettodatenrate erreichen wirst, die theoretisch bei ungestörtem Kanal 
möglich wäre.

> Wie würdet ihr dieses Problem lösen?

Daten puffern und notfalls skippen. Also genau so, wie alle anderen 
Streaminganwendungen über wenig verläßliche Kanäle auch arbeiten 
(müssen).

D.h.: du brauchst ein Protokoll, mit dem sich Sender und Empfänger ihre 
Wehwehchen möglichst effizient mitteilen können und das es erlaubt, daß 
sie sich nach einem durch den Kanal erzwungenen Verlassen der Echtzeit 
möglichst schnell wieder synchronisieren.

Eine Alternative dazu (oder ggf. auch in Kombination mit o.g.) wäre eine 
Datenkompression, die die nötigen Kanalreserven schafft.

von Kirsch (Gast)


Lesenswert?

c-hater schrieb:
> Nein, höchstwahrscheinlich ist das nicht dein Problem, sondern die
> BT-Latenzen. Bei BT passiert eben doch noch sehr viel mehr als bei einer
> einfachen UART über eine Strippe Draht.

Das glaub ich nicht, für den µC ist die Übertragung transparent, der 
schickt die Daten genauso raus als wenn er direkt mit dem PC verbunden 
ist.
Das einige was passiert, ist das die Daten länger brauchen bis sie 
ankommen, oder verloren gehen (wenn gerade eine Störung ist), da kein 
Flow-Control vorhanden ist.

Da die Datenübertragung nur in eine Richtung geht kann dir das 
zusätzliche Delay egal sein.

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Also Toggle-Ergebnisse mit ausgeschalteter Übertragung - d.h. es wird 
schlicht nichts in den transmit-Ringbuffer gesteckt (alles andere läuft 
normal):

Die komplette main-loop benötigt ca. 2µs ohne frische Werte.

Läuft der ADC (Samplerate 500Hz), braucht die main routine alle 1.75ms 
einmal 250µs länger (das ist die Übertragungsdauer der Daten per SPI 
Schnittstelle bei ADC_ready)

Zeit sollte also genug da sein!

von Jim M. (turboj)


Lesenswert?

Alex V. schrieb:
> Wie würdet ihr dieses Problem lösen?

ATSAM4S kennt garantiert auch DMA. Das ist dann zwar nicht mehr trivial 
zu programmieren, aber damit kann man während der UART Übertragung die 
CPU voll nutzen.

Übrigens kann auch Bluetooth der Flaschenhals werden, Du solltest immer 
die RTS/CTS Leitungen im Auge behalten - so es nicht schon die UART 
Hardware tut.

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Jim M. schrieb:
> Übrigens kann auch Bluetooth der Flaschenhals werden, Du solltest immer
> die RTS/CTS Leitungen im Auge behalten - so es nicht schon die UART
> Hardware tut.

Danke, habe ich auch grad noch mal überpüft, die CTS line des BT Moduls 
ist allerdings dauerhaft low (und das Modul ist ja bei active low rdy) - 
kommt also nie in Bedrängnis..

Jim M. schrieb:
> ATSAM4S kennt garantiert auch DMA. Das ist dann zwar nicht mehr trivial
> zu programmieren, aber damit kann man während der UART Übertragung die
> CPU voll nutzen.

Ja, DMA unterstützt der ATSAM4S, da kenne ich mich nun allerdings 
erstmal gar nicht mit aus. Klingt fancy, ich versuch aber erstmal noch 
drum rum zu kommen ;)

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Zu den Wehwehchen und Paketverlust:

Das komische ist auch, dass meine Pakete (von 13 bis 23 Bytes lang 
inklusive start/stopbyte) mit nur wenigen wenigen ausnahmen vollständig 
ankommen, teilweise aber die daten in den einzelnen pakete verhunzt 
sind. ich glaube das liegt derzeit wiederum an meiner verqueren 
interrupt architektur..

von Falk B. (falk)


Lesenswert?

@Alex V. L. (bastel_alex) Benutzerseite

>Mein Problem nun ist die effiziente Übertragung: Wenn der µC sozusagen
>im Hintergrund immer wenn ein Byte im UART übertragen ist, das nächste
>shiften und dann zurück zur main routine kehren würde (wo neue daten
>generiert werden), müsste alles hin hauen. Genau diese Umsetzung gelingt
>mir bisher aber nicht.

Dann musst du es verbessern, siehe Interrupt. Ein FIFO ist 
möglicherweise auch ganz praktisch, siehe die Funktion von Peter Fleury.

>Im Moment habe ich eine interrupt-Routine für den ADC, die immer, wenn
>ready, neue daten holt. Nach Berechnung in der mainroutine werden diese
>dort dann in einen transmit-ringbuffer steckt, der am ende der
>main-schleife dann stückweise geleert wird (d.h. so lange übertragen,
>bis er leer ist).
>Das ist eindeutig nicht im Hintergrund!

Klingt so. Aber so ein Beschreibeung ist wenig sinnvoll. Poste 
vollständigen Code als Anhang.

>Momentan führt meine derzeitige Lösung dazu, dass Fehler während den
>Berechnungen passieren, weil zwischendurch in den ADC-Interrupt
>gesprungen wird - und zwar nur, weil alles nicht rechtzeitig fertig
>ist...

Dann hast du noch ein grundlegendes Verständnisproblem.

von uwe (Gast)


Lesenswert?

Ich nehme an das dieses Bluetoothmodul einen Puffer besitzt, z.B. 64 
Byte erst wenn die voll sind wird gesendet. Dazu wird es vileicht noch 
sowas wie einen Timeout haben der auch wenn erst 1 oder 4 Byte im Puffer 
stehen das modul zum senden bringt z.B. 10ms. Das würde bedeuten das die 
Effizienteste Datenübertrageung nur mit Datenpacketen von der größe der 
Puffergröße stattfinden kann. Die gilt es nun herauszufinden, am besten 
im Datenblatt des Moduls. Welches modell ist es denn?

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

uwe schrieb:
> Die gilt es nun herauszufinden, am besten
> im Datenblatt des Moduls. Welches modell ist es denn?
Das SPBT2632C2A:
http://www.st.com/web/catalog/sense_power/FM1968/CL1976/SC1324/PF253470

Ich habe die Architektur auf dem µC jetzt folgendermaßen umgebaut und 
glaube, dass auf dieser Basis besser gearbeitet werden kann als auf der 
letzten:

Es gibt drei interrupt routinen:

0. Systick interrupt für den system timer

1. Interrupt-Routine des ADC:  Daten Holen und Daten verarbeiten läuft 
jetzt geschlossen hier bei einem Data-Ready Interrupt, - benötigt bei 
Ausführung ca 250µs. Daten sind alle 2ms bereit. Die fertigen daten 
werden hier in einen transmit-ringbuffer gesteckt.

2. Daneben gibt es eine Receive-Interrupt routine, die bei Eingang (also 
Nutzerbefehlen) daten in einen receive-ringbuffer schiebt.

Der Rest läuft nun mit hoher iteration in der main-loop:
Sind daten im receive buffer, werden diese verarbeitet (ist nur der fall 
wenn ein befehl vom user gesendet wurde).
Sind daten im transmit buffer, wird ein byte aus dem transmit-ringbuffer 
ins uart modul gesteckt und gesendet. Das passiert also durchgängig, nur 
unterbrochen von der alle 2ms auftretenden interrupt-routine des ADCs..

Nun versuche ich das noch zu optimieren (nutzung der 250µs der interrupt 
routine und datenkompression)

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Alex V. L. (bastel_alex) Benutzerseite

>1. Interrupt-Routine des ADC:  Daten Holen und Daten verarbeiten läuft
>jetzt geschlossen hier bei einem Data-Ready Interrupt, - benötigt bei
>Ausführung ca 250µs

Klingt ganz schön viel für so einen Interrupt, zumal du einen 32 Bit 
Controller hast. Sind während dieser Zeit die anderen Interrupts 
gesperrt oder ausführbar?

>Der Rest läuft nun mit hoher iteration in der main-loop:

Schon wieder Bullshit Bingo Zeit? Was ist denn hohe Iteration?
Du meinst vielleicht eine hohe Geschwindigkeit oder kurze Umlaufzeit der 
Hauptschleife.

>Sind daten im receive buffer, werden diese verarbeitet (ist nur der fall
>wenn ein befehl vom user gesendet wurde).
>Sind daten im transmit buffer, wird ein byte aus dem transmit-ringbuffer
>ins uart modul gesteckt und gesendet. Das passiert also durchgängig, nur
>unterbrochen von der alle 2ms auftretenden interrupt-routine des ADCs..

Alles schön und gut, aber solche Lyrik ist ebenso wie bei Schaltplänen 
nur wenig sinnvoll. Denn die kritischen Details stecken im Code, den du 
uns immer noch vorenthältst.

>Nun versuche ich das noch zu optimieren (nutzung der 250µs der interrupt
>routine und datenkompression)

Klingt nicht sinnvoll. Ein Optimierung eines noch nicht wirklich 
fehlerfreien Konzepts.

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

von Pandur S. (jetztnicht)


Lesenswert?

> Data-Ready Interrupt, - benötigt bei Ausführung ca 250µs.

Ist leider viel zu viel. Bei 115kBaud, sprich 10kByte/s ergibt das UART 
einen Interrupt alle 100us. Dh du kannst die Interrupts nicht waehrend 
250us blockieren.

Der ADC ist sicher viel schneller, du baust da Mist. Zeig mal. Float ?

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Sind während dieser Zeit die anderen Interrupts
> gesperrt oder ausführbar?

ausführbar aber mit priorisierung. Der ADS interrupt hat die 
zweithöchste priorität nach systick (alle 100µs) und UART receive (der 
aber ja wie gesagt in der runtime nur was bekommt wenn durch 
user-command abgebrochen wird - und dann ist es auch egal).

Falk B. schrieb:
> Schon wieder Bullshit Bingo Zeit? Was ist denn hohe Iteration?
> Du meinst vielleicht eine hohe Geschwindigkeit oder kurze Umlaufzeit der
> Hauptschleife.

Richtig. Offensichtlich kannst du Bullshit Bingo ja verstehen, das 
heißt, dass deine überragende Intelligenz die meine sehr eingeschränkte 
kompensieren kann beim lesen, ermutigend!

Falk B. schrieb:
> Alles schön und gut, aber solche Lyrik ist ebenso wie bei Schaltplänen
> nur wenig sinnvoll. Denn die kritischen Details stecken im Code, den du
> uns immer noch vorenthältst.

Ich gebe dir da recht, aber wie so oft unterstellst du dabei entweder 
dummheit oder andere motivation, die hier nicht zutrifft:
Das ganze Projekt ist riesig, verwendet das Atmel Software Framework und 
alleine 20 von mir erstellte header+c files.
Bevor ich also ALLES poste müsste ich, damit es überhaupt lesbar wird, 
weil ich nicht alle routinen mit posten kann, alles noch einmal 
umschreiben und neu zusammenschnipseln. Das habe ich tatsächlich schon 
einmal vor kurzem angefangen (siehe 
Beitrag "Interrupt gesteuertes UART transmit"), war aber wohl 
nicht lesbar oder interessant genug.

Falk B. schrieb:
> Klingt nicht sinnvoll. Ein Optimierung eines noch nicht wirklich
> fehlerfreien Konzepts.

Da gebe ich dir recht, deshalb habe ich es auch bisher vor mir her 
geschoben.

Oder D. schrieb:
> Ist leider viel zu viel. Bei 115kBaud, sprich 10kByte/s ergibt das UART
> einen Interrupt alle 100us.

Die Lösung, die ich oben geschrieben habe, verwendet ja nur den 
RX-Interrupt für eingehende Daten. In jedem main-durchlauf (alle 2µs) 
wird einfach geprüft ob das UART-TX Register leer ist und falls ja ein 
neues Byte übertragen. Also ganz ohne den UART_TX interrupt.

Oder D. schrieb:
> Der ADC ist sicher viel schneller, du baust da Mist.

In diesem Fall Sicher nicht. der ADC (ADS1299) läuft bei einstellbarer 
samplerate mit 24 bit tiefe - und ich verwende hier absichtlich 500Hz. 
Da läuft also alles, wie es soll. Nee, nicht float, 24bit 
zweierkomplement

: Bearbeitet durch User
von Pandur S. (jetztnicht)


Lesenswert?

Nicht die Samplerate ist entscheidend, sondern die Zeit, die dafuer 
aufgewendet wird. Wie lange ist der ADC Interrupt, nicht wie oft.
Die Behandlung des Interrupts sollte nur ein paar Zyklen benoetigen, 
also ADC lesen und abspeichern und neu starten.

von Falk B. (falk)


Lesenswert?

@ Alex V. L. (bastel_alex) Benutzerseite

>> Sind während dieser Zeit die anderen Interrupts
>> gesperrt oder ausführbar?

>ausführbar aber mit priorisierung. Der ADS interrupt hat die
>zweithöchste priorität nach systick (alle 100µs)

Ganz schön flink. Brauchst du das?

> und UART receive (der
>aber ja wie gesagt in der runtime nur was bekommt wenn durch
>user-command abgebrochen wird - und dann ist es auch egal).

Den Satz verstehe ich nicht.

>Ich gebe dir da recht, aber wie so oft unterstellst du dabei entweder
>dummheit oder andere motivation, die hier nicht zutrifft:

Weder noch.

>Das ganze Projekt ist riesig, verwendet das Atmel Software Framework und
>alleine 20 von mir erstellte header+c files.

Und? Es gib ZIP. Die .c und .h Files reichen.

>Bevor ich also ALLES poste müsste ich, damit es überhaupt lesbar wird,
>weil ich nicht alle routinen mit posten kann, alles noch einmal
>umschreiben und neu zusammenschnipseln.

Nö, da machst du nur Fehler rein oder raus.

>Da gebe ich dir recht, deshalb habe ich es auch bisher vor mir her
>geschoben.

Eine richtige Entscheidung.

Mal ganz aus dem Bauch heraus. Mittels ADC @500 Hz was messen, 
verwursten und per UART verschicken kann auch ein kleiner AVR mit 10 
MHz, ohne ins Schwitzen zu kommen. Sogar in C ;-)
Wenn es mit deinem SAM4 nicht geht, liegt das Problem in der 
Programmierung.

>Die Lösung, die ich oben geschrieben habe, verwendet ja nur den
>RX-Interrupt für eingehende Daten. In jedem main-durchlauf wird einfach
>geprüft ob das UART-TX Register leer ist und falls ja ein neues Byte
>übertragen.

Das klingt nach reichlich Unsinn. Der TX Interrupt ist ebensoschnell wie 
der RX Interrupt, da ist es egal ob man sendet oder empfängt.

>In diesem Fall Sicher nicht. der ADC (ADS1299) läuft bei einstellbarer
>sampleraten mit 24 bit tiefe - und ich verwende hier absichtlich 500Hz.
>Da läuft also alles, wie es soll. Nee, nicht float, 24bit
>zweierkomplement

Siehe oben.

Ic behaupte mal, dass das Forum innerhalb von 1 Stunde deinen Code dahin 
analysiert, dass es dir zeigt, wo dein Problem liegt. Pack die .c. und 
.h Dateien in ein ZIP-Archiv und stell es hier rein.

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Oder D. schrieb:
> Nicht die Samplerate ist entscheidend, sondern die Zeit, die dafuer
> aufgewendet wird. Wie lange ist der ADC Interrupt, nicht wie oft.

Dann haben wir uns falsch verstanden: Der ADC setzt sein data_ready 
active low wenn daten fertig sind (alle 2ms). Der ADC Interrupt ist die 
falling edge am µc.

> Die Behandlung des Interrupts sollte nur ein paar Zyklen benoetigen,
> also ADC lesen und abspeichern und neu starten.

Tut es auch - wenn ich nicht den Datenverarbeitungskram noch mit in der 
Routine mache, wie jetzt.

Falk B. schrieb:
> Ganz schön flink. Brauchst du das?

ja. für die tatsächlichen dt's der Messungen.

Falk B. schrieb:
> Den Satz verstehe ich nicht.

der UART_RX interrupt (höhere Priorität) unterbricht den ADC_Data_readdy 
Interrupt nur, wenn Daten eingehen - was nur der Fall ist, wenn der User 
ein Kommando schickt. Und das ist bei laufender Messung nur STOP.

Falk B. schrieb:
> Und? Es gib ZIP. Die .c und .h Files reichen.
Falk B. schrieb:
> Ic behaupte mal, dass das Forum innerhalb von 1 Stunde deinen Code dahin
> analysiert, dass es dir zeigt, wo dein Problem liegt. Pack die .c. und
> .h Dateien in ein ZIP-Archiv und stell es hier rein.

Da hast du recht, wirklich alles kann/darf ich dann aber auch nicht 
offenlegen hier, weil es nicht nur privat von mir genutzt wird....

Falk B. schrieb:
> Wenn es mit deinem SAM4 nicht geht, liegt das Problem in der
> Programmierung.

da stimme ich dir zu, was anderes habe ich allerdings auch nie gedacht, 
deshalb ja mein Foreneintrag hier. Meine hardware habe ich absichtlich 
eher überdimensioniert ausgelegt. Das Problem besteht aber ja auch nur 
durch den UART/BT Flaschenhals durch die Baudrate.
Übertrage ich 1/5 weniger daten, läuft (schon länger) alles am 
schnürchen. ;)

Falk B. schrieb:
> Das klingt nach reichlich Unsinn. Der TX Interrupt ist ebensoschnell wie
> der RX Interrupt, da ist es egal ob man sendet oder empfängt.

Das verstehe ich jetzt nicht so ganz. Ich nutze den TX Interrupt ja 
nicht nicht, weil ich denke er wäre zu langsam - sondern weil mir der 
oben beschrieben Aufbau üer die main-loop sinnvoller erschien (auch 
deshalb weil dann weniger interrupts sich gegenseitig in die quere 
kommen können)...?

von Falk B. (falk)


Lesenswert?

@Alex V. L. (bastel_alex) Benutzerseite

>Da hast du recht, wirklich alles kann/darf ich dann aber auch nicht
>offenlegen hier, weil es nicht nur privat von mir genutzt wird....

Jaja, Area51 und so. Dann eben nicht.

>eher überdimensioniert ausgelegt. Das Problem besteht aber ja auch nur
>durch den UART/BT Flaschenhals durch die Baudrate.
>Übertrage ich 1/5 weniger daten, läuft (schon länger) alles am
>schnürchen. ;)

D.h. dein System ist zwar schlecht, wird aber dich die Kraft der 
Hardware soweit kompensiert. geht es aber ein bisschen höher, kommt die 
Wasserleiche hoch ;-)

>> Das klingt nach reichlich Unsinn. Der TX Interrupt ist ebensoschnell wie
>> der RX Interrupt, da ist es egal ob man sendet oder empfängt.

>Das verstehe ich jetzt nicht so ganz. Ich nutze den TX Interrupt ja
>nicht nicht, weil ich denke er wäre zu langsam
> - sondern weil mir der
>oben beschrieben Aufbau üer die main-loop sinnvoller erschien

Glaub ich nicht.

> (auch
>deshalb weil dann weniger interrupts sich gegenseitig in die quere
>kommen können)...?

Stimmt, ist aber wahrscheinlich nicht das Problem. Eher dass deine 
Hauptschleife konzeptbedingt zu langsam läuft und damit deine 
Sendefunktion ausbremst.

von Kirsch (Gast)


Lesenswert?

Wenn ich was mit UART mache, hab ich immer einen FIFO mit ~80 Bytes. 
Dieser wird in der Mainloop gefüllt, und die TX-Buffer-Empty-ISR nimmt 
da immer ein Byte raus und tut sie in den UART-Buffer. Die ISR ist also 
ziemlich kurz.

von Stefan K. (stefan64)


Lesenswert?

Kirsch schrieb:
> Wenn ich was mit UART mache, hab ich immer einen FIFO mit ~80 Bytes.
> Dieser wird in der Mainloop gefüllt, und die TX-Buffer-Empty-ISR nimmt
> da immer ein Byte raus und tut sie in den UART-Buffer. Die ISR ist also
> ziemlich kurz.

Genau so macht man das.
Dein jetziges Konzept funktioniert bisher leidlich. Sobald neue 
Anforderungen dazukommen, dauert die main-loop etwas länger und Dein 
Problem beginnt von Neuem.

Im Idealfall füllst/leerst Du in jeder ISR ein Fifo, also auch von der 
ADC-ISR aus. Dann kann Deine main-loop sich auch mal mehrere Messzyklen 
Zeit lassen, z.B. weil es noch Werte auf ein lcd schreibt o.ä.

Alex V. schrieb:
> 1. Interrupt-Routine des ADC:  Daten Holen und Daten verarbeiten läuft
> jetzt geschlossen hier bei einem Data-Ready Interrupt, - benötigt bei
> Ausführung ca 250µs. Daten sind alle 2ms bereit. Die fertigen daten
> werden hier in einen transmit-ringbuffer gesteckt.

Das Daten verwursten geschieht laut Lehrbuch eher in der main-loop. Aber 
das ist Geschmackssache. Wichtig ist: je länger Deine ISR dauert, desto 
niedriger sollte deren Prio eingestellt werden.

Gruß, Stefan

von Falk B. (falk)


Lesenswert?

Noch ein Tip. Wenn man 115k2 Baud hat sind das 11,5 kB/s. Wenn man 
sowieso einen 10 kHz/100uS Timerinterrupt hat, kann man dort ohne 
Prüfung ein Byte auf die Reise schicken, damit liegt man sogar noch 
einen Tick unter der möglichen Maximaldatenrate. Vorteil: Es funkt 
niemals ein TX Interrupt dazwischen, das Timing wird vorhersagbarer.

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Falk B. schrieb:
> Noch ein Tip. Wenn man 115k2 Baud hat sind das 11,5 kB/s. Wenn man
> sowieso einen 10 kHz/100uS Timerinterrupt hat, kann man dort ohne
> Prüfung ein Byte auf die Reise schicken, damit liegt man sogar noch
> einen Tick unter der möglichen Maximaldatenrate. Vorteil: Es funkt
> niemals ein TX Interrupt dazwischen, das Timing wird vorhersagbarer.

Der ist gut, da war ich grade schon dran durch eure Anstöße oben! ;)
Der Transmit alle 100µs (bzw jetzt weil es eng wird alle 90µs) über den 
systick-counter_IR läuft soweit gut!

Ich habe eben noch das Problem, dass im worst-case derzeit eine Baudrate 
von 120.341 nötig wäre damit alle Daten rüberkommen - aber das Problem 
steht auf einem anderen Blatt und hat ja nichts hiermit sondern mit der 
Notwendigkeit einer Kompression oder Abspecken zu tun.

Also, ersteinmal danke!

von W.S. (Gast)


Lesenswert?

Falk B. schrieb:
> Noch ein Tip. Wenn man 115k2 Baud hat sind das 11,5 kB/s. Wenn man
> sowieso einen 10 kHz/100uS Timerinterrupt hat,

Das ist Oberpfusch.

Das Problem ist, daß der TO seine seriellen Ausgaben nebst dem 
dazugehörigen Interrupt nicht beherrscht.

Der ATSAM4 ist ein Cortex, also ARM. OK, die UART's dieser Branche 
ähneln sich so einigermaßen, also sollte die Interrupt-Häufigkeit 
deutlich unter den genannten 11 kHz sein, denn der UART hat ja nen 
FIFO.

Wichtig ist, einen ordentlichen Ringpuffer vorzuhalten, um die diversen 
Firmwareteile voneinander zu entkoppeln.

Ich geb mal ein Beispiel (Kinetis) zum verstehenden Lesen:
#define v24bufsize 256          /* immer 2er Potenz       */

char V24puffer0[v24bufsize];    /* Sendepuffer */
volatile int v24sin0;           /* Index im Sendepuffer 0 */
volatile int v24sout0;          /* Index im Sendepuffer 0 */

void __irq UART0_RX_TX_IRQHandler (void)
{ int si, so;
  volatile byte S1;

  if ((UART0_S1 & 128)==0) return;
  si = v24sin0;
  so = v24sout0;
  if (si!=so)                          /* wenn es was zu senden gibt */
  { UART0_C2 = (1<<7) | (1<<3) | (1<<2);   /* TIE, TE, RE 
*/
    while ((si!=so) && (UART0_TCFIFO < 7))      /* angst.. 
*/
    { UART0_D  = V24puffer0[v24sout0];          /* Zeichen senden 
*/
      v24sout0 = so = (so+1) & (v24bufsize-1);  /* Index weiterstellen 
*/
    }
  }
  else  UART0_C2 = (1<<3) | (1<<2);        /* kein TIE, aber TE und RE 
*/
  S1 = UART0_S1;
}

dword InitSerial0 (long baudrate)
{ dword i;

  v24sin0 = v24sout0 = 0;           // Puffer leeren
  SIM_SCGC4 |= (1<<10);             // Clock einschalten
  UART0_C5    = 0;                  // kein DMA
  i = (F_Platform<<1) / baudrate;
  UART0_BDH   = (i>>(8+5)) & 0x1F;  // hi Teil von SBR setzen
  UART0_BDL   = (i>>5);             // lo Teil von SBR setzen
  UART0_C4    = (i & 31);           // BFRA setzen, kein Match
  UART0_C1    =  4;                 // idle count nach Stop-Bit
  UART0_C4    = 1<<5;               // output in singlewire-mode ??
  UART0_MA1   = 0;                  // match-register 1
  UART0_MA2   = 0;                  // match-register 2
  UART0_MODEM = 0;                  // kein RTS, kein CTS
  UART0_IR    = 0;                  // kein infrarot-mode
  UART0_PFIFO = 0x88;               // TX Fifo ein, RX Fifo ein, Size = 
jeweils 8 Zeichen
  UART0_CFIFO = 128 | 64;           // beide Fifos leeren
  UART0_SFIFO = 7;                  // Fifo Errors löschen
  UART0_TWFIFO = 2;                 // int wenn < 2 Zeichen im Fifo.
  UART0_C2     = (1<<7) | (1<<3) | (1<<2); // TIE, TE, RE
  NVIC_ISER0  |= (1UL<<31);         // Int 31 freigeben
  return i;
}

char V24Char_Out0 (char c)
{ int  h;
  long L;

  h = v24sin0;
  V24puffer0[h] = c;
  v24sin0 = h = (h + 1) & (v24bufsize-1);  /* Zeiger weiterstellen    */
  h = (h + 1) & (v24bufsize-1);       /* h = nächster Ringpufferplatz */
  NVIC_STIR = 31;                     /* Int auslösen zum evtl. Starten 
*/
  L = 20000000;                       /* wir können nicht ewig warten! 
*/
  while (h==v24sout0)                 /* warten bis Platz im Puffer ist 
*/
  { --L;
    if (!L)  return 0;                     /* Timeout */
  }
  return c;
}

/* Anzahl freier Plätze im Sendepuffer ermitteln
   =============================================
 */
int V24numTxFree0 (void)
{ int i;
  i = v24sin0 - v24sout0;    /* i = belegte Plätze */
  if (i<0) i = i + v24bufsize;
  return v24bufsize - i;
}

/* Warten, bis alle Zeichen gesendet sind
   ======================================
 */
void V24TxDone0 (void) { while (v24sin0!=v24sout0); }

So, das sollte reichen als geistiger Anstoß.
Prinzip:
Die Zeichen-Ausgaberoutine stopft das Zeichen in den Ringpuffer und löst 
vorsorglich per NVIC einen Interrupt aus, um das eventuell erste Zeichen 
in die Gänge zu bekommen.
Das Interrupt-Programm befüllt den Sende-FIFO aus dem Ringpuffer, bis er 
voll ist oder nix mehr zu senden vorliegt.
Wenn nix mehr zu senden ist, schaltet das Interruptprogramm den 
Interrupt aus, um nicht in einen Teufelskreis zu kommen. Nach dem 
Soft-Int schaltet es ihn wieder ein, um den Rest auch zu bedienen.

Ist doch easy - gelle?

W.S.

von Falk B. (falk)


Lesenswert?

@W.S. (Gast)

>> Noch ein Tip. Wenn man 115k2 Baud hat sind das 11,5 kB/s. Wenn man
>> sowieso einen 10 kHz/100uS Timerinterrupt hat,

>Das ist Oberpfusch.

Unsinn! Das ist vollkommen legitim!

>Das Problem ist, daß der TO seine seriellen Ausgaben nebst dem
>dazugehörigen Interrupt nicht beherrscht.

Mag sein, aber da er mit dem Quelltext hinterm Berg hält, kann man ihm 
nur schwer helfen.

>Der ATSAM4 ist ein Cortex, also ARM. OK, die UART's dieser Branche
>ähneln sich so einigermaßen, also sollte die Interrupt-Häufigkeit
>deutlich unter den genannten 11 kHz sein, denn der UART hat ja nen
>FIFO.

Das ist eine Nebensächlichkeit. Auch ein good, old AVR kann mit 11 kHz 
den UART lückenlos befeuern.

von c-hater (Gast)


Lesenswert?

Falk B. schrieb:

>>> Noch ein Tip. Wenn man 115k2 Baud hat sind das 11,5 kB/s. Wenn man
>>> sowieso einen 10 kHz/100uS Timerinterrupt hat,
>
>>Das ist Oberpfusch.
>
> Unsinn! Das ist vollkommen legitim!

Nein, ist es nicht. Das ist die Notlösung der Doofen und Faulen. Nicht 
mehr und nicht weniger. Das kannst du dir schönreden, solange du willst.

von Falk B. (falk)


Lesenswert?

@c-hater (Gast)

>> Unsinn! Das ist vollkommen legitim!

>Nein, ist es nicht. Das ist die Notlösung der Doofen und Faulen. Nicht
>mehr und nicht weniger. Das kannst du dir schönreden, solange du willst.

Jaja, der Herr Oberschlau mal wieder. Ganz wichtige Wortmeldung! Nur mit 
dem Lesen und Verstehen hapert es etwas.

"Vorteil: Es funkt niemals ein TX Interrupt dazwischen, das Timing wird 
vorhersagbarer."

von c-hater (Gast)


Lesenswert?

Falk B. schrieb:

> Jaja, der Herr Oberschlau mal wieder. Ganz wichtige Wortmeldung! Nur mit
> dem Lesen und Verstehen hapert es etwas.
>
> "Vorteil: Es funkt niemals ein TX Interrupt dazwischen, das Timing wird
> vorhersagbarer."

Selbst dem offensichtlich nixwissenden TO war wohl schon klar, daß das 
Timing (bestenfalls) nur "versagbarerer" wird (was auch immer das im 
Detail bedeuten mag, man kann das mangels Code ja nicht so genau sagen).
Aber nicht mal der TO schwang sich zu der Deutung auf, das es dadurch 
wirklich "vorhersagbar" würde...

Und nur genau das wäre die die einzig akzeptable Indikation dafür, daß 
man eventuell wirklich einen Interrupt-Kontext sparen könnte, ohne 
Einbußen bei der Latenz der Übertragung oder der möglichen Bandbreite 
der Übertragung zu erleiden. Und das natürlich auch nur dann, wenn man 
zufällig sowieso einen Timer mit einer exakt passenden Periode (oder 
einem, nicht zu großen, ganzzahligen Vielfachen davon) am Laufen haben 
muß...

Alles andere ist suboptimal. Wie gesagt: du kannst dir das sicher 
schönreden, aber der Fakt an sich ist damit nicht aus der Welt 
schaffen...

Und übrigens: mich kannst du garnicht beleidigen. Das können nur Leute, 
die ich als fachlich kompetent einschätze und auf deren Meinung ich 
deshalb Wert lege. Du aber zeigst neben gelegentlich durchaus korrekten 
Einschätzungen immer wieder ein tiefe Wirklichkeitsverdrängung, nämlich 
immer genau dann, wenn es im Endeffekt um die Schwächen der von dir 
prädestinierten Programmiersprache geht.

Und Leuten, denen selbst nach Jahrzehnten aktiver Programmiertätigkeit 
immer noch der Überblick über das Ganze fehlt (oder die ihn gar bewußt 
verdrängen/negieren), die kann ich einfach nicht wirklich ernst nehmen.

Und eigentlich sollte niemand das tun...

von Falk B. (falk)


Lesenswert?

@ c-hater (Gast)

Versuchs mal mit Stand Up Comedy, eine Gastrolle bei Big Bang Theory ist 
dir sicher! ;-)

P S Wenn ein selbsternannter Zensor das wieder löscht, möge ihn der 
Blitz beim Scheißen treffen! Oder ist das hier die humorbefreite Zone?

von c-hater (Gast)


Lesenswert?

Falk B. schrieb:

> P S Wenn ein selbsternannter Zensor das wieder löscht, möge ihn der
> Blitz beim Scheißen treffen! Oder ist das hier die humorbefreite Zone?

Na wenigstens ein Punkt, in dem wir vollkommen übereinstimmen. Die 
Zensoren sollten sich einfach darauf beschränken, offensichtlich 
rechtsverletzende Äußerungen zu löschen, nicht aber mißliebige 
Meinungen.

Wenn das auch dein Ziel ist, bin ich jenseits jeglicher 
Meinungsverschiedenheiten in Sachfragen vorbehaltslos bereit, mit dir 
zusammen zu arbeiten, um es zu erreichen.

Es stört mich dabei kein bisschen, wenn du glaubst, daß meine Beiträge 
nur ein Witz sind. Denn, selbst wenn es wirklich so wäre (was natürlich 
nicht der Fall ist, wie jeder kompetente Mitleser leicht herausbekommen 
kann): berechtigt das eine Zensur?

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Ihr seid der Knaller..


W.S. schrieb:
> Ich geb mal ein Beispiel (Kinetis) zum verstehenden Lesen:

Danke!

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.