i2c woe on pyboard d

The official PYBD running MicroPython, and its accessories.
Target audience: Users with a PYBD
Post Reply
nherriot
Posts: 15
Joined: Wed Oct 19, 2016 1:02 pm

i2c woe on pyboard d

Post by nherriot » Fri May 17, 2019 3:44 pm

Been pulling my hair out trying to get I2C working on the new board. And I mean this in terms of following examples and documentation. In a nut shell - can someone point me to a clearly written piece of documentation that 'just works' in doing something like:

Code: Select all

>>> i2c.scan()
[60]
So what I would love is the document to specify how to construct the i2c object! :-). And breaking this down to individual points this is what I don't get:
1) Should i use I2C from the pyb library or machine library? Which is correct and best way?
Python rule 13 -There should be one-- and preferably only one --obvious way to do it.



2) From the machine library here (http://docs.micropython.org/en/latest/l ... e.I2C.html) it says that the class is constructed like this: class machine.I2C(id=-1, *, scl, sda, freq=400000)

Does this mean that we have 2 key words that are defaulted if you don't provide them which are id and freq? So you can construct the object by just providing the scl and sda arguments right? And those arguments have to be pin objects specifying the pin to use? So why does this work:

Code: Select all

>>> import machine
>>> i2c = machine.I2C('X')
>>> i2c.scan()
[60]
:-( :-( :-( nothing worse than doing something that works but you have no idea why it works..... I just supplied my class with a single parameter and it was a string not even a pin object....



3) If I try and create an I2C object and provide it with the correct values it does not seem to work for me, maybe someone can spot my school boy error!!!! :-( ....

Code: Select all

>>> i2c = I2C(id=-1, scl=machine.Pin(9), sda=machine.Pin(10), freq=400000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert 'int' object to str implicitly
So i need to pass in a string to the Pin constructors. Although it looks like the doc's are passing in integer values?
So from here: ( http://docs.micropython.org/en/latest/l ... e.Pin.html)
It says:

Code: Select all

from machine import Pin
# create an output pin on pin #0
p0 = Pin(0, Pin.OUT)
That confused me, but I changed it to provide string values for I2C - which is shown below:

Code: Select all

>>> i2c = I2C(id=-1, scl=machine.Pin('X9'), sda=machine.Pin('X10'), freq=400000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: extra keyword arguments given
This stumped me as I've provided id, scl, sda and freq. Why does it thing I've provided an extra keyword arg?



4) I'm guessing this, but for the pyboard 'D' it's already setup to use pin X9 and X10 is this correct?



OK - I'm off for a coffee and some chicken... Please someone cheer me up and answer this post! :-)

Kind regards, Nicholas.

chuckbook
Posts: 91
Joined: Fri Oct 30, 2015 11:55 pm

Re: i2c woe on pyboard d

Post by chuckbook » Fri May 17, 2019 8:15 pm

Hi Nicholas,
agreed that the pyb/machine duality is sometimes pretty confusing. But there are good reasons to have them both.
In short, pyb is only valid for stm32 MCUs, whereas machine tries to be manufacturer agnostic.

Regarding I2C this means that the driver has to cover many different architectures.
Also new features (I2C slave etc.) should go into machine rather than just pyb.
As a result, machine.I2C covers HW implementations as well as simple bit-bangers.
Of course a HW implementation should always be preferred if it exists. But sometimes bit-banging is the only solution.
On PYBD there are up to four HW I2C channels that can be mapped to several pins.
Within the default port configuration the HW USP ports are assigned to numerical values (I2C1..I2C4) and so called skin ('X' & 'Y') locations.
Another approach is to specify SDA & CLK pins and then it gets bit complicated as the pins might be capable of doing HW I2C.
This works only if both pins belong to the same I2C channel and this channel is not used elsewhere. If these conditions are not met, there is still the option to do bit-banging etc.
I try to avoid bit banging under all circumstances as these implementations are prone to be performance pigs, especially on fast MCUs.
If it comes to slave mode, bit-banging is an absolute no-go.

In short:
Simply use i2c = machine.I2C('X') if available or, if you know some of the internals, machine.I2C(1).
If you have to use bit-banging, instantiate 2 pin objects (they have to support OD operation for real I2C devices) and use them as parameters when defining a bitbang I2C instance.

BTW, this also applies to SPI in a similar way.

Please note that all said is my personal opinion.

Thomas

nherriot
Posts: 15
Joined: Wed Oct 19, 2016 1:02 pm

Re: i2c woe on pyboard d

Post by nherriot » Wed May 22, 2019 4:46 pm

Hi Thomas,

thanks for the quick reply! :D And explanations!

When you say:
"On PYBD there are up to four HW I2C channels that can be mapped to several pins"

I could only see 2 x I2C bus' on the pbd.io site from the image here:
https://store.micropython.org/media/pro ... F2_ds2.jpg
The PYBD reference shows one on Y10, Y9 and one on X10, X9. Where are the other 2 mapped?

I'm also not interested in bit banging and prefer to use the I2C bus as designed.

I also noticed you did this kind of thing:

Code: Select all

>>>  import machine
>>>  myI2C = I2C(1)
Which works for me! Great! Although I could not see where that was documented? :oops:
Is there a way to find out easily what are the default pin's for your I2C object if you allow the pyboard D to create with defaults?

Kind regards, Nicholas :-)

User avatar
dhylands
Posts: 3181
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: i2c woe on pyboard d

Post by dhylands » Wed May 22, 2019 5:02 pm

If you look at the quick reference image here: https://store.micropython.org/product/P ... 4F2#image5
it shows the pins for I2C(1) and I2C(2) as well as the other peripherals.

User avatar
jimmo
Posts: 248
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: i2c woe on pyboard d

Post by jimmo » Wed May 22, 2019 5:08 pm

I'm pretty sure you're right, there are two not four hardware I2C peripherals.

The magic values to use for the ID aren't well documented, could definitely be improved. I think people who've been used to the original pyboard would be used to this from the (deprecates) pyb.I2C class. This style is used in other places too (e.g. machine.SPI). It's possible some subtly of the difference between the ports got lost when the documentation combined all the ports together (which has otherwise been a huge improvement).

I much prefer to use I2C('X') and I2C('Y') instead of the numeric IDs e.g. I2C(1). It also means it's obvious how to use the pin mappings documented at https://pybd.io/hw/pybd_sfxw.html.

(And this interacts nicely with the tiles, where a given tile adaptor might place tiles on the X (wbus68) or Y (wbus28) "position".

It's worth noting that these are not just the default pins but the _only_ pins if you're using the hardware i2c peripheral (unlike other microcontroller families, the stm32 has fixed mappings). If you specify pins to the I2C constructor then you cannot specify the ID, and you get the software I2C implementation instead. (And worth noting that even if you manually specify the hardware pins, you'll still get software SPI).

Another thing that isn't super obvious from the documentation is that when you use hard vs soft i2c, the object you get back has more methods available when you use soft (the "primitive" methods). Almost nothing should need to use these methods, especially in v1.11 which adds writev_to for both hard and soft

chuckbook
Posts: 91
Joined: Fri Oct 30, 2015 11:55 pm

Re: i2c woe on pyboard d

Post by chuckbook » Wed May 22, 2019 7:57 pm

I'm pretty sure there are 4 I2C interfaces on the F767!
However, only two of them might be assigned to X & Y interfaces within of-the-shelf MPY binaries. I rarely use the default binaries and I have to admit that we are using our own board configurations where we define all the required interfaces and pin assignments.
Also the STM32F7xx alt function matrix is, to be polite, a bit disheveling.
But beside it's strange layout, the WBUS was carefully designed. We were able to use up to six SPI, four I2C, LCD, RMII-ETH. DCMI and several UARTs. Of course not all of them in a single design.

There are plans to create a tool that helps creating mpconfigboard.h and pins.csv files with respect to WBUS and MCU features.

User avatar
jimmo
Posts: 248
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: i2c woe on pyboard d

Post by jimmo » Thu May 23, 2019 12:01 am

Yeah sorry I should have been clearer referring to what was available at the MicroPython level. Like chuckbook says, the stm32f722 has three, the f767 has four. But as far as I know, the current pydb firmware only makes available the first two on all the PYBD boards. e.g. MICROPY_HW_I2C3_SCL and MICROPY_HW_I2C4_SCL aren't defined in the board definitions, this will prevent you from being able to use machine.I2C(3) (or 4) from Python.

The machine.I2C class on STM32 will always configure the primary pins (and always re-sets their AF), so if you try to set the same AF on a different pair of pins first you'll end up with both sets mapped to the same AF. I'm actually not sure what happens on STM32 if you have multiple pins mapped to the same AF (especially for something that uses input like I2C). It seems clear how it works for output-only (e.g. UART TX), but I can't find any documentation that describes how the alternate function lines from the pin's input schmitt triggers are joined together at the input of the peripheral. I don't think I've ever tried this, if anyone has more info would love to know!
I guess to be safe, once the machine.I2C instance is constructed, you could map the default pins back to a different AF.

User avatar
dhylands
Posts: 3181
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: i2c woe on pyboard d

Post by dhylands » Thu May 23, 2019 3:35 pm

jimmo wrote:
Thu May 23, 2019 12:01 am
The machine.I2C class on STM32 will always configure the primary pins (and always re-sets their AF), so if you try to set the same AF on a different pair of pins first you'll end up with both sets mapped to the same AF. I'm actually not sure what happens on STM32 if you have multiple pins mapped to the same AF (especially for something that uses input like I2C). It seems clear how it works for output-only (e.g. UART TX), but I can't find any documentation that describes how the alternate function lines from the pin's input schmitt triggers are joined together at the input of the peripheral. I don't think I've ever tried this, if anyone has more info would love to know!
I guess to be safe, once the machine.I2C instance is constructed, you could map the default pins back to a different AF.
It would be like they're electrically connected. Let's suppose that pin A1 and B2 were both mapped to SDA for I2C(1) and both AF's were set to the I2C1. It would essentially be the same as if there were a wire connected between pins A1 and B2. If the pins were connected to something else then you would get interference.

If the board definition had A1 connected to I2C1 and you wanted to use A1 for something else and use B2 for I2C1 but didn't want to change the board definition then you'd need to do something like:

open I2C1
configure A1 back to whatever non-I2C purpose you wanted
configure B2 to have the I2C1 AF

nherriot
Posts: 15
Joined: Wed Oct 19, 2016 1:02 pm

Re: i2c woe on pyboard d

Post by nherriot » Tue May 28, 2019 11:40 am

So much information! Yikes! :shock:

OK - so i think I've got the jist of the previous posts. Please correct if I got anything wrong.
1) The actually Microprocessor (STM32F7) has 4 I2C buses.
2) The pyboard D uses 2 of the possible 4 and allows you to use them in your program using 'machine' or 'pyd' libs.
3) The mapping is shown here: https://pybd.io/hw/pybd_sfxw.html
4) Due to the amalgamation of different documents it's not exactly spelt out what defaults are used creating I2C objects.
5) You can still map Pins onto other Pins.
6) There is some fancy mapping of 'X' and 'Y' slots which map your object to the default Pins shown in the D-series reference guide - but not that well documented yet.

Thanks to everyone for providing the information! :-) I'll try and get time to put something on the wiki.micropython.org site to capture this information. It's 'gold dust' as they say! :-)

Kind regards, Nicholas.

Post Reply