Vraag:
Waarom is Serial.Write zo langzamer bij het schrijven van x + 1 tekens dan bij het schrijven van x tekens?
Alex Millette
2018-09-10 23:45:21 UTC
view on stackexchange narkive permalink

Met Serial.Write () in een tijdgevoelige applicatie. Ik realiseer me dat het aantal tekens dat werd geschreven niet alleen een impact had op de tijd die werd besteed aan het schrijven in de buffer, maar zelfs een grotere impact had dan verwacht.

Bekijk de volgende schets, die de tijd meet die het kost om NB_CHARS-tekens te schrijven met Serial.Write () . Ik heb 1000 keer gesampled om een ​​betrouwbaardere meting te krijgen.

  #define NB_CHARS 100void setup () {Serial.begin (115200); uint8_t tekens [NB_CHARS]; voor (int i = 0; i<NB_CHARS; i ++) {chars [i] = 66; } unsigned lange time1, time2; unsigned long totalTime = 0; vertraging (100); voor (int i = 0; i<1000; i ++) {time1 = micros (); Serial.write (tekens, NB_CHARS); time2 = micros (); totalTime + = time2 - time1; vertraging (10); Serial.println (""); vertraging (10); } float meanTime = (float) totalTime / 1000.0f; Serial.print ("meanTime ="); Serial.println (meanTime);} void loop () {}  

Hier zijn enkele resultaten voor verschillende NB_CHARS-waarden:

  NB_CHARS: Tijd (microseconden) 1: 122: 183: 24 [...] 99: 2912100: 2997101: 3082 [...] 199: 11412200: 11497201: 11582  

Het lijkt erop dat in eerste instantie elke nieuwe karakter voegt ongeveer 6 microseconden vertraging toe, maar daarna wordt het 85 microseconden. Ik heb alle waarden van 1 tot 100 getest om te zien waar het verandert en het lijkt direct na 70 te zijn:

Serial.Write() delay as a function of number of chars

Het lijkt raar om me.

Een snelle Google-zoekopdracht brengt me bij deze post uit 2013 van Tom Carpenter. Ik weet niet zeker hoe waar dit is of dat het nog steeds relevant is.

Een deel van het probleem is dat de Hardware Serial-bibliotheek zo ongelooflijk inefficiënt is (ik denk niet dat ze het hadden kunnen maken minder efficiënt als ze het probeerden!). Nadat elke byte is verzonden, wordt er een interrupt aangeroepen die een leven lang de volgende byte in de buffer laadt om te verzenden. De schrijffunctie verspilt ook veel tijd aan het doen berekeningen over waar een teken in een ringbuffer moet worden geplaatst.

[... ]

Waarom is er zo'n "lange" vertraging voor elk teken en is is er een manier om het sneller te maken?

Wat gebeurt er na 70 tekens dat het nog verder vertraagt?

hogere baudrate instellen. bij goede verbindingen hoort minimaal 460800 baud.
het citaat is niet meer waar. bij bitsnelheden van meer dan 500 kbit / s wordt de interrupt niet gebruikt als de buffer leeg is. (bitsnelheid, geen baudsnelheid)
Drie antwoorden:
Majenko
2018-09-10 23:49:02 UTC
view on stackexchange narkive permalink

De Arduino-kern heeft een overdrachtsbuffer van 64 bytes. Tekens worden door de hardware uit die buffer gestuurd.

Als je veel tekens uit de serie schiet, worden de eerste 64 gewoon in die buffer geplaatst - bijna onmiddellijk.

Eenmaal die buffer is vol met je sketchblokken totdat de hardware een karakter heeft gestuurd om ruimte te maken.

Aangezien het direct verzendt vanaf het moment dat je het eerste karakter in de buffer plaatst, krijg je iets meer dan 64 bytes aan buffertijd (64 + het aantal dat is verzonden tegen de tijd dat de buffer volloopt).

Vanaf dat moment ben je volledig overgeleverd aan de baudrate die je hebt gekozen.

