-
Notifications
You must be signed in to change notification settings - Fork 169
Clarification in ENCODER for _tsf_ready() #128
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
Comments
Thank you for raising this.
At the time the only way to clear down the TSF was to wait for it to be set, then to wait on it. So the purpose of this code if delay > 0 and self._tsf_ready(): # Ensure ThreadSafeFlag is clear
await self._tsf.wait()
await self._tsf.wait() is to ensure that it waits for an edge. Without the first two lines, if the tsf.clear()
await self._tsf.wait() The delay (if specified) starts on an edge. The purpose of the delay is to rate-limit the callback and the rate of triggering the asynchronous iterator. You might like to experiment with the sample code to get a feel for the use of the poll mechanism. Please don't hesitate to ask if you require further clarification. |
I should add that the reason you can poll a TSF is because it has an |
Hi Peter, many thanks for the explanations and informative links. I am now working through them and will edit this comment, once I have sorted all out... CU later, Michael. |
I have pushed an update to the tutorial which aims to clarify the Feedback on the doc is welcome. |
I have drawn a small image, describing the delay and the latency issue, you solved in your code. Let me see, if I get it right:
The issue is arising, if we have e.g. contact bounces on a manually controlled encoder (I am ignoring high speed encoders like on CNC machines here) Assuming that at least not a single contact bounces, but that both contacts on x and y bounce so quickly that the ISR starts, when the x signal is again on the same level as before. This is the purple shaded section. So with your code that compares the previous value with the current value, this super short bounce is effectively ignored. Which works great by the way... This is just a confirmation that I got the understanding right. Did you ever encounter a situation where only e.g. the x signal flipped back and forth and not the y signal (the teal section)? I understand it depends on the mechanical properties of the encoder and on a typical Keyes KY-040 there seem to be mechanical contacts gliding over a notched plate. So I guess here the bounce will probably be one pin only, as the second one has not reached the edge to the notch. Another assumption of mine is, that when the x and y pins change in very quick succession (but no bouncing), then even though the y pin changes when still the x ISR runs, still the y ISR will be scheduled, it just runs shortly after the x ISR finishes.
If the microcontroller initialises the code init will define the ISRs on lines 54/55, then the async task is created and enters the _run loop. So during that time, it may happen, that the encoder has already been turned and the ._v value has already reached 4. So if the next turn happens a longer time after the first, then we would ignore those first 2 for this period. I was thinking of moving the .clear() rather to the end of the _run() function at line 102, after we have obtained all current values. Maybe this is nitpicky, because in a continuously running loop it would also be the next instruction to be executed. I was just wondering about the reason of having it at the beginning... Anyway I think I have now a solid understanding of your code and it works beautifully in my lighting setup. I am still making tweaks and will be converting the button and touch classes to the same style of minimal ISRs, ThreadSafeFlags and async iterators. But I needed to understand all of it first - just blindly copying code from the Internet without grasping what's going on is not my style... Thanks a lot for your support and the updated documentation. It helped really a lot! I am now closing this issue! Have a nice weekend! Michael. Nota bene: I tried this now on another encoder/pico and just what I assumed, if I get contact bounce, it is on one signal only, like so:
Nota optime: I noticed that there may be an micropython-async/v3/primitives/encoder.py Line 107 in b4becb1
__anext__ shall be declared as async
|
You are right about Re encoders I don't know if you've seen this doc. I wrote it in response to various people who insisted that you had to write a state machine to decode an incremental encoder and others who argued for explicit debouncing of the signals. There is more nonsense spouted on this topic than you can shake a stick at. High speed encoders on an NC machine also suffer from contact bounce, caused by vibration. However I have never encountered simultaneous bounce on both channels. This should be physically impossible. Re clearing the TSF, the general principle is that the ISR's maintain a correct count. The Re absolute accuracy I believe that my approach produces results which are as good as can be achieved with a Python solution. It's not perfect. An encoder can produce arbitrarily short pulses which may or may not trigger interrupts. Even hardware solutions require several stages of synchronisation to avoid metastability issues. If you examine the specification for the STM microcontroller on the Pyboard (which has an encoder interface) it has such synchronisation. I can claim some experience here. In the mid 70's I designed a hardware interface for encoders on a machine tool. It was essential that this never missed an edge. Achieving this was difficult, however I eventually got it working. I estimate that it probably processed about 500 billion edges in the lifetime of the project. It used the XOR algorithm. |
Thank you Peter! I agree with all of your comment. Yes, I read through that linked doc as well. Somehow I think that the Pico W still has some problems with micropython. In my code I have now three asyncio tasks running in app.py, which deal with the three async for loops getting data from the encoder, the encoder's switch and the touch button. Each of the instance runs such a _run() loop, doing all the python stuff, after it got triggered by the ISR. On the surface, the code runs fine and pushing the encoder's button produces the same calls to the NeoPixel strip as the touch button. However, sometimes, even if it is absolutely the same code path (all triggers the same I know that the rp2 implementation for NeoPixel uses bitstream and not PIO to push the data to the strip, but that does not explain, why the same call triggered from a different asyncio task works not... I read that Adafruit removed lots of micropython's functions in their CircuitPython version:
Maybe that is the reason it behaves so flaky for me. |
It's hard to comment without further information however I use Pico and Pico W for most of my efforts these days because they are cheap, well specified and support hard IRQ's. My test rigs for micro-gui and micropython-touch use them, and two of the configurations of micro-gui use an encoder. Internally the GUIs make heavy use of
To a point. CircuitPython has a different target audience to MicroPython. If they disable hard IRQ's and threading it's because they require expertise to use properly - a level of know-how that their user base may not possess. Multi-core coding in MicroPython is really quite challenging. I choose MicroPython because these techniques are available, the limitations are documented, and we're grown-ups :) |
Thanks - that is reassuring to know, that it is very stable for you. I decided to flash the Pico again with the micropython 1.23.0 in case something got corrupted during my tests with neopixels and the PIO and I had to take out the Pico from the enclosure to get to the BOOTSEL button. When I put it back I saw the same strange artifacts with the Neopixel strip - I guess the perfboard hardware may have a bad solder joint somewhere... But I want to fire up KiCAD and create a proper PCB like I did with my Anyhow, if you like to see what I am talking about, here is the repo - I mentioned your great support also there in the README. Thanks again, Peter, for your support! Stay healthy and enjoy life! |
Dear Peter,
thank you so much for the detailed write ups and examples. I am using your Encoder class in one test project and would like to understand one particular section of the code. I did not find a discussion or wiki here, so I raised this issue, I hope that this is ok with you.
micropython-async/v3/primitives/encoder.py
Line 93 in 5575954
Here to my understanding this ready function tests, if at the time this code is reached, the ThreadSafeFlag is already set. Three questions arose for me:
Thank you in advance for your time!
Michael.
The text was updated successfully, but these errors were encountered: