Skip to content
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

applet.interface.i2c_master: add "scan" command #166

Merged
merged 2 commits into from
Oct 6, 2019

Conversation

electroniceel
Copy link
Member

"scan" scans for i2c devices on read and write addresses in one go.
Some devices only ACK one of the addresses, so scanning both gives
a better overview of the bus. With this command you don't need to
do a "scan-read" and then a "scan-write" to get the result.

@whitequark
Copy link
Member

Should we maybe have a single scan command with options -r, -w, (combined) -rw instead?

@electroniceel
Copy link
Member Author

would also be an option. I'm indifferent, both ways are a reasonable interface IMO.

@whitequark
Copy link
Member

Let's do that instead. Would you adjust the PR please?

@electroniceel
Copy link
Member Author

will do


p_scan.add_argument(
"--readwrite", "-rw", action="store_true", default=False,
help="scan read and write addresses")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the combination of -r and -w already work as -rw with no additional effort? Or does argparse not provide that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, you are right. I didn't give argparse enough credit, it can do it.
Will fix it in the next rev.

self._logger.log(self._level, "I2C scan: found write address %s",
"{:#09b}".format(addr))
found.append(addr)
return found
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently returns the exact same result if the device at addr can read and if it can write, right? I think this interface is not good enough to be in our public API.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently returns the exact same result if the device at addr can read and if it can
write, right?

correct.

I think this interface is not good enough to be in our public API.

I actually thought a bit about the interface and this is the one I liked best.

I thought how would one use the scan function. I think the most common use case will
be getting all addresses that answer somehow (read or write) and then iterate over
the result, maybe probing the devices in more detail.

The case were it is relevant to distinguish between read ack and write ack is a valid
use case, but I think it will be used less often. So the interface is easy to use
for the common case and if you want read vs. write, you just call it twice to get separate
lists.

Another idea would be to add methods like scan_read_addr(self, i2c_addr) and scan_write_addr(self, i2c_addr)
which you could use to probe an address in more details when iterating the result list.

The runtime is dominated by the i2c transaction. The scan function in read/write mode
will skip scanning the write addr if it found a read addr, saving time.

Another interface would be to return something like

{
  "0x40" : {
    "read": True,
    "write" : False
  },
  "0x41" : {
    "read": True,
    "write" : True
  }
}

but I'm not sure it is worth to add that complexity for the not-so-common case.

Copy link
Member

@whitequark whitequark Oct 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

The scan function in read/write mode will skip scanning the write addr if it found a read addr, saving time.

I actually feel like it's interesting to discover both read and write addresses (as printed in the log), in case some peripheral answers to reads but not writes. What about making found a set() and changing this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another useful improvement I think would be to restrict scanning to a few addresses. E.g. I know some peripheral can only be at 0b1000000..0b1000111.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what kind of structure do you want to return exactly? The one I posted above under "Another interface would be" or something else?

A simple set will not convey if a device answered read or write.

Copy link
Member

@whitequark whitequark Oct 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the current interface (one function) is sufficient.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another useful improvement I think would be to restrict scanning to a few addresses. E.g. I know some peripheral can only be at 0b1000000..0b1000111.

how about
scan(self, *, read_addrs=True, write_addrs=True, scan_from=0b0001_000, scan_to=0b1111_000)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd do something like scan(self, range=range(0b0001_000, 0b1111_000), *, read=True, write=True), personally

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, but I'd use scan(self, address_sequence=range(0b0001_000, 0b1111_000),... then, because the parameter is a sequence that is just filled with a range by default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just addresses is fine then. "Sequence" means a different thing in Python (what you'd call an array in C, strangely enough), and this is an "iterable".

@@ -233,6 +233,24 @@ def __init__(self, interface, logger):
revision = device_id[2] & 0x7
return (manufacturer, part_ident, revision)

async def scan(self, read_addrs=True, write_addrs=True):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should mark any boolean flags as keyword-only arguments, like async def scan(self, *, read_addrs=True, write_addrs=True):. (I should add a CONTRIBUTING.md file that explains these things...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I wasn't thinking pythonic enough. Will fix that in the next rev.

Unverified

This user has not yet uploaded their public signing key.
…mands with "scan"

"-r", "-w" and "-rw" allow to select which type of addresses to scan.
"-rw" is the default.

Some devices only ACK one of the addresses, so scanning both gives
a better overview of the bus.
…sterInterface

Allows to use it from other applets or the repl interface
@whitequark whitequark merged commit 89c5c05 into GlasgowEmbedded:master Oct 6, 2019
@whitequark
Copy link
Member

Thanks!

@electroniceel
Copy link
Member Author

Do you think side effects are more likely with read scanning than write scanning?
If so, we could change the order of read and write scanning.

@whitequark
Copy link
Member

In case of read scanning you're reading one byte, in case of write scanning you're aborting the transaction after the address, which is why I thought it's more likely.

@electroniceel
Copy link
Member Author

I remember reading a datasheet where the interrupt line was reset when reading out the irq register or something like that. That would match your line of thought.
So let's do the write scan first. If the device answers, no read scan is done. This will reduce the likeliness of read-side effects.

@whitequark
Copy link
Member

Let's.

@electroniceel
Copy link
Member Author

grmpf. additions to a branch aren't possible in github once it's merged.

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.

None yet

2 participants