8000 Microsecond Timing? · Issue #9 · bxparks/AceRoutine · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Microsecond Timing? #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mkarliner opened this issue Aug 17, 2019 · 15 comments
Closed

Microsecond Timing? #9

mkarliner opened this issue Aug 17, 2019 · 15 comments

Comments

@mkarliner
Copy link
mkarliner commented Aug 17, 2019

I find myself needing sub-millisecond timings.
Presumably COROUTINE_AWAIT(microSeconds() > somestaticvariable)
will work, however, is there a better way, or is it worth making a new
convenience macro?

@bxparks
Copy link
Owner
bxparks commented Aug 17, 2019

I don't recommend using AceRoutine (or any cooperative multitasking framework for that matter) for sub-millisecond timing. Once your coroutine gives up control, there is no guarantee that it will regain control within any timeframe because the other coroutines may take arbitrary amounts of time. I suppose if ALL your coroutines take much less than the desiredMicroSeconds that you are waiting for, then it MIGHT work, but I don't know, this seems too fragile to be robust. My guess is that if you need this level of accurate timing, you probably want to a timer, and use an interrupt service routine instead.

@mkarliner
Copy link
Author

Well, I'm taking care to keep my coroutines really, really tight, and I was going to
use something speedier than a stock arduino, say the new 600Mhz Teensy, and
the application in this case is high speed pulsing of stepper motors, so a bit
of jitter is OK, so long as they all arrive in the same place after thousands of of pulses.
However, I'll think about the ISR approach. Thanks.

@bxparks
Copy link
Owner
bxparks commented Aug 18, 2019

Wow, that Teensy 4.0 is super fast. If you use only a handful of coroutines, and make sure that they yield within a few microseconds, then maybe AceRoutine could work for you.

With regards to adding a convenience macro COROUTINE_DELAY_MICROS(variable, micros), I'm hesitant because I'm not convinced that it would be generally useful. In the meantime, yeah, COROUTINE_AWAIT() will give you the functionality you want. If you try it on the Teensy 4.0, let me know how it works out.

@mkarliner
Copy link
Author

I guess I have a couple of points about this.

  • Through painful experience, implementing hand built scheduling works OK, but as inevitably, the complexity grows, you end up building a generalised system anyway, which I suspect doesn't have much less overhead than yours. The coroutine abstraction, even with the stackless caveats, allows me to think about my design easily. That being said, I stand in awe of say, GRBL, which gets up to some serious speeds, and I assume without coroutines?
  • My applications for this tend be more complex in the interactions between coroutines, rather than heavy on processing. I usually have a UI/backend on a desktop machine anyway, where there is the power to do the FFTs or whatever as background.
  • I'm aiming for timing in the hundreds of microseconds anyway, and often 'at least' rather than exactly. It's just frustrating to have a minimum of 1ms, when that's a lot of instructions on a ESP32 at 240Mhz with an overhead of .5us.

@bxparks
Copy link
Owner
bxparks commented Aug 18, 2019

No need to be frustrated. :-) The building blocks for what you want are already there. The convenience macro is something like:

#define COROUTINE_DELAY_MICROS(microCounter, delayMicros) \
    do { \
      microCounter = micros(); \
      COROUTINE_AWAIT(micros() - microCounter >= delayMicros); \
    } while (false)

To use this, you need to provide the microCounter as a static variable, just like COROUTINE_DELAY_SECONDS():

const uint16_t DELAY_MICROS = 200;

COROUTINE(foo) {
  static uint16_t microCounter;
  COROUTINE_LOOP() {
     ...
     COROUTINE_DELAY_MICROS(microCounter, DELAY_MICROS);
  }
}

I don't have any program that needs microsecond resolution to be able to test this. Let me know if it works for you.

Edit: There's a problem with integer overflow in the above code. Change the uint16_t to unsigned long. OR, insert a cast in the COROUTINE_AWAIT(), like this:

