does serial.end() release all serial resources? optimization

Hello there,

Using a Nano and an oscilloscope to measure the accuracy of pulse sent every 100*µs, I found something weird that serial.end(), does not fully get you back the full performance compare to a sketch with no serial used a all. Here is the procedure I used:

3 cases to explain how I found out this:
1- Code with no serial: pulse is generated every 100*µs with an accuracy +-200ns ---> Very good

2-Code with in the setup serial.begin(9600), but no serial calls used in the loop : pulse is generated every 100*µs with an accuracy +-1.5µs ---> not precise enough for my application but understandable as serial.begin must add some overlay even is not used

3-Code with in the setup serial.begin(9600) but no serial calls used in the loop then use of serial.end() : pulse is generated every 100*µs with an accuracy +-1.5µs ---> still not precise enough for my application, I though that serial.end() would have released all serial resources and get back to case #1 performance, but no...

In my application, sometime I need to do some operation with the serial, bt when I'm not using serial I would like to make a Serial.end() and have my full 200ns accuracy...
What do you think?

The description of Serial.send() in the reference section merely says

Disables serial communication, allowing the RX and TX pins to be used for general input and output. To re-enable serial communication, call Serial.begin().

It says nothing about freeing all resources, only the Tx and Rx pins.

How are you generating the pulses ?

So my full code is quite long and complex as I generate a pluse when I receive a high input form an external source. But I tried to illustrate the issue with this simplified 2 pieces of code, to prove that even if you do a Serial.end() you don’t get full performance back compare to a code with no serial reference.

Any body knows how to use serial, then totally disable it to get back full performances?

Code 1: NO serial, the period of the generated signal is 1µs
Code 2: Serial. begin(9600) then just after Serial.end() but still performance are affected since the period of the generated signal is 2.8µs, almost 3 times slower!!!

Code 1: NO serial, the period of the generated signal is 1µs:
void setup() {
pinMode(5, OUTPUT);
noInterrupts(); //I noticed that I have a stabler signal disabling interrupts
}

void loop() {
PORTD |= 1 << 5; //fast digitalwrite HIGH on output 5
PORTD &= ~(1 << 5); //fast digitalwrite LOW on output 5
}

Code 2: Serial. begin(9600) then just after Serial.end() but still performance are affected since the period of the generated signal is 2.8µs, almost 3 times slower!!!
void setup() {
Serial.begin(9600);// serial used
Serial.end(); //then end
pinMode(5, OUTPUT);
noInterrupts(); //I noticed that I have a stabler signal disabling interrupts
}

void loop() {
PORTD |= 1 << 5; //fast digitalwrite HIGH on output 5
PORTD &= ~(1 << 5); //fast digitalwrite LOW on output 5
}

Any chance that there is traffic on your serial ports, even though you’re not reading it?
Does the timing variation go away if you do you own loop:

void loop() {
  while (1) {
     // existing code.
  }
}

In code that includes serial, main() will call SerialEventRun() in between each loop. That ought to be pretty deterministic in the absence of traffic, or with serial.end() called, but it’s still something you probably don’t need.

But I tried to illustrate the issue with this simplified 2 pieces of code, to prove that even if you do a Serial.end() you don't get full performance back compare to a code with no serial reference.

To be fair, I don't think that anyone has suggested that Serial.end() would get full performance back. Cutting out the hidden call to SerialEvent() as suggested does, however, seem a possibility.

  1. with Interrupts serial can't do any thing.

  2. THe only "resources" it use are pins 0+1.

  3. The code for serialEvent in main is olny put in IF serialEvent is declared. It's not in as standard.

Looks to me more like an artifact of the compilers code optimization.

Expecting software, even on a microcontroller, to do this kind of thing even with the simplest of code is silly. And turning on interrupts and it real will go all over the place.

Mark

It works!

I used the while(1) loop and getting great performances. Now I can use the serial and have some maximum performance in a same sketch. Thanks for your help. To be fair I also tried to change the title of the thread.

For reference and future generations, to measure performance I try to generate the shortest period as possible:

code1: no serial → period 1µs
code2: Serial → period 2.8µs
code3: Serial + while(1) → period 0.37µs =2.7Mhz!

code 3:

void setup() {
Serial.begin(9600);// serial used
noInterrupts(); //I noticed that I have a stabler signal disabling interrupts
}

void loop() {

//I do my serial stuff here

while(1)
{
PORTD |= 1 << 5; //fast digitalwrite HIGH on output 5
PORTD &= ~(1 << 5); //fast digitalwrite LOW on output 5
}
}

