Teensy 3.x multithreading library first release

Morning tonton81. That's for the lesson. Think I am going to print this thread to a file and extract a lessons learned from all the comments/suggestions on threading.

Thanks for the info the I2C bus. Was searching the web but gets pretty confusing, tmi sometimes. I came across this for I2c https://www.tindie.com/products/SwitchDocLabs/grove-i2c-4-channel-mux-extender--expander-board-/ and this https://shop.controleverything.com/products/long-distance-i2c-bus-extender. Wanted to be sure I didn't need it.

Thanks
Mike
 
you will not need it, if i can run utp at 10feet in an automotive environment, im sure your arm wont be radiating an EMF field to disrupt the signal :)

i too was confused about the mutex lock names, but thats just what they are, named locks, they cant be locked twice so they wait their turn. just know this if you put 2 different locks on Serial1 they will collide, thats why everything you do to serial1 you MUST keep the same mutex as other locations related to Serial1, and also note this, you can have several locks in each function, taking turns at writing data/ports spi/uart/spi, the more locks you put, the faster your sketch goes, because the lock doesnt need to do the entire function, this speeds up the data on both threads to access the ports while their both doing their functions taking turns without locking the entire duration of the function
 
just know this if you put 2 different locks on Serial1 they will collide, that's why everything you do to serial1 you MUST keep the same mutex as other locations related to Serial1, and also note this, you can have several locks in each function, taking turns at writing data/ports spi/uart/spi, the more locks you put, the faster your sketch goes, because the lock doesn't need to do the entire function, this speeds up the data on both threads to access the ports while their both doing their functions taking turns without locking the entire duration of the function

That's an interesting tidbit. Now to figure out how to use that with library calls that I have that use I2C and Serial in the lib. I know somewhere in this thread we talked about it. But I think if I put the locks around the calls it should suffice.
 
here is a good question
is it possible to lock 2 different mutexes before running a function since A) reads a value from i2c and B) writes update to spi bus
ex

Code:
{
Threads::Scope scope(wire1);
Threads::Scope scope(spi1);
if (read wire byte ) write (spi byte) <-- yes i know this isnt right but you get what i mean
}

will this dual lock work?
Yes, this works, but it's dangerous. If a second thread uses the locks in the reverse order, you can get a deadlock (1st thread may have locked wire1, 2nd thread spi1 and they are stuck waiting for the other guy to release his lock).

Code:
{
// 2nd thread, which will cause deadlocks
Threads::Scope scope(spi1);
Threads::Scope scope(wire1);
...
}

You need to make sure that all threads always acquire these nested locks in the same order.
 
ahh okay tni, so everywhere i need 2 or more locks, i could use same method above but have to keep the same order everywhere

i guess its asking for trouble if we put 3 locks then

i guess i could lock and add a local variable to store the i2c data then scope out and start a lock for the spi write
it will eat up a byte of memory but itll be gone with the scope as well. i guess its safer not to double up scopes then

thanks tni
 
mjs513: it should be possible, i havnt added the mutex locks but i have a thread writing to serial2 along with thread0, i havnt had issues yet but im sure it wont stay long as sooner or later itll walk over each other. however, it may just be a library i wrote and tested using protected variables, its working with multithreading in 2 threads, but also be aware in my previous post a few pages back, the private section of the code was definately not stable in multiple threads, once i removed private and replaced it with protected in the *.h file, its been threading great, so if your testing libraries thats one thing to look out for, i couldnt multithread my library until i changed private to protected in the *.h file
 
Last edited:
tonton81: I was looking at that post a couple of nights ago. Right now I am writing only to either Serial or Serial4. Getting ready to attach a GPS to another Serial port and I want to thread it, but have to see what affects its going to have. First I want to make sure I understand threads and what we discussed a couple of posts back.
 
update: in the quad function classless unlimited mcp23s17 chip control code i wrote, i added mutex lock enclosing each spi transaction, all the other calls to the function can remain untouched and theres 8 devices on that bus! its working well! all 8 chips on that bus are read/write polling great!

