Skip to content

Add pinmapping functionality. #263

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 1 commit into from
Closed

Conversation

dhylands
Copy link
Contributor

@dhylands dhylands commented Feb 6, 2014

This is more or less what I was thinking about for pin mapping functionality.

I only modified the gpio function for now, but if this gets merge, then it can be used in other places as well.

@Neon22
Copy link
Contributor

Neon22 commented Feb 6, 2014

So this would be the ''string' based indexing version.

As a side note I found someone elses GPIO fun with the STM32F here:
https://github.com/Torrentula/STM32F4-examples

@dhylands
Copy link
Contributor Author

dhylands commented Feb 6, 2014

So this would be the ''string' based indexing version.

Yes and it has the advantage that it uses no RAM (well until you call the function). I checked the .o file and pinmap.o only uses 4 bytes of RAM to store the function pointer to the user mapping function.

We could use the strings to create symbols pretty easily. Somebody just has to decide where they should live and what exactly they should be called.

@blmorris
Copy link
Contributor

blmorris commented Feb 6, 2014

Is the idea that pinmap.c would take over some of the board configuration from mpconfigport.h? If so, would it be appropriate to also incorporate board-specific mapping for the SD card detect pin? Given that you included the user switch and LED mapping, I was wondering if there was a specific reason to leave SD detect out.

@dpgeorge
Copy link
Member

dpgeorge commented Feb 6, 2014

Is the idea that pinmap.c would take over some of the board configuration from mpconfigport.h?

No. The idea with pinmap is that it provides a user friendly way of naming GPIO pins. If the user wants to use pin "X2", they call in by name "X2". Or can use "A10", etc.

LED and switch are mapped because they are broken out to GPIO header pins. The SD detect pin could also be named (and might be a nice idea, so the user could access it by the name "SDSW"), but you would still need to configure the actual SD detect pin in mpconfigport.h.

pinmap is used to name pins, not to configure the low level hardware.

@dpgeorge
Copy link
Member

dpgeorge commented Feb 6, 2014

