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

Thread: Hooking into yield()

  1. #1
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935

    Hooking into yield()

    I know how to override yield() but doing this in a library is not very polite since it disables the standard yield functionality (serial event etc..)

    Does anyone know if it is possible to hook into yield without completely overriding it?

  2. #2
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,665
    I would override it and copy also existing yield functionality (backward compatibility)

  3. #3
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935
    I would override it and copy also existing yield functionality (backward compatibility)
    Pragmatic solution but not very elegant (need to change my lib whenever the core yield changes). Anyway, it does the job, so I implemented it this way.

  4. #4
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,378
    A weak blank subcall function called from yield() could be implemented to retain core configuration. run_from_yield()

    It can be teensy specific without breaking backward compatibility with arduino, and work through all teensyduino updates, thats how I implemented up to 3 background callbacks in flexcan for external accesses

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935
    run_from_yield()
    Ah, that's interesting. Never bothered to read into the event responder thing. On a first glance it looks like it should work, will give it a try later.

  6. #6
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935
    Somehow I still don't understand if / how it is possible to hook into yield without replacing it. I tried to use the event responder but this stuff is hard to understand without any documentation.

    What I would like to achieve is to add a function which should be called from yield. I.e., something with this effect:

    Code:
    void setup()
    {
         attachToYield(myFunction)
    }
    
    void yield()
    {
          // do all the usual yield stuff
    
         myFunction(); 
    }
    @Kurt: since you were working on yield recently you might know if this is even possible...
    @tonton81: Don't find the code you are referring above anymore. Do you have a link to the relevant part of flexcan?

  7. #7
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    11,780
    have stared at yield() regularly - if not using EventResponder or and Arduino serialEvent() code it can be replaced, but the PJRC weak version is all(weak) or nothing(replaced locally).

    PJRC yield() calls eventResponder - perhaps that is what tonton81 did? Added a run_from_yield() task to that?

  8. #8
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935
    Yes, currently I'm replacing it with my code but in libraries this is quite rude since users might need SerialEvents or the EventResponder.... Also copied the yield content from the core to my yield which works of course but that gets messy when the core yield gets updated as just happened.

  9. #9
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    11,780
    Quote Originally Posted by luni View Post
    Yes, currently I'm replacing it with my code but in libraries this is quite rude since users might need SerialEvents or the EventResponder.... Also copied the yield content from the core to my yield which works of course but that gets messy when the core yield gets updated as just happened.
    Yes, it did just happen. Big drop in overhead from work by @KurtE, but still better without it if unused. But new code won't work on old builds with added support 'stuff'
    Saw the yield options for timer (?) code - but didn't read sources how the option was done there.

  10. #10
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,916
    I believe the prescribed way is for you to use an eventResponder object (like I did for Async SPI), And you use the simple attach method, which adds your function to the list of things called by yield...

  11. #11
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935
    Thanks Kurt, tried your suggestion but it didn't work.

    Code:
    #include "EventResponder.h"
    
    EventResponder er;
    
    void testfunc(EventResponderRef r)
    {
      digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));  
    }
    
    void setup()
    {
      pinMode(LED_BUILTIN, OUTPUT);
      er.attach(testfunc);
    }
    
    void loop()
    {
    }

    Obviously something needs to trigger the event, so I did

    Code:
    #include "EventResponder.h"
    
    EventResponder er;
    
    void testfunc(EventResponderRef r)
    {
      digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));
    
      r.triggerEvent();  // trigger the next event after finishing this one
    }
    
    void setup()
    {
      pinMode(LED_BUILTIN, OUTPUT); 
    
      er.attach(testfunc);
      er.triggerEvent();    // start the chain
    }
    
    void loop()
    {
    }
    which works. Anyway, feels not right to do it that way... probably quite inefficient?

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935
    I meanwhile tested this hook to yield method and found that it is actually quite efficient. I put some explanations, example code and an easy to use attach function in the user wiki https://github.com/TeensyUser/doc/wi...ing-into-yield. I also changed the tick strategy of TeensyTimerTool software timers accordingly.

    Here the naked code for reference:

    Code:
    #include "EventResponder.h"
    
     void myCallback() // test function will be called from yield
     {
       digitalToggleFast(0);
     }
    
    void setup()
    {
      pinMode(LED_BUILTIN, OUTPUT);
      pinMode(0,OUTPUT);
    
      attachYieldFunc(myCallback); // attach the callback to the standard yield stack
    }
    
    void loop()
    {
      digitalToggleFast(LED_BUILTIN);
      delay(250);
    }
    
    
    // helpers-------------------------------------------------
    
    void attachYieldFunc(void (*_callback)(void))  // pass a pointer to the function you want to be called from yield
    {
      static void (*callback)() = _callback;       // store the passed in function pointer
      static EventResponder er;                    // define a new EventResponder which will handle the calls. 
    
      er.attach([](EventResponderRef r) {          // we can not directly attach our function to the responder since we need to retrigger the repsonder 
        callback();                                // instead we attach an inline defined relay function which ...
        r.triggerEvent();                          //... calls our function and then retriggers the responder to schedule the next call. 
      });
    
      er.triggerEvent();                           // to start the call chain we need to trigger the responder once
    }
    Last edited by luni; 06-28-2020 at 03:21 PM.

  13. #13
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,744

    Cool

    If you are willing to edit boards.txt to add linker options, you can use the wrap functionality of the linker:

    --wrap=symbol
    Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to "__wrap_symbol". Any undefined reference to "__real_symbol" will be resolved to symbol.

    This can be used to provide a wrapper for a system function. The wrapper function should be called "__wrap_symbol". If it wishes to call the system function, it should call "__real_symbol".

    Here is a trivial example:

    Code:
    void *
    __wrap_malloc (size_t c)
    {
      printf ("malloc called with %zu\n", c);
      return __real_malloc (c);
    }
    If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "__wrap_malloc" instead. The call to "__real_malloc" in "__wrap_malloc" will call the real "malloc" function.

    You may wish to provide a "__real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of "__real_malloc" in the same file as "__wrap_malloc"; if you do, the assembler may resolve the call before the linker has a chance to wrap it to "malloc".

    Note --wrap is an option to the linker. You will need to use the -Wl,--wrap=yield option when calling the GCC driver (i.e. gcc or g++).

  14. #14
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    935
    Interesting indeed! But I don't see how that would work if more than one user would like to add functions to yield?

  15. #15
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    11,780
    with something like:
    Code:
    void TeensyYield() {
      // real PJRC yeild
    }
    attribute 'weak' >> void yield() { TeensyYield(); }
    Will the compiler just replace the call to yield() with TeensyYield() as an optimization? Or will the HEX have a call to yield() calling TeensyYield()?

Posting Permissions

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