this means we can just edit the library spi/i2c/uart transactions and lock them within that section, no need to touch any other of your sketch!

this makes implementing teensythreads faster to do with many libraries and SHARING compatible! nice :)

i used in my function:

Code:
{ Threads::Scope scope(HW_SPI1);

now if i add a shared spi devices to SPI1 bus i just need to add that above code and everything will be happy together :)
 
Last edited:
mjs513, yup!

however, if your directly accessing the hardware (Wire1.write(), Wire1.read()) for example, you have no choice since those commands are not in one location, so you must scope those as well:

{ Threads::Scope scope(HW_WIRE1);

This will be quite a bit time consuming for editing if you dont have your calls in one location, as each direct access needs to have identical lock for the same bus

since my expander code is centralized, i only needed to lock the functions, all the calls to it are taken care of :)
 
Last edited:
Now. Here's the million dollar question. If I have to devices on the same bus that each use a different library or a different class instance, if I just wrap each call to the library and each are in a different thread but I just wrap each lib call with the same lock would that be sufficient. I haven't had a chance to test that. I just started playing with calls to a GPS which is on Serial4 and printing to Serial.

By the way decided to use to play with atomic variables since everything I have been reading is warning to stay away from volatiles for threading application.
 
i havnt touched my variables as of yet, just mutex, so far its running great. if you have 2+ classes accessing a library via threading, i recommend setting :private to :protected in the *.h file as :private didnt work at all in my testing of a library i worked on. if you use the same lock for everything, yes it will work, they will take turns passing the lock around. however, to increase performance, multiple locks can be better, like individual bus locks, as 2 or more locks can run simuletaneously since they are different, which is why i names the locks as the bus they are. example, while spi1 is locked, wire1 can still lock and run an i2c device in parallel with the spi bus lock, so you have i2c and spi happening at same time. if you keep the locks the same, the shortest thread will run more times than the longer thread but the "feel" of it all will just seem single threaded as each time it locks the other thread waits for release, and likewise, in this case its more or less a prioritized thread in a singlethreaded-like environment, kinda hard to explain but you prolly will get the idea

the point is, while something is locked, anything else not related to it can still run in the process, only if theres no same lock is ran into, regular code and different named locks can still perform, if the other thread hits the same mutex lock it just stits there like this:

while(1); //forever

the moment the other thread lets go, the block releases, and the thread continues
 
also you can run many libraries and devices on the same spi bus, and to make it work before implemtning it, in the *.cpp file find the spi transaction sections and put your scope lock around it the SAME name as the one used for the SAME spi lock you named. this should make not only the spi bus mutex lock work but also gives you ability to work with many classes without touching your sketch code after, because youve protected the section related to accessing the bus, theres no reason to scope lock any of your code at that point :)

like i said my spi1 bus has 8 spi port expanders, polled read and writing 24/7, both threads are read/write polling the gpios and/or their registers during runtime
 
I actually understand what you are saying and thanks for the confirmation. Since most of the fun I am having is with IMUs its probably better to put them on separate busses as opposed to the same bus with a different address. Unless of course I only have one bus.

Have to get into the I2C lib for Teensy. Would be even nicer is there was a threaded version.

Ok. I give up, my curiosity has gotten the better of me now. What the heck are you doing that you have so much hanging off your Teensy?
 
https://m.youtube.com/watch?v=bEWWOAI2ocM

IMG_0163.jpg
 
Wow. That's a project and a half. When are you going to add obstacle avoidance, lane departure, etc :)
 
that would be overkill no? ;)

both screens are on separate threads, but they are both using the same SPI bus to talk to the 8 expanders. both lcds are running at 625K baud uart speeds on serial1 and serial2 and data is always flowing from teensy to lcds as the animations are manually done by teensy as opposed by the lcd itself, so I can control them. Visually you can see the difference in speeds between before and now regarding the animations. They slow down on single threading because the 70kbyte code the loop has to go through before looping again takes time, especially with canbus and i2c devices being read all the time. now the 2nd screen has it's own dedicated loop and both are working at normal speeds now.
 
