I don't think that DMA really is helpful. First all chars to transmit have to be copied into the ring buffer, so that these chars can no more be modified in user code. That's blocking code again if the buffer gets full. DMA has to be enabled with every new char in the buffer, causing further overhead.
A ring buffer (Stream class) with Rx and Tx seems sufficient to me.
As I have mentioned before I have implementation that uses their ring buffer, although does not use DMA, it does use the TxFIFO to reduce the number of interrupts... There is a very stale PR on it that has not been touched.
Not sure if it still works. For the most part not playing with these boards, these days.
It is if it is implemented. Current release, not implemented as part of the Serial code, so defaults back to Stream (or was it Print) class that returns 0.
The code is/was setup to use the buffer already built into the software code. That is the Serial object has:
arduino::SafeRingBufferN<SERIAL_BUFFER_SIZE> rxBuffer;
arduino::SafeRingBufferN<SERIAL_BUFFER_SIZE> txBuffer;
Whereby default the size is 512 bytes...
Where the FIFO comes into usage is for reducing how many interrupts have to be serviced for what you output. Without fifo (typically then double buffered), your interrupt handler is called for each character. Should mention only some of the UARTS have FIFO... If my memory is correct the Serial object (on Wifi) does not use one with FIFO so only double buffered.
The only time you need to or should wait is when your code calls something like Serial1.flush(). Not sure if that part was fully working or not, as at least before, the underlying code would return once the stuff was placed into the double buffer.
Note: I also ran into bugs in their implementation (unless it has been changed) that at times your output loses data. Why?
When the code output multiple bytes it returned as soon as it placed that last byte into the output register. If you immediately tried to start another output, it would place the first byte into the output register without checking to see if it was empty or not and if not empty, that last byte was lost.
Or if you output more stuff than can fit into the ring buffer. Note: I did have availableForWrite implemented...
Sorry, I have now sort of given up on these boards. Maybe at some later date, I might pull them out of storage...
Unless they changed it recently, that interrupt is called in two cases:
the case you mentioned and the case of double buffer, when the write register is not full...
Exactly they are not using it... They waste all of the memory for it, and don't use it...
I suppose I should sync up my dev branch and see if they have changed anything... But for now
I punted...
Good luck
Side note: That is one of the reasons I converted the main WIFI board I was using, to use native USB for the main Serial object. The UART that communicates between the ESP32 and it is only double buffer, and if they have not changed things, writing one byte to Serial while in a normal ISR hung the board... I tried to at least get them to up the priority of the Serial interrupt (lower the value) and in most cases it would not hang the processor (one line change...)
If you do anything serious with this platform and have problems, and wish to understand what the underlying stuff is doing, I highly suggest that you install a second install of the platform with the developers setup... Here is a link to a thread where @ptillisch give very good instructions on how to do this.
Again it has been awhile since I updated it. But a nice thing is that you can then browse through more of the sources to find out what some of the underlying code is doing.
That is probably the right one. My guess is that it is not a high enough priority, to assign someone to fully go through this. There was a reasonable number of changes that were needed, so would take a bit of work to double check everything.
At one point there were suggestions, that my leaving a debug code in it, should not be there, so I removed that. Also earlier I replaced their Safe Ring buffer with my own slightly modified version. I did not like that their implementation requires all interrupts to be disabled on each add and remove from the queue. Why? Because they share a common count variable, instead of simply having only one function touch the head pointer and another touch the tail pointer and compute count only when needed... So I tore that back out.
I don't know if I understood and did it right ... I only replaced the two files Serial.h and Serial.cpp in the original Arduino 1.1.0 core, then I tried this simple program on Arduino UNO R4 WiFi:
void setup() {
char msg[] = "The quick brown fox jump over the lazy dogs\r\n"
"The quick brown fox jump over the lazy dogs\r\n"
"The quick brown fox jump over the lazy dogs\r\n";
unsigned long t_start, t_end;
//
delay ( 500 );
// Serial.useDMA ( 0 );
Serial.begin ( 9600 );
t_start = micros();
Serial.println ( msg );
t_end = micros();
Serial.print ( "Number of printed chars: " );
Serial.println ( strlen ( msg ) );
Serial.print ( "Time to print in microseconds: " );
Serial.println ( t_end - t_start );
Serial.println ();
}
void loop() {
}
If I run it without the useDMA() I get the following result:
The quick brown fox jump over the lazy dogs
The quick brown fox jump over the lazy dogs
The quick brown fox jump over the lazy dogs
Number of printed chars: 135
Time to print in microseconds: 383
If instead I run it by removing the comment in front of the Serial.useDMA ( 0 ); line, I just get:
Th
so ... only the first two char printed
What am I doing wrong? Do I need to replace any other files besides the two above?
Also tried with a UNO R4 Minima ...
... the result is worse ... 435 µsec versus the 383 µsec of UNO R4 WiFi:
The quick brown fox jump over the lazy dogs
The quick brown fox jump over the lazy dogs
The quick brown fox jump over the lazy dogs
Number of printed chars: 135
Time to print in microseconds: 435
Trying to enable DMA, the compiler returns, as expected, the following error:
exit status 1
'class _SerialUSB' has no member named 'useDMA'
Hi @Delta_G, tried on Uno R4 WiFi using "Serial1" ...
... It works, but I was expecting a better result
The quick brown fox jump over the lazy dogs
The quick brown fox jump over the lazy dogs
The quick brown fox jump over the lazy dogs
Number of printed chars: 135
Time to print in microseconds: 377
... 377 µsec using DMA versus the 383 µsec with the standard serial.
Is it possible that there are only 7 µsec differences? ... what am I doing wrong?
Guglielmo
P.S.: ... but there seems to be a problem with the "Serial" going through the ESP32.
I have also tried this version of Serial.cpp and Serial.h, but ... I always get values very similar to the standard version ...
As I said, with the standard Arduino version I get a time of 383 µsec, while with the modified version (to use the txBuffer) I get 377 µsec (like DMA version) ...
Mmmm ... I was actually hoping that by using DMA or the TX buffer, the sending of the characters would be done in a non-blocking way, i.e. Serial.print() would immediately return control to the caller and the sending would then continue in the background ... instead, as far as I can see, control does not return to the program until the end of the transmission, delaying the execution of what follows ...
Mmm ... if I am not doing wrong, 135 characters at 9600 should take about 140 msec, and indeed, with the standard Arduino Serial1.println() I measure the value of 142724 µsec. (the previously reported value was wrong, my fault).
... do you think it's possible for you to bring the modification you made for the txBuffer to the SerialUSB as well? Because, on the MINIMA the Serial port is associated with the native USB and it would be nice to have the same functionality
... I can confirm that using the native USB "Serial" on Arduino MINIMA gives comparable times as using your version for UART with the txBuffer (or with DMA) ... around 440 µsec.
I was able to get the code to compile as below, but it only printed out 5 statements and then blocked the micro.
Serial1.useDMA(0);
Serial1.begin(115200);
What's also weird is that if I try it with just Serial, it won't compile. See below. Any ideas? Your fix with the ' Modify UART Class to Make Use of the txBuffer' PR definitely helped my system, but I can still tell that it is slightly blocking.
Serial.useDMA(0);
Serial.begin(115200);
error: 'class _SerialUSB' has no member named 'useDMA'
Serial.useDMA(0);
Interesting Ok. So the changes that you made, even to the 'Modify UART Class to Make Use of the txBuffer' won't fix regular Serial.print() calls? Only Serial1.print()? I swear it seems to have improved my system by at least 80%.
My device communicates with a computer via status reports, so I have been using Serial. How would I even get this to work with Serial1 or 2 or UART serial?
Basically I am finding that my host single board computer is very slow to read incoming reports. This causes blocking as the TX buffer is trying to empty, and I am missing interrupt triggers on my encoders, but only when running from the single board computer, not a desktop.