-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
WIP docs: Add machine.PWM #2283
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
Conversation
This is based on esp8266 implementation. Is this also correct for wipy? Is the wipy `duty()` range 0-1023 or 0-10000?
As far as I know, WiPy doesn't have a machine.PWM module at all. You can do PWM on pins 1, 3, 6, 7 and 8 only, and you do that by configuring the appropriate timer yourself -- see https://micropython.org/resources/docs/en/latest/wipy/wipy/quickref.html#pwm-pulse-width-modulation |
On stmhal, teensy, and wipy, PWM is part of the timer module. |
I wonder if it would make sense to have a simple |
I think that the issue is that there are interactions between the pins (unless you're talking about Software PWM). For example if I setup pin Y1 to be 50 Hz, I can't set Y2 to be anything other than 50 HZ because its on the same timer. And you can only use pins that are timer pins. So you could create a PWM module for stmhal, but it would have lots of gotchas. |
As long as the gotchas give you informative error messages, I think that would work. For example, you could remember that Y1 has been set to 50Hz, and raise an exception if you try to set Y2 to anything else... Same for using non-timer pins -- just raise an exception (and list the available pins in the documentation). |
Another method for providing a simplified PWM class is to fix the frequency overall. Either hard code it or allow for it to only be changed for all pins at once. By fixing frequency you don't need to worry about setting different frequencies. If someone does want to get that advanced they can always use the specific Timer class for the hardware. |
You should always be able to use a Timer object to configure a PWM output, but I agree that it would be nice to also have a more high-level way of configuring PWM on a pin. The reason is because PWM is a very common thing to do, and, as has been said, it would help with portability to have a simple way to do it (for the common, simple cases). So there would be a hardware-specific way of creating a PWM channel connected to a timer (as currently done in stmhal, wipy), and a simple factory function (like in esp8266). The objects that are created by these 2 approaches should be compatible, ie they should have the same methods like A simple API for a PWM-like object could be: PWM.duty([value]) # get or set the duty cycle in units of the timer period
PWM.duty_percent([value]) # get or set the duty as a percentage (int or float)
PWM.timer() # get the underlying timer object If you want to get/set the frequency you need to go through the underlying timer object (which gives you a hint that the timer may be shared with another PWM object). You also need to query the timer to see what the maximum period is for the Construction of a PWM object the easy way would be like in the esp port: |
So it sounds like PWM is almost an alias for TimerChannel (in the stmhal case), I guess it might be more correct to say that it's a specialized constructor of a TimerChannel. Currently TimerChannel uses pulse_width and pulse_width_percent. Are you suggesting we add duty and duty_percent as aliases for those? |
How should we separate classes that are standard versus those that aren't? |
On the esp8266 there is an "esp" module for non-standard stuff. I think it's a good idea to have something like that on other platforms too, at least it's then clear at the first glance if a given program is portable or not. |
Yes, that would be a good way to look at it (at least coming from an stmhal point of view).
I don't remember the reason for using pulse_width... it's pretty clear what it is but also quite verbose/long. The word "duty" is much shorter and I think still just as understandable, even for someone looking at it for the first time. We can add duty as aliases for stmhal (note wipy uses duty_cycle with an argument in percentage * 100, ie between 0 and 10000).
machine.Timer already exists and is (supposed to be) the standard way of interacting with timers on the board. Extensions to peripherals provided by a given port should be exposed in the Python API as either additional methods to standard classes, or additional keyword arguments to standard methods. At least that's the idea :) That way, a given port provides the lowest-common-denominator functionality for all the peripherals as specified by the machine API, but can also add extra stuff to take advantage of any fancy hardware features that are specific to it.
Yes, there are the The whole idea of the @tannewt I'm sure you saw it, but just in case here is a document about the machine API: https://github.com/micropython/micropython/wiki/Hardware-API . Note that there are quite a few things that are wrong or inconsistent. We are still working on the details of this and your input is most welcome! |
Thanks @dpgeorge! @dhylands pointed me to the hardware API already and I need to go back and make sure my code conforms. Additional methods and keywords worry me a little bit because it blurs the line of portable and non-portable implementation. I totally get not wanting to have two Timer classes, one in machine and one in atmel_samd, but wonder if we should standardize on a platform prefix for non-portable extensions. That would make it clear to the programmer what is standardized and what isn't. An argument against that would be common extensions to the baseline that apply across ports. Maybe we should create an analysis script that can pull all of the APIs from each port. That way we can compare them in a table and ensure ports stay compliant. Overall, I'm super excited to be hacking on this. I've loved Python for a long time and have recently fallen in love with microcontrollers. I want to make sure I'm working the same direction you all want to take it. Do you regularly use any chatroom stuff? I found the freenode channel but I've become spoiled by Gitter and Slack which allow you browse past conversation. Do you use either of those? |
The features that are present in Timer on stmhal are pretty typical of most timers on most of the MCUs I've worked with. The teensy port (teensy is based on a FreeScale chip) uses the exact same Timer/TimerChannel model as stmhal. I've worked with the AVR quite a bit and it would also fit that model (even though it probably can't run MicroPython). For some devices there might only be one channel per timer. The way it's setup the Timer determines the frequency and the timer channel determines the function (and is what gets assigned to a pin). |
IMO this discussion forms a good basis for adding a PWM class to the machine module, so would be good to keep discussing it. |
…-interface-names Allow boards to change the "CircuitPython" text in their USB interface description.
I'll close this now that #4237 is continuing this discussion about PWM. |
This adds an initial specification of the machine.PWM class, to provide a way to generate PWM output that is portable across the different ports. Such functionality may already be available in one way or another (eg through a Timer object), but because configuring PWM via a Timer is very port-specific, and because it's a common thing to do, it's beneficial to have a top-level construct for it. The specification in this commit aims to provide core functionality in a minimal way. It also somewhat matches most existing ad-hoc implementations of machine.PWM. See discussion in #2283 and #4237. Signed-off-by: Damien George <damien@micropython.org>
This adds an initial specification of the machine.PWM class, to provide a way to generate PWM output that is portable across the different ports. Such functionality may already be available in one way or another (eg through a Timer object), but because configuring PWM via a Timer is very port-specific, and because it's a common thing to do, it's beneficial to have a top-level construct for it. The specification in this commit aims to provide core functionality in a minimal way. It also somewhat matches most existing ad-hoc implementations of machine.PWM. See discussion in micropython#2283 and micropython#4237. Signed-off-by: Damien George <damien@micropython.org>
This is based on esp8266 implementation. Is this also correct for wipy?
Is the wipy
duty()
range 0-1023 or 0-10000?Is that line about
all pins except Pin(16)
valid for wipy?