A Serial.write()
funkcióval 53 bájtot továbbítok PC-re. Az időméréshez a micros()
-t használom (az írási függvény előtt és után). Minden adás után 1 másodperces késés van.
A Serial.write()
függvény ideje 532 minket 1000000 átviteli sebességgel és 360 minket 9600 átviteli sebességgel.
A Serial.write()
függvény egyértelműen aszinkron, mert 53 bájt 9600 átviteli sebességgel történő továbbítása 53 * 8/9600 * 1e6 = 44167 minket. (Egyébként 1000000 baud sebesség esetén nem annyira nyilvánvaló, hogy a függvény aszinkron.)
A iv id előtt a Serial.availableForWrite()
szót használom. = “7de0d4d647”>
annak megerősítésére, hogy van-e elegendő hely a pufferben (ez minden alkalommal 63-at ad vissza, a puffer mérete alapértelmezés szerint 64).
Nem értem ezeket a számokat. memcpy()
az 53 bájt másolása csak 32-et vesz igénybe. A soros pufferre másolás nem azonos a memcpy()
függvénnyel? És miért különbség van a másolási időkben, amikor az átviteli sebesség eltér? A Serial.write () az eredmények alapján még lassabb, ha nagyobb az adatátviteli sebesség. Miért adja vissza a Serial.availableForWrite()
63, míg a puffer mérete 64 (a SERIAL_TX_BUFFER_SIZE szerint)?
Frissítés: Köszönöm minden válaszát.
Kipróbáltam egy másik könyvtárat a soros kommunikációhoz: https://github.com/greiman/SerialPort
Úgy tűnik, gyorsabb, mint az eredeti. 2 tim-t használok A mérések és a kód késleltetése miatt ez a 3 deltaTime az egész lépést a következőképpen ábrázolja:
writeTime + memcpyTime + delaymicros = computedTime! = realTime
Az első 2 alkalommal mérünk, a delaymicros az elméleti késés (900 us), ezekből ki tudom számolni a lépésidőt. Ez a kiszámított lépésidő különbözik a realTime-től (a mért lépésidő, én is az egész lépést mérem). Úgy tűnik, hogy a további idő megtalálható a késésben.
SerialPort könyvtár: 100 + 30 + 900 = 1030! = 1350
writeReal = 100 + 1350 – 1030 = 430
Arduino Serial: 570 + 30 + 900 = 1500! = 1520
writeReal = 570 + 1520 – 1500 = 590
Ezután megmértem a delaymicros időt (ami 900 elméletileg), a hiányzó idő ott található. 900 us késleltetést programoztam, de az első késés az elsőnél 1200 körül volt, a második tesztnél pedig 920 körül.
Ezek a mérések igazolhatják a megszakítások létét, mert csak az írási függvények mérése nem “t” adja meg az összes írási időt (különösen a letöltött soros könyvtár esetén). A letöltött könyvtár gyorsabban működhet, de a hibák miatt nagyobb tx puffert igényel (64 helyett 256 működik megfelelően).
Itt van a kód : CheckSum-ot használok a sendig függvényben (ami 570-532 = 38 us). A Simulink-et használom az adatok fogadására és feldolgozására.
struct sendMsg1 { byte errorCheck;//1 unsigned long dT1;//4 unsigned long dT2;//4 unsigned long t;//4 unsigned long plus[10];//40 };//53 sendMsg1 msg1; byte copyMsg1[53]; unsigned long time1; unsigned long time2; unsigned long time3; unsigned long time4; void setup() { Serial.begin(1000000); } void loop() { sensorRead(); sendMsg1_f(); //time3 = micros(); delayMicroseconds(900); //time4 = micros(); } void sensorRead() { time3 = micros(); msg1.t = micros(); for (unsigned long i = 0; i < 1; i++) { memcpy((void*)&(copyMsg1[i*sizeof(sendMsg1)]), (void*)&msg1, sizeof(sendMsg1)); } time4 = micros(); msg1.dT2 = time4 - time3; } void sendMsg1_f() { time1 = micros(); msg1.errorCheck = 0; for (int i = 0; i < sizeof(sendMsg1) - 1; i++) { msg1.errorCheck += ((byte*)&msg1)[i]; } Serial.write((byte*)&msg1,sizeof(sendMsg1)); time2 = micros(); msg1.dT1 = time2 - time1; }
Megjegyzések
- Megadhatja a kódot, amellyel ezeket az eredményeket kapta?
- 1 bájt puffer elvész a gyűrűs puffer megvalósítása miatt. A teljes puffer feje faraghat ‘ t ugyanabba az indexbe, mert a farok egy fej ugyanazon indexen van üres puffer esete
An swer
Ha megnézi a megvalósítást:
size_t HardwareSerial::write(uint8_t c) { _written = true; // If the buffer and the data register is empty, just write the byte // to the data register and be done. This shortcut helps // significantly improve the effective datarate at high (> // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) { // If TXC is cleared before writing UDR and the previous byte // completes before writing to UDR, TXC will be set but a byte // is still being transmitted causing flush() to return too soon. // So writing UDR must happen first. // Writing UDR and clearing TC must be done atomically, otherwise // interrupts might delay the TXC clear so the byte written to UDR // is transmitted (setting TXC) before clearing TXC. Then TXC will // be cleared when no bytes are left, causing flush() to hang ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { *_udr = c; #ifdef MPCM0 *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); #else *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << TXC0))); #endif } return 1; } tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE; // If the output buffer is full, there"s nothing for it other than to // wait for the interrupt handler to empty it a bit while (i == _tx_buffer_tail) { if (bit_is_clear(SREG, SREG_I)) { // Interrupts are disabled, so we"ll have to poll the data // register empty flag ourselves. If it is set, pretend an // interrupt has happened and call the handler to free up // space for us. if(bit_is_set(*_ucsra, UDRE0)) _tx_udr_empty_irq(); } else { // nop, the interrupt handler will free up space for us } } _tx_buffer[_tx_buffer_head] = c; // make atomic to prevent execution of ISR between setting the // head pointer and setting the interrupt flag resulting in buffer // retransmission ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { _tx_buffer_head = i; sbi(*_ucsrb, UDRIE0); } return 1; }
Alapvetően megválaszolja az összes kérdést.
Ha a puffer üres és nem küld semmit, akkor a karaktert közvetlenül elküldi, így szinkronizálja (de a karakter elküldése sokkal gyorsabb lesz, mint a függvényhívások fölött és így tovább)
Nem, nem használhatod a memcpy-t, mivel az csak a puffert helyettesíti, de semmi mást nem tesz, mint például az adatregiszter valójában készen indítása a fej / farok számlálók megszakítása és megfelelő beállítása (ez egy kerek puffer, így előfordulhat olyan eset, hogy felülírsz valamit a pufferen kívül)
És az írási funkciót külön-külön hívják meg az egyes karakterekhez (minden egyéb specializáció az írást használja ezt)
Ha a puffer megtelt, akkor “várni fog, amíg van hely egy másik karakter számára.
Válasz
A soros pufferbe másolás nem azonos a
, nem.
A soros puffer egy kör alakú puffer . Emiatt rengeteg számítás vesz részt annak meghatározásában, hogy pontosan hol található a pufferben a következő karakter elhelyezése. Ehhez idő kell. memcpy()
csak átmásolja az egyik memóriablokkot közvetlenül a másikra. Nem végez ellenőrzést, és nem lehet körkörös pufferként körbefutni.
A magasabb adatátviteli sebesség lassabbnak tűnik, mert az egyes karaktereket egy megszakítás veszi ki a pufferből.Minél nagyobb az adatátviteli sebesség, annál gyakrabban vált ki ez a megszakítás, és így a CPU annál több időt tölt el a megszakítás feldolgozásával a következő karakter küldéséhez. Hasonlóképpen, annál kevesebb idő áll a CPU rendelkezésére az adatok soros pufferbe helyezésének feldolgozásához.
Válasz
A tesztelt funkció feltehetően Serial.write(const uint8_t *buffer, size_t size)
. Ha a HardwareSerial.h fájlban keresi, akkor megjelenik a
using Print::write; // pull in write(str) and write(buf, size) from Print
és a megvalósítás a Print.cpp fájlban található:
/* default implementation: may be overridden */ size_t Print::write(const uint8_t *buffer, size_t size) { size_t n = 0; while (size--) { if (write(*buffer++)) n++; else break; } return n; }
Ez a bájtokat írja egyenként és minden bájt esetén a HardwareSerial::write(uint8_t c)
teljes kódja fut. Ide tartoznak a teljes vagy üres puffer tesztjei, valamint a _tx_buffer_head
frissítésének számtana.
Amint a kód feletti megjegyzésben szerepel, lehetséges lett volna felülírja ezt egy speciális megvalósítással. Elvileg lemásolhatja a lineáris puffert a gyűrűs pufferbe, legfeljebb két hívást használva a memcpy()
címre, és a _tx_buffer_head
frissítést csak egyszer végezheti el. Ez valószínűleg hatékonyabb lenne, mint a jelenlegi megvalósítás, legalábbis az Ön által használt tartományban lévő pufferméreteknél (közel, de kevesebb, mint a gyűrűs pufferkapacitás).
Megérné? Talán a te esetedben ez lenne. De bonyolultabbá teheti a kódot és több flash-t is igényelhet. Az előny pedig valószínűleg kicsi azok számára, akik kis puffereket írnak. Nem vagyok biztos abban, hogy az ilyen típusú optimalizálást megvalósító húzási kérelem elfogadható. Megpróbálhatja, ha van kedve hozzá.