Circuits.I2C (circuits_i2c v2.1.0)
View SourceCircuits.I2C
lets you communicate with hardware devices using the I2C
protocol.
Summary
Types
I2C device address
Backends specify an implementation of a Circuits.I2C.Backend behaviour
Connection to a real or virtual I2C controller
I2C open options
Function to report back whether a device is present
I2C transfer options
Functions
Return a list of available I2C bus names
Close the I2C bus
Convenience method to scan all I2C buses for devices
Scan the I2C bus for devices
Return whether a device is present
Scan all I2C buses for one or more devices
Scans all I2C buses for one specific device
Same as discover_one/2
but raises on error
Return info about the low level I2C interface
Open an I2C bus
Initiate a read transaction to the I2C device at the specified address
Initiate a read transaction and raise on error
Write data
to the I2C device at address
.
Write data
to the I2C device at address
and raise on error
Write data
to an I2C device and then immediately issue a read.
Write data
to an I2C device and then immediately issue a read. Raise on errors.
Types
@type address() :: 0..127
I2C device address
This is a "7-bit" address for the device. Some devices specify an "8-bit" address in their documentation. You can tell if you have an "8-bit" address if it's greater than 127 (0x7f) or if the documentation talks about different read and write addresses. If you have an 8-bit address, divide it by 2.
Backends specify an implementation of a Circuits.I2C.Backend behaviour
The second parameter of the Backend 2-tuple is a list of options. These are passed to the behaviour function call implementations.
@type bus() :: Circuits.I2C.Bus.t()
Connection to a real or virtual I2C controller
@type open_options() :: keyword()
I2C open options
See I2C backend documentation, device driver caveats, and function calls in this module for notes. In general,
:retries
- the number of times to retry a transaction. I.e. 2 retries means the transaction is attempted at most 3 times. Defaults to 0 retries.:timeout
- the time in milliseconds to wait for a transaction to complete. Any value <0 means to use the device driver or hardware default. The default value is -1 to avoid setting it, but it is very common for the default to be 1000 ms.
@type present?() :: (Circuits.I2C.Bus.t(), address() -> boolean())
Function to report back whether a device is present
See discover/2
for how a custom function can improve device detection when
the type of device being looked for is known.
@type transfer_options() :: keyword()
I2C transfer options
See I2C backend documentation, device driver caveats, and function calls in this module for notes. In general,
:retries
- override the number of times to retry a transaction from what was passed toopen/3
.
Functions
@spec bus_names() :: [String.t()]
Return a list of available I2C bus names
If nothing is returned, it's possible that the kernel driver for that I2C bus
is not enabled or the kernel's device tree is not configured. On Raspbian,
run raspi-config
and look in the advanced options.
iex> Circuits.I2C.bus_names()
["i2c-1"]
@spec close(Circuits.I2C.Bus.t()) :: :ok
Close the I2C bus
@spec detect_devices() :: :"do not show this result in output"
Convenience method to scan all I2C buses for devices
This is only intended to be called from the IEx prompt. Programs should
use detect_devices/1
.
@spec detect_devices(Circuits.I2C.Bus.t() | binary()) :: [address()] | {:error, term()}
Scan the I2C bus for devices
Since there's no official way of enumerating I2C devices, this attempts to figure out what's on the bus by seeing what devices respond.
iex> Circuits.I2C.detect_devices("i2c-1")
[4]
The return value is a list of device addresses that were detected on the
specified I2C bus. If you get back 'Hh'
or other letters, then IEx
converted the list to an Erlang string. Run i v()
to get information about
the return value and look at the raw string representation for addresses.
Warning
This is intended to be a debugging aid. Reading bytes from devices can advance internal state machines and might cause them to get out of sync with other code.
If you already have opened an I2C bus, then pass that to detect_devices/1
instead of the string name.
@spec device_present?(Circuits.I2C.Bus.t(), address()) :: boolean()
Return whether a device is present
This function performs a simplistic check for an I2C device on the specified bus and address. It's not perfect, but works enough to be useful. Be warned that it does perform an I2C read on the specified address and this may cause some devices to actually do something.
Scan all I2C buses for one or more devices
This function takes a list of possible addresses and an optional detection function. It only scans addresses in the possible addresses list to avoid disturbing unrelated I2C devices.
If a detection function is not passed in, a default one that performs a
simple read and checks whether it succeeds is used. If the desired device has
an ID register or other means of identification, the optional function should
try to query that. If passing a custom function, be sure to return false
rather than raise if there are errors.
A list of bus name and address tuples is returned. The list may be empty.
See also discover_one/2
.
@spec discover_one([address()], present?()) :: {:ok, {binary(), address()}} | {:error, :not_found | :multiple_possible_matches}
Scans all I2C buses for one specific device
This function and discover_one!/2
are convenience functions for the use
case of helping a user find a specific device. They both call discover/2
with
a list of possible I2C addresses and an optional function for checking whether
the device is present.
This function returns an :ok
or :error
tuple depending on whether one and
only one device was found. See discover_one!/2
for the raising version.
Same as discover_one/2
but raises on error
Return info about the low level I2C interface
This may be helpful when debugging I2C issues.
@spec open(String.t(), open_options()) :: {:ok, Circuits.I2C.Bus.t()} | {:error, term()}
Open an I2C bus
I2C bus names depend on the platform. Names are of the form "i2c-n" where the
"n" is the bus number. The correct bus number can be found in the
documentation for the device or on a schematic. Another option is to call
Circuits.I2C.bus_names/0
to list them for you.
The same I2C bus may be opened more than once. There is no need to share it between modules.
On success, this returns a Circuits.I2C.Bus.t()
struct for accessing the
I2C bus. Use this in subsequent calls to read and write I2C devices.
Options depend on the backend. The following are for the I2CDev (default) backend:
:retries
- the number of times to retry a transaction. I.e. 2 retries means the transaction is attempted at most 3 times. Defaults to 0 retries.:timeout
- the time in milliseconds to wait for a transaction to complete or <0 to avoid setting it. Defaults to -1.
@spec read(Circuits.I2C.Bus.t(), address(), pos_integer(), transfer_options()) :: {:ok, binary()} | {:error, term()}
Initiate a read transaction to the I2C device at the specified address
See transfer_options/0
for options, but backend may provide more.
@spec read!(Circuits.I2C.Bus.t(), address(), pos_integer(), transfer_options()) :: binary()
Initiate a read transaction and raise on error
@spec write(Circuits.I2C.Bus.t(), address(), iodata(), transfer_options()) :: :ok | {:error, term()}
Write data
to the I2C device at address
.
See transfer_options/0
for options, but backend may provide more.
@spec write!(Circuits.I2C.Bus.t(), address(), iodata(), transfer_options()) :: :ok
Write data
to the I2C device at address
and raise on error
See transfer_options/0
for options, but backend may provide more.
@spec write_read( Circuits.I2C.Bus.t(), address(), iodata(), pos_integer(), transfer_options() ) :: {:ok, binary()} | {:error, term()}
Write data
to an I2C device and then immediately issue a read.
This function is useful for devices that want you to write the "register"
location to them first and then issue a read to get its contents. Many
devices operate this way and this function will issue the commands
back-to-back on the I2C bus. Some I2C devices actually require that the read
immediately follows the write. If the target supports this, the I2C
transaction will be issued that way. On the Raspberry Pi, this can be enabled
globally with File.write!("/sys/module/i2c_bcm2708/parameters/combined", "1")
See transfer_options/0
for options, but backend may provide more.
@spec write_read!( Circuits.I2C.Bus.t(), address(), iodata(), pos_integer(), transfer_options() ) :: binary()
Write data
to an I2C device and then immediately issue a read. Raise on errors.
See transfer_options/0
for options, but backend may provide more.