Faster (than) Serial.write()?

Status
Not open for further replies.

someteen

Well-known member
Is there a way to send a byte over USB serial (Teensy 3.1) faster than using Serial.write()?

Using micros(), I have measured about 4us for a Serial.write() command (sending one byte). I'm using an IntervalTimer of 2us and I want to put that Serial.write() inside the timer subroutine. Actually, I don't need to send that byte every 2us but every one second.

I could put that Serial.write() in the main loop() but I don't know what's happening if the duration of Serial.write() is 4us and my timer is triggering every 2us. Is the serial transmission broken (interrupted)?
 
So just to be clear, it isn't the data transfer rate of USB that is of concern, if you only send one byte per second, it is the delay before the Serial.write() call returns? In other words, you want a non-blocking write?
 
Yes, you got it. I need to send one single byte (system status) every second, but I have this timer triggering every 2us. Could the serial write be splitted in chunks (noob question), between two timer interrupts? Or will it be canceled by the new timer occurrence?

I've just write a short sketch to test it. I've got Serial.write() working but I don't know if there was any bad influence over timer occurrences (delay/skip).
 
Last edited:
Are you doing other stuff on the 2us interrupt that can't wait? Just wondering about the over sample for needed 1sec updates. Seems a less time critical parallel timer could be set to handle it.
 
That 2us timer represents a timebase for generating various PWM signals and it couldn't be modified.

Anyway, I put a counter inside that timer subroutine and I've checked its value in the main loop(). When the counter reaches 500 000 ticks (thus 1 second), it prints the total duration of those 500 000 cycles. Surprisingly enough, it prints "996xxx" to "998xxx" microseconds, thus that timer is actually running faster than 2us.

Should I believe in IntervalTimer precision or should I trust micros()?

That's the code:

Code:
volatile int count;
double time1=0.0;
volatile double time2=0.0;
double time3=0.0;
IntervalTimer myTimer;

void setup() 
{
    Serial.begin(38400);
    myTimer.begin(dummy,2);
    time1=micros();
}

void loop() 
{  
    if (count>500000) 
    {
        count=0;
        time3=time2;
        time3=time3-time1;
        Serial.println(time3);
        time1=micros();
    }
}

void dummy() {time2=micros(); count++;}

PS: I found another issue while writing this message. If I declare "myTimer.begin(dummy,2.0);" instead of "myTimer.begin(dummy,2);", the counter prints "100xxxx" to "101xxxx" microseconds! So now it's clear that IntervalTimer precision isn't the best.
 
I am unsurprised at your results for a couple of reasons, I think the 996xxx to 101xxxx isn't that bad considering how much you manipulate the values of floating point variables - it would surprise me more if your interrupt is not firing a couple of times while the conditional portion of your 'loop' routine is executing as well rather than if it never does fire while that executes.

Would you mind elaborating how you are going to use this 2uS interrupt in relation to PWM? I ask because doing PWM is like breathing (only even easier) to processors in general and it sounds like you might be reinventing spokes while you could just be rolling around on wheels, so to speak.
 
Those PWM signals are interleaved and have a dynamically changed duty cycle ratio.

The minimum ON time is 2us so that's why I've choose this value. Anyway, you're right, those results were pretty good, actually. I've tried to further fine tune the timer interval (1.9001 to 1.9999) but I couldn't get a better result. Seems like I've reached the timer accuracy threshold so I'm going to live with that.

By the way, how to find out if there were any skipped interrupts during Serial.write() process?
 
Do you have an oscilloscope?

Edit: Soz, wait, oscilloscope isn't going to do it unless it has capture and enough memory.

Maybe you could use a second teensy; toggle a pin in your interrupt, connect that pin (100R resistor between safer than hard link) to a pin on the other teensy and count pulses - dodge floating point stuff for better performance and try it with and without the first teensy trying to send Serial data.
 
Last edited:
Well.. I have good news.

First, that micros() instruction is time consumming so there was a problem with interrupts cycle counting. I had this condition in my loop() subroutine:

Code:
if (count>=500000)

Actually, printing this "count" variable, I realized that it was in a range of 500,020 - 500,080, thus the CPU was so busy managing the interrupt subroutine that the first (closer) value of "count" to be tested in the loop() were much bigger than 500,000. That means there was a mistake to count 500,000 ticks x 2us to verify that "one second" result (there were actually 500,0xx x 2us).

After optimizing the test code, I've got consistent results (the loop condition actually triggering at count = 500,000) and those 500,000 interrupt occurrences ran in (exactly) 1,000,000us. I even ran a single Serial.write() during those 500,000 cycles with no timming influence.
 
If you change it back to
Code:
if (count>[B]=[/B]500000)
I expect you won't see any other result for 'count' than 500000 unless you 'un-optimise' your code - I doubt having the compiler add the bigger than condition will make any difference to your current results.

Anyway, well done getting the result you wanted :)
 
That condition it's still the same (count>=500000) but, before optimization, first value to be checked was in range of 500020-500080. The CPU was too busy with the interrupt subroutine (triggered every 2us) so the main loop() and its condition was skipped a lot of times, between two interrupt occurences.

For example, the loop() condition was checked when "count" was 499975 and the next available CPU time for that was when count was 500025 and so on.

After removing "time2=micros()" from the interrupt subroutine, the CPU actually had time to check the loop() condition after every occurrence of interrupt subroutine so it actually checked that condition when "count" was exactly 500000.

Thanks for your support!
 
Status
Not open for further replies.
Back
Top