Forum Rule: Always post complete source code & details to reproduce any issue!
Page 2 of 2 FirstFirst 1 2
Results 26 to 36 of 36

Thread: Need examples of how to use eventResponder properly

  1. #26
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,874
    The "dumb" version is just this:
    Code:
    void yield(void)
    {      
             if (!_eventResponder_enabled) return;
    
        static uint8_t running=0;
    
        if (running) return; // TODO: does this need to be atomic?
        running = 1;
    
    
        // USB Serail - Add hack to minimize impact...
        if (usb_enable_serial_event_processing && Serial.available() ) serialEvent();
    
        // Current workaround until integrate with EventResponder.
        if (HardwareSerial::serial_event_handlers_active) HardwareSerial::processSerialEvents();
    
        running = 0;
        EventResponder::runFromYield();
        
    };
    A simpler version of "smart" could look like this:
    Code:
    void yield(void)
    {  
         static uint8_t running=0;    
         uint32_t e = _eventResponder_flags
         if ( (e & 1 == 0) || running) return;    
        
        if (e > 1) {
            running = 1;   
    
            // USB Serail - Add hack to minimize impact...
            if ((e & 0x02 ) ) serialEvent();
    
            // Current workaround until integrate with EventResponder.
            if ((e & 0x04) ) HardwareSerial::processSerialEvents();
    
        }
        running = 0;
        EventResponder::runFromYield();
    };
    I personally would be very happy with the first version, because I don't use "SerialEVents".
    The additions in the 2nd Version are just for the few who use it. It needs some more tweaks in the core to toggle the bits.
    You see - it is faster in any case.

    Both would switch to a faster Systick version, if it is disabled, too.
    Last edited by Frank B; 05-19-2020 at 03:25 PM.

  2. #27
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,624
    Frank, I guess I am missing something with your "dumb" version, that is how is: _eventResponder_enabled
    This set?

    If you say that either a program sets it or an API sets it... Then now sure if any easier than simply telling those sketches to do:
    Code:
    void yield() {}
    If it is sort of computed in the way that we are doing but is a composite value such that you only check maybe one variable instead of 3... Then yes easily doable. Still with all of the caveat, that it will stay active for USB and every Serial port you do a begin on until something causes their event function (and it is our default weak one) to be called, which removes themself.

    Again I am perfectly happy to help out. Now if we really wanted to remove the yield overhead, one could also go to bypass most/all of the yield calls.

    That is suppose we rename your: _eventResponder_enabled to _yield_enabled which maybe API to set value.

    Then change many/all places the call yield like:
    delay.c
    Code:
    void delay(uint32_t msec)
    {
    	uint32_t start;
    
    	if (msec == 0) return;
    	start = micros();
    	while (1) {
    		while ((micros() - start) >= 1000) {
    			if (--msec == 0) return;
    			start += 1000;
    		}
    		if(_yield_enabled) yield();
    	}
    	// TODO...
    }
    main.cpp
    Code:
    {
    extern "C" int main(void)
    #ifdef USING_MAKEFILE
    
    	// To use Teensy 4.0 without Arduino, simply put your code here.
    	// For example:
    
    	pinMode(13, OUTPUT);
    	while (1) {
    		digitalWriteFast(13, HIGH);
    		delay(500);
    		digitalWriteFast(13, LOW);
    		delay(500);
    	}
    
    
    #else
    	// Arduino's main() function just calls setup() and loop()....
    	setup();
    	while (1) {
    		loop();
    		if (_yield_enabled) yield();
    	}
    #endif
    }
    From Stream.cpp
    Code:
    int Stream::timedRead()
    {
      int c;
      unsigned long startMillis = millis();
      do {
        c = read();
        if (c >= 0) return c;
        if(_yield_enabled)yield();
      } while(millis() - startMillis < _timeout);
      return -1;     // -1 indicates timeout
    }
    
    // private method to peek stream with timeout
    int Stream::timedPeek()
    {
      int c;
      unsigned long startMillis = millis();
      do {
        c = peek();
        if (c >= 0) return c;
        if(_yield_enabled)yield();
      } while(millis() - startMillis < _timeout);
      return -1;     // -1 indicates timeout
    }
    I believe there are a few others scattered around, but I think this would hit the majority of them...

  3. #28
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,624
    EDIT of previous post, we could also maybe make where (or if) yield is called.
    Example we could have an api like: enableYieldOn(uint8_t yield_flags);
    And have flags like: YIELD_ON_LOOP, YIELD_ON_DELAY, YIELD_ON_STREAM, ...
    probably defaults to all. But one could make their sketch more compatible by setting on only ON_LOOP...


    Also I think I see that me may have some code missing from yield

    Code:
    #if defined(CDC2_STATUS_INTERFACE) && defined(CDC2_DATA_INTERFACE)
    if (SerialUSB1.available()) serialEventUSB1();
    #endif
    #if defined(CDC3_STATUS_INTERFACE) && defined(CDC3_DATA_INTERFACE)
    if (SerialUSB2.available()) serialEventUSB2();
    #endif
    And default implementations for these... Suppose we should add them(They are actually defined in usb_serial.h) :P

  4. #29
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,874
    Disable: #Post 14

    User calls a disable function

    (or enabled if he want to switch it on again)

    No it's NOT easier to add a emtpy yield to the skecth.
    You have to replace the Systick, too.

    Of course this can this do the user too .. but then we are at the point where we are now.

    Great, if you want to replace all the calls - perfect. Even better. I hope Paul will merge it. Plaese add a efficient way for the systick, too.

    My version would look like this:
    Code:
    extern "C" void systick_isr(void)  // ORIGINAL
    {
        systick_cycle_count = ARM_DWT_CYCCNT;
        systick_millis_count++;
        MillisTimer::runFromTimer();
    }
    
    extern "C" void systick_isr_noEventResponder(void)
    {
        systick_cycle_count = ARM_DWT_CYCCNT;
        systick_millis_count++;
    }
    
    uint8_t _eventResponder_enabled = true;
    void eventResponder_enable(void)
    {    
        _VectorsRam[15] = systick_isr;
        _eventResponder_enabled = true;
    }
    void eventResponder_disable(void) {
        _VectorsRam[15] = systick_isr_noEventResponder;
        _eventResponder_enabled = false;
    }
    (+ the new yield code - yours or mine)

  5. #30
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,874
    I let you the honor to do the PR.
    Thanks!

  6. #31
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,624
    @Frank - I am starting off with simplistic changes to see how far they get toward desired goal without requiring code to be changed to make use of it.
    I believe that makes it easier to get PR pulled in.

    That is instead of adding a new member which disables he eventResponder, I instead try to localize down the effects of it.
    So I did take your two version of the ISR and renamed them. The default on does not call the runFromTimer.

    Instead I only run the runFromTimer version if something in the sketch calls some thing like: myEventResponder.attachInterrupt(&myFunction);
    Which my guess will be about 99.98% of the sketches will not enable that code.

    As for Yield - I just made the yield function to be a friend of the eventResponder class and so it can check to see if there is anything waiting on the queue before calling off.
    So it is still testing maybe 4 things and then return...

    Will next look to see about reducing these tests slightly, but again not sure how big a win that will be. Will hack it slightly and see what you think.

    Pass one is up on https://github.com/KurtE/cores/tree/...educe_overhead

    Update: I know have the different parts all set one flag variable, such that it can try just one test at the start and if 0 returns.

    Currently that variable is defined in core_pins.h ...

    Let me know what you think.
    Last edited by KurtE; 05-19-2020 at 08:16 PM.

  7. #32
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,874
    I'll take a look tomorrow.
    Thanks.

  8. #33
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,874
    Quote Originally Posted by Frank B View Post
    I'll take a look tomorrow.
    Thanks.
    Looks good to me!
    Have you tested this?

  9. #34
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,624
    Quote Originally Posted by Frank B View Post
    Looks good to me!
    Have you tested this?
    I have done some rudimentary tests, but have not done anything yet with eventResponder.

    I will hack up one of the SPI test programs, to try out eventResponder using 3 different ways to be called. (normally I just use immediate)

  10. #35
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,624
    Here is a quick and dirty test to see if/when things change:
    Code:
    #include <SPI.h>
    #include <EventResponder.h>
    #define CS_PIN 10
    volatile bool event_happened = false;
    
    EventResponder event;
    static const uint8_t buffer[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
    void asyncEventResponder(EventResponderRef event_responder)
    {
      digitalWriteFast(CS_PIN, HIGH);
      event_happened = true;
      //Serial.println("Event happened");
    }
    
    void setup() {
      pinMode(CS_PIN, OUTPUT);
      digitalWriteFast(CS_PIN, HIGH);
      while (!Serial && millis() < 4000) ;  // wait for Serial port
      Serial.begin(115200);
      SPI.begin();
      Serial.println("SPI Test program");
    }
    
    void TimeYieldCalls(const char *sz) {
      yield();
      Serial.print(sz); Serial.flush();
      elapsedMicros em = 0;
      for (uint32_t i = 0; i < 1000; i++) yield();
      uint32_t elapsed = em;
      Serial.print(": ");
      Serial.println(elapsed, DEC);
      Serial.flush();
    }
    
    void loop() {
      while (Serial.read() != -1) ; // Make sure queue is empty.
      Serial.println("Press any key to run test");
      while (!Serial.available()) ; // will loop until it receives something
      while (Serial.read() != -1) ; // loop until queue is empty
      Serial.printf("start test yield_active_check_flags %x\n", yield_active_check_flags);
      Serial.printf("  systick ISR: %x\n", (uint32_t) _VectorsRam[15]);
      TimeYieldCalls("Start");
    
      // First try with immediate call.
      event.attachImmediate(&asyncEventResponder);
      Serial.printf("Test Immediate: %x %x\n", yield_active_check_flags, (uint32_t) _VectorsRam[15]);
      event.clearEvent();
      digitalWriteFast(CS_PIN, LOW);
      SPI.transfer(buffer, NULL, sizeof(buffer), event);
      while (!event_happened) ;
      TimeYieldCalls("After Immediate");
    
      // Use yield .
      event.detach();
      event.attach(&asyncEventResponder);
      Serial.printf("Test Immediate: %x %x\n", yield_active_check_flags, (uint32_t) _VectorsRam[15]);
      event.clearEvent();
      digitalWriteFast(CS_PIN, LOW);
      SPI.transfer(buffer, NULL, sizeof(buffer), event);
      while (!event_happened) ;
      TimeYieldCalls("After yield");
    
      // Use Interrupt .
      event.detach();
      event.attachInterrupt(&asyncEventResponder);
      Serial.printf("Test Interrupt: %x %x\n", yield_active_check_flags, (uint32_t) _VectorsRam[15]);
      event.clearEvent();
      digitalWriteFast(CS_PIN, LOW);
      SPI.transfer(buffer, NULL, sizeof(buffer), event);
      while (!event_happened) ;
      TimeYieldCalls("After Interrupt");
    }
    And test output...
    Code:
    SPI Test program
    
    Press any key to run test
    
    start test yield_active_check_flags 1
      systick ISR: 211d
    Start: 57
    
    Test Immediate: 1 211d
    After Immediate: 57
    
    Test Immediate: 5 211d
    After yield: 64
    
    Test Interrupt: 5 213d
    After Interrupt: 63
    
    Press any key to run test
    I may go ahead and do PR on it.

  11. #36
    Senior Member
    Join Date
    Aug 2017
    Posts
    249
    @KurtE, @Frank B - I have spent today understanding how EventResponder works using SPI.* and @KurtE test sketch. I think I have a fairly good understanding of how I can use it with MSC. In one version of MSC I have a three stage transfer() function now that I can implement the EventResponder in. Maybe using that with queuing a fifo'd array of transfers. Still trying to minimize MSC transfer complete polling.

    Anyway thanks guys

Posting Permissions

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