i2c woe on pyboard d

The official PYBD running MicroPython, and its accessories.
Target audience: Users with a PYBD
Post Reply
nherriot
Posts: 5
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: 86
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

Post Reply