Hallo zusammen,
über UART und einem RS232 Transceiver sende ich Messdaten zur Kontrolle
an den Rechner. Das ganze passiert mit 1KHz bei 115200 Baud und
funktioniert soweit auch einwandfrei.
Da der µC (STM32G081KBT)einige Aufgaben paralell erledigen soll habe ich
mich dafür entschieden FreeRTOS zu verwenden. In einem Task mit höchster
Priorität (REALTIME 7) sendet der Controller jede Millisekunde die Daten
raus.
Jetzt zum Problem:
Wie im Titel zu lesen "ruckelt" es dabei, was so viel heißen soll, dass
die Daten im Terminal immer nur etwa sekündlich ankommen. Es kommen
quasi sekündlich alle Daten mit einem Schwall an. Da ich einen
RS232-USB-Transceiver verwende dachte ich erst, dass dieser vielleicht
zwischenpuffert und nur sekündlich Daten rausschickt. Auf einem anderen
Controller ohne FreeRTOS kommen die Daten allerdings im Terminal über
den selben RS232-USB-Transceiver in einem kontinuierlichen Fluss an.
Hat jemand eine Vorstellung darüber woran das liegen könnte? Die
UART-Puffer schicke ich per DMA raus und der zugehörige Task hat die
höchste Priorität weshalb ja eigentlich nichts dafür sprechen würde,
dass die Daten nicht kontinuierlich rausgeschickt werden können.
Vielen Dank schonmal für eure Hilfe :)
Das ganze habe ich sogar auch über einen MUX versucht für andere Tasks
zu blockieren. Die Funktion SMP_STM32_Send(SMP_HandleTypeDef CON)
schickt die Messdaten über eine konfigurierte Verbindung (im
SMP_HandleTypeDef agespeichert) per UART-DMA raus.
Hier der Task:
woher kommt osMutexAcquire
das ist keine Funktion von FreeRTOS
hat das einen Rückgabewert den man überprüfen kann, anscheinend
übergibst du ihm einen Timeout von 1000ms
Und du willst einen Task jede Millisekunde ausführen, der Scheduler von
FreeRTOS läuft doch selbst nur mit einem Takt von 1KHz
CK schrieb:> woher kommt osMutexAcquire> das ist keine Funktion von FreeRTOS>> hat das einen Rückgabewert den man überprüfen kann, anscheinend> übergibst du ihm einen Timeout von 1000ms>> Und du willst einen Task jede Millisekunde ausführen, der Scheduler von> FreeRTOS läuft doch selbst nur mit einem Takt von 1KHz
Auf den STM32 läuft die CMSIS V2 Version von FreeRTOS. osMutexAcquire()
entspricht einem xSemaphoreTake im standart FreeRTOS.
Hier mal zum nachlesen:
https://arm-software.github.io/CMSIS_5/RTOS2/html/group__CMSIS__RTOS__MutexMgmt.html
genau der Thread wartet theoretisch bis zu 1000ms, was aber eigentlich
nicht der Fall sein soll. Mit und ohne MUX hakt es aber sowieso, weshalb
ich nicht denke, dass dort der Fehler liegt.
Da hast du natürlich recht, das der Sheduler nur mit 1KHz läuft.
Eventuell muss ich den schneller laufen lassen oder ich lasse das Senden
der Daten über einen Timer triggern.
Fragenhagel schrieb:> genau der Thread wartet theoretisch bis zu 1000ms, was aber eigentlich> nicht der Fall sein soll.
Was ich gelernt habe: Alles was du nicht geprüft hast, weißt du nicht :)
Fragenhagel schrieb:> genau der Thread wartet theoretisch bis zu 1000ms, was aber eigentlich> nicht der Fall sein soll. Mit und ohne MUX hakt es aber sowieso, weshalb> ich nicht denke, dass dort der Fehler liegt.
Aber wenn du sagst, dass die Daten nur einmal pro Sekunde gesendet
werden und im sende-Thread zufällig mit einem Timeout von genau einer
Sekunde gewartet wird, würde ich auf jeden Fall prüfen, ob es da nicht
vielleicht einen Zusammenhang gibt. Sollte sich ja auch sehr leicht
testen lassen.
> Da hast du natürlich recht, das der Sheduler nur mit 1KHz läuft.> Eventuell muss ich den schneller laufen lassen oder ich lasse das Senden> der Daten über einen Timer triggern.
Taskwechsel werden nicht nur im Timer-Tick durchgeführt, sondern in
jeder Switching-ISR oder manuell mit einem Aufruf, der auf irgendwas
wartet, so wie dein osDelay().
Rolf M. schrieb:> Taskwechsel werden nicht nur im Timer-Tick durchgeführt, sondern in> jeder Switching-ISR oder manuell mit einem Aufruf, der auf irgendwas> wartet, so wie dein osDelay().
Nö. Taskwechsel in FreeRTOS auf Cortex-M-MCUs werden nur im
PendSV-Interrupt durchfeführt.
Im Systick z.B. wird der PendSV-Interrupt nur ausgelöst.
Planloser schrieb:> Rolf M. schrieb:>> Taskwechsel werden nicht nur im Timer-Tick durchgeführt, sondern in>> jeder Switching-ISR oder manuell mit einem Aufruf, der auf irgendwas>> wartet, so wie dein osDelay().>> Nö. Taskwechsel in FreeRTOS auf Cortex-M-MCUs werden nur im> PendSV-Interrupt durchfeführt.> Im Systick z.B. wird der PendSV-Interrupt nur ausgelöst.
Hmm, ok, wie das dort implementiert ist, weiß ich nicht. Ich habe früher
mal ein FreeRTOS auf einen ARM7TDMI portiert und dort alle wichtigen
ISRs zu Switching-ISRs gemacht mit Scheduler-Aufruf. Und ist das dann
tatsächlich so, dass eine Delay-Funktion oder ein Warten auf eine Queue
einfach sinnlos CPU-Zeit verbrät, bis der nächste Tick kommt? Das klingt
mir sehr ineffizient.
900ss D. schrieb:> Fragenhagel schrieb:>> genau der Thread wartet theoretisch bis zu 1000ms, was aber eigentlich>> nicht der Fall sein soll.>> Was ich gelernt habe: Alles was du nicht geprüft hast, weißt du nicht :)
Ich rufe in einem anderen Programm die Sende-Funktion nun in der
Callback-Funktion eines Timers auf (1KHz) und beobachte hier genau das
selbe Verhalten. Eventuell ist es doch der USB Adapter...
Rolf M. schrieb:> Hmm, ok, wie das dort implementiert ist, weiß ich nicht. Ich habe früher> mal ein FreeRTOS auf einen ARM7TDMI portiert und dort alle wichtigen> ISRs zu Switching-ISRs gemacht mit Scheduler-Aufruf. Und ist das dann> tatsächlich so, dass eine Delay-Funktion oder ein Warten auf eine Queue> einfach sinnlos CPU-Zeit verbrät, bis der nächste Tick kommt? Das klingt> mir sehr ineffizient.
Kann ich mir so ehrlich gesagt auch nicht vorstellen. Das würde ja
bedeuten, dass der MCU nur jede Millisekunde zwischen zwei Tasks
wechseln könnte und somit quasi immer fast ausschließlich damit
beschäftigt wäre darauf zu warten einen anderen Task ausführen zu
können.
Planloser schrieb:> Nö. Taskwechsel in FreeRTOS auf Cortex-M-MCUs werden nur im> PendSV-Interrupt durchfeführt.
Glaub ich auch nicht und es ist auch nicht die Regel bei RTOS.
Wenigstens bei wartenden API-Calls wird wahrscheinlich auch die Task
gewechselt. Und weshalb sollte das bei Cortex-M CPUs nur in dem Tick
gemacht werden? Der Code auf Kernelebene unterscheidet sich eher nicht
wenn eine andere Architektur im Spiel ist.
Edit: Ich hab das gerade mal recherchiert. Im Freertos-Forum wurde das
von einem Freertos Kollegen bestätigt, der Kontext-Switch passiert auch
in der API-Funktion.
900ss D. schrieb:> Planloser schrieb:>> Nö. Taskwechsel in FreeRTOS auf Cortex-M-MCUs werden nur im>> PendSV-Interrupt durchfeführt.>> Glaub ich auch nicht und es ist auch nicht die Regel bei RTOS.
Glauben gehört in die Kirche.
> Wenigstens bei wartenden API-Calls wird wahrscheinlich auch die Task> gewechselt. Und weshalb sollte das bei Cortex-M CPUs nur in dem Tick> gemacht werden?
Habe ich so gar nicht geschrieben.
Geschrieben habe ich, dass der Task-Wechsel nur im PendSV-Interrupt
durchgeführt wird.
> Edit: Ich hab das gerade mal recherchiert. Im Freertos-Forum wurde das> von einem Freertos Kollegen bestätigt, der Kontext-Switch passiert auch> in der API-Funktion.
Tja, warum ist wohl OPENSOURCE so hilfreich?
Damit man nicht bei Recherchen inkompetente Kollegen fragen muss, die
dann Blödsinn raushauen.
Daher hier für diejenigen, die - aus welchen Gründen auch immer - den
Sourcecode nicht verstehen:
1
#define portYIELD() \
2
{ \
3
/* Set a PendSV to request a context switch. */ \
4
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
5
\
6
/* Barriers are normally not required but do ensure the code is completely \
7
within the specified behaviour for the architecture. */ \
8
__asm volatile( "dsb" ::: "memory" ); \
9
__asm volatile( "isb" ); \
10
}
portYIELD() wird von allen Funktionen wie den Queue-Funktionen, Delays,
etc. aufgerufen, wenn ein Task-Wechsel vorgenommen werden soll. Das
Posten der vielen Stellen spare ich mir, wer es nicht glaubt, sollte
sich jemanden besorgen, der den C-Sourcecode lesen kann.
Also nochmal für die Unsinn-Schreiber hier:
Auf Cortex-M-MCUs findet der Taskwechsel in FreeRTOS nur im
PendSV-Interrupt statt!
Fragenhagel schrieb:> Okay, also kann der Controller mit FreeRTOS nur jede millisekunde den> Task wechseln,
Nein, das schrieb Planloser nicht, sondern die Tasks wechseln im PendSV
Int. Wichtig ist in den Tasks portYield oder Funktionen aufzurufen die
Zeit abgegeben um anderen eine Chance zu geben.
Fragenhagel schrieb:> Okay, also kann der Controller mit FreeRTOS nur jede millisekunde den> Task wechseln, was ja bei mir zu Problemen führen wird, da ich stand> jetzt zwei Tasks habe, die jede Milisekunde laufen sollen. Den Sheduler> Takt kann ich nicht erhöhen, da ist in CubeMX 1KHz das Maximum. Macht> das denn überhaupt sinn oder wie kann man das am elegantesten Lösen?
Nein, in FreeRTOS wird halt nur der reine Taskwechsel ausschliesslich im
PendSV-IRQ durchgeführt, also das "klassische" Sichern des TCB und
Aktivieren des Neuen.
Den PendSV-IRQ triggern tut dann nicht nur der Systick, sondern jede
API-Funktion, die das braucht. Also zum Beispiel, wenn man etwas aus
einer Queue auslesen möchte und die noch leer ist, dann wird ein
PendSV-IRQ über den Aufruf von taskYield() in der entsprechenden
API-Funktion getriggert.
Ein vTaskDelay() triggert ebenso den PendSV-IRQ.
taskYield() selber braucht man übrigens nicht aufrufen. Es gibt übrigens
gleich eine Reihe von taskYield-Funktionen (je nach Kontext ruft
FreeRTOS eine andere auf).
Ich kann übrigens aus eigener Erfahrung jedem, der mehr mit FreeRTOS
macht, raten, sich die Sources mal anzusehen. Bringt viel fürs
Verständnis.
Am besten mit port.c anfangen, dann tasks.c. Den Rest dann nach Bedarf.
FreeRTOS ist wirklich überschaubar.
Planloser schrieb:> portYIELD() wird von allen Funktionen wie den Queue-Funktionen, Delays,> etc. aufgerufen, wenn ein Task-Wechsel vorgenommen werden soll. Das> Posten der vielen Stellen spare ich mir, wer es nicht glaubt, sollte> sich jemanden besorgen, der den C-Sourcecode lesen kann.>> Also nochmal für die Unsinn-Schreiber hier:> Auf Cortex-M-MCUs findet der Taskwechsel in FreeRTOS nur im> PendSV-Interrupt statt!
Ach dir ging es nur um Wortklauberrei. Ob das jetzt über einen
PendSV-Interrupt geht oder nicht, ist doch wurscht. Es ging darum, dass
man das von verschiedenen Stellen aus antriggern kann und es nicht nur
hart einmal alle Millisekunde einen Taskwechsel gibt, als Antwort auf
diese Aussage:
Fragenhagel schrieb:> Da hast du natürlich recht, das der Sheduler nur mit 1KHz läuft.> Eventuell muss ich den schneller laufen lassen oder ich lasse das Senden> der Daten über einen Timer triggern.
Oben nennst du dann genau die Sachen, die ich auch schon genannt hatte.
Planloser schrieb:> Also nochmal für die Unsinn-Schreiber hier
Ich weiß nicht weshalb du so aggressiv wirst, ich habe dich einfach
falsch verstanden.
Deine Angabe oben ließ vermuten, daß der PendSV Interrupt nur vom
Systick getriggert wird. Dass der auch von den API-Funktionen angestoßen
wird, wußte ich nicht und hast du auch nicht geschrieben. Andere
scheinen deine Aussage auch falsch verstanden zu haben.
Also stimmt die Aussage der Kollegen auch, API-Calls in den Kernel lösen
gegebenenfalls Taskwechsel aus (über den PendSV IRQ).
So und jetzt beruhige dich wieder.
Ende OT..
Jonas B. schrieb:>>In einem Task mit höchster>>Priorität (REALTIME 7) sendet der Controller jede Millisekunde die Daten>>raus.>> Nochmal, wieviele Daten sind das??Jonas B. schrieb:> Sendest du mehr als 10 Byte am Stück?
Es sind exakt 10 Bytes zwei 16Bit Variablen auf je 3Byte mit
Byte-Kennung aufgeteilt und einem Delimitter zur Kennung eines Pakets
(War hauptsächlich für die Python Implementation gedacht weil so bis zum
Delimiter gelesen werden kann).
10Bytes sollten ja zeittechnisch kein Thema sein. Auf einem STM32F407
läuft das Programm auch über FreeRTOS problemlos und sendet
kontinuierlich, jedoch über den VCOM des ST-Links, da es sich dabei um
ein DEV Board handelt.
John Doe schrieb:> Nein, in FreeRTOS wird halt nur der reine Taskwechsel ausschliesslich im> PendSV-IRQ durchgeführt, also das "klassische" Sichern des TCB und> Aktivieren des Neuen.> Den PendSV-IRQ triggern tut dann nicht nur der Systick, sondern jede> API-Funktion, die das braucht. Also zum Beispiel, wenn man etwas aus> einer Queue auslesen möchte und die noch leer ist, dann wird ein> PendSV-IRQ über den Aufruf von taskYield() in der entsprechenden> API-Funktion getriggert.> Ein vTaskDelay() triggert ebenso den PendSV-IRQ.> taskYield() selber braucht man übrigens nicht aufrufen. Es gibt übrigens> gleich eine Reihe von taskYield-Funktionen (je nach Kontext ruft> FreeRTOS eine andere auf).>> Ich kann übrigens aus eigener Erfahrung jedem, der mehr mit FreeRTOS> macht, raten, sich die Sources mal anzusehen. Bringt viel fürs> Verständnis.> Am besten mit port.c anfangen, dann tasks.c. Den Rest dann nach Bedarf.> FreeRTOS ist wirklich überschaubar.
Ich werde mir den Source-Code bei Zeiten mal (nach der Klausurenphase
;)) genauer anschauen. Danke auf jeden Fall aber schonmal für die
Erklärungen.
Gruß
Jonas B. schrieb:> Nochmal, wieviele Daten sind das??
Mit einem anderen µC (STM32G031) hatte ich auch schon über UART/RS232
auf die selbe Art und Weise Daten geschickt hatte. Da allerdings nur
eine Variable, sprich 4Bytes mit 1KHz. Das kam auch kontinuierlich im
Terminal über den USB-RS232-Adapter an.
Fragenhagel schrieb:> Es sind exakt 10 Bytes
Mit dem Kopfrechnen klappts auch nicht mehr so gut. 2*3Bytes + 1Byte
ergeben natürlich 7Byte...
Rolf M. schrieb:> Kein Thema ist übertrieben. Bei 115200 Baud liegt das Maximum bei 11> Bytes.
Ja gut, das stimmt natürlich wenn man davon ausgeht, dass ein Parity-Bit
und 2 Stop-Bits mitgesendet werden. Mit der Baudrate könnte ich
theoretisch aber auch mal versuchsweise hochgehen. Der USB-RS232 Wandler
kann theoretisch bis 1MBaud. Ich weiß garnicht wo da die Grenze vom PC
liegt aber 256000Baud sollten ja noch drin sein.
Fragenhagel schrieb:> Rolf M. schrieb:>> Kein Thema ist übertrieben. Bei 115200 Baud liegt das Maximum bei 11>> Bytes.>> Ja gut, das stimmt natürlich wenn man davon ausgeht, dass ein Parity-Bit> und 2 Stop-Bits mitgesendet werden.
Nö. Damit bekommst du nur noch 9 Bytes drüber.
1 Startbit, 8 Datenbits, 1 Stopbit, macht 10 Bits pro Byte. Also 11.520
Bytes pro Sekunde bzw. 11,52 Bytes pro Millisekunde.
>Kein Thema ist übertrieben. Bei 115200 Baud liegt das Maximum bei 11>Bytes.
Eben und das auch nur wenn sonst nichts anderes gemacht wird, du hast ja
noch den Overhead from freertos.
>Mit der Baudrate könnte ich>theoretisch aber auch mal versuchsweise hochgehen.
Das oder eben weniger senden, einfach mal zum Testen um den Fehler
einzukreisen...
Gruß J
Jonas B. schrieb:> Eben und das auch nur wenn sonst nichts anderes gemacht wird, du hast ja> noch den Overhead from freertos.>>>Mit der Baudrate könnte ich>>theoretisch aber auch mal versuchsweise hochgehen.>> Das oder eben weniger senden, einfach mal zum Testen um den Fehler> einzukreisen...
Werde ich machen wenn ich wieder im Labor bin. Das wird leider erst
nächste Woche wieder der Fall sein. Der F4 packt es ja problemlos aber
klar der Taktet auch mehr als doppelt so schnell (64MHz -> 168MHz).
Rolf M. schrieb:> Nö. Damit bekommst du nur noch 9 Bytes drüber.> 1 Startbit, 8 Datenbits, 1 Stopbit, macht 10 Bits pro Byte. Also 11.520> Bytes pro Sekunde bzw. 11,52 Bytes pro Millisekunde.
Da hast du natürlich recht. Keine Ahnung was da rechentechnisch in
meinem Kopf gestern los war.
Du könntest evtl. in den relevanten Funktionen ein Portpin wackeln und
mit dem Scope ansehen. Dann siehst du das Timing und erkennst ob es
eingehalten wird.
So ich habe es nun endlich wieder geschafft mich dem Problem zu widmen.
Ich habe die Baudrate mal auf 500.000 erhöht -> keine Änderung.
Jetzt wollte ich das ganze mal ohne FreeRTOS umsetzen und stoße dabei
auf weitere Probleme. So wie es aussieht hat der SysTick einen weg.
Lasse ich einfach nur ein leere Programm laufen in dem ich in der
while-loop mittels HAL_Delay() und HAL_GPIO_TogglePin() jede Sekunde
eine LED togglen möchte passiert nichts. Ohne Delay im Debugger lief das
Programm Schritt für Schritt durch weshalb ich darauf geschlossen hatte
das etwas mit dem SysTick nicht stimmt. Und tatsächlich, sobald ich TIM1
als SysCLK Konfiguriere funktioniert das Programm und es wird anständig
eine Sekunde gewartet. Leider schickt die UART immernoch "schwallartig"
raus.
Den Chip hatte ich letze Woche noch gewechselt also kann es eigentlich
kein defekter Chip sein. Oder beim Einlöten ist der selbe Fehler
aufgetreten. Es ist kein externer Tacktgeber angeschlossen.
Ich nutze weder CubeMX mit generierten Code noch HAL
Ich konfiguriere die Clock nach Datenblatt manuell.
Rufe dann die SystemCoreClockUpdate() aus der CMSIS auf
welches die SystemCoreClock-Variable setzt.
Dann setze ich den SysTick Interrupt z.b. auf 1ms
SysTick_Config(SystemCoreClock / 1000)
Das ist ebenfalls eine Funktion aus der CMSIS.
Alle Cortex-M CPUs haben einen extra Timer für den SysTick, sodass man
keinen General-Purpose-Timer dafür opfern muss.
Christian K. schrieb:> Alle Cortex-M CPUs haben einen extra Timer für den SysTick, sodass man> keinen General-Purpose-Timer dafür opfern muss.
Das ist mir natürlich bewusst. Aus welchem Grund aber auch immer scheint
dieser nicht richtig zu funktionieren. Bei der Konfiguration in CubeMX
kann man eigentlich nichts falsch machen und ein Fehler ist mir bisher
auch nicht aufgefallen.
Zum eigentlichen Thema:
Ich habe im Datenblatt des USB-RS232 Adapter herausgefunden, dass dieser
tatsächlich default 4kB zwischen puffert. Er wenn dieser Puffer gefüllt
oder ein Timeout erreicht ist, werden die Daten an den PC
weitergeleitet. Das würde erklären warum "schwallartig" Daten
reinkommen.
Leider konnte ich bisher noch nicht herausfinden wie man die Packetgröße
ändert.
Irgendwie lässt sich das ganze wohl konfigurieren.
Fragenhagel schrieb:> Die Empfangs- und Sendebuffer lassen sich direkt im Windowstreiber> konfigurieren
Immer das gleiche mit USB-Seriell Wandler. Die machen so oft Ärger.....
Aber schön dass du es gefunden hast.