Compare commits

...

1 commit

Author SHA1 Message Date
James Harton 16ef8ac43e Port over to using circuits_i2c instead of elixir_ale. 2019-12-02 17:42:00 +13:00
5 changed files with 262 additions and 184 deletions

View file

@ -1,18 +1,30 @@
defmodule PCA9641 do
@moduledoc """
Documentation for PCA9641.
PCA9641 Driver for Elixir using Circuits.
## Usage:
Add your devices to your config like so:
config :pca9641,
devices: [
%{bus: "i2c-1", address: 0x70, interrupt_pin: 7}
]
Then use the functions in [PCA9641.Device] to send image data.
Pretty simple.
"""
@doc """
Hello world.
## Examples
iex> PCA9641.hello()
:world
Connect to an PCA9641 device.
"""
def hello do
:world
def connect(config),
do: Supervisor.start_child(PCA9641.Supervisor, {PCA9641.Device, config})
@doc """
Disconnect an PCA9641 device.
"""
def disconnect(device_name) do
Supervisor.terminate_child(PCA9641.Supervisor, {PCA9641.Device, device_name})
Supervisor.delete_child(PCA9641.Supervisor, {PCA9641.Device, device_name})
end
end

View file

@ -1,6 +1,6 @@
defmodule PCA9641.Commands do
alias PCA9641.Registers
alias ElixirALE.I2C
alias Circuits.I2C
use Bitwise
@moduledoc """
@ -8,18 +8,27 @@ defmodule PCA9641.Commands do
the device.
"""
@doc false
def start_link(bus, address), do: I2C.start_link(bus, address)
@type bus_address :: {I2C.bus(), I2C.address()}
@doc false
def release(pid), do: I2C.release(pid)
@spec start_link(I2C.bus(), I2C.address()) :: {:ok, bus_address} | {:error, term}
def start_link(bus, address) when is_integer(address) and address >= 0 and address <= 127 do
case I2C.open(bus) do
{:ok, bus} -> {:ok, {bus, address}}
error -> error
end
end
@doc false
@spec release(bus_address) :: :ok
def release({bus, _address}), do: I2C.release(bus)
@doc """
Retrieve the device ID. For PCA9641 this should be 0x38.
"""
@spec id(pid) :: {:ok, non_neg_integer} | {:error, term}
def id(pid) do
<<id>> = Registers.id(pid)
@spec id(bus_address) :: {:ok, non_neg_integer} | {:error, term}
def id(bus_address) do
<<id>> = Registers.id(bus_address)
{:ok, id}
end
@ -29,8 +38,8 @@ defmodule PCA9641.Commands do
Master can set this register bit for setting priority of the winner when two
masters request the downstream bus at the same time.
"""
@spec priority?(pid) :: boolean
def priority?(pid), do: read_bit_as_boolean(pid, :control, 7)
@spec priority?(bus_address) :: boolean
def priority?(bus_address), do: read_bit_as_boolean(bus_address, :control, 7)
@doc """
PRIORITY
@ -38,8 +47,8 @@ defmodule PCA9641.Commands do
Master can set this register bit for setting priority of the winner when two
masters request the downstream bus at the same time.
"""
@spec priority(pid, boolean) :: :ok | {:error, term}
def priority(pid, value), do: write_bit_as_boolean(pid, :control, 7, value)
@spec priority(bus_address, boolean) :: :ok | {:error, term}
def priority(bus_address, value), do: write_bit_as_boolean(bus_address, :control, 7, value)
@doc """
SMBUS_DIS
@ -51,8 +60,9 @@ defmodule PCA9641.Commands do
- `true` -> Connectivity between master and downstream bus will be
disconnected upon detecting an SMBus time-out condition.
"""
@spec downstream_disconnect_on_timeout?(pid) :: boolean
def downstream_disconnect_on_timeout?(pid), do: read_bit_as_boolean(pid, :control, 6)
@spec downstream_disconnect_on_timeout?(bus_address) :: boolean
def downstream_disconnect_on_timeout?(bus_address),
do: read_bit_as_boolean(bus_address, :control, 6)
@doc """
SMBUS_DIS
@ -64,9 +74,9 @@ defmodule PCA9641.Commands do
- `true` -> Connectivity between master and downstream bus will be
disconnected upon detecting an SMBus time-out condition.
"""
@spec downstream_disconnect_on_timeout(pid, boolean) :: :ok | {:error, term}
def downstream_disconnect_on_timeout(pid, value),
do: write_bit_as_boolean(pid, :control, 6, value)
@spec downstream_disconnect_on_timeout(bus_address, boolean) :: :ok | {:error, term}
def downstream_disconnect_on_timeout(bus_address, value),
do: write_bit_as_boolean(bus_address, :control, 6, value)
@doc """
IDLE_TIMER_DIS
@ -81,8 +91,8 @@ defmodule PCA9641.Commands do
reserve timer is disabled, if the downstream bus is idle for more than 100
ms, the connection between master and downstream bus will be disconnected.
"""
@spec idle_timer_disconnect?(pid) :: boolean
def idle_timer_disconnect?(pid), do: read_bit_as_boolean(pid, :control, 5)
@spec idle_timer_disconnect?(bus_address) :: boolean
def idle_timer_disconnect?(bus_address), do: read_bit_as_boolean(bus_address, :control, 5)
@doc """
IDLE_TIMER_DIS
@ -97,8 +107,9 @@ defmodule PCA9641.Commands do
reserve timer is disabled, if the downstream bus is idle for more than 100
ms, the connection between master and downstream bus will be disconnected.
"""
@spec idle_timer_disconnect(pid, boolean) :: :ok | {:error, term}
def idle_timer_disconnect(pid, value), do: write_bit_as_boolean(pid, :control, 5, value)
@spec idle_timer_disconnect(bus_address, boolean) :: :ok | {:error, term}
def idle_timer_disconnect(bus_address, value),
do: write_bit_as_boolean(bus_address, :control, 5, value)
@doc """
SMBUS_SWRST
@ -110,8 +121,8 @@ defmodule PCA9641.Commands do
- `true` -> Enable sending SMBus time-out to downstream bus, after receiving a
general call soft reset from master.
"""
@spec smbus_software_reset?(pid) :: boolean
def smbus_software_reset?(pid), do: read_bit_as_boolean(pid, :control, 4)
@spec smbus_software_reset?(bus_address) :: boolean
def smbus_software_reset?(bus_address), do: read_bit_as_boolean(bus_address, :control, 4)
@doc """
SMBUS_SWRST
@ -123,8 +134,9 @@ defmodule PCA9641.Commands do
- `true` -> Enable sending SMBus time-out to downstream bus, after receiving a
general call soft reset from master.
"""
@spec smbus_software_reset(pid, boolean) :: :ok | {:error, term}
def smbus_software_reset(pid, value), do: write_bit_as_boolean(pid, :control, 4, value)
@spec smbus_software_reset(bus_address, boolean) :: :ok | {:error, term}
def smbus_software_reset(bus_address, value),
do: write_bit_as_boolean(bus_address, :control, 4, value)
@doc """
BUS_INIT
@ -139,8 +151,8 @@ defmodule PCA9641.Commands do
- `true` -> Start initialization on next bus connect function to downstream
bus.
"""
@spec bus_init?(pid) :: boolean
def bus_init?(pid), do: read_bit_as_boolean(pid, :control, 3)
@spec bus_init?(bus_address) :: boolean
def bus_init?(bus_address), do: read_bit_as_boolean(bus_address, :control, 3)
@doc """
BUS_INIT
@ -155,8 +167,8 @@ defmodule PCA9641.Commands do
- `true` -> Start initialization on next bus connect function to downstream
bus.
"""
@spec bus_init(pid, boolean) :: :ok | {:error, term}
def bus_init(pid, value), do: write_bit_as_boolean(pid, :control, 3, value)
@spec bus_init(bus_address, boolean) :: :ok | {:error, term}
def bus_init(bus_address, value), do: write_bit_as_boolean(bus_address, :control, 3, value)
@doc """
BUS_CONNECT
@ -167,8 +179,8 @@ defmodule PCA9641.Commands do
- `false` -> Do not connect I2C-bus from master to downstream bus.
- `true` -> Connect downstream bus; the internal switch is closed only if LOCK_GRANT = 1.
"""
@spec bus_connect?(pid) :: boolean
def bus_connect?(pid), do: read_bit_as_boolean(pid, :control, 2)
@spec bus_connect?(bus_address) :: boolean
def bus_connect?(bus_address), do: read_bit_as_boolean(bus_address, :control, 2)
@doc """
BUS_CONNECT
@ -179,8 +191,8 @@ defmodule PCA9641.Commands do
- `false` -> Do not connect I2C-bus from master to downstream bus.
- `true` -> Connect downstream bus; the internal switch is closed only if LOCK_GRANT = 1.
"""
@spec bus_connect(pid, boolean) :: :ok | {:error, term}
def bus_connect(pid, value), do: write_bit_as_boolean(pid, :control, 2, value)
@spec bus_connect(bus_address, boolean) :: :ok | {:error, term}
def bus_connect(bus_address, value), do: write_bit_as_boolean(bus_address, :control, 2, value)
@doc """
LOCK_GRANT
@ -193,8 +205,8 @@ defmodule PCA9641.Commands do
- `false` -> This master does not have a lock on the downstream bus.
- `true` -> This master has a lock on the downstream bus.
"""
@spec lock_grant?(pid) :: boolean
def lock_grant?(pid), do: read_bit_as_boolean(pid, :control, 1)
@spec lock_grant?(bus_address) :: boolean
def lock_grant?(bus_address), do: read_bit_as_boolean(bus_address, :control, 1)
@doc """
LOCK_REQ
@ -209,8 +221,8 @@ defmodule PCA9641.Commands do
up the lock if master had a lock on the downstream bus.
- `true` -> Master is requesting a lock on the downstream bus.
"""
@spec lock_request?(pid) :: boolean
def lock_request?(pid), do: read_bit_as_boolean(pid, :control, 0)
@spec lock_request?(bus_address) :: boolean
def lock_request?(bus_address), do: read_bit_as_boolean(bus_address, :control, 0)
@doc """
LOCK_REQ
@ -225,8 +237,8 @@ defmodule PCA9641.Commands do
up the lock if master had a lock on the downstream bus.
- `true` -> Master is requesting a lock on the downstream bus.
"""
@spec lock_request(pid, boolean) :: :ok | {:error, term}
def lock_request(pid, value), do: write_bit_as_boolean(pid, :control, 0, value)
@spec lock_request(bus_address, boolean) :: :ok | {:error, term}
def lock_request(bus_address, value), do: write_bit_as_boolean(bus_address, :control, 0, value)
@doc """
SDA_IO
@ -243,8 +255,8 @@ defmodule PCA9641.Commands do
- `true` -> When read, indicates the SDA pin of the downstream bus is HIGH.
When written, PCA9641 drives SDA pin of the downstream bus HIGH.
"""
@spec sda_becomes_io?(pid) :: boolean
def sda_becomes_io?(pid), do: read_bit_as_boolean(pid, :status, 7)
@spec sda_becomes_io?(bus_address) :: boolean
def sda_becomes_io?(bus_address), do: read_bit_as_boolean(bus_address, :status, 7)
@doc """
SDA_IO
@ -261,8 +273,8 @@ defmodule PCA9641.Commands do
- `true` -> When read, indicates the SDA pin of the downstream bus is HIGH.
When written, PCA9641 drives SDA pin of the downstream bus HIGH.
"""
@spec sda_becomes_io(pid, boolean) :: :ok | {:error, term}
def sda_becomes_io(pid, value), do: write_bit_as_boolean(pid, :status, 7, value)
@spec sda_becomes_io(bus_address, boolean) :: :ok | {:error, term}
def sda_becomes_io(bus_address, value), do: write_bit_as_boolean(bus_address, :status, 7, value)
@doc """
SCL_IO
@ -279,8 +291,8 @@ defmodule PCA9641.Commands do
- `true` -> When read, shows the SCL pin of the downstream bus is HIGH. When
written, PCA9641 drives SCL pin of the downstream bus HIGH.
"""
@spec scl_becomes_io?(pid) :: boolean
def scl_becomes_io?(pid), do: read_bit_as_boolean(pid, :status, 6)
@spec scl_becomes_io?(bus_address) :: boolean
def scl_becomes_io?(bus_address), do: read_bit_as_boolean(bus_address, :status, 6)
@doc """
SCL_IO
@ -297,8 +309,8 @@ defmodule PCA9641.Commands do
- `true` -> When read, shows the SCL pin of the downstream bus is HIGH. When
written, PCA9641 drives SCL pin of the downstream bus HIGH.
"""
@spec scl_becomes_io(pid, boolean) :: :ok | {:error, term}
def scl_becomes_io(pid, value), do: write_bit_as_boolean(pid, :status, 6, value)
@spec scl_becomes_io(bus_address, boolean) :: :ok | {:error, term}
def scl_becomes_io(bus_address, value), do: write_bit_as_boolean(bus_address, :status, 6, value)
@doc """
TEST_INT
@ -313,8 +325,9 @@ defmodule PCA9641.Commands do
Interrupt Mask register. Allows this master to invoke its Interrupt Service
Routine to handle housekeeping tasks.
"""
@spec test_interrupt_pin(pid, boolean) :: :ok | {:error, term}
def test_interrupt_pin(pid, value), do: write_bit_as_boolean(pid, :status, 5, value)
@spec test_interrupt_pin(bus_address, boolean) :: :ok | {:error, term}
def test_interrupt_pin(bus_address, value),
do: write_bit_as_boolean(bus_address, :status, 5, value)
@doc """
MBOX_FULL
@ -326,8 +339,8 @@ defmodule PCA9641.Commands do
- `false` -> No data is available for *this* master.
- `true` -> Mailbox contains data for *this* master from the other master.
"""
@spec mailbox_full?(pid) :: boolean
def mailbox_full?(pid), do: read_bit_as_boolean(pid, :status, 4)
@spec mailbox_full?(bus_address) :: boolean
def mailbox_full?(bus_address), do: read_bit_as_boolean(bus_address, :status, 4)
@doc """
MBOX_EMPTY
@ -342,8 +355,8 @@ defmodule PCA9641.Commands do
- `true` -> *Other* master mailbox is empty. *Other* master has read previous
data and it is permitted to write new data.
"""
@spec mailbox_empty?(pid) :: boolean
def mailbox_empty?(pid), do: read_bit_as_boolean(pid, :status, 3)
@spec mailbox_empty?(bus_address) :: boolean
def mailbox_empty?(bus_address), do: read_bit_as_boolean(bus_address, :status, 3)
@doc """
BUS_HUNG
@ -357,8 +370,8 @@ defmodule PCA9641.Commands do
- `true` -> Downstream bus hung; when SDA signal is LOW and SCL signal does
not toggle for more than 500 ms or SCL is LOW for 500 ms.
"""
@spec bus_hung?(pid) :: boolean
def bus_hung?(pid), do: read_bit_as_boolean(pid, :status, 2)
@spec bus_hung?(bus_address) :: boolean
def bus_hung?(bus_address), do: read_bit_as_boolean(bus_address, :status, 2)
@doc """
BUS_INIT_FAIL
@ -372,8 +385,8 @@ defmodule PCA9641.Commands do
- `true` -> Bus initialization has failed. SDA still LOW, the downstream bus
cannot recover.
"""
@spec bus_initialisation_failed?(pid) :: boolean
def bus_initialisation_failed?(pid), do: read_bit_as_boolean(pid, :status, 1)
@spec bus_initialisation_failed?(bus_address) :: boolean
def bus_initialisation_failed?(bus_address), do: read_bit_as_boolean(bus_address, :status, 1)
@doc """
OTHER_LOCK
@ -386,8 +399,8 @@ defmodule PCA9641.Commands do
- `false` -> The other master does not have a lock on the downstream bus.
- `true` -> The other master has a lock on the downstream bus.
"""
@spec other_lock?(pid) :: boolean
def other_lock?(pid), do: read_bit_as_boolean(pid, :status, 0)
@spec other_lock?(bus_address) :: boolean
def other_lock?(bus_address), do: read_bit_as_boolean(bus_address, :status, 0)
@doc """
RES_TIME
@ -397,9 +410,9 @@ defmodule PCA9641.Commands do
Returns `{:ok, n}` where `n` is the number if milliseconds remaining in the
reservation.
"""
@spec reserve_time(pid) :: {:ok, non_neg_integer} | {:error, term}
def reserve_time(pid) do
<<ms>> = Registers.reserve_time(pid)
@spec reserve_time(bus_address) :: {:ok, non_neg_integer} | {:error, term}
def reserve_time(bus_address) do
<<ms>> = Registers.reserve_time(bus_address)
{:ok, ms}
end
@ -410,11 +423,11 @@ defmodule PCA9641.Commands do
`ms` is the number of milliseconds remaining in the reservation.
"""
@spec reserve_time(pid) :: {:ok, non_neg_integer} | {:error, term}
def reserve_time(pid, ms) when is_integer(ms) and ms >= 0 and ms <= 0xFF,
do: Registers.reserve_time(pid, ms)
@spec reserve_time(bus_address) :: {:ok, non_neg_integer} | {:error, term}
def reserve_time(bus_address, ms) when is_integer(ms) and ms >= 0 and ms <= 0xFF,
do: Registers.reserve_time(bus_address, ms)
def reserve_time(_pid, _ms), do: {:error, "Invalid milliseconds value"}
def reserve_time(_bus_address, _ms), do: {:error, "Invalid milliseconds value"}
@doc """
BUS_HUNG_INT
@ -427,8 +440,8 @@ defmodule PCA9641.Commands do
signal is LOW and SCL signal does not toggle for more than 500 ms or SCL is
LOW for 500 ms,
"""
@spec bus_hung_interrupt?(pid) :: boolean
def bus_hung_interrupt?(pid), do: read_bit_as_boolean(pid, :interrupt_status, 6)
@spec bus_hung_interrupt?(bus_address) :: boolean
def bus_hung_interrupt?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_status, 6)
@doc """
BUS_HUNG_INT
@ -441,8 +454,9 @@ defmodule PCA9641.Commands do
signal is LOW and SCL signal does not toggle for more than 500 ms or SCL is
LOW for 500 ms,
"""
@spec bus_hung_interrupt(pid, boolean) :: :ok | {:error, term}
def bus_hung_interrupt(pid, value), do: write_bit_as_boolean(pid, :interrupt_status, 6, value)
@spec bus_hung_interrupt(bus_address, boolean) :: :ok | {:error, term}
def bus_hung_interrupt(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_status, 6, value)
@doc """
MBOX_FULL_INT
@ -452,8 +466,9 @@ defmodule PCA9641.Commands do
- `false` -> No interrupt generated; mailbox is not full.
- `true` -> Interrupt generated; mailbox full.
"""
@spec mailbox_full_interrupt?(pid) :: boolean
def mailbox_full_interrupt?(pid), do: read_bit_as_boolean(pid, :interrupt_status, 5)
@spec mailbox_full_interrupt?(bus_address) :: boolean
def mailbox_full_interrupt?(bus_address),
do: read_bit_as_boolean(bus_address, :interrupt_status, 5)
@doc """
MBOX_FULL_INT
@ -463,9 +478,9 @@ defmodule PCA9641.Commands do
- `false` -> No interrupt generated; mailbox is not full.
- `true` -> Interrupt generated; mailbox full.
"""
@spec mailbox_full_interrupt(pid, boolean) :: :ok | {:error, term}
def mailbox_full_interrupt(pid, value),
do: write_bit_as_boolean(pid, :interrupt_status, 5, value)
@spec mailbox_full_interrupt(bus_address, boolean) :: :ok | {:error, term}
def mailbox_full_interrupt(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_status, 5, value)
@doc """
MBOX_EMPTY_INT
@ -475,8 +490,9 @@ defmodule PCA9641.Commands do
- `false` -> No interrupt generated; sent mail is not empty.
- `true` -> Interrupt generated; mailbox is empty.
"""
@spec mailbox_empty_interrupt?(pid) :: boolean
def mailbox_empty_interrupt?(pid), do: read_bit_as_boolean(pid, :interrupt_status, 4)
@spec mailbox_empty_interrupt?(bus_address) :: boolean
def mailbox_empty_interrupt?(bus_address),
do: read_bit_as_boolean(bus_address, :interrupt_status, 4)
@doc """
MBOX_EMPTY_INT
@ -486,9 +502,9 @@ defmodule PCA9641.Commands do
- `false` -> No interrupt generated; sent mail is not empty.
- `true` -> Interrupt generated; mailbox is empty.
"""
@spec mailbox_empty_interrupt(pid, boolean) :: :ok | {:error, term}
def mailbox_empty_interrupt(pid, value),
do: write_bit_as_boolean(pid, :interrupt_status, 4, value)
@spec mailbox_empty_interrupt(bus_address, boolean) :: :ok | {:error, term}
def mailbox_empty_interrupt(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_status, 4, value)
@doc """
TEST_INT_INT
@ -500,8 +516,9 @@ defmodule PCA9641.Commands do
- `true` -> Interrupt generated; master activates its interrupt pin via the
TEST_INT bit in STATUS register.
"""
@spec test_interrupt_pin_interrupt?(pid) :: boolean
def test_interrupt_pin_interrupt?(pid), do: read_bit_as_boolean(pid, :interrupt_status, 3)
@spec test_interrupt_pin_interrupt?(bus_address) :: boolean
def test_interrupt_pin_interrupt?(bus_address),
do: read_bit_as_boolean(bus_address, :interrupt_status, 3)
@doc """
TEST_INT_INT
@ -513,9 +530,9 @@ defmodule PCA9641.Commands do
- `true` -> Interrupt generated; master activates its interrupt pin via the
TEST_INT bit in STATUS register.
"""
@spec test_interrupt_pin_interrupt(pid, true) :: :ok | {:error, term}
def test_interrupt_pin_interrupt(pid, value),
do: write_bit_as_boolean(pid, :interrupt_status, 2, value)
@spec test_interrupt_pin_interrupt(bus_address, true) :: :ok | {:error, term}
def test_interrupt_pin_interrupt(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_status, 2, value)
@doc """
LOCK_GRANT_INT
@ -526,8 +543,9 @@ defmodule PCA9641.Commands do
downstream bus.
- `true` -> Interrupt generated; this master has a lock on the downstream bus.
"""
@spec lock_grant_interrupt?(pid) :: boolean
def lock_grant_interrupt?(pid), do: read_bit_as_boolean(pid, :interrupt_status, 2)
@spec lock_grant_interrupt?(bus_address) :: boolean
def lock_grant_interrupt?(bus_address),
do: read_bit_as_boolean(bus_address, :interrupt_status, 2)
@doc """
LOCK_GRANT_INT
@ -538,8 +556,9 @@ defmodule PCA9641.Commands do
downstream bus.
- `true` -> Interrupt generated; this master has a lock on the downstream bus.
"""
@spec lock_grant_interrupt(pid, boolean) :: :ok | {:error, term}
def lock_grant_interrupt(pid, value), do: write_bit_as_boolean(pid, :interrupt_status, 2, value)
@spec lock_grant_interrupt(bus_address, boolean) :: :ok | {:error, term}
def lock_grant_interrupt(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_status, 2, value)
@doc """
BUS_LOST_INT
@ -552,8 +571,8 @@ defmodule PCA9641.Commands do
- `true` -> Interrupt generated; this master has involuntarily lost the
control of the downstream bus.
"""
@spec bus_lost_interrupt?(pid) :: boolean
def bus_lost_interrupt?(pid), do: read_bit_as_boolean(pid, :interrupt_status, 1)
@spec bus_lost_interrupt?(bus_address) :: boolean
def bus_lost_interrupt?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_status, 1)
@doc """
BUS_LOST_INT
@ -566,8 +585,9 @@ defmodule PCA9641.Commands do
- `true` -> Interrupt generated; this master has involuntarily lost the
control of the downstream bus.
"""
@spec bus_lost_interrupt(pid, boolean) :: :ok | {:error, term}
def bus_lost_interrupt(pid, value), do: write_bit_as_boolean(pid, :interrupt_status, 1, value)
@spec bus_lost_interrupt(bus_address, boolean) :: :ok | {:error, term}
def bus_lost_interrupt(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_status, 1, value)
@doc """
INT_IN_INT
@ -578,8 +598,9 @@ defmodule PCA9641.Commands do
- `false` -> No interrupt on interrupt input pin INT_IN.
- `true` -> Interrupt on interrupt input pin INT_IN.
"""
@spec interupt_in_interrupt?(pid) :: boolean
def interupt_in_interrupt?(pid), do: read_bit_as_boolean(pid, :interrupt_status, 0)
@spec interupt_in_interrupt?(bus_address) :: boolean
def interupt_in_interrupt?(bus_address),
do: read_bit_as_boolean(bus_address, :interrupt_status, 0)
@doc """
INT_IN_INT
@ -590,9 +611,9 @@ defmodule PCA9641.Commands do
- `false` -> No interrupt on interrupt input pin INT_IN.
- `true` -> Interrupt on interrupt input pin INT_IN.
"""
@spec interupt_in_interrupt(pid, boolean) :: :ok | {:error, term}
def interupt_in_interrupt(pid, value),
do: write_bit_as_boolean(pid, :interrupt_status, 0, value)
@spec interupt_in_interrupt(bus_address, boolean) :: :ok | {:error, term}
def interupt_in_interrupt(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_status, 0, value)
@doc """
BUS_HUNG_MSK
@ -600,8 +621,8 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when BUS_HUNG function is set.
- `true` -> Disable output interrupt when BUS_HUNG function is set.
"""
@spec bus_hung_mask?(pid) :: boolean
def bus_hung_mask?(pid), do: read_bit_as_boolean(pid, :interrupt_mask, 6)
@spec bus_hung_mask?(bus_address) :: boolean
def bus_hung_mask?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_mask, 6)
@doc """
MBOX_FULL_MSK
@ -609,8 +630,8 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when MBOX_FULL function is set.
- `true` -> Disable output interrupt when MBOX_FULL function is set.
"""
@spec mailbox_full_mask?(pid) :: boolean
def mailbox_full_mask?(pid), do: read_bit_as_boolean(pid, :interrupt_mask, 5)
@spec mailbox_full_mask?(bus_address) :: boolean
def mailbox_full_mask?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_mask, 5)
@doc """
MBOX_FULL_MSK
@ -618,8 +639,9 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when MBOX_FULL function is set.
- `true` -> Disable output interrupt when MBOX_FULL function is set.
"""
@spec mailbox_full_mask(pid, boolean) :: :ok | {:error, term}
def mailbox_full_mask(pid, value), do: write_bit_as_boolean(pid, :interrupt_mask, 5, value)
@spec mailbox_full_mask(bus_address, boolean) :: :ok | {:error, term}
def mailbox_full_mask(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_mask, 5, value)
@doc """
MBOX_EMPTY_MSK
@ -627,8 +649,8 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when MBOX_EMPTY function is set.
- `true` -> Disable output interrupt when MBOX_EMPTY function is set.
"""
@spec mailbox_empty_mask?(pid) :: boolean
def mailbox_empty_mask?(pid), do: read_bit_as_boolean(pid, :interrupt_mask, 4)
@spec mailbox_empty_mask?(bus_address) :: boolean
def mailbox_empty_mask?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_mask, 4)
@doc """
MBOX_EMPTY_MSK
@ -636,8 +658,9 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when MBOX_EMPTY function is set.
- `true` -> Disable output interrupt when MBOX_EMPTY function is set.
"""
@spec mailbox_empty_mask(pid, boolean) :: :ok | {:error, term}
def mailbox_empty_mask(pid, value), do: write_bit_as_boolean(pid, :interrupt_mask, 4, value)
@spec mailbox_empty_mask(bus_address, boolean) :: :ok | {:error, term}
def mailbox_empty_mask(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_mask, 4, value)
@doc """
TEST_INT_MSK
@ -645,8 +668,8 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when TEST_INT function is set.
- `true` -> Disable output interrupt when TEST_INT function is set.
"""
@spec test_interrupt_mask?(pid) :: boolean
def test_interrupt_mask?(pid), do: read_bit_as_boolean(pid, :interrupt_mask, 3)
@spec test_interrupt_mask?(bus_address) :: boolean
def test_interrupt_mask?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_mask, 3)
@doc """
TEST_INT_MSK
@ -654,8 +677,9 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when TEST_INT function is set.
- `true` -> Disable output interrupt when TEST_INT function is set.
"""
@spec test_interrupt_mask?(pid, boolean) :: :ok | {:error, term}
def test_interrupt_mask?(pid, value), do: write_bit_as_boolean(pid, :interrupt_mask, 3, value)
@spec test_interrupt_mask?(bus_address, boolean) :: :ok | {:error, term}
def test_interrupt_mask?(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_mask, 3, value)
@doc """
LOCK_GRANT_MSK
@ -663,8 +687,8 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when LOCK_GRANT function is set.
- `true` -> Disable output interrupt when LOCK_GRANT function is set.
"""
@spec lock_grant_mask?(pid) :: boolean
def lock_grant_mask?(pid), do: read_bit_as_boolean(pid, :interrupt_mask, 2)
@spec lock_grant_mask?(bus_address) :: boolean
def lock_grant_mask?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_mask, 2)
@doc """
LOCK_GRANT_MSK
@ -672,8 +696,9 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when LOCK_GRANT function is set.
- `true` -> Disable output interrupt when LOCK_GRANT function is set.
"""
@spec lock_grant_mask(pid, boolean) :: :ok | {:error, term}
def lock_grant_mask(pid, value), do: write_bit_as_boolean(pid, :interrupt_mask, 2, value)
@spec lock_grant_mask(bus_address, boolean) :: :ok | {:error, term}
def lock_grant_mask(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_mask, 2, value)
@doc """
BUS_LOST_MSK
@ -681,8 +706,8 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when BUS_LOST function is set.
- `true` -> Disable output interrupt when BUS_LOST function is set.
"""
@spec bus_lost_mask?(pid) :: boolean
def bus_lost_mask?(pid), do: read_bit_as_boolean(pid, :interrupt_mask, 1)
@spec bus_lost_mask?(bus_address) :: boolean
def bus_lost_mask?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_mask, 1)
@doc """
BUS_LOST_MSK
@ -690,8 +715,9 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when BUS_LOST function is set.
- `true` -> Disable output interrupt when BUS_LOST function is set.
"""
@spec bus_lost_mask(pid, boolean) :: :ok | {:error, term}
def bus_lost_mask(pid, value), do: write_bit_as_boolean(pid, :interrupt_mask, 1, value)
@spec bus_lost_mask(bus_address, boolean) :: :ok | {:error, term}
def bus_lost_mask(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_mask, 1, value)
@doc """
INT_IN_MSK
@ -699,8 +725,8 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when INT_IN function is set.
- `true` -> Disable output interrupt when INT_IN function is set.
"""
@spec int_in_mask?(pid) :: boolean
def int_in_mask?(pid), do: read_bit_as_boolean(pid, :interrupt_mask, 0)
@spec int_in_mask?(bus_address) :: boolean
def int_in_mask?(bus_address), do: read_bit_as_boolean(bus_address, :interrupt_mask, 0)
@doc """
INT_IN_MSK
@ -708,44 +734,45 @@ defmodule PCA9641.Commands do
- `false` -> Enable output interrupt when INT_IN function is set.
- `true` -> Disable output interrupt when INT_IN function is set.
"""
@spec int_in_mask(pid, boolean) :: :ok | {:error, term}
def int_in_mask(pid, value), do: write_bit_as_boolean(pid, :interrupt_mask, 0, value)
@spec int_in_mask(bus_address, boolean) :: :ok | {:error, term}
def int_in_mask(bus_address, value),
do: write_bit_as_boolean(bus_address, :interrupt_mask, 0, value)
@doc """
Read shared mailbox.
"""
@spec read_mailbox(pid) :: {:ok, binary} | {:error, term}
def read_mailbox(pid), do: Registers.mailbox(pid)
@spec read_mailbox(bus_address) :: {:ok, binary} | {:error, term}
def read_mailbox(bus_address), do: Registers.mailbox(bus_address)
@doc """
Write shared mailbox.
"""
@spec write_mailbox(pid, binary) :: :ok | {:error, term}
def write_mailbox(pid, message), do: Registers.mailbox(pid, message)
@spec write_mailbox(bus_address, binary) :: :ok | {:error, term}
def write_mailbox(bus_address, message), do: Registers.mailbox(bus_address, message)
@doc """
Request access to downstream bus.
Requests access to the downstream bus and blocks until given access.
"""
@spec request_downstream_bus(pid) :: :ok | {:error, term}
def request_downstream_bus(pid), do: request_downstream_bus(pid, 0)
@spec request_downstream_bus(bus_address) :: :ok | {:error, term}
def request_downstream_bus(bus_address), do: request_downstream_bus(bus_address, 0)
@doc """
Request access to downstream bus.
Requests access to the downstream bus and blocks until given access.
"""
@spec request_downstream_bus(pid, non_neg_integer) :: :ok | {:error, term}
def request_downstream_bus(pid, reserve_time)
@spec request_downstream_bus(bus_address, non_neg_integer) :: :ok | {:error, term}
def request_downstream_bus(bus_address, reserve_time)
when is_integer(reserve_time) and reserve_time >= 0 and reserve_time <= 0xFF do
with :ok <- Registers.reserve_time(pid, reserve_time),
with :ok <- Registers.reserve_time(bus_address, reserve_time),
# Request the bus lock.
:ok <- Registers.control(pid, 0x1),
:ok <- block_until_lock_granted(pid),
:ok <- Registers.control(bus_address, 0x1),
:ok <- block_until_lock_granted(bus_address),
# Connect the downstream bus.
:ok <- Registers.control(pid, 0x5),
true <- bus_connect?(pid) do
:ok <- Registers.control(bus_address, 0x5),
true <- bus_connect?(bus_address) do
:ok
end
end
@ -753,46 +780,46 @@ defmodule PCA9641.Commands do
@doc """
Abandon access to the downstream bus.
"""
@spec abandon_downstream_bus(pid) :: :ok | {:error, term}
def abandon_downstream_bus(pid), do: Registers.control(pid, 0)
@spec abandon_downstream_bus(bus_address) :: :ok | {:error, term}
def abandon_downstream_bus(bus_address), do: Registers.control(bus_address, 0)
defp block_until_lock_granted(pid) do
if lock_grant?(pid) do
defp block_until_lock_granted(bus_address) do
if lock_grant?(bus_address) do
:ok
else
:timer.sleep(5)
block_until_lock_granted(pid)
block_until_lock_granted(bus_address)
end
end
defp read_bit_as_boolean(pid, register, bit)
when is_pid(pid) and is_atom(register) and is_integer(bit) and bit >= 0 and bit < 8 do
defp read_bit_as_boolean(bus_address, register, bit)
when is_atom(register) and is_integer(bit) and bit >= 0 and bit < 8 do
value =
Registers
|> apply(register, [pid])
|> apply(register, [bus_address])
|> get_bit(bit)
value == 1
end
defp write_bit_as_boolean(pid, register, bit, true)
when is_pid(pid) and is_atom(register) and is_integer(bit) and bit >= 0 and bit < 8 do
defp write_bit_as_boolean(bus_address, register, bit, true)
when is_atom(register) and is_integer(bit) and bit >= 0 and bit < 8 do
value =
Registers
|> apply(register, [pid])
|> apply(register, [bus_address])
|> set_bit(bit)
apply(Registers, register, [pid, value])
apply(Registers, register, [bus_address, value])
end
defp write_bit_as_boolean(pid, register, bit, false)
when is_pid(pid) and is_atom(register) and is_integer(bit) and bit >= 0 and bit < 8 do
defp write_bit_as_boolean(bus_address, register, bit, false)
when is_atom(register) and is_integer(bit) and bit >= 0 and bit < 8 do
value =
Registers
|> apply(register, [pid])
|> apply(register, [bus_address])
|> clear_bit(bit)
apply(Registers, register, [pid, value])
apply(Registers, register, [bus_address, value])
end
defp get_bit(<<byte>>, bit), do: byte >>> bit &&& 1