BTW, if you want short and accurately timed pulses, you should be looking at using the hardware peripherals; the timers are ... timers, but you can also think about sending particular bit sequences out SPI or UART ports to create pulses.

  1. The code for serialEvent in main is only put in IF serialEvent is declared. It's not in as standard.

Apparently not so :frowning: You get a default empty routine for serialEvent() itself, but there is substantial work that happens before that in serialEventRun():

void serialEventRun(void)
{
#ifdef serialEvent_implemented
  if (Serial.available()) serialEvent();

What makes you think the the compiler will leave an empty function in the code?

Mark

What makes you think the the compiler will leave an empty function in the code?

I dissassembled a sketch and looked at the code:

void setup() {
  Serial.begin(9600);
}
void loop(){}

I thought it was supposed to be smarter than that, too :frowning:

Yes but did you find a call to serialEventRun ?

Mark

Maybe the compiler only removes functions that are not called/referenced. And of course loop () is called from main ().

did you find a call to serialEventRun ?

yes, or I wouldn’t be saying that it happened. I tried 1.5.2, 1.0.5, 1.0.2, and 1.0, and they were all the same:

main()

int main(void) {
 4f0:   cf 93           push    r28
 4f2:   df 93           push    r29
        init();
 4f4:   0e 94 f9 02     call    0x5f2   ; 0x5f2 <init>
#if defined(USBCON)
        USB.attach();
#endif
        setup();
 4f8:   0e 94 60 00     call    0xc0    ; 0xc0 <setup>
        for (;;) {
                loop();
                if (serialEventRun) serialEventRun();
 4fc:   ce e9           ldi     r28, 0x9E       ; 158
 4fe:   d0 e0           ldi     r29, 0x00       ; 0
#endif
 500:   0e 94 5f 00     call    0xbe    ; 0xbe <loop>
                if (serialEventRun) serialEventRun();
 504:   20 97           sbiw    r28, 0x00       ; 0
 506:   e1 f3           breq    .-8             ; 0x500 <main+0x10>
 508:   0e 94 9e 00     call    0x13c   ; 0x13c <_Z14serialEventRunv>
 50c:   f9 cf           rjmp    .-14            ; 0x500 <main+0x10>

serialEventRun()

void serialEventRun(void)
{
#ifdef serialEvent_implemented
  if (Serial.available()) serialEvent();
 13c:   e0 91 a4 01     lds     r30, 0x01A4
 140:   f0 91 a5 01     lds     r31, 0x01A5
 144:   e0 5c           subi    r30, 0xC0       ; 192
 146:   ff 4f           sbci    r31, 0xFF       ; 255
 148:   81 91           ld      r24, Z+
 14a:   91 91           ld      r25, Z+
 14c:   20 81           ld      r18, Z
 14e:   31 81           ldd     r19, Z+1        ; 0x01
 150:   82 1b           sub     r24, r18
 152:   93 0b           sbc     r25, r19
 154:   8f 73           andi    r24, 0x3F       ; 63
 156:   90 70           andi    r25, 0x00       ; 0
 158:   89 2b           or      r24, r25
 15a:   11 f0           breq    .+4             ; 0x160 <_Z14serialEventRunv+0x24>
 15c:   0e 94 69 00     call    0xd2    ; 0xd2 <_Z11serialEventv>
 160:   08 95           ret

serialEvent()

000000d2 <_Z11serialEventv>:
!defined(SIG_UART0_RECV) && !defined(USART0_RX_vect) &&
!defined(SIG_UART_RECV)
#error “Don’t know what the Data Received vector is called for the first UART”
#else
void serialEvent() attribute((weak));
void serialEvent() {}
d2: 08 95 ret

so it would be better to wrap the test and call with an #ifdef instead of in the function...

int main(void) {
        init();
#if defined(USBCON)
        USB.attach();
#endif
        setup();
        for (;;) {
                loop();
#ifdef serialEvent_implemented
                if (serialEventRun) serialEventRun();
#endif
        }

Well, #ifdef won't work, because it's a compile-time decision, and the serialEvent() is in the user's sketch, serialEventRun() is in HardwareSerial.cpp but called from main(). You need some sort of link-time fixup (or later. This probably should have been implemented with a user-provided callback, similar to "attachInterrupt()")

As a workaround, it looks like you can put a null serialEventRun() in your sketch, and it will get used instead of the weightier version from HardwareSerial.