Modify the API to be more sensible, and add test coverage.

This commit is contained in:
James Harton 2020-01-17 10:16:05 +13:00
parent 37432189c4
commit 1342ba7952
6 changed files with 1183 additions and 288 deletions

56
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,56 @@
image: elixir:latest
cache:
key: "$CI_JOB_NAME"
paths:
- deps
- _build
- /root/.mix
variables:
MIX_ENV: "test"
before_script:
- mix local.hex --force
- mix local.rebar --force
- mix deps.get --only test
test:
stage: test
script:
- mix test
credo:
stage: test
script:
- mix credo
audit:
stage: test
script:
- mix hex.audit
format:
stage: test
script:
- mix format --check-formatted
pages:
stage: deploy
script:
- mix docs -o public
artifacts:
paths:
- public
only:
- master
package:
stage: deploy
only:
- /^v[0-9]+\.[0-9]+\.[0-9]+$/
script:
- mix hex.build
artifacts:
paths:
- "pca9641-*.tar"

View file

@ -2,15 +2,33 @@ defmodule PCA9641 do
@derive [Wafer.Chip, Wafer.DeviceID]
defstruct ~w[conn int_pin state]a
@behaviour Wafer.Conn
alias Wafer.Conn
alias PCA9641.Registers
alias Wafer.Conn
import Wafer.Twiddles
@moduledoc """
PCA9641 Driver for Elixir using Wafer.
The PCA9641 I2C bus arbiter allows two I2C masters (eg Raspberry Pis) to be
connected to the same downstream I2C bus. The chip decides which master has
access to the bus based on it's own internal state machine.
The device also contains an interrupt input pin which can be used for
downstream devices to signal interrupts and be passed to the two interrupt
outputs connected to the two masters.
## Examples
Connecting to the device using Elixir Circuits.
iex> {:ok, i2c_conn} = Wafer.Driver.Circuits.I2C.acquire(bus_name: "i2c-1", address: 0x70)
...> {:ok, int_conn} = Wafer.Driver.Circuits.GPIO.acquire(pin: 7, direction: :in)
...> {:ok, conn} = PCA9641.acquire(conn: i2c_conn, int_pin: int_conn)
"""
@interrupts %{
@interrupts_fwd %{
bus_hung: 6,
mbox_full: 5,
mbox_empty: 4,
@ -19,6 +37,7 @@ defmodule PCA9641 do
bus_lost: 1,
int_in: 0
}
@interrupts_bkwd @interrupts_fwd |> Enum.map(fn {k, v} -> {v, k} end) |> Enum.into(%{})
@type interrupt_name ::
:bus_hung | :mbox_full | :mbox_empty | :test_int | :lock_grant | :bus_lost | :int_in
@ -54,9 +73,13 @@ defmodule PCA9641 do
It should match the expected value of `#{@pca9641_id}`.
"""
@spec verify_identify(t) :: {:ok, t} | {:error, reason :: any}
def verify_identify(%PCA9641{conn: conn}) do
with {:ok, <<@pca9641_id>>} <- Registers.read_id(conn), do: {:ok, conn}
@spec verify_identity(t) :: {:ok, t} | {:error, reason :: any}
def verify_identity(%PCA9641{conn: conn}) do
case Registers.read_id(conn) do
{:ok, <<@pca9641_id>>} -> {:ok, conn}
{:ok, <<id>>} -> {:error, "Found incorrect ID #{inspect(id)}"}
{:error, reason} -> {:error, reason}
end
end
@doc """
@ -65,8 +88,8 @@ defmodule PCA9641 do
@spec request_downstream_bus(t) :: {:ok, t} | {:error, reason :: any}
def request_downstream_bus(%PCA9641{conn: conn} = dev) do
with {:ok, conn} <- Registers.write_control(conn, 0x1),
{:ok, conn} <- wait_for_bus_init(conn) do
{:ok, %{dev | conn: conn}}
{:ok, dev} <- wait_for_bus_init(%{dev | conn: conn}) do
{:ok, dev}
end
end
@ -78,8 +101,8 @@ defmodule PCA9641 do
"""
@spec priority?(t) :: boolean
def priority?(conn) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 7),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 7),
do: true,
else: (_ -> false)
end
@ -91,8 +114,8 @@ defmodule PCA9641 do
masters request the downstream bus at the same time.
"""
@spec priority(t, boolean) :: {:ok, t} | {:error, reason :: any}
def priority(%PCA9641{conn: conn} = dev, true) do
with {:ok, conn} <- Registers.update_control(conn, &set_bit(&1, 7)),
def priority(%PCA9641{conn: conn} = dev, value) do
with {:ok, conn} <- Registers.update_control(conn, &set_bit(&1, 7, value)),
do: {:ok, %{dev | conn: conn}}
end
@ -108,8 +131,8 @@ defmodule PCA9641 do
"""
@spec downstream_disconnect_on_timeout?(t) :: boolean
def downstream_disconnect_on_timeout?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 6),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 6),
do: true,
else: (_ -> false)
end
@ -145,8 +168,8 @@ defmodule PCA9641 do
"""
@spec idle_timer_disconnect?(t) :: boolean
def idle_timer_disconnect?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 5),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 5),
do: true,
else: (_ -> false)
end
@ -182,8 +205,8 @@ defmodule PCA9641 do
"""
@spec smbus_software_reset?(t) :: boolean
def smbus_software_reset?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 4),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 4),
do: true,
else: (_ -> false)
end
@ -219,8 +242,8 @@ defmodule PCA9641 do
"""
@spec bus_init?(t) :: boolean
def bus_init?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 3),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 3),
do: true,
else: (_ -> false)
end
@ -255,8 +278,8 @@ defmodule PCA9641 do
"""
@spec bus_connect?(t) :: boolean
def bus_connect?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 2),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 2),
do: true,
else: (_ -> false)
end
@ -289,8 +312,8 @@ defmodule PCA9641 do
"""
@spec lock_grant?(t) :: boolean
def lock_grant?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 1),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 1),
do: true,
else: (_ -> false)
end
@ -310,8 +333,8 @@ defmodule PCA9641 do
"""
@spec lock_request?(t) :: boolean
def lock_request?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_control(conn),
true <- get_bool(conn, 0),
with {:ok, data} <- Registers.read_control(conn),
true <- get_bool(data, 0),
do: true,
else: (_ -> false)
end
@ -352,8 +375,8 @@ defmodule PCA9641 do
"""
@spec sda_becomes_io?(t) :: boolean
def sda_becomes_io?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_status(conn),
true <- get_bool(conn, 7),
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 7),
do: true,
else: (_ -> false)
end
@ -396,8 +419,8 @@ defmodule PCA9641 do
"""
@spec scl_becomes_io?(t) :: boolean
def scl_becomes_io?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_status(conn),
true <- get_bool(conn, 6),
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 6),
do: true,
else: (_ -> false)
end
@ -431,6 +454,27 @@ defmodule PCA9641 do
clear this interrupt, master must write 1 to TEST_INT_INT in Interrupt
Status register.
- `false` -> Normal operation.
- `true` -> Causes PCA9641 INT pin to go LOW if not masked by TEST_INT_INT in
Interrupt Mask register. Allows this master to invoke its Interrupt Service
Routine to handle housekeeping tasks.
"""
@spec test_interrupt_pin?(t) :: boolean
def test_interrupt_pin?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 5),
do: true,
else: (_ -> false)
end
@doc """
TEST_INT
Test interrupt output pin; a master can send an interrupt to itself by writing
1 to this register bit. Writing 0 to this register bit has no effect. To
clear this interrupt, master must write 1 to TEST_INT_INT in Interrupt
Status register.
- `false` -> Normal operation.
- `true` -> Causes PCA9641 INT pin to go LOW if not masked by TEST_INT_INT in
Interrupt Mask register. Allows this master to invoke its Interrupt Service
@ -454,8 +498,8 @@ defmodule PCA9641 do
"""
@spec mailbox_full?(t) :: boolean
def mailbox_full?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_status(conn),
true <- get_bool(conn, 4),
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 4),
do: true,
else: (_ -> false)
end
@ -475,8 +519,8 @@ defmodule PCA9641 do
"""
@spec mailbox_empty?(t) :: boolean
def mailbox_empty?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_status(conn),
true <- get_bool(conn, 3),
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 3),
do: true,
else: (_ -> false)
end
@ -495,8 +539,8 @@ defmodule PCA9641 do
"""
@spec bus_hung?(t) :: boolean
def bus_hung?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_status(conn),
true <- get_bool(conn, 2),
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 2),
do: true,
else: (_ -> false)
end
@ -515,8 +559,8 @@ defmodule PCA9641 do
"""
@spec bus_initialisation_failed?(t) :: boolean
def bus_initialisation_failed?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_status(conn),
true <- get_bool(conn, 1),
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 1),
do: true,
else: (_ -> false)
end
@ -534,8 +578,8 @@ defmodule PCA9641 do
"""
@spec other_lock?(t) :: boolean
def other_lock?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_status(conn),
true <- get_bool(conn, 0),
with {:ok, data} <- Registers.read_status(conn),
true <- get_bool(data, 0),
do: true,
else: (_ -> false)
end
@ -575,7 +619,7 @@ defmodule PCA9641 do
def interrupt_reason(%PCA9641{conn: conn}) do
with {:ok, <<value>>} <- Registers.read_interrupt_status(conn) do
reasons =
@interrupts
@interrupts_fwd
|> Enum.reduce([], fn {name, idx}, interrupts ->
if get_bit(value, idx) == 1,
do: [name | interrupts],
@ -587,11 +631,26 @@ defmodule PCA9641 do
end
@doc """
Clears the interrupt status register.
Clear specific interrupts from the interrupt status register
"""
@spec interrupt_clear(t) :: {:ok, t} | {:error, term}
def interrupt_clear(%PCA9641{conn: conn} = dev) do
with {:ok, conn} <- Registers.write_interrupt_status(conn, 0x7F),
@spec interrupt_clear(t, :all | [interrupt_name()]) :: {:ok, t} | {:error, term}
def interrupt_clear(%PCA9641{conn: conn} = dev, :all) do
with {:ok, conn} <- Registers.write_interrupt_status(conn, <<0x7F>>),
do: {:ok, %{dev | conn: conn}}
end
def interrupt_clear(%PCA9641{conn: conn} = dev, interrupt_names)
when is_list(interrupt_names) do
clear_mask =
interrupt_names
|> Enum.reduce(<<0>>, fn name, mask ->
case Map.fetch(@interrupts_fwd, name) do
{:ok, i} -> set_bit(mask, i)
_ -> mask
end
end)
with {:ok, conn} <- Registers.write_interrupt_status(conn, clear_mask),
do: {:ok, %{dev | conn: conn}}
end
@ -600,23 +659,45 @@ defmodule PCA9641 do
"""
@spec interrupt_enable(t, :all | :none | [interrupt_name()]) :: {:ok, t} | {:error, term}
def interrupt_enable(%PCA9641{conn: conn} = dev, :all) do
with {:ok, conn} <- Registers.write_interrupt_mask(conn, 0), do: {:ok, %{dev | conn: conn}}
with {:ok, conn} <- Registers.write_interrupt_mask(conn, <<0>>),
do: {:ok, %{dev | conn: conn}}
end
def interrupt_enable(%PCA9641{conn: conn} = dev, :none) do
with {:ok, conn} <- Registers.write_interrupt_mask(conn, 0x7F), do: {:ok, %{dev | conn: conn}}
with {:ok, conn} <- Registers.write_interrupt_mask(conn, <<0x7F>>),
do: {:ok, %{dev | conn: conn}}
end
def interrupt_enable(%PCA9641{conn: conn} = dev, interrupts) do
mask =
@interrupts
|> Enum.reduce(0x7F, fn {name, idx}, result ->
if Enum.member?(interrupts, name),
do: clear_bit(result, idx),
else: result
end)
with {:ok, conn} <-
Registers.update_interrupt_mask(conn, fn data ->
interrupts
|> Enum.reduce(data, fn interrupt_name, data ->
bit = Map.fetch!(@interrupts_fwd, interrupt_name)
clear_bit(data, bit)
end)
end),
do: {:ok, %{dev | conn: conn}}
end
with {:ok, conn} <- Registers.write_interrupt_mask(conn, mask), do: {:ok, %{dev | conn: conn}}
@doc """
Returns a list of atoms containing the
"""
@spec interrupt_enabled(t) :: {:ok, [interrupt_name()]} | {:error, term}
def interrupt_enabled(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn) do
interrupts =
data
|> find_zeroes()
|> Enum.reduce([], fn i, interrupts ->
case Map.fetch(@interrupts_bkwd, i) do
{:ok, name} -> [name | interrupts]
_ -> interrupts
end
end)
{:ok, interrupts}
end
end
@doc """
@ -632,29 +713,12 @@ defmodule PCA9641 do
"""
@spec bus_hung_interrupt?(t) :: boolean
def bus_hung_interrupt?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_status(conn),
true <- get_bool(conn, 6),
with {:ok, data} <- Registers.read_interrupt_status(conn),
true <- get_bool(data, 6),
do: true,
else: (_ -> false)
end
@doc """
BUS_HUNG_INT
Indicates to both masters that SDA signal is LOW and SCL signal does not
toggle for more than 500 ms or SCL is LOW for 500 ms.
- `false` -> No interrupt generated; normal operation.
- `true` -> Interrupt generated; downstream bus cannot recover; 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_interrupt(t, boolean) :: {:ok, t} | {:error, term}
def bus_hung_interrupt(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_status(conn, &set_bit(&1, 6, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
MBOX_FULL_INT
@ -665,26 +729,12 @@ defmodule PCA9641 do
"""
@spec mailbox_full_interrupt?(t) :: boolean
def mailbox_full_interrupt?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_status(conn),
true <- get_bool(conn, 5),
with {:ok, data} <- Registers.read_interrupt_status(conn),
true <- get_bool(data, 5),
do: true,
else: (_ -> false)
end
@doc """
MBOX_FULL_INT
Indicates the mailbox has new mail.
- `false` -> No interrupt generated; mailbox is not full.
- `true` -> Interrupt generated; mailbox full.
"""
@spec mailbox_full_interrupt(t, boolean) :: {:ok, t} | {:error, term}
def mailbox_full_interrupt(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_status(conn, &set_bit(&1, 5, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
MBOX_EMPTY_INT
@ -695,26 +745,12 @@ defmodule PCA9641 do
"""
@spec mailbox_empty_interrupt?(t) :: boolean
def mailbox_empty_interrupt?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_status(conn),
true <- get_bool(conn, 4),
with {:ok, data} <- Registers.read_interrupt_status(conn),
true <- get_bool(data, 4),
do: true,
else: (_ -> false)
end
@doc """
MBOX_EMPTY_INT
Indicates the sent mail is empty, other master has read the mail.
- `false` -> No interrupt generated; sent mail is not empty.
- `true` -> Interrupt generated; mailbox is empty.
"""
@spec mailbox_empty_interrupt(t, boolean) :: {:ok, t} | {:error, term}
def mailbox_empty_interrupt(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_status(conn, &set_bit(&1, 4, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
TEST_INT_INT
@ -727,28 +763,12 @@ defmodule PCA9641 do
"""
@spec test_interrupt_pin_interrupt?(t) :: boolean
def test_interrupt_pin_interrupt?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_status(conn),
true <- get_bool(conn, 3),
with {:ok, data} <- Registers.read_interrupt_status(conn),
true <- get_bool(data, 3),
do: true,
else: (_ -> false)
end
@doc """
TEST_INT_INT
Indicates this master has sent an interrupt to itself.
- `false` -> No interrupt generated; master has not set the TEST_INT bit in
STATUS register.
- `true` -> Interrupt generated; master activates its interrupt pin via the
TEST_INT bit in STATUS register.
"""
@spec test_interrupt_pin_interrupt(t, true) :: {:ok, t} | {:error, term}
def test_interrupt_pin_interrupt(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_status(conn, &set_bit(&1, 3, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
LOCK_GRANT_INT
@ -760,27 +780,12 @@ defmodule PCA9641 do
"""
@spec lock_grant_interrupt?(pid) :: boolean
def lock_grant_interrupt?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_status(conn),
true <- get_bool(conn, 2),
with {:ok, data} <- Registers.read_interrupt_status(conn),
true <- get_bool(data, 2),
do: true,
else: (_ -> false)
end
@doc """
LOCK_GRANT_INT
Indicates the master has a lock (ownership) on the downstream bus.
- `false` -> No interrupt generated; this master does not have a lock on the
downstream bus.
- `true` -> Interrupt generated; this master has a lock on the downstream bus.
"""
@spec lock_grant_interrupt(t, boolean) :: {:ok, t} | {:error, term}
def lock_grant_interrupt(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_status(conn, &set_bit(&1, 2, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
BUS_LOST_INT
@ -794,29 +799,12 @@ defmodule PCA9641 do
"""
@spec bus_lost_interrupt?(t) :: boolean
def bus_lost_interrupt?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_status(conn),
true <- get_bool(conn, 1),
with {:ok, data} <- Registers.read_interrupt_status(conn),
true <- get_bool(data, 1),
do: true,
else: (_ -> false)
end
@doc """
BUS_LOST_INT
Indicates the master has involuntarily lost the ownership of the downstream
bus.
- `false` -> No interrupt generated; this master is controlling the downstream
bus.
- `true` -> Interrupt generated; this master has involuntarily lost the
control of the downstream bus.
"""
@spec bus_lost_interrupt(t, boolean) :: {:ok, t} | {:error, term}
def bus_lost_interrupt(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_status(conn, &set_bit(&1, 1, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
INT_IN_INT
@ -828,37 +816,22 @@ defmodule PCA9641 do
"""
@spec interupt_in_interrupt?(t) :: boolean
def interupt_in_interrupt?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_status(conn),
true <- get_bool(conn, 0),
with {:ok, data} <- Registers.read_interrupt_status(conn),
true <- get_bool(data, 0),
do: true,
else: (_ -> false)
end
@doc """
INT_IN_INT
Indicates that there is an interrupt from the downstream bus to both the
granted and non-granted masters.
- `false` -> No interrupt on interrupt input pin INT_IN.
- `true` -> Interrupt on interrupt input pin INT_IN.
"""
@spec interupt_in_interrupt(pid, boolean) :: {:ok, t} | {:error, term}
def interupt_in_interrupt(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_status(conn, &set_bit(&1, 0, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
BUS_HUNG_MSK
- `false` -> Enable output interrupt when BUS_HUNG function is set.
- `true` -> Disable output interrupt when BUS_HUNG function is set.
"""
@spec bus_hung_mask?(t) :: boolean
def bus_hung_mask?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_mask(conn),
true <- get_bool(conn, 6),
@spec bus_hung_interrupt_enabled?(t) :: boolean
def bus_hung_interrupt_enabled?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn),
false <- get_bool(data, 6),
do: true,
else: (_ -> false)
end
@ -869,36 +842,10 @@ defmodule PCA9641 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?(t) :: boolean
def mailbox_full_mask?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_mask(conn),
true <- get_bool(conn, 5),
do: true,
else: (_ -> false)
end
@doc """
MBOX_FULL_MSK
- `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, t} | {:error, term}
def mailbox_full_mask(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_mask(conn, &set_bit(&1, 5, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
MBOX_EMPTY_MSK
- `false` -> Enable output interrupt when MBOX_EMPTY function is set.
- `true` -> Disable output interrupt when MBOX_EMPTY function is set.
"""
@spec mailbox_empty_mask?(t) :: boolean
def mailbox_empty_mask?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_mask(conn),
true <- get_bool(conn, 4),
@spec mailbox_full_interrupt_enabled?(t) :: boolean
def mailbox_full_interrupt_enabled?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn),
false <- get_bool(data, 5),
do: true,
else: (_ -> false)
end
@ -909,22 +856,10 @@ defmodule PCA9641 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(t, boolean) :: {:ok, t} | {:error, term}
def mailbox_empty_mask(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_mask(conn, &set_bit(&1, 4, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
TEST_INT_MSK
- `false` -> Enable output interrupt when TEST_INT function is set.
- `true` -> Disable output interrupt when TEST_INT function is set.
"""
@spec test_interrupt_mask?(t) :: boolean
def test_interrupt_mask?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_mask(conn),
true <- get_bool(conn, 3),
@spec mailbox_empty_interrupt_enabled?(t) :: boolean
def mailbox_empty_interrupt_enabled?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn),
false <- get_bool(data, 4),
do: true,
else: (_ -> false)
end
@ -935,22 +870,10 @@ defmodule PCA9641 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(t, boolean) :: {:ok, t} | {:error, term}
def test_interrupt_mask(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_mask(conn, &set_bit(&1, 3, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
LOCK_GRANT_MSK
- `false` -> Enable output interrupt when LOCK_GRANT function is set.
- `true` -> Disable output interrupt when LOCK_GRANT function is set.
"""
@spec lock_grant_mask?(t) :: boolean
def lock_grant_mask?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_mask(conn),
true <- get_bool(conn, 2),
@spec test_interrupt_interrupt_enabled?(t) :: boolean
def test_interrupt_interrupt_enabled?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn),
false <- get_bool(data, 3),
do: true,
else: (_ -> false)
end
@ -961,22 +884,10 @@ defmodule PCA9641 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(t, boolean) :: {:ok, t} | {:error, term}
def lock_grant_mask(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_mask(conn, &set_bit(&1, 2, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
BUS_LOST_MSK
- `false` -> Enable output interrupt when BUS_LOST function is set.
- `true` -> Disable output interrupt when BUS_LOST function is set.
"""
@spec bus_lost_mask?(t) :: boolean
def bus_lost_mask?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_mask(conn),
true <- get_bool(conn, 1),
@spec lock_grant_interrupt_enabled?(t) :: boolean
def lock_grant_interrupt_enabled?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn),
false <- get_bool(data, 2),
do: true,
else: (_ -> false)
end
@ -987,22 +898,10 @@ defmodule PCA9641 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(t, boolean) :: {:ok, t} | {:error, term}
def bus_lost_mask(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_mask(conn, &set_bit(&1, 1, value)),
do: {:ok, %{dev | conn: conn}}
end
@doc """
INT_IN_MSK
- `false` -> Enable output interrupt when INT_IN function is set.
- `true` -> Disable output interrupt when INT_IN function is set.
"""
@spec int_in_mask?(t) :: boolean
def int_in_mask?(%PCA9641{conn: conn}) do
with {:ok, conn} <- Registers.read_interrupt_mask(conn),
true <- get_bool(conn, 0),
@spec bus_lost_interrupt_enabled?(t) :: boolean
def bus_lost_interrupt_enabled?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn),
false <- get_bool(data, 1),
do: true,
else: (_ -> false)
end
@ -1013,10 +912,12 @@ defmodule PCA9641 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, t} | {:error, term}
def int_in_mask(%PCA9641{conn: conn} = dev, value) when is_boolean(value) do
with {:ok, conn} <- Registers.update_interrupt_mask(conn, &set_bit(&1, 0, value)),
do: {:ok, %{dev | conn: conn}}
@spec interrupt_in_interrupt_enabled?(t) :: boolean
def interrupt_in_interrupt_enabled?(%PCA9641{conn: conn}) do
with {:ok, data} <- Registers.read_interrupt_mask(conn),
false <- get_bool(data, 0),
do: true,
else: (_ -> false)
end
@doc """
@ -1040,7 +941,7 @@ defmodule PCA9641 do
"""
@spec abandon_downstream_bus(t) :: {:ok, t} | {:error, term}
def abandon_downstream_bus(%PCA9641{conn: conn} = dev) do
with {:ok, conn} <- Registers.write_control(conn, 0), do: {:ok, %{dev | conn: conn}}
with {:ok, conn} <- Registers.write_control(conn, <<0>>), do: {:ok, %{dev | conn: conn}}
end
defp wait_for_bus_init(conn), do: wait_for_bus_init(conn, 0)
@ -1053,7 +954,7 @@ defmodule PCA9641 do
else
if bus_initialisation_failed?(conn),
do: {:error, :bus_init_fail},
else: :ok
else: {:ok, conn}
end
end
end

11
mix.exs
View file

@ -33,11 +33,14 @@ defmodule PCA9641.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:elixir_ale, "~> 1.2", optional: true},
{:circuits_i2c, "~> 0.3", optional: true},
{:circuits_gpio, "~> 0.4", optional: true},
{:wafer, git: "https://gitlab.com/jimsy/wafer"},
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false}
{:circuits_i2c, "~> 0.3", optional: true},
{:credo, "~> 1.1", only: [:dev, :test], runtime: false},
{:earmark, ">= 0.0.0", only: [:dev, :test]},
{:elixir_ale, "~> 1.2", optional: true},
{:ex_doc, ">= 0.0.0", only: [:dev, :test]},
{:mimic, "~> 1.1", only: :test},
{:wafer, git: "https://gitlab.com/jimsy/wafer"}
]
end
end

View file

@ -1,12 +1,16 @@
%{
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"circuits_gpio": {:hex, :circuits_gpio, "0.4.4", "09823645b82d84177549c9520e822ffb87d228413527f8a3134f4f436be16efe", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"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"},
"circuits_i2c": {:hex, :circuits_i2c, "0.3.6", "348b8de3cf1ade6c4b92ad7c9de7068df1af0d441df341d7d126d53671924f64", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
"credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [: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"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
"wafer": {:git, "https://gitlab.com/jimsy/wafer", "cac08bc5f037bc0fb544330e2262478b9e916747", []},
"mimic": {:hex, :mimic, "1.1.3", "3bad83d5271b4faa7bbfef587417a6605cbbc802a353395d446a1e5f46fe7115", [:mix], [], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm"},
"wafer": {:git, "https://gitlab.com/jimsy/wafer", "edb85a48b75215dede060016f46d783d9da67672", []},
}

930
test/pca9641_test.exs Normal file
View file

@ -0,0 +1,930 @@
defmodule PCA9641Test do
use ExUnit.Case, async: true
use Mimic
use Bitwise
alias PCA9641.Registers, as: Registers
alias Wafer.Driver.Fake, as: Driver
describe "acquire/1" do
test "fails when called without a connection" do
assert {:error, _} = PCA9641.acquire([])
end
test "returns a device struct" do
assert {:ok, %PCA9641{}} = PCA9641.acquire(conn: Driver.acquire([]))
end
end
describe "verify_identity/1" do
test "succeeds when the register contains the correct value" do
Registers
|> expect(:read_id, 1, fn _conn -> {:ok, <<0x38>>} end)
assert {:ok, _conn} = PCA9641.verify_identity(conn())
end
test "fails when the register contains an incorrect value" do
Registers
|> expect(:read_id, 1, fn _conn -> {:ok, <<0x13>>} end)
assert {:error, _} = PCA9641.verify_identity(conn())
end
end
describe "request_downstream_bus/1" do
test "writes to the control register and then polls until bus init is true" do
Registers
|> expect(:write_control, 1, fn conn, command ->
assert command == 0x1
{:ok, conn}
end)
|> expect(:read_control, 1, fn _conn ->
{:ok, <<0b1000>>}
end)
|> expect(:read_status, 1, fn _conn ->
{:ok, <<0b00>>}
end)
assert {:ok, _conn} = PCA9641.request_downstream_bus(conn())
end
end
describe "priority?/1" do
test "reads byte 7 of CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b10000000} end)
assert PCA9641.priority?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.priority?(conn())
end
end
describe "priority/2" do
test "writes to bit 7 of CONTROL" do
Registers
|> expect(:update_control, 1, fn conn, callback ->
assert <<0b10000000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.priority(conn(), true)
end
end
describe "downstream_disconnect_on_timeout?/1" do
test "reads bit 6 from CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b01000000} end)
assert PCA9641.downstream_disconnect_on_timeout?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.downstream_disconnect_on_timeout?(conn())
end
end
describe "downstream_disconnect_on_timeout/2" do
test "writes to bit 6 of CONTROL" do
Registers
|> expect(:update_control, 1, fn conn, callback ->
assert <<0b01000000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.downstream_disconnect_on_timeout(conn(), true)
end
end
describe "idle_timer_disconnect?/1" do
test "reads bit 6 from CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b00100000} end)
assert PCA9641.idle_timer_disconnect?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.idle_timer_disconnect?(conn())
end
end
describe "idle_timer_disconnect/2" do
test "writes to bit 5 of CONTROL" do
Registers
|> expect(:update_control, 1, fn conn, callback ->
assert <<0b00100000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.idle_timer_disconnect(conn(), true)
end
end
describe "smbus_software_reset?/1" do
test "reads bit 4 from CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b00010000} end)
assert PCA9641.smbus_software_reset?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.smbus_software_reset?(conn())
end
end
describe "smbus_software_reset/2" do
test "writes to bit 4 of CONTROL" do
Registers
|> expect(:update_control, 1, fn conn, callback ->
assert <<0b00010000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.smbus_software_reset(conn(), true)
end
end
describe "bus_init?/1" do
test "reads bit 3 from CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b00001000} end)
assert PCA9641.bus_init?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.bus_init?(conn())
end
end
describe "bus_init/2" do
test "writes to bit 3 of CONTROL" do
Registers
|> expect(:update_control, 1, fn conn, callback ->
assert <<0b00001000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.bus_init(conn(), true)
end
end
describe "bus_connect?/1" do
test "reads bit 2 from CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b00000100} end)
assert PCA9641.bus_connect?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.bus_connect?(conn())
end
end
describe "bus_connect/2" do
test "writes to bit 2 of CONTROL" do
Registers
|> expect(:update_control, 1, fn conn, callback ->
assert <<0b00000100>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.bus_connect(conn(), true)
end
end
describe "lock_grant?/1" do
test "reads bit 1 from CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b00000010} end)
assert PCA9641.lock_grant?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.lock_grant?(conn())
end
end
describe "lock_request?/1" do
test "reads bit 0 from CONTROL" do
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0b00000001} end)
assert PCA9641.lock_request?(conn())
Registers
|> expect(:read_control, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.lock_request?(conn())
end
end
describe "lock_request/2" do
test "writes to bit 0 of CONTROL" do
Registers
|> expect(:update_control, 1, fn conn, callback ->
assert <<0b00000001>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.lock_request(conn(), true)
end
end
describe "sda_becomes_io?/1" do
test "reads bit 7 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b10000000} end)
assert PCA9641.sda_becomes_io?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.sda_becomes_io?(conn())
end
end
describe "sda_becomes_io/2" do
test "writes to bit 7 of STATUS" do
Registers
|> expect(:update_status, 1, fn conn, callback ->
assert <<0b10000000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.sda_becomes_io(conn(), true)
end
end
describe "scl_becomes_io?/1" do
test "reads bit 6 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b01000000} end)
assert PCA9641.scl_becomes_io?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.scl_becomes_io?(conn())
end
end
describe "scl_becomes_io/2" do
test "writes to bit 6 of STATUS" do
Registers
|> expect(:update_status, 1, fn conn, callback ->
assert <<0b01000000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.scl_becomes_io(conn(), true)
end
end
describe "test_interrupt_pin?/1" do
test "reads bit 5 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b00100000} end)
assert PCA9641.test_interrupt_pin?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.test_interrupt_pin?(conn())
end
end
describe "test_interrupt_pin/2" do
test "writes to bit 5 of STATUS" do
Registers
|> expect(:update_status, 1, fn conn, callback ->
assert <<0b00100000>> = callback.(<<0>>)
{:ok, conn}
end)
PCA9641.test_interrupt_pin(conn(), true)
end
end
describe "mailbox_full?/1" do
test "reads bit 4 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b00010000} end)
assert PCA9641.mailbox_full?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.mailbox_full?(conn())
end
end
describe "mailbox_empty?/1" do
test "reads bit 3 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b00001000} end)
assert PCA9641.mailbox_empty?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.mailbox_empty?(conn())
end
end
describe "bus_hung?/1" do
test "reads bit 2 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b00000100} end)
assert PCA9641.bus_hung?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.bus_hung?(conn())
end
end
describe "bus_initialisation_failed?/1" do
test "reads bit 1 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b00000010} end)
assert PCA9641.bus_initialisation_failed?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.bus_initialisation_failed?(conn())
end
end
describe "other_lock?/1" do
test "reads bit 0 of STATUS" do
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0b00000001} end)
assert PCA9641.other_lock?(conn())
Registers
|> expect(:read_status, 1, fn _conn -> {:ok, 0} end)
refute PCA9641.other_lock?(conn())
end
end
describe "reserve_time/1" do
test "returns the contents of the RESERVE_TIME register" do
Registers
|> expect(:read_reserve_time, 1, fn _conn ->
{:ok, <<123>>}
end)
assert {:ok, 123, _conn} = PCA9641.reserve_time(conn())
end
end
describe "reserve_time/2" do
test "sets the contents of the RESERVE_TIME register" do
Registers
|> expect(:write_reserve_time, 1, fn conn, ms ->
assert ms == 123
{:ok, conn}
end)
assert {:ok, _conn} = PCA9641.reserve_time(conn(), 123)
end
end
describe "interrupt_reason/1" do
test "returns :bus_hung when bit 6 of INTERRUPT_STATUS is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, <<0b01000000>>}
end)
assert {:ok, [:bus_hung]} = PCA9641.interrupt_reason(conn())
end
test "returns :mbox_full when bit 5 of INTERRUPT_STATUS is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, <<0b00100000>>}
end)
assert {:ok, [:mbox_full]} = PCA9641.interrupt_reason(conn())
end
test "returns :mbox_empty when bit 4 of INTERRUPT_STATUS is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, <<0b00010000>>}
end)
assert {:ok, [:mbox_empty]} = PCA9641.interrupt_reason(conn())
end
test "returns :test_int when bit 3 of INTERRUPT_STATUS is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, <<0b00001000>>}
end)
assert {:ok, [:test_int]} = PCA9641.interrupt_reason(conn())
end
test "returns :lock_grant when bit 2 of INTERRUPT_STATUS is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, <<0b00000100>>}
end)
assert {:ok, [:lock_grant]} = PCA9641.interrupt_reason(conn())
end
test "returns :bus_lost when bit 1 of INTERRUPT_STATUS is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, <<0b00000010>>}
end)
assert {:ok, [:bus_lost]} = PCA9641.interrupt_reason(conn())
end
test "returns :int_in when bit 0 of INTERRUPT_STATUS is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, <<0b00000001>>}
end)
assert {:ok, [:int_in]} = PCA9641.interrupt_reason(conn())
end
end
describe "interrupt_clear/2" do
test "writes all ones to INTERRUPT_STATUS when passed `:all`" do
Registers
|> expect(:write_interrupt_status, 1, fn conn, <<data>> ->
assert data == 0b01111111
{:ok, conn}
end)
PCA9641.interrupt_clear(conn(), :all)
end
test "writes INTERRUPT_STATUS bit 5 high when passed `[:mbox_full]`" do
Registers
|> expect(:write_interrupt_status, 1, fn conn, <<data>> ->
assert data == 0b00100000
{:ok, conn}
end)
PCA9641.interrupt_clear(conn(), [:mbox_full])
end
test "writes INTERRUPT_STATUS bit 4 high when passed `[:mbox_empty]`" do
Registers
|> expect(:write_interrupt_status, 1, fn conn, <<data>> ->
assert data == 0b00010000
{:ok, conn}
end)
PCA9641.interrupt_clear(conn(), [:mbox_empty])
end
test "writes INTERRUPT_STATUS bit 3 high when passed `[:test_int]`" do
Registers
|> expect(:write_interrupt_status, 1, fn conn, <<data>> ->
assert data == 0b00001000
{:ok, conn}
end)
PCA9641.interrupt_clear(conn(), [:test_int])
end
test "writes INTERRUPT_STATUS bit 2 high when passed `[:lock_grant]`" do
Registers
|> expect(:write_interrupt_status, 1, fn conn, <<data>> ->
assert data == 0b00000100
{:ok, conn}
end)
PCA9641.interrupt_clear(conn(), [:lock_grant])
end
test "writes INTERRUPT_STATUS bit 1 high when passed `[:bus_lost]`" do
Registers
|> expect(:write_interrupt_status, 1, fn conn, <<data>> ->
assert data == 0b00000010
{:ok, conn}
end)
PCA9641.interrupt_clear(conn(), [:bus_lost])
end
test "writes INTERRUPT_STATUS bit 0 high when passed `[:int_in]`" do
Registers
|> expect(:write_interrupt_status, 1, fn conn, <<data>> ->
assert data == 0b00000001
{:ok, conn}
end)
PCA9641.interrupt_clear(conn(), [:int_in])
end
end
describe "interrupt_enable/2" do
test "writes all zeroes when called with `:all`" do
Registers
|> expect(:write_interrupt_mask, 1, fn conn, <<data>> ->
assert data == 0
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), :all)
end
test "writes all ones when called with `:none`" do
Registers
|> expect(:write_interrupt_mask, 1, fn conn, <<data>> ->
assert data == 0b01111111
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), :none)
end
test "writes INTERRUPT_MASK bit 6 low when called with [:bus_hung]" do
Registers
|> expect(:update_interrupt_mask, 1, fn conn, callback ->
assert <<0b10111111>> = callback.(<<0xFF>>)
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), [:bus_hung])
end
test "writes INTERRUPT_MASK bit 5 low when called with [:mbox_full]" do
Registers
|> expect(:update_interrupt_mask, 1, fn conn, callback ->
assert <<0b11011111>> = callback.(<<0xFF>>)
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), [:mbox_full])
end
test "writes INTERRUPT_MASK bit 4 low when called with [:mbox_empty]" do
Registers
|> expect(:update_interrupt_mask, 1, fn conn, callback ->
assert <<0b11101111>> = callback.(<<0xFF>>)
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), [:mbox_empty])
end
test "writes INTERRUPT_MASK bit 3 low when called with [:test_int]" do
Registers
|> expect(:update_interrupt_mask, 1, fn conn, callback ->
assert <<0b11110111>> = callback.(<<0xFF>>)
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), [:test_int])
end
test "writes INTERRUPT_MASK bit 2 low when called with [:lock_grant]" do
Registers
|> expect(:update_interrupt_mask, 1, fn conn, callback ->
assert <<0b11111011>> = callback.(<<0xFF>>)
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), [:lock_grant])
end
test "writes INTERRUPT_MASK bit 1 low when called with [:bus_lost]" do
Registers
|> expect(:update_interrupt_mask, 1, fn conn, callback ->
assert <<0b11111101>> = callback.(<<0xFF>>)
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), [:bus_lost])
end
test "writes INTERRUPT_MASK bit 0 low when called with [:int_in]" do
Registers
|> expect(:update_interrupt_mask, 1, fn conn, callback ->
assert <<0b11111110>> = callback.(<<0xFF>>)
{:ok, conn}
end)
PCA9641.interrupt_enable(conn(), [:int_in])
end
end
describe "interrupt_enabled/1" do
test "returns [:bus_hung] when INTERRUPT_MASK bit 6 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b10111111}
end)
assert {:ok, [:bus_hung]} = PCA9641.interrupt_enabled(conn())
end
test "returns [:mbox_full] when INTERRUPT_MASK bit 5 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11011111}
end)
assert {:ok, [:mbox_full]} = PCA9641.interrupt_enabled(conn())
end
test "returns [:mbox_empty] when INTERRUPT_MASK bit 4 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11101111}
end)
assert {:ok, [:mbox_empty]} = PCA9641.interrupt_enabled(conn())
end
test "returns [:test_int] when INTERRUPT_MASK bit 3 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11110111}
end)
assert {:ok, [:test_int]} = PCA9641.interrupt_enabled(conn())
end
test "returns [:lock_grant] when INTERRUPT_MASK bit 2 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11111011}
end)
assert {:ok, [:lock_grant]} = PCA9641.interrupt_enabled(conn())
end
test "returns [:bus_lost] when INTERRUPT_MASK bit 1 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11111101}
end)
assert {:ok, [:bus_lost]} = PCA9641.interrupt_enabled(conn())
end
test "returns [:int_in] when INTERRUPT_MASK bit 0 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11111110}
end)
assert {:ok, [:int_in]} = PCA9641.interrupt_enabled(conn())
end
test "returns all interrupts when all INTERRUPT_MASK bits are low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0}
end)
assert {:ok, interrupts} = PCA9641.interrupt_enabled(conn())
assert :bus_hung in interrupts
assert :mbox_full in interrupts
assert :mbox_empty in interrupts
assert :test_int in interrupts
assert :lock_grant in interrupts
assert :bus_lost in interrupts
assert :int_in in interrupts
end
test "returns no interrupts when all INTERRUPT_MASK bits are high" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0x7F}
end)
assert {:ok, []} = PCA9641.interrupt_enabled(conn())
end
end
describe "bus_hung_interrupt?/1" do
test "returns `true` when INTERRUPT_STATUS bit 6 is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, 0b01000000}
end)
assert PCA9641.bus_hung_interrupt?(conn())
end
end
describe "mailbox_full_interrupt?/1" do
test "returns `true` when INTERRUPT_STATUS bit 5 is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, 0b00100000}
end)
assert PCA9641.mailbox_full_interrupt?(conn())
end
end
describe "mailbox_empty_interrupt?/1" do
test "returns `true` when INTERRUPT_STATUS bit 4 is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, 0b00010000}
end)
assert PCA9641.mailbox_empty_interrupt?(conn())
end
end
describe "test_interrupt_pin_interrupt?/1" do
test "returns `true` when INTERRUPT_STATUS bit 3 is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, 0b00001000}
end)
assert PCA9641.test_interrupt_pin_interrupt?(conn())
end
end
describe "lock_grant_interrupt?/1" do
test "returns `true` when INTERRUPT_STATUS bit 2 is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, 0b00000100}
end)
assert PCA9641.lock_grant_interrupt?(conn())
end
end
describe "bus_lost_interrupt?/1" do
test "returns `true` when INTERRUPT_STATUS bit 1 is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, 0b00000010}
end)
assert PCA9641.bus_lost_interrupt?(conn())
end
end
describe "interupt_in_interrupt?/1" do
test "returns `true` when INTERRUPT_STATUS bit 0 is high" do
Registers
|> expect(:read_interrupt_status, 1, fn _conn ->
{:ok, 0b00000001}
end)
assert PCA9641.interupt_in_interrupt?(conn())
end
end
describe "bus_hung_interrupt_enabled?/1" do
test "returns `true` when INTERRUPT_MASK bit 6 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b10111111}
end)
assert PCA9641.bus_hung_interrupt_enabled?(conn())
end
end
describe "mailbox_full_interrupt_enabled?/1" do
test "returns `true` when INTERRUPT_MASK bit 5 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11011111}
end)
assert PCA9641.mailbox_full_interrupt_enabled?(conn())
end
end
describe "mailbox_empty_interrupt_enabled?/1" do
test "returns `true` when INTERRUPT_MASK bit 4 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11101111}
end)
assert PCA9641.mailbox_empty_interrupt_enabled?(conn())
end
end
describe "test_interrupt_interrupt_enabled?/1" do
test "returns `true` when INTERRUPT_MASK bit 3 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11110111}
end)
assert PCA9641.test_interrupt_interrupt_enabled?(conn())
end
end
describe "lock_grant_interrupt_enabled?/1" do
test "returns `true` when INTERRUPT_MASK bit 2 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11111011}
end)
assert PCA9641.lock_grant_interrupt_enabled?(conn())
end
end
describe "bus_lost_interrupt_enabled?/1" do
test "returns `true` when INTERRUPT_MASK bit 1 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11111101}
end)
assert PCA9641.bus_lost_interrupt_enabled?(conn())
end
end
describe "interrupt_in_interrupt_enabled?/1" do
test "returns `true` when INTERRUPT_MASK bit 0 is low" do
Registers
|> expect(:read_interrupt_mask, 1, fn _conn ->
{:ok, 0b11111110}
end)
assert PCA9641.interrupt_in_interrupt_enabled?(conn())
end
end
describe "read_mailbox/1" do
test "returns the contents of the MAILBOX register" do
Registers
|> expect(:read_mailbox, 1, fn _conn ->
{:ok, <<0x1, 0x2>>}
end)
assert {:ok, <<0x1, 0x2>>, _conn} = PCA9641.read_mailbox(conn())
end
end
describe "write_mailbox/2" do
test "writes the contents of the MAILBOX register" do
Registers
|> expect(:write_mailbox, 1, fn conn, data ->
assert <<0x1, 0x2>> == data
{:ok, conn}
end)
assert {:ok, _conn} = PCA9641.write_mailbox(conn(), <<0x1, 0x2>>)
end
end
describe "abandon_downstream_bus/1" do
test "writes the CONTROL register to zero" do
Registers
|> expect(:write_control, 1, fn conn, data ->
assert <<0>> == data
{:ok, conn}
end)
assert {:ok, conn} = PCA9641.abandon_downstream_bus(conn())
end
end
defp conn do
with {:ok, conn} <- Driver.acquire([]), {:ok, conn} <- PCA9641.acquire(conn: conn), do: conn
end
end

View file

@ -1 +1,2 @@
Mimic.copy(PCA9641.Registers)
ExUnit.start()