Teensy 3.x multithreading library first release

ftrias

Well-known member
I created a preemptive threading library for the Teensy 3.x and am releasing it here in case anyone needs it and for comments and suggestions. It's a first release, so I would expect some bugs.

Source code: https://github.com/ftrias/TeensyThreads

Direct link to ZIP: https://github.com/ftrias/TeensyThreads/raw/master/Threads.zip

This library follows the strategy recommended in the Corex-M4 reference guide. It uses the built-in threading support of the Teensy's Cortex-M4 to implement basic threading. It supports a Teensy-like interface and a minimal std::thread interface from the C++11 standard. More technical information in the source code.

Examples:

Code:
#include <Threads.h>
volatile int count = 0;
void thread_func(int data){
  while(1) count += data;
}
void setup() {
  threads.addThread(thread_func, 1);
}
void loop() {
  Serial.println(count);
}

Using std::thread

Code:
#include <Threads.h>
volatile int count = 0;
void thread_func(int data){
  while(1) count += data;
}
void setup() {
  std::thread th1(thread_func, 1);
  th1.detach();
}
void loop() {
  Serial.println(count);
}

This project came about because I was coding a Teensy application with multiple things happening at the same time and really missed
multithreading available in other OSs. I searched for threading systems, but found nothing.

This combined with boredom and excess free time led to complete overkill for the solution and thus the implementation of simple threads.

For more info and documentation see the Git repository.
 
This seems to be very interesting - i'm sure it's useful and, if I have time, I'll take a closer look next weekend.
 
Thanks Frank B for looking at it if you have time! I forgot to mention that I only tested this on a Teensy 3.2, and not the newer Teensy 3.5 or 3.6.
 
Just downloaded latest, running "tests" on a 3.2 everything looks good, on a 3.6 get "Error while setting serial port parameters: 115,200 N 8 1" everytime the sketch tries to print to TeensyMonitor. Running 1.8.0 on Windows 10.
 
on a 3.6 get "Error while setting serial port parameters: 115,200 N 8 1" everytime the sketch tries to print to TeensyMonitor. Running 1.8.0 on Windows 10.

I'll be getting a 3.6 in the next few days and will see what the problem is and post my results here.
 
i would see a problem if both threads were accessing same hardware (i2c/spi/canbus) however, this looks indeed interesting when one thread can do spi/i2c/canbus polling while the other runs the rest of the program
 
Just wanted to confirm that I am getting a similar error on the "tests" example when running a 3.5, "Error while setting serial port parameters: 9,600 N 8 1". Running 1.6.12 on Windows 10. As cartere says the "Print" example works on the 3.5 as well. Also posted on GITHUB.
 
Thanks for the feedback on the Teensy 3.5. I just got a Teensy 3.6. I am waiting for a 3.5 to test. There are two issues:

1. The new Teensy has a floating point unit and its state must be saved between threads. That should not be to difficult to add.

2. The yield() function is not thread-safe because of some of the functions it calls. That is, it can't be called from two threads at the same time. Unfortunately, it is called by "delay()" and after every "loop()" call. Because of this, you will cause a crash if you use delay() within a thread. The Test example makes copious use of delay() and thus fails, whereas the Print example does not. I'm not sure why this isn't a problem for the Teensy 3.2, but maybe I just haven't seen it yet.

As a workaround, you can use threading library's "threads.delay()" function to yield CPU time to other threads. This should work fine and is probably preferred anyway. I will look into the root cause of why yield() is not thread-safe to see if there is a more universal solution.
 
Just to confirm are you saying that I should just substitute threads.delay() for delay() in the "tests" sketch? I gave it a try it anyway and it did run but all the test thread speed failed:
Code:
Test thread start ok
Test thread run state ok
Test thread return ok
Test thread speed ***FAIL***
Speed no threads: 7995502
Speed 1 thread: 8560480
Ratio: 1.07
Test set time slice ***FAIL***
Speed default ticks: 8558685
Speed 10 ticks: 8556003
Expected: 799550.25
Ratio with expected: 10.70
Test delay yield ok
Yield wait ratio: 1.07
Test thread end state ok
Test thread reinitialize ok
Test thread suspend ok
Test thread restart ok
Test thread stop
 
Just to confirm are you saying that I should just substitute threads.delay() for delay() in the "tests" sketch? I gave it a try it anyway and it did run but all the test thread speed failed

Yes, exactly. But looking at your output, only two test failed. The others were "ok". I'll have to fix up the wording to make it clearer. The tests that fail have to do with run time, which is affected by using "threads.delay()". I'll have to patch the code tomorrow to account for this problem. Thanks for trying it out.
 