Last edited:
hello, sorry for asking such a question,
but I don't know what is a "thread" yet, I'm still noob in coding.

from the replys of this post,
I'm guessing its kind of hardware flash paging/partition
how's it different from choosing loop with a variable/boolean flag?
is it writing flash to better locations after compiler, and that runs the code faster?
or does threading means more?
not sure are my assumings correct?

thanks for answering :)
 
Hi Po Ting. You might want to check this YouTube video out:


From https://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm I got this definition.

Multithreading is a specialized form of multitasking and a multitasking is the feature that allows your computer to run two or more programs concurrently. In general, there are two types of multitasking: process-based and thread-based.
Process-based multitasking handles the concurrent execution of programs. Thread-based multitasking deals with the concurrent execution of pieces of the same program.

A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called a thread, and each thread defines a separate path of execution.
 
tonton81. Don't want to hijack this thread but what lcd's are u using. Also, do you have a reference on the Canbus and how to use it?

Anyway, I noticed a similar improvement in sensor responses. They seem to be return changes in say distances a lot quick and for me playing around with obstacle avoidance makes a big difference.

Thanks
Mike
 
just scoped lock HW_WIRE1 everywhere where Wire1 is being used, and moved small things into the 2nd thread to clean up the loop :) all 12 i2c devices on Wire1 are multithreading fine on the shared bus :)

I also did SPI2, even tho it's not on separate threads, so later on i can add another device on the same bus if needed
 
Last edited:
Just started working on impact of using library calls from within a thread. For the I am using my FreeIMU library with a MPU9250 and a MS5367 pressure sensor. I call one function from the freeimu library which uses the I2Cdev, MPU9250 and baro libraries. As a test I ran it first without threading and worked fine. I created a thread and called the getYawPitchRoll function which has the form getYawPitchRoll(* ypr). What happens is that it returns on set of values but the never updates. It gets stuck on one set of values. There are only sequential calls to query the sensors. Timing issue? Here is the code I used:

Code:
#include <Wire.h>
#include <SPI.h>
#include "TeensyThreads.h"

#include <atomic>
#include <I2Cdev.h>
#include <MPU60X0.h>
#include <BaroSensor.h>

//These are mandatory
#include <AP_Math_freeimu.h>
#include <Butter.h>    // Butterworth filter
#include <iCompass.h>
#include <MovingAvarageFilter.h>

//#define DEBUG
#include "DebugUtils.h"
#include "CommunicationUtils.h"
#include "DCM.h"
#include "FilteringScheme.h"
#include "RunningAverage.h"
#include "FreeIMU.h"

#include <EEPROM.h>
#define HAS_EEPPROM 1

//char str[512];
float ypr[3]; // yaw pitch roll
std::atomic<float> roll, pitch, yaw;

int sensor, sensor1;

// Set the FreeIMU object
FreeIMU my3IMU = FreeIMU();

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire.setClock(400000);
  
  delay(5);
  my3IMU.init(0x68, true); // the parameter enable or disable fast mode
  delay(5);
  sensor = threads.addThread(readSensor);
  threads.setTimeSlice(0, 1);
  threads.setTimeSlice(sensor, 1000);
  if (threads.getState(sensor) == Threads::RUNNING) Serial.println("Sensor 0 thread started");
}

void loop() { 
  //my3IMU.getYawPitchRoll(ypr);
  Serial.print("Sensor 0 Yaw: ");
  Serial.print(yaw);
  Serial.print(" Pitch: ");
  Serial.print(pitch);
  Serial.print(" Roll: ");
  Serial.print(roll);
  Serial.println("");
}

void readSensor(){
  float ypr[3]; // yaw pitch roll
  my3IMU.getYawPitchRoll(ypr);
  yaw = ypr[0];
  pitch = ypr[1];
  roll = ypr[2];
}

Don't know if I am missing something.
 
Back
Top