@dhylands This is very nice indeed, I like that it uses almost 0 RAM and is extensible. Maybe I missed some of the discussion, but what about also providing a dictionary map, as well as the generalised Python function that can do arbitrary mapping? Eg, you call `pin_map("X1", "my pin name").

We could use the strings to create symbols pretty easily. Somebody just has to decide where they should live and what exactly they should be called.

What exactly do you mean by symbols? Something like pin_map.X1?

@dhylands
Copy link
Contributor Author

dhylands commented Feb 6, 2014

So yeah - it probably makes sense to add the sd-detect pin. I can imagine scenarios where the python code would want to know that the sdcard is present, although it might make sense to provide a function that determines that the card is present, and correctly formatted, etc.

@dpgeorge So about the dictionary, you could do a dictionary lookup by just having the function do that. Or did you mean that it should just create an empty dictionary that could have mappings added to, and have the pinmap_map_user_obj do a lookup in said dictionary along with the other methods?

I was reluctant to populate a dictionary with the regular mappings, since then it would take up RAM.

If I'm reading you correctly, pinmap_map_user_obj would then do something like the following (stopping at the first one that indicates a successful mapping).

1 - Call the (optionally) registered function for a mapping.
2 - Check the (initially empty) dictionary for a mapping.
3 - Search the "fixed" board mapping array.
4 - See if it fits the "A3" style designation.
5 - Could also allow passing in an integer which wouldn't require any further mapping.

About the symbols, yeah you could create pin_map.X1 which is essentially a constant, but I didn't want to do that since these would then take up RAM, whereas passing in a string would only allocate ram temporarily, which could then be garbage collected later.

Once we can put constants like pin_map.X1 in flash, then I think that would be worth doing.

It might also make sense to have the board mapping constants moved to mpconfigport.h since it may very well vary on a per-board basis. I'll also fix the LED/User Switch/SD-Card-Detect to use the constants defined in mpconfigport.h rather than duplicating them.

@dpgeorge
Copy link
Member

dpgeorge commented Feb 6, 2014

I'm a bit behind on the discussion about constants, strings, RAM, flash, etc. But, let me point out the following: gpio("X1") is actually more efficient and uses less memory than gpio(pins.X1). The reason is as follows. In the compiler, all identifiers and all strings found in the script are interned (put in RAM permanently). Running gpio("X1") gets the string "X1" and passes it to the gpio function. The gpio function can then check this against known strings, or parse it to work out which port and pin it refers to. Running gpio(pins.X1) requires looking up pins in the global dictionary, then loading the string X1, then looking up X1 in the pins object, then passing the result (an integer) to gpio.

In uPy, you can basically consider all strings (that originate from a script) to belong to one big enumeration. So they are just like using constant symbols.

To eliminate RAM usage, we can preload the interned string table with strings like X1, etc. These can live in flash.

Regarding the user-populated dictionary, yes, the 5 steps above were my thoughts. I totally agree with your decision not to use RAM for the fixed board mapping array.

Re pin_map.X1 being in flash. We can already do this, although it's not obvious at first thought. One would create a new type and give it a custom load_attr method. This method could actually be exactly your pinmap_map_user_obj, but instead of having a string argument it would have a symbol argument (both of which are interned strings).

@dhylands
Copy link
Contributor Author

dhylands commented Feb 7, 2014

Re pin_map.X1 being in flash. We can already do this, although it's not obvious at first thought. One would create a new type and give it a custom load_attr method. This method could actually be exactly your pinmap_map_user_obj, but instead of having a string argument it would have a symbol argument (both of which are interned strings).

So I created: https://github.com/dhylands/micropython/blob/testattr/unix/testattr.c

and in my type I put a .load_attr function.

So after building that branch, I can do:

>>> dh.Test.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'bar'
>>> t = dh.Test()
>>> t.bar
27
>>> 

I think the attributes have to be function attributes in order to be useful. Otherwise you can't pass them to the function like so:

g = pyb.Gpio(pyb.Gpio.X1)

You need to instantiate the object and then you could use g.X1, but that's too late. So I'll dig around a bit and see if I can muddle my way through function attributes.

@blmorris
Copy link
Contributor

blmorris commented Feb 7, 2014

Thanks, I get what this is for now. I had been thinking about a way to tweak mpconfigport.h so that a new board could be fully defined within that one file; I was wondering if the new pinmap stuff would affect that at all.

To do this I am hoping to clean up the few remaining references to pyboard3 and pyboard4 remaining outside of mpconfigport.h; these references are mostly telling the compiler whether the polarity of the user switch or the sd detect switch is active high or active low. I was thinking that we could define macros like SDSW_ACTIVE_HI and USRSW_ACTIVE_LO within mpconfigport.h, then these more generic directives could replace any remaining references to PYBOARD3/4.

I will put together a patch for this if nobody gets to it before me ;-) Mostly I would like to be able to keep changes for my board within mpconfigport.h; my only reason for not contributing my board configuration to the main source tree is that there are so few of these boards in the wild that it won't be useful to anybody else for a while.

@dhylands
Copy link
Contributor Author

dhylands commented Feb 7, 2014

@dpgeorge So I was able to get function attributes to work in a somwhat hacky fashion by commenting out the assertion at the top of fun_native_call and then doing:

const mp_obj_type_t test_fun_native_type = {
{ &mp_const_type },
"test_function",
.call = fun_native_call,
.load_attr = test_load_attr,
};

#define fun_native_type test_fun_native_type
MP_DEFINE_CONST_FUN_OBJ_0(pyb_Test_obj, pyb_Test);
#undef fun_native_type

Then I was able to do

dh.Test()
and was also able to do:
dh.Test.foo
and have it call test_load_attr to lookup foo.

So then the question becomes "Is this acceptable"?

@dhylands
Copy link
Contributor Author

dhylands commented Feb 7, 2014

Aha. I think I figured out another way.

https://github.com/dhylands/micropython/blob/testattr/unix/testobj.c#L69

I made dh.TestObj be a type
I made a meta type (which the type has a base)
I put the .call and .load_attr functions in the meta type. The .call method creates new instances.
I put the .methods in the regular type.

If this seems like a reasonable approach, then I'll implement this for the pinmap stuff.
Now I can do:

>>> dh.TestObj.bar
test_load_attr called
27
>>> t = dh.TestObj()
test_make_new called type_in = 0x428690
>>> t.foo()
0
>>> t.foo(23)
>>> t.foo()
23
>>> 

@dpgeorge
Copy link
Member

dpgeorge commented Feb 8, 2014

@dhylands yes, your second version is correct.

Everything is an object (even types). A given object is an instance (perhaps singleton instance) of whatever you put in the mp_obj_base_t entry in the mp_obj_type_t struct for that object. When you make a normal type (like list) you put type in the base slot and so list (a type) is an instance of type. When you make an actual list object, you put list in its base slot so the list object is an instance of list type.

Methods of an object come from its type, not from itself.

You have created an object (test_meta) which is an instance of type. This object is a type and has methods for instances of the type. You then created an instance of this type (test), which is also a type but doesn't need to be. Being a type you can also create instances of it.

If you didn't need to create instances of test (I don't think you need to for pinmap), then you could just create a single instance of test_meta which is not a type but just a dummy object. For example, see objbool.c which creates a bool type and then 2 const instances of this type (namely false and true). You could do the same, just make a pinmap instance and make it globally known in some namespace/module.

@dhylands
Copy link
Contributor Author

dhylands commented Feb 8, 2014

@dpgeorge Thanks

This is what I'm going to propose for pin mapping then (from the python perspective).

All board pins will have predefined mappings. for the PyBoard4:
pyb.PinMap.X1 would have the value 22 (which corresponds to B6).

To allocate a GPIO for that, you'd do something like this:

g = pyb.Gpio(pyb.PinMap.X1, pyb.Gpio.DIR_INPUT)

Users can add their own mappings by doing:

pyb.PinMap("MyUserPinName", pyb.PinMap.X3)

and they would then be able to do:

g = pyb.Gpio("MyUserPinName", pyb.Gpio.DIR_INPUT)

Users would also be able to add a mapper function:

def MyMapper(pin_name):
    if (pin_name == "MyUserPinName":
        return pyb.PinMap.X1

pyb.PinMap.register_mapper(MyMapper)

g = pyb.Gpio("MyUserPinName", pyb.Gpio.DIR_INPUT)

In this case the "MyUserPinName" object is passed through to MyMapper.

@Neon22
Copy link
Contributor

Neon22 commented Feb 8, 2014

@dhylands which of those two methods uses less resources ?
@dpgeorge - cool description. Do you think this would be a useful thing to try to explain on a wiki page ?
I'm sure others will want to expose c in python while trying to keep the resource impact as low as possible...

@dhylands
Copy link
Contributor Author

dhylands commented Feb 8, 2014

@Neon22 I'd say that the resource usage is the same (between using a meta class and the function attributes). The function example uses a few less flash bytes than the meta object example (0x29d versus 0x2D0 or a difference of 0x33). Both use no RAM at compile time and should cause the same amount of RAM to be consumed at runtime.

@dpgeorge
Copy link
Member

@Neon22 It's good to have docs on this, but at the moment I think it's a bit premature, because things will still change. For example, I want to be able to define an entire uPy module in flash (at the moment it's not possible). This then would be the preferred way to make global, pre-defined entities.

@dpgeorge
Copy link
Member

What should we do about this pull request? We have a big discussion about pin names in issue #265, and perhaps we should sort that out before implementing something??

@dhylands
Copy link
Contributor Author

I'll go ahead and close it for now. I was about to do another pull request, but the discussions, I'll make more changes before doing so, and open up a new pull request.

I've got the pin data (extracted from the data sheet) in a csv file (this has all of the alternate functions which are available per-pin), as well as the data about which pins are available on which packages. I used pdftk to extract the pages from the datasheet containing the tables, and then used https://github.com/ashima/pdf-table-extract to gets the tables into csv form.

So I'm planning on writing a python script which will generate the pin objects, and we can add stuff as needed (like pointers to registers etc)

@dhylands dhylands closed this Feb 11, 2014
@dhylands dhylands deleted the pinmap branch June 10, 2015 02:22
tannewt added a commit to tannewt/circuitpython that referenced this pull request Feb 8, 2018
This introduces SAMD51 support and re-enables SAMD21 support.

Fixes micropython#263
tannewt added a commit to tannewt/circuitpython that referenced this pull request Apr 12, 2018
This evolves the API from 2.x (and breaks it). Playback devices are now
separate from the samples themselves. This allows for greater playback
flexibility. Two sample sources are audioio.RawSample and audioio.WaveFile.
They can both be mono or stereo. They can be output to audioio.AudioOut or
audiobusio.I2SOut.

Internally, the dma tracking has changed from a TC counting block transfers
to an interrupt generated by the block event sent to the EVSYS. This reduces
the overhead of each DMA transfer so multiple can occure without using up TCs.

Fixes micropython#652. Fixes micropython#522. Huge progress on micropython#263
tannewt added a commit to tannewt/circuitpython that referenced this pull request May 1, 2018
I2SOut.

The API is almost the same except the frequency attribute has been
renamed to sample_rate so that its less likely to be confused with
frequencies within the audio itself.

Fixes micropython#263.
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.

4 participants