My pleasure. Been looking for something like this for a long time. You put in a lot of effort into making this lib and it promises to be resolve a few challenges that I have been having. Great job on the lib.
 
If a local " void yield() {} " is defined it will override the 'weak' default system yield() - including calls from delay(). The ZILCH system uses this as its task switch mechanism. You can redefine it to empty or whatever would be useful and thread safe.
 
Took your suggestion and added void yield(){} to the "tests" sketch using just delay() and it just froze.
 
Took your suggestion and added void yield(){} to the "tests" sketch using just delay() and it just froze.

After looking more closely, I think there was more to this problem. I just finished adding support for the FPU on the Teensy 3.5 & 3.6. I also added a small fix for context switching within interrupts. This fixes some sporadic problems.

It seems to work on my Teensy 3.2 and 3.6. I don't have a 3.5 to test on at the moment, but one is on the way.

Do you mind downloading the latest from github and seeing if it works for you?
 
Sorry for the delay but I was out. I just downloaded and ran the tests.ino sketch and it seems to work no problem. Ran it at 120mHz. Here is the output from the monitor:
Code:
Test thread start OK
Test thread run state OK
Test thread return OK
Test thread speed OK
Speed no threads: 7993733
Speed 1 thread: 4318147
Ratio: 0.54
Test set time slice OK
Speed default ticks: 4317751
Speed 200 ticks: 5968047
Expected: 5757001.50
Ratio with expected: 1.04
Test delay yield OK
Yield wait ratio: 1.07
Test thread end state OK
Test thread reinitialize OK
Test stack usage OK
Test thread suspend OK
Test thread restart OK
Test thread stop OK
Test thread start OK
Test thread wait OK
Test thread wait time OK
Test thread kill OK
Test std::thread scope OK
Test infinite loop (will not end)
0: 0 sec 48357370
1: 5 sec 91535425

I will keep you posted as I incorporate it into applications. May be a while though. Have a couple of other coals in the fire as they say. Thanks for getting the issue fixed.

Mike
 
Hello! I am a beginner in programing and i have a problem with one of my projects. I need to run my loop very fast and also write data on an lcd. The lcd is blocking my other functions.
Can this library be used to run the LCD part of the program?
Thank you!
 
I tried it like the example.. But nothing... Like it doesn't control the outputs... I will try with something simpler. Can someone explain me the thread.delay() function? And what if I woul use delay instead of thread.delay()? Woul it stop both threads?
I am sorry for such stupid questions. I hope you can understand:)
 
@Tady, sometimes hard to know depending on what your hardware setup is. Like what type of LCD or how fast you need things, what do you need to do fast in your main loop?

With my Well monitor code, I want to read sensors reasonably often and update a PJRC screen. What I am doing is using an IntervalTimer (https://www.pjrc.com/teensy/td_timing_IntervalTimer.html), which I use to read my sensors (mostly analogReads) and set start variables.

Then the main code in the loop checks for state changes and updates the display...
 
I'm making a 3 axis servo driver for a car racing simulator (roll, pitch, traction loss). The regulator gets it position commands over UDP and I can make adjustments via a webpage. Now i have added a 20x4 lcd and a dashboard from a motorcycle. The motorcycle gauge cluster needs variable frequency for speed and RPM. Here I use a simple blink without delay example and I am varying the period lenght. Here is the problem. When the refresh LCD function is triggered the time the CPU spends on refreshing the LCD is so big that the needle of the RPM gauge jitters and the speed is all over the place. The motors aren't affected that much but the gauge is no good. I tryed the LiquidCrystalFast library. I wired the extra RW pin but the LCD gives me strange characters it seems that RW doesn't do much, i should be checking the LCD busy flag... i tried the Tone library ( it only works with one pin, so no good), I tried analogWriteFrequency and then analogWrite with abou 50% duty cycle again not working.
My only two options are to fix the lcd with the liquidCrystalFast of use multithreading...
So far no luck :)
 
Good luck: I have not used one of these type of displays (Parallel ) in a long long time. The last 2xsomething or 4xsomething displays I used what I think was called Serial backpack...

A quick look at the Liquid...Fast library, it looks like the RW pin keeps it from doing a delayMicroseconds(320) and instead do lots of pinMode, digitalWrites and digitalReads to figure out when the display is not busy.

The only other option I can think of is if possible not update all of your display at once. But try to break it up into small chunks...
 
What kind of displays do you use is not these? Graphical? I have a ili9341 laying around:) maybe i can adapt the code
 
Back
Top