Threads... possibly going to cause issues?

Status
Not open for further replies.

SteveSFX

Well-known member
Hello all

I have a program that runs in a loop, and issues DMX values as required, depending on the state of 5 buttons. Pretty simple.

The DMX values are set up to either switch on and off immediately to preset levels, or ramps up and down over a set period of time in seconds.

These adjusted DMX values are adjusted and send every 100ms using a millis() timer.

It all worked fine until I added some other code to the loop that checks some serial data from another sensor. This has now thrown that timing out the window and the ramping of the values takes a third longer than it should, and I assume this will only get worse as I add more code to the main loop.

I have never used threads on the Teensy before. Is this a possible use for threading? As in, could I allocate that serial DMX data to be sent on it's own thread?
Or, am I totally mis-understanding how that would work.

Also, if the DMX data is sent on Serial1, but my other main loop data is read on Serial3, am I going to run into conflicts?

Thanks!
 
Sounds like it could work.

As long as two threads don't use the same Serial or other resource

Expect The DMX thread to get called many times before it is ready to send perhaps and when not time to send just thread.yield
 
yes if you are using the same resource in both threads you will need to use a mutex, but it should work fine, I've used 2 UART displays in threads and the responses are visually noticable :)
 
Threads might or might not solve your issues, this depends a lot on the thread scheduling algorithm and the synchronization needs. Threads does not 'magically' give create more time, in most cases threads uses more resources and processor time than a straight loop.
If you want a 'high performance' thread that is run exactly every 100ms independent of other UI and IO then perhaps run that code in a timer interrupt. If the sending of DMX packets is done by DMA from a fixed buffer through a serial port then the time spent in that timer interrupt is just to setup the dma and perhaps give the serial port a small kick to get started
 
Yes. After further investigation, I think I may end up making the problem just as bad, but in a different way.
I would imagine I will run into issues with the serial ports 1 and 3, as their use would probably overlap. I would need to safe-guard against that.

I think I may have to simply put in place logic to prevent reading the sensor, while any DMX traffic is likely
 
Can anyone assist with pausing a thread?

I have a thread that is collecting data each second, and it's working fine.

Reading the library notes, I believe you can pause and resume the threads, but I cannot seem to format the command correctly.

I include my library: #include "TeensyThreads.h"
I start my thread: threads.addThread(ReadPZEM); (The sub-routine is called ReadPZEM)
In the sub-routine, there is a threads.delay(1000);
I end the sub-routine with threads.yield();

https://github.com/ftrias/TeensyThreads/blob/master/readme.md

I don't seem to be able to issue the suspend command correctly... int suspend(int id).
Obviously its not: threads.suspend(ReadPZEM);

I get a: error: invalid conversion from 'void (*)()' to 'int' [-fpermissive]
 
why not just create a volatile global variable bool and set it when you want it to pause and unset it for when you want it to continue.. then in that thread you could always say

Code:
while (var_busy); // it will stay here until var_busy is 0.

if you are trying to ressolve resource conflicts, use a scoped mutex instead.

You mentioned serial port(s) overlapping, you want a mutex here, and keep the ports AND mutex out of interrupts, to prevent a deadlock.
 
Can anyone assist with pausing a thread?

I have a thread that is collecting data each second, and it's working fine.

Reading the library notes, I believe you can pause and resume the threads, but I cannot seem to format the command correctly.

I include my library: #include "TeensyThreads.h"
I start my thread: threads.addThread(ReadPZEM); (The sub-routine is called ReadPZEM)
In the sub-routine, there is a threads.delay(1000);
I end the sub-routine with threads.yield();

https://github.com/ftrias/TeensyThreads/blob/master/readme.md

I don't seem to be able to issue the suspend command correctly... int suspend(int id).
Obviously its not: threads.suspend(ReadPZEM);

I get a: error: invalid conversion from 'void (*)()' to 'int' [-fpermissive]

actually suspend does work but not with the way you are using it.
1. create a global variable: int PZEM;
2. in setup do:
PZEM = threads.addThread(ReadPZEM);
3. Then you can use suspend like this
threads.suspend(PZEM);
4, and to restart
threads.restart(PZEM);

Note: PZEM is the thread ID which is required by suspend and restart not the thread name.

Use it that way all the time.
 
be aware that if you suspend it during resource usage (serial) you may have overlapping serial prints back to back after it resumes. Something to think about :)
 
OK. Thanks for all the advice. I will tinker further

I do get data from Serial1 in the main loop, and Serial3 in the thread. Not sure how to implement the Mutex thing.
I will have to look into that.

Thanks
 
OK. Thanks for all the advice. I will tinker further

I do get data from Serial1 in the main loop, and Serial3 in the thread. Not sure how to implement the Mutex thing.
I will have to look into that.

