Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 25 of 25

Thread: Threads... possibly going to cause issues?

  1. #1
    Senior Member
    Join Date
    Sep 2019
    Posts
    123

    Threads... possibly going to cause issues?

    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!

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,481
    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

  3. #3
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    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

  4. #4
    Senior Member
    Join Date
    Aug 2013
    Location
    Gothenburg, Sweden
    Posts
    419
    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

  5. #5
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,481
    not used DMX ... but see it in p#1 and then this current thread :: forum.pjrc.com/threads/67318-Question-regarding-TeensyDMX-h

    Not sure if the DMX in use is this one and it offers and help ...

    ... but if so the p#4 Timer interrupt idea might be good if the DMX value adjustment is a 'trivial' call

  6. #6
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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

  7. #7
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,481
    Serial1 and Serial3, or any other, are independent as long as a single thread, or isr(), uses them.

  8. #8
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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/TeensyThre...ster/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]

  9. #9
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    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.

  10. #10
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    7,220
    Quote Originally Posted by SteveSFX View Post
    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/TeensyThre...ster/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.

  11. #11
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    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

  12. #12
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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

  13. #13
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    7,220
    Quote Originally Posted by SteveSFX View Post
    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/41504...l=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.

  14. #14
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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

  15. #15
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    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

  16. #16
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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!

  17. #17
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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 by SteveSFX; 06-06-2021 at 10:57 AM.

  18. #18
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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.

  19. #19
    Senior Member
    Join Date
    Apr 2014
    Location
    Cheltenham, UK
    Posts
    302
    You might solve your problem if you use Flush(Serial2); after sending the bits out.

  20. #20
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    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

  21. #21
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    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

  22. #22
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    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 defragster; 06-09-2021 at 10:32 PM. Reason: edit per p#25

  23. #23
    Senior Member
    Join Date
    Sep 2019
    Posts
    123
    Quote Originally Posted by tonton81 View Post
    protect the resource, not unnecessary code, for performance reasons:

    [CODE]
    Threads::Scope scope(LockAnimation) {
    This line throws up and error... error: expected initializer before 'LockAnimation'

  24. #24
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    hmm it should not.. im not home to check what i used to have

  25. #25
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,834
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •