Skip to content

Addition of a machine.Counter class (ESP32 only initially) #5496

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
wants to merge 5 commits into from

Conversation

tve
Copy link
Contributor

@tve tve commented Jan 6, 2020

This PR proposes the addition of a machine.Counter class that uses a timer or counter register depending on the architecture to count transitions on an input pin and to optionally generate interrupts when the counter wraps around.

Right now this PR only consists of the documentation so the design can be debated before much code gets written. (The docs can be viewed in semi-formatted state by using the view file entry of the 3-dots menu at the right of the file's diff.)

@dpgeorge dpgeorge added the docs label Jan 6, 2020
@tve
Copy link
Contributor Author

tve commented Jan 6, 2020

Lots of discussion on slack: https://micropython.slack.com/archives/C48TVHRQC/p1578293600192800

Example usage::

from machine import Counter, Pin
ctr = Counter(0, pin=4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pin=Pin(4)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes & no... I'm copying the conversation from the slack thread https://micropython.slack.com/archives/C48TVHRQC/p1578281256187400?thread_ts=1578281225.187300&cid=C48TVHRQC:

jimmo 1 day ago
the general idea is that anything that takes a pin should be able to accept either a machine.Pin instance or a port-specific value

jimmo 1 day ago
so on STM32, the port-specific value is a string, which is either a "CPU" pin name (e.g. PA1) or a "BOARD" pin name (e.g. "X1" on a pyboard, or "A0", "D12", etc on an arduino-layout-compatible board)
each board definition defines a pins.csv which maps the board pins to CPU pins, and the CPU pins are generated automatically based on which stm32 part it is (edited)

jimmo 1 day ago
on ESP32, the port-specific value is the pin number, and the pins.csv board mapping etc isn't implemented (i recently added basic support for board definitions, and hopefulyl someone who is more involved with ESP32 can finish it off and add pins.csv)

[...]

TvE 1 day ago
Is it OK to use pin numbers (ints) on the esp32 now and then later the code can test the type of the pin parameter and if it's a string look up the pin by name and if it's a Pin use that?

jimmo 1 day ago
yes, that will at least be compatible with the rest of ESP32

jimmo 1 day ago
i hope that in the future that just a few extra calls to the esp32 version of pin_find will be all that's required

jimmo 1 day ago
then ESP32 will support pin number, pin name, and pin obj in all cases

@nevercast
Copy link
Contributor

nevercast commented Jan 7, 2020

Thanks for opening this, I like that you're starting with the docs. This definition looks good. I unfortunately cannot weigh in on how this affects other ports as I am only really familiar with ESP32 hardware.

@tve
Copy link
Contributor Author

tve commented Jan 7, 2020

Easy to read docs page: https://micropython-tve.readthedocs.io/en/counter/library/machine.Counter.html

@MrSurly
Copy link
Contributor

MrSurly commented Jan 7, 2020

So there doesn't seem to be any way to do periodic counting (like how a frequency counter works). How would I accurately count how many pulses occur in 1 second?

How would you use this for the doing RPM measurements?

Seems this would be more suitable for long time periods with relatively fewer pulses?

@tve
Copy link
Contributor Author

tve commented Jan 7, 2020

So there doesn't seem to be any way to do periodic counting (like how a frequency counter works). How would I accurately count how many pulses occur in 1 second?

How would you use this for the doing RPM measurements?

I would sample the counter value in a periodic timer and calculate RPM based on delta_count / delta_time. But from your question I assume you're looking for something more accurate?

Seems this would be more suitable for long time periods with relatively fewer pulses?

Possibly. Is there a different way you would use the hardware to get more precise frequency counts (whether esp32 PCNT, stm32 timer, or other)?

@MrSurly
Copy link
Contributor

MrSurly commented Jan 7, 2020

I would sample the counter value in a periodic timer and calculate RPM based on delta_count / delta_time. But from your question I assume you're looking for something more accurate?

Possibly. Is there a different way you would use the hardware to get more precise frequency counts (whether esp32 PCNT, stm32 timer, or other)?

I think what I'm looking for would best be implemented using the MCPWM, since it has a "capture" capability. Couple of years ago, I was looking into implementing that for MP. Maybe I should revisit.

@tve
Copy link
Contributor Author

tve commented Jan 9, 2020

Initial implementation, interrupts not yet implemented.

@pidou46
Copy link

pidou46 commented Jan 24, 2020

Is there plan to merge this PR anytime soon ?

Counting periodic or eratic pluses is a very common use case for microcontroler, I'm surprised by the low interest it leverage.

Never the less thanks tve for this addition

@mattytrentini
Copy link
Contributor

@dpgeorge we probably need some of your opinion on this interface; I'm sure you've given it plenty of thought in the past!

@dpgeorge
Copy link
Member

@tve thanks for working on this idea/proposal. It's nice to see that you started with docs with a clear description of the behaviour that is hardware agnostic.

I would be supportive of this addition, although there doesn't seem to be that much interest in it (yet).

In terms of general availability across ports (the following goes for most additions to machine module):

  • For ports that don't have this counting capability, or don't want it, then they can simply not implement this class.
  • For ports that want this feature but don't have exact hardware for it, they could use other hardware blocks to emulate it, eg a timer.
  • There could even be an implementation of this Counter class using Pin.irq() and a software counter.

A few suggestions about the PR itself:

  • machine.Counter seems like a very generic name. How about something more specific like machine.PinCounter?
  • The RISING and FALLING constants also have BOTH associated with them, but in other classes this is achieved by RISING|FALLING, so allowing to or in this class would be more consistent with how it's done elsewhere in the machine module (eg Pin irq).

@tve
Copy link
Contributor Author

tve commented Feb 21, 2020

@dpgeorge thanks for the review and suggestions. My plan was to wait for one of the "call ESP-IDF functions from loadable modules" PRs to be merged and then to rework this PR as an example that uses the new functionality. Ideally using some auto-generated espidf class such that this PR can be 100% python...

IMHO the thing for you to think about is what you'd like to see in the MP repo once one can call ESP-IDF from loadable modules. E.g. the entire set of machine classes could be deleted from MP and implemented by other people in their own private repos. That probably wouldn't be all that great. So what should be in MP and what should be strewn around github et. al.?

@tve
Copy link
Contributor Author

tve commented Mar 2, 2020

I'm closing this PR in favor of https://github.com/tve/mpy-lib/tree/master/esp32-counter which uses native module linking against esp-idf in order to implement a Counter class in pure python with automatically generated stubs for the ESP-IDF pcnt_* functions. That repo is based on #5711

@tve tve closed this Mar 2, 2020
@dpgeorge
Copy link
Member

dpgeorge commented Mar 3, 2020

So what should be in MP and what should be strewn around github et. al.?

That's a good question. I guess the short answer is that the main MP repo here should provide basic, fundamental classes that work the same across all ports (eg machine.Pin, ADC, PMW), and anything which is port-specific (like esp32 RMT) could be a loadable .mpy file. And if an .mpy extension proves to be useful and widely used then it could make its way into this repo.

I'm closing this PR in favor of https://github.com/tve/mpy-lib/tree/master/esp32-counter which uses native module linking against esp-idf in order to implement a Counter class in pure python with automatically generated stubs for the ESP-IDF pcnt_* functions.

Just so I understand: that esp32-counter uses a combination of 1) dynamic loadable native modules, 2) a large port-specific table of exported symbols (from the IDF), and 3) a stub generator which makes a reduced set of C stub functions that are needed by the Python code and which are then dynamically loaded (ie not part of a statically available espidf module)?

That sounds like a good way to do it.

@tve
Copy link
Contributor Author

tve commented Mar 3, 2020

Just so I understand: that esp32-counter uses a combination of 1) dynamic loadable native modules, 2) a large port-specific table of exported symbols (from the IDF), and 3) a stub generator which makes a reduced set of C stub functions that are needed by the Python code and which are then dynamically loaded (ie not part of a statically available espidf module)?

Yes. The table in #5711 has 670 functions and thus uses ~2680 bytes, dunno whether you consider that "large"... If you weigh it against the size of all the pending PRs which could be declined with a polite "please implement this as a loadable module" then the table is tiny ;-).

That sounds like a good way to do it.
Great, I'm looking forward to your comments on #5711 and/or Jimmo's variant :-) (I understand you're focusing on uasyncio at the moment.)

Edit: forgot to add: I'm using a small hacky stub generator, which is in the repo I linked to. Not sure this is what I would recommend to others, but would be interesting to have some discussion...

@dpgeorge
Copy link
Member

dpgeorge commented Mar 3, 2020

I'm using a small hacky stub generator, which is in the repo I linked to. Not sure this is what I would recommend to others, but would be interesting to have some discussion...

That's a secondary thing which we can discuss later, and for the moment is nicely self-contained within that esp32-counter example. The primary thing is to work out how to expose port-specific symbols to the dynamic loader.

@pidou46
Copy link

pidou46 commented Mar 3, 2020

I try to figure out own it will behave for the module (counter for instance) end user :

Based on tve repository, I understand that one will have to download 2 files:

  • a *.py file - that will interface with ESP-IDF
  • a *.in file - that map functions between MPY and ESP-IDF

I guess the *.py file could also be a *.mpy

  • End user will not have to build a firmware to get extended fonctionalities.
  • firmware rom size will not be affected.
  • Drawback it will use ram.

Does I understand it well ?

@tve
Copy link
Contributor Author

tve commented Mar 3, 2020

I guess the *.py file could also be a *.mpy

I believe it would be good practice to check in the compiled mpy, perhaps also the generated modpcnt.c stubs.

Does I understand it well ?

Yes.

@IhorNehrutsa
Copy link
Contributor

docs\machine: Add Counter and Encoder classes. #8072
ESP32: New hardware PCNT Counter() and Encoder(). #6639

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

Successfully merging this pull request may close these issues.

7 participants