Serial.write not working as expected (for me)
I got an unusual problem with a seemingly blocking call to write() while using Serial2 with 20MHz
I have put together a minimum working example to show my problem.
Futher down are several results shown, 20MHz, 16MHz and a comparision between 6MHz using both pll3_80m and osc_clk for the UART clock
For now I can confirm, that
- it does not depend on the Serial port, it is the same for all of them.
- it does not depend on the size of the buffer, that is sent, as long as the additional buffer is big enough (Serial2.addMemoryForWrite(addSndBuf, ADDBUFFERSIZE))
- it does not depend on wether an additional buffer is added
- it does depend on the Baudrate
I have not found anything specific for the teensy (4), but the arduino documentation talks about the .write function to be non-blocking.
"If there is enough empty space in the transmit buffer, Serial.write() will return before any characters are transmitted over serial"
But obviously it seems to be for different the Teensy. Even worse: for a lower baudrate the write function returns faster (seems to be true for 6+MHz)
Is there a way to make the Serial.write non-blocking and return immediatly?
I need these few micros desperatly for other stuff, as I need both, the fast transmission of data and low latency of Serial.write.
Also it is not the expected behaviour, for me. I am sorry, if I expect something that is not intented and if I missed a note somewhere. I am leaning onto arduinos notes here
Just to be clear about what I expect the behaviour to be:
I expected the write function to immediatly return, as the transmit buffer has enough free space.
That means sending should take 1-2 micros and flush about 70 to 80micros. Also the available bytes for write after the write function should be less than before, which would indicate unsent bytes.
The last point can be seen with lower baudrates, where most of the bytes get sent during flush(), as expected. Althoug the time for write() is still too high.
EDIT:
I have little experience with interrupts on the teensy, thus its just a thought: Would it be possible to achieve a fast returning write() by disabling the responsible interrupt right before write() and immediatelyactivate it afterwards?
Code:
// bare minimum for blocking Serial2.write
#define BAUD 20000000
#define ADDBUFFERSIZE (255)
uint8_t* addSndBuf;
uint8_t* buf2;
uint16_t bufLength2 = 132;
int microsA = 0;
int microsB = 0;
int microsC = 0;
int microsD = 0;
int A = 0;
int B = 0;
int C = 0;
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 2000) {
// wait up to 2 seconds for Arduino Serial Monitor
}
addSndBuf = new uint8_t[ADDBUFFERSIZE];
Serial2.addMemoryForWrite(addSndBuf, ADDBUFFERSIZE);
Serial2.begin(BAUD, SERIAL_8E1);
CCM_CSCDR1=105450240; //UART_CLK_SEL bit set to 0
buf2 = new uint8_t[bufLength2];
}
void loop() {
microsA = micros();
for (int i = 0; i < bufLength2; i++) buf2[i] = i;
microsB = micros();
A = Serial2.availableForWrite();
Serial2.write(buf2, bufLength2);
B = Serial2.availableForWrite();
microsC = micros();
Serial2.flush();
C = Serial2.availableForWrite();
microsD = micros();
Serial.println("+++++++++++++++++++++++++");
Serial.print("sending takes = "); Serial.println(microsC-microsB);
Serial.print("flush takes = "); Serial.println(microsD-microsC);
Serial.print("logging takes = "); Serial.println(microsD-microsA);
Serial.print("bytes to send = "); Serial.println(bufLength2);
Serial.print("A = "); Serial.println(A);
Serial.print("B = "); Serial.println(B);
Serial.print("C = "); Serial.println(C);
Serial.println("+++++++++++++++++++++++++");
delay(100); // more than enough time to send!
}
20 MBaud
Code:
+++++++++++++++++++++++++
sending takes = 72
flush takes = 1
logging takes = 75
bytes to send = 132
A = 294
B = 294
C = 294
+++++++++++++++++++++++++
16 MBaud
Code:
+++++++++++++++++++++++++
sending takes = 25
flush takes = 67
logging takes = 94
bytes to send = 132
A = 294
B = 201
C = 294
+++++++++++++++++++++++++
6 MBaud; pll3_80m
Code:
+++++++++++++++++++++++++
sending takes = 18
flush takes = 219
logging takes = 238
bytes to send = 132
A = 294
B = 175
C = 294
+++++++++++++++++++++++++
6 MBaud; osc_clk
Code:
+++++++++++++++++++++++++
sending takes = 18
flush takes = 225
logging takes = 245
bytes to send = 132
A = 294
B = 176
C = 294
+++++++++++++++++++++++++