COROUTINE_AWAIT((uint16_t)(micros() - microCounter) >= delayMicros); \
```

@mkarliner
Copy link
Author

Thanks very much for those, my original question was really if there was a fundamental reason they wouldn't work.
I'll keep you posted.

bxparks added a commit that referenced this issue Aug 27, 2019
…void ambiguity with global millis() method (#9)
bxparks added a commit that referenced this issue Aug 27, 2019
bxparks added a commit that referenced this issue Aug 27, 2019
…INE_DELAY(), COROUTINE_DELAY_MILLIS() and COROUTINE_DELAY_MICROS() (#9)
bxparks added a commit that referenced this issue Aug 27, 2019
bxparks added a commit that referenced this issue Aug 27, 2019
@bxparks
Copy link
Owner
bxparks commented Aug 27, 2019

I had some idle time, so I implemented a COROUTINE_DELAY_MICROS() and wrote some validation tests. On a 16MHz ATmega328P, calling COROUTINE_DELAY_MICROS(50) with 50 microseconds can be off by 50%, which is to be expected. On a SAMD21, coroutine overhead is 20%. On an ESP8266, same 20% overhead. On an ESP32, the overhead error is about 8%.

@mkarliner
Copy link
Author

I've been playing around, and came to similar conclusions. However, I have to say that's great!
If you have a 16us switch time 50us is really on the edge but a few hundred should be acceptable, and with the fast processors even more so. Part of the motivation for this is to run the core of an EDM machine (Electro Discharge Machining), which has a very simple cycle of switch power on, analog read, maybe start stepper step, wait at least 100us. The 100us is a wait time for debris to clear, so if it's more, it just makes the machine a little slower. I was going to use a Blue Pill, (72Mhz Cortex M3), which should be fine for that job. I have a few other projects like that, so I hope this explains my motivation and awareness of the caveats. :-)

@bxparks
Copy link
Owner
bxparks commented Aug 27, 2019

Cool, sounds like a lot of fun. This feature has been released with v0.3, so I'm going to close this issue. If you run into trouble feel free to reopen, or create a new issue.

@bxparks bxparks closed this as completed Aug 27, 2019
@mkarliner
Copy link
Author

Thanks again!

@mkarliner
Copy link
Author
mkarliner commented Jun 2, 2021

Pity you dropped official support for microsecond timing, although I understand why.
Just for reference, here's a video of a TV ad I made using a rig with your uS delays to time all the pneumatics for this. https://www.youtube.com/watch?v=juc0Wh1qoe0. The petals were shot at 1000 fps (1 ms / frame) and each sequence was < 250 ms end to end in real time, so sub ms timing was pretty important.

@bxparks
Copy link
Owner
bxparks commented Jun 2, 2021

That's a great video. Just out of curiosity, because I get no feedback on how my libraries are used, what hardware stack did you use, what does the Coroutine instance actually do, and why do you need submillisecond resolution if the entire sequence is 250ms?

The fundamental problem with COROUTINE_DELAY_MICROS() is that I could not figure out how to implement that feature without increasing flash memory consumption of code that does not use that feature. I'm a big believer that if a program does not use a feature, it should not have to pay for it. But sometimes, this is very difficult to achieve.

The other problem that I find myself constantly struggling with is the tension between 8-bit processors (with little memory) and 32-bit processors (with lots of memory). Things would be so much easier if I didn't have to worry about the constraints of 8-bit processors. Maybe having 2 versions of the library would make sense, then but I'd have to maintain both versions. Sometimes, adding a bunch of #if/#else might work, but if there is too many of that, it makes the library code much harder to maintain.

Hmm, I think got an idea about supporting COROUTINE_DELAY_MICROS() without imposing costs on COROUTINE_DELAY(). Let me noodle over this..

@mkarliner
Copy link
Author

The board was a stock Arduino Mega, running a set of solid state relays in turn switching pneumatic valves. Each pop had its own separate air tank so one didn't suck air from the others.
Each pop was run by a separate coroutine.
The architecture was an Electron desktop app which had a time line on it for each pop.
Each time you changed the desktop UI, a set of JSON was sent to the micro.
The actual running of the sequence was done solely by the micro on a hardware switch (with some safeties!), so it was pretty deterministic, but I had a nice desktop UI to set up the rig.
The microsecond requirement is to be able to get to millisecond with some reliability. Although these sequences are 250 ms long, the director was asking for one pop to be just a couple of inches ahead of another, which came to around 5ms increments. If you look at my original request for usecs, it was about very tight timings around spark erosion machines (a project still in progress). That use case was around 100us, which for the fast microcontrollers is a lot of instructions. The idea is to replace hardware with comparators and such with a fast micro.

I agree with you about minority cases hindering the general library user BTW.

In one of my other lives, I find myself trying to explain why a coroutine is so much nicer than callbacks!

And thanks for such a great piece of software!

@bxparks
Copy link
Owner
bxparks commented Jun 2, 2021

Thanks for the info. Sounds like a lot of fun.

Ok, I have reimplemented COROUTINE_DELAY_MICROS() and COROUTINE_DELAY_SECONDS() so that they consume no resources if they are not used at all. I'll post more info in #29.

@mkarliner
Copy link
Author

+1
:-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
0