8000 esp8266, stmhal, zephyr: Rename machine.Pin high/low methods to on/off. by pfalcon · Pull Request #3025 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

esp8266, stmhal, zephyr: Rename machine.Pin high/low methods to on/off. #3025

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 2 commits into from

Conversation

pfalcon
Copy link
Contributor
@pfalcon pfalcon commented Apr 16, 2017

For consistent Pin/Signal class hierarchy. With it, Signal is a proper
(while still ducktyped) subclass of a Pin, and any (direct) usage of Pin
can be replace with Signal.

@pfalcon
Copy link
Contributor Author
pfalcon commented Apr 16, 2017

This change would conclude finalization of machine.Pin and machine.Signal interface, i.e. the most basic level of the MicroPython HW API. Without this change, the potential of the Signal class comes not fully realized. The basic requirement for the introduction of Signal class was that it's fully duck-type compatible with Pin, and the following usecase can be realized: "If someone (simple-mindedly) developed an application using Pin and assuming a particular polarity for that Pin, it should be possible to make it compatible with devices using different polarity by just replacing Pin object declaration with a Signal declaration, without changing the rest of the program logic."

@dpgeorge
Copy link
Member

This will break too many things for pyboard (and probably also a lot for esp8266). In stmhal, machine.Pin and pyb.Pin have the same underlying implementation and pyb.Pin will continue to have high/low.

If you run grep -r --include "*.py" 'high\|low' in the drivers/ dir there are quite a few uses of the original methods. And for example cs.high()/cs.low() is going to be confusing when replaced with cs.on()/cs.off().

the following usecase can be realized: "If someone (simple-mindedly) developed an application using Pin and assuming a particular polarity for that Pin, it should be possible to make it compatible with devices using different polarity by just replacing Pin object declaration with a Signal declaration,

That's already achievable just by using pin.value([x]), with no mention of on/off/high/low.

@pfalcon
Copy link
Contributor Author
pfalcon commented Apr 18, 2017

In stmhal, machine.Pin and pyb.Pin have the same underlying implementation and pyb.Pin will continue to have high/low.

Sounds good for pyb.Pin to continue to have high/low, even if it leaks into stmhal's Pin.

If you run grep -r --include "*.py" 'high|low' in the drivers/ dir there are quite a few uses of the original methods.

Well, then we need to change them to make this saga over.

And for example cs.high()/cs.low() is going to be confusing when replaced with cs.on()/cs.off().

Then it's a usecase for using Signal.

That's already achievable just by using pin.value([x]), with no mention of on/off/high/low.

Right, but we wanted to go a step further than .value(), and add convenience methods e.g. to turn on LED on and off. We actually did that, and added methods .high() and .low(). But later it became clear that the naming isn't right for all cases. But we'd like to keep functionality (spirit of the API), even if we need to make backward incompatible changes to method names.

The Signal is intended to be a simple, but powerful addition to Hardware API, but of course it brings some compromises to it. Even if it's a compromise, it comes as a natural next (also, final) step on this way. And it's similar to refactorings done to I2C and SPI classes - they both required incompatible changes, but now we have much better APIs for them.

@pfalcon
Copy link
Contributor Author
pfalcon commented Apr 23, 2017

