Skip to content

Consistent Pin API for all ports #1408

Closed

Description

@danicampora

This continues what started being discussed in #1405. The Pin API as far as the constructor (and the init()) method is concerned needs to be modified a bit in order to make it more general and suitable for a wide range of MCUs. Here's my proposal:

Pin(pin, mode, *, type=Pin.PP, pull=None, af=0)
  • mode can be IN, OUT or AF
  • type can be PP or OD. Irrelevant when mode=Pin.IN.
  • pull can be PULL_UP, PULL_DOWN or None (or PULL_NONE but we could save on QSTRs)
  • af is only relevant when mode=Pin.AF. Alternate functions can vary widely from micro to micro so I think it's OK for the values of this param to be port specific.

Activity

  1. dhylands commented on Aug 3, 2015

    @dhylands
    Contributor

    That looks reasonable to me, and if we decide to go forward with this, I'm happy to change the stmhal stuff to implement this (and keep the older stuff for backwards compatability - perhaps with an ifdef for size considerations - so ifdef on).

  2. danicampora commented on Aug 4, 2015

    @danicampora
    MemberAuthor

    Cool, let's wait a bit for @dpgeorge 's opinion.

    On Aug 3, 2015, at 10:00 PM, Dave Hylands [email protected] wrote:

    That looks reasonable to me, and if we decide to go forward with this, I'm happy to change the stmhal stuff to implement this (and keep the older stuff for backwards compatability - perhaps with an ifdef for size considerations - so ifdef on).

    —
    Reply to this email directly or view it on GitHub.

  3. dpgeorge commented on Aug 5, 2015

    @dpgeorge
    Member

    I'd rather not have PP and OD separate. I think it's simpler to have just mode and pull (ignore for now alternate function). Then mode would be one of: IN, OUT or OUT_OD. In this case OUT would be push-pull (and we provide OUT_PP as an alias on pyboard for legacy support). Or, IN, OUT and OD.

    There may also be an analog mode for some MCUs (eg mode=Pin.ANALOG); but this could be encompassed by ADC setup and/or alternate-function.

    There are other things we can support:

    • pull strength (available on ESP8266)
    • drive strength (available on CC3200)
    • speed (available on STM)

    To support pull strength, those chips with it can have PULL_DOWN as the default strength, and provide PULL_DOWN_4K or whatever for specific strengths (or use STRONG, WEAK etc).

    But maybe this is overcomplicating it. Most people will only ever use direction and pull up/down.

  4. dhylands commented on Aug 5, 2015

    @dhylands
    Contributor

    drive strengths and pull strengths start to become more important when the speeds start increasing, and trace lengths start getting longer. When I was working on phone chips we'd often have to increase both on the dev boards (which were way bigger than a phone) and reduce them (for power considerations) on a real phone (which often have 10 layer boards to get all of the traces routed).

    I like having PULL_DOWN just be a "default" and let the board config decide what's appropriate for the default.

    I think having OUT imply OUT_PP sounds reasonable (I'd do the same for AF). OD usage is alot less common (bit banged i2c and speciality power considerations are typically where this is used). I guess you could also use it for interfacing with odd voltages (i.e. have an external pullup to the desired voltage).

  5. dpgeorge commented on Aug 6, 2015

    @dpgeorge
    Member

    I think OD is very useful when it's available, so should certainly be an option.

    Do we need to support both AF_OD and AF_PP (with AF=AF_PP)?

  6. dhylands commented on Aug 6, 2015

    @dhylands
    Contributor

    AF_OD is useful for the voltage conversion thing. If you were interfacing with a 1.8v part that wasn't 3.3v tolerant (for example) then you could use AF_OD with external pullups that pullup to 1.8v, or you were interfacing with a 5v part that didn't properly recognize 3.3v as a high (I've done that before). Using AF_OD on the PWM would allow the voltage translation chip to be eliminated (for example, this H-Bridge chip https://www.pololu.com/product/712/specs needs 4v to get a proper logic high)

  7. danicampora commented on Aug 6, 2015

    @danicampora
    MemberAuthor

    Do we need to support both AF_OD and AF_PP (with AF=AF_PP)?

    We do. For instance, the CC3200 needs to configure the pins with mode=AF and type=OD for I2C operation, otherwise it doesn't work (simply assigning the AF to SDA or SCL is not enough).

    I'd rather not have PP and OD separate. I think it's simpler to have just mode and pull

    Yes, it is simpler, but less flexible and will be harder to map to other MCUs in the future. I'd prefer to have it separate, even if it doesn't make much sense for the pyboard (but it makes sense for the CC3200).

    There are other things we can support:

    • pull strength (available on ESP8266)
    • drive strength (available on CC3200)
    • speed (available on STM)

    We could do two things:

    1. Leave this extra options out of the basic API and let each port provide the ones it needs.
    2. Make them part of the basic API and simply ignore them if not supported.
  8. dpgeorge commented on Aug 6, 2015

    @dpgeorge
    Member

    Ok, we need PP & OD for AF mode.

    Yes, it is simpler, but less flexible and will be harder to map to other MCUs in the future.

    I'm not convinced it's less flexible. Both cc3200 and stmhal are pretty much the same when configuring GPIO: they have a general mode (IN, OUT, AF, ANALOG) and then 1 bit to select PP or OD, 1 bit for PULL_UP and 1 bit for PULL_DOWN. cc3200 has an extra 2 bits for drive strength, and stmhal has bits for io speed.

    We can do it 3 different ways:

    1. All in one: mode= one of IN, IN_PU, IN_PD, OUT, OUT_OD, AF, AF_OD, ... (this is how Espruino does it, you probably also want OUT_OD_PU, AF_OD_PU, etc).
    2. All separated: mode= IN, OUT, AF, ANALOG; drive= PP, OD; pull= NONE, UP, DOWN (then strength=...; af=...; etc).
    3. Hybrid of above: mode= IN, OUT, OUT_OD, AF, AF_OD, ANALOG; pull= NONE, UP, DOWN (then strength=...; af=...; etc).

    [Note that mbed has 2 config values: direction= INPUT or OUTPUT, and type= PULL_NONE, PULL_UP, PULL_DOWN and OPEN_DRAIN (so no OD with PU).]

    We want either 2 or 3, since they are the most flexible, and I'd say they both have the same flexibility.

    One advantage(?) of 2 is that the drive parameter can be extended to include the drive strength when an MCU supports that. Eg drive=PP_2MA for cc3200. Then it doesn't need the strength parameter. drive=PP would give the default strength. (Is strength relevant for OD? If so then you'll need to do, eg, drive=OD_4MA.)

    Ultimately we want the following features:

    1. Base-line config is simple, easy, short, common across MCUs and for the most case all you'll need. Eg pin = Pin('X', Pin.OUT) or pin = Pin('Y', Pin.IN, Pin.PULL_UP). Then code like the onewire driver will work on all uPy boards.
    2. Easy path for individual ports to support more sophisticated features such as AF, drive strength
  9. danicampora commented on Aug 6, 2015

    @danicampora
    MemberAuthor

    All in one: mode= one of IN, IN_PU, IN_PD, OUT, OUT_OD, AF, AF_OD, ... (this is how Espruino does it, you probably also want OUT_OD_PU, AF_OD_PU, etc).

    This is crazy for me, hard to read and will lead to like a hundred values to cover all the possible combinations. The fact that others do it doesn't mean that it's right. For instance, the Arduino Pin API is extremely simple to the point that it sucks (mode: INPUT, OUTPUT, or INPUT_PULLUP, that's it, pff...).

    [Note that mbed has 2 config values: direction= INPUT or OUTPUT, and type= PULL_NONE, PULL_UP, PULL_DOWN and OPEN_DRAIN (so no OD with PU).]

    This is absurd. It's perfectly fine (and logical) to want OD with a PU.

    Ultimately we want the following features:

    • Base-line config is simple, easy, short, common across MCUs and for the most case all you'll need. Eg pin = Pin('X', Pin.OUT) or pin = Pin('Y', Pin.IN, Pin.PULL_UP). Then code like the onewire driver will work on all uPy boards.
    • Easy path for individual ports to support more sophisticated features such as AF, drive strength

    I agree with both.

    One advantage(?) of 2 is that the drive parameter can be extended to include the drive strength when an MCU supports that.

    The problem with that is, that if a MCU doesn't support drive strengths and a driver library in Python uses drive=PP_2MA it will fail. If instead the strength is passed separately it can simply ignore it and continue.

    I vote for either 2 or 3, but I think we should keep the special values (drive strength, pull strength, speed, etc) passed as separate params.

  10. danicampora commented on Aug 6, 2015

    @danicampora
    MemberAuthor

    (Is strength relevant for OD? If so then you'll need to do, eg, drive=OD_4MA.)

    I don't think drive strength is relevant for OD.

  11. blmorris commented on Aug 6, 2015

    @blmorris
    Contributor

    Does anyone know if drive strength and (signal) speed can be mapped to each other? To me it looks like they are different ways to specify driver output impedance: reducing the impedance increases the drive strength and increases the signal transition speed. It also increases the power required.
    If we can determine that the likely target architectures support at most one of [speed, drive strength, output impedance] then we can safely assume that it really is just different names for the same parameter. In this case we could either choose a common name that can be shared across ports, or accept all names and map them to the same underlying parameter (if it exists for that port) for compatibility across ports.

  12. danicampora commented on Aug 6, 2015

    @danicampora
    MemberAuthor

    I'll investigate.

    On Aug 6, 2015, at 4:18 PM, Bryan Morrissey [email protected] wrote:

    Does anyone know if drive strength and (signal) speed can be mapped to each other? To me it looks like they are different ways to specify driver output impedance: reducing the impedance increases the drive strength and increases the signal transition speed. It also increases the power required.
    If we can determine that the likely target architectures support at most one of [speed, drive strength, output impedance] then we can safely assume that it really is just different names for the same parameter. In this case we could either choose a common name that can be shared across ports, or accept all names and map them to the same underlying parameter (if it exists for that port) for compatibility across ports.

    —
    Reply to this email directly or view it on GitHub.

  13. blmorris commented on Aug 6, 2015

    @blmorris
    Contributor

    One architecture that I have some experience with is the Analog Devices Blackfin. The data sheets for these devices provide detailed plots of current vs pin voltage and rise/fall times vs load capacitance for each driver type, even plotting for different pin supply voltages (1.8, 2.5, 3.3V). It all amounts to a detailed specification of output impedance.
    For the STM32F405 (pyboard processor) the corresponding information can be found in 'Table 50. I/O AC characteristics' of http://www.st.com/web/en/resource/technical/document/datasheet/DM00037051.pdf
    There are 2 OSPEEDRy bits; for each of the four available output configurations the table lists maximum frequency and maximum rise/fall times for different values of load capacitance and VDD. Again, this looks like a specification of output impedance, for which we have four possible choices.
    It seems to me that for drive strength (speed, impedance, etc) we are likely choosing from at most four values; each port can assign names to these values (i.e. low = 0, med = 1, fast = 2, high = 3) but this doesn't preclude passing integer values for script portability across ports. A port that doesn't support drive strength (cc3200?) can just ignore the parameter.

  14. danicampora commented on Aug 6, 2015

    @danicampora
    MemberAuthor

    Interesting. The CC3200 does support drive strength.

  15. blmorris commented on Aug 6, 2015

    @blmorris
    Contributor

    @danicampora -

    Interesting. The CC3200 does support drive strength.

    Sorry, I did a quick scan of the cc3200 data sheet and missed it. I see it now; options are 2, 4, 8, and 14mA; strength is implemented via 3 bits, 14mA is achieved by activating all of the drivers:
    2mA + 4mA + 8mA = 14mA
    The specified rise and fall times for the cc3200 aren't a perfect match for the pyboard, but probably close enough for this API to treat them as the same.

  16. 9 remaining items

  17. dhylands commented on Aug 11, 2015

    @dhylands
    Contributor

    When declaring a pin as an output, I think it makes sense to provide an initial value for the pin, so that it doesn't "glitch". So I'd propose that we add a value=0/1 argument.

  18. dpgeorge commented on Aug 11, 2015

    @dpgeorge
    Member

    I'm happy with @danicampora's suggestion just above. Except that drive_str is not the best name (str means string in Python). It could be just drive, or strength or out_power.

  19. dhylands commented on Aug 11, 2015

    @dhylands
    Contributor

    I think you could also shorten drive_strength to either just drive or strength. I don't see any ambiguity for either one.

  20. chrismas9 commented on Aug 11, 2015

    @chrismas9
    Contributor

    I prefer drive because it covers drive strength (current limit) and drive speed (slew rate). An instance of where slew rate important is when you have a lot of simultaneous switching outputs you want low slew rate, but you would want high slew rate on SPI if you were running it at say 20MHz. Some chips limit the number of fast simultaneous switching outputs per ground pin to prevent ground bounce which can crash it.

  21. blmorris commented on Aug 11, 2015

    @blmorris
    Contributor

    re: drive_strength - STM uses 'speed' for this parameter, which I found confusing coming to this architecture given that it has nothing to do with clock speed. The EE in me likes 'impedance', but it is probably a non-starter for two reasons: First, it would be defined as reciprocal to 'strength'; low impedance <--> high strength. Second, it would only make sense to EE's and not to most software developers ;)
    I'm happy with either drive or strength being LOW, MED, HIGH, so long as it can also be specified directly as an integer. That way we don't need to specify finer granularity in the API but it can still be accessed if the architecture offers it. We can talk about slew later when it becomes an issue, and let @dhylands decide if / how to implement it on the teensy.
    @chrismas9 - agreed; and I appreciate the details regarding slew rate.

  22. danicampora commented on Aug 11, 2015

    @danicampora
    MemberAuthor

    Ok I like drive too. I think that adding the optional value param to the constructor is also a good idea. Does everyone agree?

    On Aug 11, 2015, at 5:33 PM, Chris Mason [email protected] wrote:

    I prefer drive because it covers drive strength (current limit) and drive speed (slew rate). An instance of where slew rate important is when you have a lot of simultaneous switching outputs you want low slew rate, but you would want high slew rate on SPI if you were running it at say 20MHz. Some chips limit the number of fast simultaneous switching outputs per ground pin to prevent ground bounce which can crash it.

    —
    Reply to this email directly or view it on GitHub.

  23. dpgeorge commented on Aug 11, 2015

    @dpgeorge
    Member

    So drive means whatever a given port defines it to mean (something to do with strength)? And all ports provide LOW_DRIVE, MED_DRIVE and HIGH_DRIVE constants?

  24. blmorris commented on Aug 11, 2015

    @blmorris
    Contributor

    drive is good, but it just occurs to me that drive = Pin.LOW or drive = Pin.HIGH might be confused for specifying a level. drive_current avoids the ambiguity entirely, if we think it is worth a few extra bytes / keystrokes. Maybe drive_cur? Or resolve it by @dpgeorge suggestion of naming the constant LOW_DRIVE instead?

  25. danicampora commented on Aug 11, 2015

    @danicampora
    MemberAuthor

    So drive means whatever a given port defines it to mean (something to do with strength)? And all ports provide LOW_DRIVE, MED_DRIVE and HIGH_DRIVE constants?

    Yes.

  26. pramasoul commented on Aug 12, 2015

    @pramasoul

    An open-drain is a LOW_DRIVE and HIGH_FLOAT output, and a push-pull is LOW_DRIVE and HIGH_DRIVE ;-). We might look for words more connected with force or speed, like SOFT, MODERATE, HARD, or EASE, PUSH, SLAM.

  27. danicampora commented on Aug 13, 2015

    @danicampora
    MemberAuthor

    An open-drain is a LOW_DRIVE and HIGH_FLOAT output, and a push-pull is LOW_DRIVE and HIGH_DRIVE ;-). We might look for words more connected with force or speed, like SOFT, MODERATE, HARD, or EASE, PUSH, SLAM.

    Well, finding the perfect name is almost impossible... what about naming the drive constants LOW_STRENGTH, MED_STRENGTH and HIGH_STRENGTH ? or too long ?

  28. pfalcon commented on Sep 13, 2015

    @pfalcon
    Contributor

    Should this be closed in favor of #1430?

  29. danicampora commented on Sep 13, 2015

    @danicampora
    MemberAuthor

    Yes, thanks.

  30. added a commit that references this issue on Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    rfcRequest for Comment

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions