-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
ESP32: New hardware PCNT Counter() and Encoder(). #6639
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
base: master
Are you sure you want to change the base?
ESP32: New hardware PCNT Counter() and Encoder(). #6639
Conversation
To offer a general comment, encoder interfaces are difficult because of the effects of vibration. Accuracy can be lost as the inputs can experience brief pulses of arbitrary duration. These don't constitute systematic rotation but can be wrongly interpreted as such. I haven't studied the code in detail but the fact that this solution offers detection of rising, falling, or both edges rings an alarm bell: in my experience the state machine must detect both edges to avoid loss of precision in the presence of vibration. |
Rename enum puType to pullResistor. Rename enum encType to clockMultiplier and it content. Rename class EncoderType to ClockMultiplier.
While that is worthwhile, it is hard to replicate the situation where the encoder is positioned precisely at a transition and machine vibration causes jitter around that point. That is best simulated with a pulse generator over a range of pulse widths. There is a risk of creating an interface where small positional errors only become apparent after days of continuous machine running. |
How does the QUAD API code return the direction of the rotation in addition to the counts? |
STATIC void register_isr_handler(void) { | ||
if (pcnt_isr_handle == NULL) { | ||
check_esp_err(pcnt_isr_register(pcnt_intr_handler, (void *)0, (int)0, (pcnt_isr_handle_t *)&pcnt_isr_handle)); | ||
if (pcnt_isr_handle == NULL) { | ||
mp_raise_msg(&mp_type_Exception, MP_ERROR_TEXT("wrap interrupt failed")); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using pcnt_isr_handler_add() instead would give the unit demultiplexing inside the isr handler for free, in turn simplifying the whole handler. See .e.g https://github.com/espressif/esp-idf/blob/faf0f61cdba67fa5f7e680f2f5b0966c8d69ce9a/examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c
The intent is to have a common MicroPython API that works across different ports that implement counting and quadrature decoding in different ways. There has been much discussion of this issue across this PR and #8072, #7911, #7582, #5216 and #5496. Perhaps someone in the future will have a burning need to use some missing feature of the raw hardware and will refactor this implementation to work on top of a new Right now I feel like just getting anything into mainline would be great. |
I don't really agree with the anything. But anyway, I think that adding a simple esp32 specific module is more likely to work as this would not depend on any yet-to-agree-on API. But a python module subclassing from that could easily be added implementing these APIs once they become available.
I don't think there's additional complexity, PCNT is rather simple. Both counter modules together are IMHO more complex than a simple wrapper module around PCNT. And the python subclasses don't count as there's no need to make them part of the firmware although you could of course freeze them into it if you wish to. |
The pcnt_get_event_status() may be use later when MicroPython will use IDFv4.3.
|
Honestly, I'm not sure why the The documentation for the full common API should be covered by #8072, with any port differences summarised briefly in |
The pcnt.rst was added more then year ago. |
if (PCNT.status_unit[id].ZERO_LAT) { | ||
if (self->counter == 0) { | ||
self->status |= EVT_ZERO; | ||
mp_sched_schedule(self->handler_zero, MP_OBJ_FROM_PTR(self)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this supposed to be used by the user? This triggers when the hardware counter passes zero but the event is only scheduled if self->counter also is zero.
But the user setting the counter using value() imho can set an arbitrary offsets on self->counter to values != 0. Then the hardware counter and self->counter won't pass zero at the same time. Or am I misunderstanding something?
int aPinNumber; | ||
int bPinNumber; | ||
|
||
volatile int64_t counter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am trying to understand how self->counter is supposed to work. And I am not sure it actually does what it's intended to. This is my understanding:
self->counter serves two purposes: a) it's something like additional upper bits for the 16 bit hardware counter and b) it allows to set the counter to any value which the hardware counter does not allow. self->counter then basically becomes an offset
The problem I see is in the trigger events of the hardware counter, especially EVT_ZERO. The hardware triggers only on the hardware counter. The interrupt routines try to take this into account by additionally comparing self->counter with zero or the match values. This may actually work as long as self->counter is only used as the upper bits. But it will IMHO fail if it's used to adopt to the fact that the hardware counter cannot be set. If self->counter is set instead while the user tries to set the counter then the hardware counter and self->counter imho get out of sync. And thus they'll not pass the trigger values at the same time. Thus they e.g. don't pass zero at the same time and the EVT_ZERO will not trigger and it seems to me that the match triggers also won't work as intended.
Thanks for everyone who is working on this! Any updates on merging this PR? @dpgeorge |
Just rebase to Version 1.19.xx from micropython#6639
This pull request is not possible to rebase on master. |
readline: make ctrl-l clear screen & redraw line
Just rebase to Version 1.19.xx from micropython#6639
Just rebase to Version 1.19.xx from micropython#6639
Just rebase to Version 1.19.xx from micropython#6639
Just rebase to Version 1.19.xx from micropython#6639
Just rebase to Version 1.19.xx from micropython#6639
This is an automated heads-up that we've just merged a Pull Request See #13763 A search suggests this PR might apply the STATIC macro to some C code. If it Although this is an automated message, feel free to @-reply to me directly if |
This PR consistent to
docs\machine: Add Counter and Encoder classes #8072
ESP32 Pulse Counter
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html
Wrapped around
https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/pcnt.h
https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/pcnt_types.h
https://github.com/espressif/esp-idf/blob/master/components/driver/pcnt.c
See also
https://github.com/espressif/esp-idf/tree/master/examples/peripherals/pcnt/pulse_count_event
ESP32 Quadrature Encoder based on Pulse Counter(PCNT)
Based on
https://github.com/madhephaestus/ESP32Encoder
https://github.com/bboser/MicroPython_ESP32_psRAM_LoBo/blob/quad_decoder/MicroPython_BUILD/components/micropython/esp32/machine_dec.c
See also
https://github.com/espressif/esp-idf/tree/master/examples/peripherals/pcnt/rotary_encoder
If your port does not support hardware encoder use machine.Pin() Interrupt-based MicroPython quadrature incremental encoder
UPDATE:
This pull request is not possible to rebase on master.
Please switch to the
ESP32: Add Quadrature Encoder and Pulse Counter classes. #8766