View file

@ -1,5 +1,5 @@
defmodule PCA9641.Registers do
alias ElixirALE.I2C
alias Circuits.I2C
use Bitwise
@moduledoc """
@ -10,57 +10,96 @@ defmodule PCA9641.Registers do
use the `Commands` module instead.
"""
@type bus_address :: {I2C.bus(), I2C.address()}
@type byte :: 0..255
@type double :: 0..65535
@doc """
Register 0: ID Register. 1 byte. RO.
"""
def id(pid), do: read_register(pid, 0)
@spec id(bus_address) :: {:ok, byte} | {:error, term}
def id(bus_address), do: read_register(bus_address, 0)
@doc """
Register 1: Control. 1 byte. RW.
"""
def control(pid), do: read_register(pid, 1)
def control(pid, byte), do: write_register(pid, 1, byte)
@spec control(bus_address) :: {:ok, byte} | {:error, term}
def control(bus_address), do: read_register(bus_address, 1)
@doc """
Register 1: Control. 1 byte. RW.
"""
@spec control(bus_address, byte) :: :ok | {:error, term}
def control(bus_address, byte), do: write_register(bus_address, 1, byte)
@doc """
Register 2: Status. 1 byte. RO.
"""
def status(pid), do: read_register(pid, 2)
@spec status(bus_address) :: {:ok, byte} | {:error, term}
def status(bus_address), do: read_register(bus_address, 2)
@doc """
Register 3: Reserve Time. 1 byte. RW.
"""
def reserve_time(pid), do: read_register(pid, 3)
def reserve_time(pid, byte), do: write_register(pid, 3, byte)
@spec reserve_time(bus_address) :: {:ok, byte} | {:error, term}
def reserve_time(bus_address), do: read_register(bus_address, 3)
@doc """
Register 3: Reserve Time. 1 byte. RW.
"""
@spec reserve_time(bus_address, byte) :: :ok | {:error, term}
def reserve_time(bus_address, byte), do: write_register(bus_address, 3, byte)
@doc """
Register 4: Interrupt Status Register. 1 byte. RW.
"""
def interrupt_status(pid), do: read_register(pid, 4)
def interrupt_status(pid, byte), do: write_register(pid, 4, byte)
@spec interrupt_status(bus_address) :: {:ok, byte} | {:error, term}
def interrupt_status(bus_address), do: read_register(bus_address, 4)
@doc """
Register 4: Interrupt Status Register. 1 byte. RW.
"""
@spec interrupt_status(bus_address, byte) :: :ok | {:error, term}
def interrupt_status(bus_address, byte), do: write_register(bus_address, 4, byte)
@doc """
Register 5: Interrupt Mask Register. 1 byte. RW.
"""
def interrupt_mask(pid), do: read_register(pid, 5)
def interrupt_mask(pid, byte), do: write_register(pid, 5, byte)
@spec interrupt_mask(bus_address) :: {:ok, byte} | {:error, term}
def interrupt_mask(bus_address), do: read_register(bus_address, 5)
@doc """
Register 5: Interrupt Mask Register. 1 byte. RW.
"""
@spec interrupt_mask(bus_address, byte) :: :ok | {:error, term}
def interrupt_mask(bus_address, byte), do: write_register(bus_address, 5, byte)
@doc """
Register 6 (and 7): Mailbox. 2 bytes. RW.
"""
def mailbox(pid) do
lsb = read_register(pid, 6)
msb = read_register(pid, 7)
@spec mailbox(bus_address) :: {:ok, double} | {:error, term}
def mailbox(bus_address) do
lsb = read_register(bus_address, 6)
msb = read_register(bus_address, 7)
(msb <<< 8) + lsb
end
def mailbox(pid, message) do
@doc """
Register 6 (and 7): Mailbox. 2 bytes. RW.
"""
@spec mailbox(bus_address, double) :: :ok | {:error, term}
def mailbox(bus_address, message) do
msb = message >>> 8 &&& 0xFF
lsb = message &&& 0xFF
with :ok <- write_register(pid, 6, lsb), :ok <- write_register(pid, 7, msb), do: :ok
with :ok <- write_register(bus_address, 6, lsb),
:ok <- write_register(bus_address, 7, msb),
do: :ok
end
defp read_register(pid, register, bytes \\ 1), do: I2C.write_read(pid, <<register>>, bytes)
defp read_register({bus, address}, register, bytes \\ 1),
do: I2C.write_read(bus, address, <<register>>, bytes)
defp write_register(pid, register, byte) when is_integer(byte),
do: I2C.write(pid, <<register, byte>>)
defp write_register({bus, address}, register, byte) when is_integer(byte),
do: I2C.write(bus, address, <<register, byte>>)
end

View file

@ -4,7 +4,7 @@ defmodule PCA9641.MixProject do
def project do
[
app: :pca9641,
version: "0.1.0",
version: "0.2.0",
elixir: "~> 1.9",
start_permanent: Mix.env() == :prod,
description: "Driver for PCA9641 2-channel I2C bus master arbiter chip",
@ -34,7 +34,7 @@ defmodule PCA9641.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:elixir_ale, "~> 1.2"},
{:circuits_i2c, "~> 0.3"},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false}
]
end

View file

@ -1,6 +1,6 @@
%{
"circuits_i2c": {:hex, :circuits_i2c, "0.3.5", "43e043d7efc3aead364061f8a7ed627f81ff7cef52bfa47cb629d8a68ca56a9f", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.4.1", "07bb382826ee8d08d575a1981f971ed41bd5d7e86b917fd012a93c51b5d28727", [:mix], [], "hexpm"},
"elixir_ale": {:hex, :elixir_ale, "1.2.1", "07ac2f17a0191b8bd3b0df6b526c7f699a3a4d690c9def573fcb5824eef24d98", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},