Reinstated pyb.Pin.high/low for stmhal. Updated drivers/* to use .value(1)/.value(0) instead of .high()/.low() (didn't go to add Signal dependency there for now).

This patch should be ready to merge now.

@pfalcon
Copy link
Contributor Author
pfalcon commented May 2, 2017

@dpgeorge : ping

@dpgeorge
Copy link
Member
dpgeorge commented May 3, 2017

I'm not happy how all this pin/signal stuff has turned out. I think that high()/low() methods are useful for pins and should be kept as part of the Pin API.

For example, if I create a CS (chip select) pin then it's conceptually an entity which goes low to enable it, and so writing cs.on() is a little confusing: does it enable the CS pin, or does it make it go high thus disabling it? Furthermore, if I use a Pin object to implement CS, it may one day (accidentally) be replaced by a Signal object which is inverted. Then suddenly my code stops working (but without any syntax/attribute errors) and behaves strangely.

The issue is that duck typing is overly used in this situation, that on/off are being overused for too many concepts.

The solution is to keep high()/low() methods strictly for Pin only, and they should be used when you really mean that you want the output to be vdd/gnd. And then there would be an error if you passed in a Signal object.

And then on()/off() can be implemented for both Pin and Signal, and are used when you conceptually want to turn something on or off (eg an LED, whose implementation as active low or high may change).

@pfalcon
Copy link
Contributor Author
pfalcon commented May 3, 2017

For example, if I create a CS (chip select) pin then it's conceptually an entity which goes low to enable it, and so writing cs.on() is a little confusing: does it enable the CS pin, or does it make it go high thus disabling it?

The set of answers is clear:

  1. Properly, you should be using Signal, and think in terms of signal being asserted or deasserted. We had long enough discussion on which verbs to use for these states, things like assert_() and deassert_() were proposed. But the conclusion was that on() and off() are compromisingly the best choices.

  2. if you have concerns with .on/.off(), or with a Signal, your remaining option is to use .value(0) and .value(1), 0 & 1 there won't let you be confused.

if I use a Pin object to implement CS, it may one day (accidentally) be replaced by a Signal object which is inverted. Then suddenly my code stops working (but without any syntax/attribute errors) and behaves strangely.

Accidentally sounds good. Well, that's the nature of Pins vs Signals - they're supposed to be fully replaceable. Yes, it brings some entailments regarding "accidentally". But someone can remove the main code of the application accidentally, then there're no errors, it just behaves strangely.

The issue is that duck typing is overly used in this situation, that on/off are being overused for too many concepts.

Those things are like atoms - all the differently looking things consist of the same atoms, so it's easy to get confused, so one should take care not to.

The solution is to keep high()/low() methods strictly for Pin only, and they should be used when you really mean that you want the output to be vdd/gnd. And then there would be an error if you passed in a Signal object.

Well, that's pretty good compromise for something started with "I'm not happy how all this pin/signal stuff has turned out.". That still doesn't adhere to the original requirements I started with (the original idea was to add inversion capabilities to Pin; that led to unneeded bloat on Pin level, so the idea became to split out inversion capability to a separate class, but naturally, Pin and this new class should be able to fully replace each other). It also has a problem from the point of the minimality of the API. So, for consistency, I still think that design where Pin and Signal are fully replaceable and maintain the minimal API (no high()/low()) is better. But well, it's only so much of the arguments I have. It's hard to predict real-world usage fully, again, I think we can get over it, the principle of the minimal API will be our guiding star, but well... .

So, please tell what should be done.

@chrismas9
Copy link
Contributor

The solution is to keep high()/low() methods strictly for Pin only, and they should be used when you really mean that you want the output to be vdd/gnd. And then there would be an error if you passed in a Signal object.

@dpgeorge on/off doesn't make sense for many uses of a digital pin or an inverted pin. It makes sense to have high/low in Signal (see use cases below). I agree with @pfalcon that Pin and Signal should be interchangeable, which means both should support on/off and high/low.

Pins are often used as a selector, not just as an on/off switch. Examples are LCD displays have one address pin called CMD/DAT. High for command, low for data. 2 channel ADC's have an S pin to select channel. High for ch1, low for ch0. Garage door. High for up, low for down. In these cases there are only two on states, and no off state. The data sheet will be written on terms of 1/0 or high/low. In some cases the user will need to drive that pin through an inverter so using Signal inverted makes sense. High/low represent the signal level at the device. A real world example:

An automatic milk shake machine needs a 5V signal to select flavour. The data sheet says high for Strawberry and low for Chocolate. I use an open drain pin with a 5V pullup. I use a Pin (or Signal non inverted) constructor and flavour.high() for Strawberry. After building a thousand we find that occasionally in hot whether the pullup isn't strong enough and the machine won't make Strawberry milkshakes. I decide to use a spare 74ACT04 gate as an inverting level translator (the others are driving blue LEDs so I had a free gate left over). I change the constructor to use Signal inverted and the method all through the program doesn't change. flavour.high() is still Strawberry. Using flavour.on() does not make sense for Strawberry.

The good thing about keeping Pin and Signal fully compatible is you only need to change the constructor in one place to accommodate hardware changes. You never have to change the methods throughout your program.

I will restate my previous comment on the name "signal". Signal is confusing. It has many other meanings, probably on the same system that is using Signal for DigitalIO. Audio signal, vibration signal, WiFi signal strength, etc. It would be much less confusing for users to keep Pin as the standard function for application level pin waggling, including the invert feature, and provide a fast pin class for libraries and users who need the speed for serial busses, or lower memory usage.

  1. rename Signal to Pin.
  2. rename Pin to Pin_fast or Pin_raw.
  3. both classes should be interchangeable, except for the extensions in Signal.
  4. update libraries to use Pin_fast if necessary. This is no more work than updating all the libraries to user value(x).

This means users don't need to be encouraged to change to Signal. Most users won't notice any difference and can slowly clean up their code to use inverted where applicable. Those that hit problems with speed or memory are probably more advanced users and will understand to switch to Pin_fast. I don't see a downside to this - less impact on most users.

On the subject of names pin.value(x) does not match the actual hardware of most microcontrollers. Usually you write to the output data register (ODR) and sometime this changes the pin state. A better syntax would be pin.out_value(x) as the complement to pin.out_value(). However pin.value() is probably too entrenched to change now.

@pfalcon
Copy link
Contributor Author
pfalcon commented May 14, 2017

Initial doc for Signal was pushed: 0ba136f

@dpgeorge
Copy link
Member

Ok, going by the principle of "micro" to keep things minimal, and by Python's principle of "there should be only one way to do something" let us indeed remove high()/low() from the Pin API (except for pyb.Pin which retains them for legacy reasons).

@pfalcon I would prefer to use the short-hand __call__() instead of .value() in the drivers (ssd1306, sdcard, nrd24l01) because they are intended as (efficient) low-level drivers, as opposed to examples for users to learn from. Using the short-hand call will reduce bytecode and make it run faster.

Finally: if Signal is to really replicate Pin then it's going to need the irq() method, and it's going to need to invert the IRQ_RISING/IRQ_FALLING levels for the interrupt.

@pfalcon
Copy link
Contributor Author
pfalcon commented May 17, 2017

Thanks! I think it's good approach "simple things (e.g. Pin) should be simple, and more complex (more advanced blocks) can be more elaborated". I'm preparing to travel, so it make take into a weekend to update this patch and merge it.

@pfalcon I would prefer to use the short-hand call() instead of .value() in the drivers (ssd1306, sdcard, nrd24l01) because they are intended as (efficient) low-level drivers, as opposed to examples for users to learn from. Using the short-hand call will reduce bytecode and make it run faster.

Yes, per the previous discussion, there was no talk about removing it, as .value() is implemented in terms of __call__ in the existing code, and that's natural way to keep doing it. The question was whether we document __call__. My suggestion was "no", to motivate folks to write more explicit code in general.

Finally: if Signal is to really replicate Pin then it's going to need the irq() method, and it's going to need to invert the IRQ_RISING/IRQ_FALLING levels for the interrupt.

Well, I'm not sure about this. The idea was that Signal is fully portable across ports, and being able to trigger IRQ is by definition machine-specific. Of related note, I come to conclusion that machine.time_pulse_us() should not work with Signal (not a usecase for time_pulse_us() which is intended to be able to time real-time signals), so https://github.com/micropython/micropython/blob/master/examples/hwapi/button_reaction.py#L14 will need to rewritten.

All in all, this patch finishes defining the core interface of Pin and Signal. And we can start elaborating and fully defining interface for IRQ, wake, sleep config (like recent report of being able to configure sleep Pin levels for esp8266, the same problem should be with esp32) - all that in connection with soft irq aka .schedule(). Once that's done, we can see what of that can "leak thru" to Signal.

For consistent Pin/Signal class hierarchy. With it, Signal is a proper
(while still ducktyped) subclass of a Pin, and any (direct) usage of Pin
can be replace with Signal.

As stmhal's class is reused both as machine.Pin and legacy pyb.Pin,
high/low methods actually retained there.
@pfalcon
Copy link
Contributor Author
pfalcon commented May 21, 2017

All changes are done, the patch is ready for merge.

@pfalcon
Copy link
Contributor Author
pfalcon commented May 21, 2017

Merged.

@pfalcon pfalcon closed this May 21, 2017
@pfalcon pfalcon deleted the pin-on-off branch May 21, 2017 14:45
@dpgeorge
Copy link
Member

Ok, thanks for doing this.

kamtom480 pushed a commit to kamtom480/micropython that referenced this pull request Jun 16, 2020
STM32: Add DTCM and ITCM support to F7 series
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

Successfully merging this pull request may close these issues.

3 participants
0