Aan om een ​​meer voorspelbare doorvoer te krijgen (en de snelheid te maximaliseren), moet u de Ardiuno-kern omzeilen en de UART-registers rechtstreeks manipuleren.

"je moet de Arduino-kern omzeilen en de UART-registers rechtstreeks manipuleren". Is hier een goede bibliotheek voor?
@AlexMillette Ummm ... nee. Dat is het hele punt. U vermijdt alle bibliotheek- / kernfuncties en gebruikt de registers rechtstreeks. Lees de datasheet.
Ik denk dat het @Majenko handig zou zijn als je Alex naar een voorbeeldcode zou kunnen wijzen ...
De kans is groot dat als je eenmaal de kern hebt omzeild, je code net zo traag zal werken als het rechtergedeelte en niet zo snel als het linkergedeelte.
Ja. In het beste geval kunt u met efficiëntere code meer dingen doen tussen het verzenden van de tekens door. Maar u kunt het verzenden van de tekens niet versnellen. Maar misschien is "doe meer dingen tussendoor" wat je nodig hebt.
Voor een vlotte seriële communicatie wilt u echt een MCU met DMA om de overdrachten voor u te doen ...
Jot
2018-09-11 03:38:08 UTC
view on stackexchange narkive permalink

Majenko gaf al het goede antwoord: zodra de buffer vol is, valt alles uit elkaar (voor een tijdgevoelige applicatie).

Het is mogelijk om de seriële buffer voor je project te vergroten. In de toekomst, met een nieuwe Arduino-versie, ben je misschien vergeten dat je een bibliotheek hebt gewijzigd en dat het project niet meer zo goed werkt.

Ik wil erop wijzen dat je de usb seriële poort die zich in sommige microcontrollers en processors bevindt. Dat is geen echte seriële uart met een baudrate en een klokje, maar een usb CDC device.
Een atmega32u4 en de arm m0 + hebben zo'n usb CDC seriële poort.
De arduino leonardo, micro en pro micro gebruiken de atmega32u4.
De arduino zero, m0, mkr series gebruiken de arm m0 + processor.

Hoe zit het met deze getallen:

  NB_CHARS: Tijd (microsec) 1: 6.932 : 7.603: 8.06 [...] 99: 153100: 154101: 152 [...] 199: 1685200: 1624201: 1705  

Dat is met een arm m0 + processor. Ik heb dezelfde sketch gebruikt als jij, maar ik heb de Serial gewijzigd in SerialUSB .

IMil
2018-09-11 08:55:39 UTC
view on stackexchange narkive permalink

Je "lange" vertraging is helemaal niet te lang.

Als je de doorvoer van het rechterdeel meet, heb je ongeveer 30 tekens verzonden in 2500 microseconden. Dit betekent dat de snelheid ruwweg (30 * 8 * 1.000.000) / 2500 is, wat 96.000 bits / sec is.

Het is iets minder dan 115.200 die u hebt opgegeven, maar seriële protocollen gebruiken servicebits, dus je zou in feite 8 moeten vervangen door 10. Dit geeft 120.000 baud wat binnen de meetfout valt.

Het linkerdeel is eigenlijk veel sneller dan je nodig had, maar zoals @Majenko terecht opmerkte , dit is te danken aan buffering. Blijkbaar kunt u de buffergrootte vergroten tot 256 bytes. Als u consistentie nodig heeft in plaats van maximale snelheid, kunt u in plaats daarvan de buffergrootte verkleinen.

Ten slotte moet u de prestaties testen in een realistisch scenario. In uw voorbeeld blijft u gegevens in een lus verzenden; Uiteraard vult u de buffer snel en loopt u tegen de snelheidslimiet aan. In het echte leven zou je lus waarschijnlijk wat invoer lezen, wat berekeningen uitvoeren en dan het resultaat verzenden. In dit geval zou u de buffer effectiever gebruiken.



Deze Q&A is automatisch vertaald vanuit de Engelse taal.De originele inhoud is beschikbaar op stackexchange, waarvoor we bedanken voor de cc by-sa 4.0-licentie waaronder het wordt gedistribueerd.
Loading...