Thanks

@tonton81 did a lot of explaining about mutex and scope locks in the primary thread on TeensyThreads. YOu can start here https://forum.pjrc.com/threads/4150...-first-release?p=177202&viewfull=1#post177202 and do a search for further info on doing it. He had a really great write up on it for serial but can not find it at the moment.
 
OK. well the threads method is interesting, and did work.... to a degree.
But I have found it also can lead to the processor totally locking up for reasons I don't understand and cannot work out. TOTALLY sure it's my programming error, but I think I will have to come up with a different method.

Thanks for all your help
 
if it locks up it means:

1) you are accessing the same resource in multiple threads/isr without a mutex (mutex is not for isr use!)
2) you are using a mutex in an isr
3) mutex deadlock (setting the same mutex twice within same thread
 
Hi

I worked out what was causing the lock-up. I was jumping to the Thread routine from another line by accident.
My interrupts are not in that thread

See where we go from here!
 
So, I declare a Mutex:

Code:
Threads::Mutex LockAnimation;

Then, in the subroutine, I call it first to 'protect' that sub routine?

Code:
void Animation() {

  Threads::Scope scope(LockAnimation);

  while (1) {

    do {
      threads.yield();
    } while (AnimateRun == 0);

    Animate++;
    if (Animate > 3) {
      Animate = 1;
    }

    threads.delay(1000);                                                                                                                                 // Sampling time

    if (Animate == 1) {
      Serial2.print("t1.picc=2"); Serial2.print("\xFF\xFF\xFF");
      Serial2.print("t3.picc=1"); Serial2.print("\xFF\xFF\xFF");
    }
    else if (Animate == 2) {
      Serial2.print("t1.picc=1"); Serial2.print("\xFF\xFF\xFF");
      Serial2.print("t2.picc=2"); Serial2.print("\xFF\xFF\xFF");
    }
    else {
      Serial2.print("t2.picc=1"); Serial2.print("\xFF\xFF\xFF");
      Serial2.print("t3.picc=2"); Serial2.print("\xFF\xFF\xFF");
    }
    threads.yield();
  }

}

This should animate three arrows on my screen (which it does). But, it's still not doing it cleanly. and I am not sure why.
Sometimes it will draw two of the arrows at the same time. So it's missing out a step (because it doesn't delete the previous arrow).
 
Last edited:
Hmm OK.

If I write to my Nextion screen with a command in the main loop, occasionally it doesn't carry it out.
This must be because the thread has been called and therefore the serial command to the screen never completes. If I disable the thread, then the problem goes away.

I think this thread idea is more hassle than its worth.

I am starting to think I would be better off taking the processing of the 5x AC monitoring modules that the thread reads and putting them on their own processor. I though the Teensy 3.2 would be fast enough, but it doesn't appear to be.
 
Binned them. More trouble than they were worth. Interesting learning curve, and did get them working to a degree, but they created more issues than they solved.
I re-wrote the code more efficiently, and used a millis(); based system instead

Thanks for everyone's assistance
 
Threads::Scope scope(LockAnimation);

that suppose to be a scope, not a command. use { } brackets around the code.

effectively, the way you did it, the scope exits immediately making the mutex completely unused with your code
 
protect the resource, not unnecessary code, for performance reasons:

Code:
void Animation() {

  while (1) {

    do {
      threads.yield();
    } while (AnimateRun == 0);

    Animate++;
    if (Animate > 3) {
      Animate = 1;
    }

    threads.delay(1000);   // Sampling time

{ Threads::Scope scope(LockAnimation) 

    if (Animate == 1) {
      Serial2.print("t1.picc=2"); Serial2.print("\xFF\xFF\xFF");
      Serial2.print("t3.picc=1"); Serial2.print("\xFF\xFF\xFF");
    }
    else if (Animate == 2) {
      Serial2.print("t1.picc=1"); Serial2.print("\xFF\xFF\xFF");
      Serial2.print("t2.picc=2"); Serial2.print("\xFF\xFF\xFF");
    }
    else {
      Serial2.print("t2.picc=1"); Serial2.print("\xFF\xFF\xFF");
      Serial2.print("t3.picc=2"); Serial2.print("\xFF\xFF\xFF");
    }

} // end of scope mutex

    threads.yield();
  }

}

the reason is the delay in your code, if the mutex locks with the 1 sec delay, the other thread will lock at the mutex request until 1sec is up before it's released and code resumes. With the code I posted above, your thread delay won't affect other threads...

this is probably your arrow issue (the delay, plus a mutex that was not set correctly :)
 
Last edited by a moderator:
ok sorry you have to encapsulate the scope as well:

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

so move the bracket to the start of that line
 
Status
Not open for further replies.
Back
Top