PCA9641 (pca9641 v2.0.0)

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

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)

Link to this section Summary

Functions

Abandon the downstream bus.

Acquire a connection to the PCA9641 device using the passed in I2C connection.

BUS_CONNECT

Clear specific interrupts from the interrupt status register

Enable the specified interrupts.

Returns a list of atoms containing the

Disable interrupts on the int_pin.

Enable interrupts on the int_pin.

Enable interrupts on the int_pin.

Indicates the reasons for which an interrupt was generated (if any).

PRIORITY

Read shared mailbox.

Attempt to acquire the downstream I2C bus.

Verify the contents of the identity register.

Waits for downstream bus initialisation to succeed.

Write shared mailbox.

Link to this section Types

Link to this type

acquire_option()

@type acquire_option() :: {:conn, Wafer.Conn.t(), [{:int_pin, Wafer.Conn.t()}]}
Link to this type

acquire_options()

@type acquire_options() :: [acquire_option()]
Link to this type

interrupt_name()

@type interrupt_name() ::
  :bus_hung
  | :mbox_full
  | :mbox_empty
  | :test_int
  | :lock_grant
  | :bus_lost
  | :int_in
Link to this type

mailbox_data()

@type mailbox_data() :: <<_::16>>
@type t() :: %PCA9641{conn: Wafer.Conn.t(), int_pin: Wafer.Conn.t(), state: term()}

Link to this section Functions

Link to this function

abandon_downstream_bus(dev)

@spec abandon_downstream_bus(t()) :: {:ok, t()} | {:error, term()}

Abandon the downstream bus.

Link to this function

acquire(options)

@spec acquire(acquire_options()) :: {:ok, t()} | {:error, reason :: any()}

Acquire a connection to the PCA9641 device using the passed in I2C connection.

options

Options:

  • conn (required) an I2C connection, probably from ElixirALE.I2C or Circuits.I2C.
  • int_pin a (optional) GPIO connection for the interrupt pin This should be already set to direction :in.
Link to this function

bus_connect(dev, value)

@spec bus_connect(t(), boolean()) :: {:ok, t()} | {:error, term()}

BUS_CONNECT

Connectivity between master and downstream bus; the internal switch connects I2C-bus from master to downstream bus only if LOCK_GRANT = 1.

  • 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.
Link to this function

bus_connect?(pca9641)

@spec bus_connect?(t()) :: boolean()

BUS_CONNECT

Connectivity between master and downstream bus; the internal switch connects I2C-bus from master to downstream bus only if LOCK_GRANT = 1.

  • 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.
Link to this function

bus_hung?(pca9641)

@spec bus_hung?(t()) :: boolean()

BUS_HUNG

This is a read-only status register bit. If this register bit is ‘0’, it indicates the bus is in normal condition. If this bit is ‘1’, it indicates the bus is hung. The hung bus means SDA signal is LOW and SCL signal does not toggle for more than 500 ms or SCL is LOW for 500 ms.

  • false -> Normal operation.
  • 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.
Link to this function

bus_hung_interrupt?(pca9641)

@spec bus_hung_interrupt?(t()) :: boolean()

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,
Link to this function

bus_hung_interrupt_enabled?(pca9641)

@spec bus_hung_interrupt_enabled?(t()) :: boolean()

BUS_HUNG_MSK

  • false -> Enable output interrupt when BUS_HUNG function is set.
  • true -> Disable output interrupt when BUS_HUNG function is set.
Link to this function

bus_init(dev, value)

@spec bus_init(t(), boolean()) :: {:ok, t()} | {:error, term()}

BUS_INIT

Bus initialization for PCA9641 sends one clock out and checks SDA signal. If SDA is HIGH, PCA9641 sends a ‘not acknowledge’ and a STOP condition. The BUS_INIT function is completed. If SDA is LOW, PCA9641 sends other clock out and checks SDA again. The PCA9641 will send out 9 clocks (maximum), and if SDA is still LOW, PCA9641 determines the bus initialization has failed.

  • false -> Normal operation.
  • true -> Start initialization on next bus connect function to downstream bus.
Link to this function

bus_init?(pca9641)

@spec bus_init?(t()) :: boolean()

BUS_INIT

Bus initialization for PCA9641 sends one clock out and checks SDA signal. If SDA is HIGH, PCA9641 sends a ‘not acknowledge’ and a STOP condition. The BUS_INIT function is completed. If SDA is LOW, PCA9641 sends other clock out and checks SDA again. The PCA9641 will send out 9 clocks (maximum), and if SDA is still LOW, PCA9641 determines the bus initialization has failed.

  • false -> Normal operation.
  • true -> Start initialization on next bus connect function to downstream bus.
Link to this function

bus_initialisation_failed?(pca9641)

@spec bus_initialisation_failed?(t()) :: boolean()

BUS_INIT_FAIL

This is a read-only status register bit. If this register bit is ‘0’, it indicates the bus initialization function has passed. The downstream bus is in idle mode (SCL and SDA are HIGH). If this register bit is ‘1’, it indicates the bus initialization function has failed. The SDA signal could be stuck LOW.

  • false -> Normal operation.
  • true -> Bus initialization has failed. SDA still LOW, the downstream bus cannot recover.
Link to this function

bus_lost_interrupt?(pca9641)

@spec bus_lost_interrupt?(t()) :: boolean()

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.
Link to this function

bus_lost_interrupt_enabled?(pca9641)

@spec bus_lost_interrupt_enabled?(t()) :: boolean()

BUS_LOST_MSK

  • false -> Enable output interrupt when BUS_LOST function is set.
  • true -> Disable output interrupt when BUS_LOST function is set.
Link to this function

downstream_disconnect_on_timeout(dev, value)

@spec downstream_disconnect_on_timeout(t(), boolean()) ::
  {:ok, t()} | {:error, term()}

SMBUS_DIS

When PCA9641 detects an SMBus time-out, if this bit is set, PCA9641 will disconnect I2C-bus from master to downstream bus.

  • false -> Normal operation.
  • true -> Connectivity between master and downstream bus will be disconnected upon detecting an SMBus time-out condition.
Link to this function

downstream_disconnect_on_timeout?(pca9641)

@spec downstream_disconnect_on_timeout?(t()) :: boolean()

SMBUS_DIS

When PCA9641 detects an SMBus time-out, if this bit is set, PCA9641 will disconnect I2C-bus from master to downstream bus.

  • false -> Normal operation.
  • true -> Connectivity between master and downstream bus will be disconnected upon detecting an SMBus time-out condition.
Link to this function

idle_timer_disconnect(dev, value)

@spec idle_timer_disconnect(t(), boolean()) :: {:ok, t()} | {:error, term()}

IDLE_TIMER_DIS

After RES_TIME is expired, I2C-bus idle for more than 100 ms, PCA9641 will disconnect master from downstream bus and takes away its grant if this register bit is enabled. This IDLE_TIMER_DIS function also applies when there is a grant of a request with zero value on RES_TIME.

  • false -> Normal operation.
  • true -> Enable 100 ms idle timer. After reserve timer expires or if 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.
Link to this function

idle_timer_disconnect?(pca9641)

@spec idle_timer_disconnect?(t()) :: boolean()

IDLE_TIMER_DIS

After RES_TIME is expired, I2C-bus idle for more than 100 ms, PCA9641 will disconnect master from downstream bus and takes away its grant if this register bit is enabled. This IDLE_TIMER_DIS function also applies when there is a grant of a request with zero value on RES_TIME.

  • false -> Normal operation.
  • true -> Enable 100 ms idle timer. After reserve timer expires or if 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.
Link to this function

interrupt_clear(dev, interrupt_names)

@spec interrupt_clear(t(), :all | [interrupt_name()]) :: {:ok, t()} | {:error, term()}

Clear specific interrupts from the interrupt status register

Link to this function

interrupt_enable(dev, interrupts)

@spec interrupt_enable(t(), :all | :none | [interrupt_name()]) ::
  {:ok, t()} | {:error, term()}

Enable the specified interrupts.

Link to this function

interrupt_enabled(pca9641)

@spec interrupt_enabled(t()) :: {:ok, [interrupt_name()]} | {:error, term()}

Returns a list of atoms containing the

Link to this function

interrupt_in_interrupt_enabled?(pca9641)

@spec interrupt_in_interrupt_enabled?(t()) :: boolean()

INT_IN_MSK

  • false -> Enable output interrupt when INT_IN function is set.
  • true -> Disable output interrupt when INT_IN function is set.
Link to this function

interrupt_pin_disable(dev)

@spec interrupt_pin_disable(t()) :: {:ok, t()} | {:error, reason :: any()}

Disable interrupts on the int_pin.

Disables interrupts on the GPIO pin which is connected to the PCA9641 interrupt pin.

Link to this function

interrupt_pin_enable(dev)

@spec interrupt_pin_enable(t()) :: {:ok, t()} | {:error, reason :: any()}

Enable interrupts on the int_pin.

Enables interrupts on the GPIO pin which is connected to the PCA9641 interrupt pin. The Wafer interrupt message metadata field will contain this PCA9641 conn.

Link to this function

interrupt_pin_enable(dev, metadata)

@spec interrupt_pin_enable(t(), metadata :: any()) ::
  {:ok, t()} | {:error, reason :: any()}

Enable interrupts on the int_pin.

Enables interrupts on the GPIO pin which is connected to the PCA9641 interrupt pin. The Wafer interrupt message metadata field will contain a two-tuple of this PCA9641 conn and metadata.

Link to this function

interrupt_reason(pca9641)

@spec interrupt_reason(t()) :: {:ok, [interrupt_name()]} | {:error, reason :: any()}

Indicates the reasons for which an interrupt was generated (if any).

Link to this function

interupt_in_interrupt?(pca9641)

@spec interupt_in_interrupt?(t()) :: boolean()

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.
Link to this function

lock_grant?(pca9641)

@spec lock_grant?(t()) :: boolean()

LOCK_GRANT

This is a status read only register bit. Lock grant status register bit indicates the ownership between reading master and the downstream bus. If this register bit is 1, the reading master has owned the downstream bus. If this register bit is zero, the reading master has not owned the downstream bus.

  • false -> This master does not have a lock on the downstream bus.
  • true -> This master has a lock on the downstream bus.
Link to this function

lock_grant_interrupt?(pca9641)

@spec lock_grant_interrupt?(pid()) :: boolean()

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.
Link to this function

lock_grant_interrupt_enabled?(pca9641)

@spec lock_grant_interrupt_enabled?(t()) :: boolean()

LOCK_GRANT_MSK

  • false -> Enable output interrupt when LOCK_GRANT function is set.
  • true -> Disable output interrupt when LOCK_GRANT function is set.
Link to this function

lock_request(dev, value)

@spec lock_request(t(), boolean()) :: {:ok, t()} | {:error, term()}

LOCK_REQ

Lock request register bit is for a master requesting the downstream bus when it does not have a lock on downstream bus. When a master has a lock on downstream bus, it can give up the ownership by writing zero to LOCK_REQ register bit. When LOCK_REQ becomes zero, LOCK_GRANT bit becomes zero and the internal switch will be open.

  • false -> Master is not requesting a lock on the downstream bus or giving up the lock if master had a lock on the downstream bus.
  • true -> Master is requesting a lock on the downstream bus.
Link to this function

lock_request?(pca9641)

@spec lock_request?(t()) :: boolean()

LOCK_REQ

Lock request register bit is for a master requesting the downstream bus when it does not have a lock on downstream bus. When a master has a lock on downstream bus, it can give up the ownership by writing zero to LOCK_REQ register bit. When LOCK_REQ becomes zero, LOCK_GRANT bit becomes zero and the internal switch will be open.

  • false -> Master is not requesting a lock on the downstream bus or giving up the lock if master had a lock on the downstream bus.
  • true -> Master is requesting a lock on the downstream bus.
Link to this function

mailbox_empty?(pca9641)

@spec mailbox_empty?(t()) :: boolean()

MBOX_EMPTY

This is a read-only status register bit. If this bit is ‘0’, it indicates other master mailbox is full, and this master cannot send more data to other master mailbox. If it is ‘1’, it indicates other master is empty and this master can send data to other master mailbox.

  • false -> Other master mailbox is full; wait until other master reads data.
  • true -> Other master mailbox is empty. Other master has read previous data and it is permitted to write new data.
Link to this function

mailbox_empty_interrupt?(pca9641)

@spec mailbox_empty_interrupt?(t()) :: boolean()

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.
Link to this function

mailbox_empty_interrupt_enabled?(pca9641)

@spec mailbox_empty_interrupt_enabled?(t()) :: boolean()

MBOX_EMPTY_MSK

  • false -> Enable output interrupt when MBOX_EMPTY function is set.
  • true -> Disable output interrupt when MBOX_EMPTY function is set.
Link to this function

mailbox_full?(pca9641)

@spec mailbox_full?(t()) :: boolean()

MBOX_FULL

This is a read-only status register bit. If this bit is ‘0’, it indicates no data is available in the mail box. If it is ‘1’, it indicates new data is available in the mail box.

  • false -> No data is available for this master.
  • true -> Mailbox contains data for this master from the other master.
Link to this function

mailbox_full_interrupt?(pca9641)

@spec mailbox_full_interrupt?(t()) :: boolean()

MBOX_FULL_INT

Indicates the mailbox has new mail.

  • false -> No interrupt generated; mailbox is not full.
  • true -> Interrupt generated; mailbox full.
Link to this function

mailbox_full_interrupt_enabled?(pca9641)

@spec mailbox_full_interrupt_enabled?(t()) :: boolean()

MBOX_FULL_MSK

  • false -> Enable output interrupt when MBOX_FULL function is set.
  • true -> Disable output interrupt when MBOX_FULL function is set.
Link to this function

other_lock?(pca9641)

@spec other_lock?(t()) :: boolean()

OTHER_LOCK

This is a status read-only register bit. Other master lock status indicates the ownership between other master and the downstream bus. If this register bit is ‘1’, the other master has owned the downstream bus. If this register bit is ‘0’, the other master does not own the downstream bus.

  • false -> The other master does not have a lock on the downstream bus.
  • true -> The other master has a lock on the downstream bus.
Link to this function

priority(dev, value)

@spec priority(t(), boolean()) :: {:ok, t()} | {:error, reason :: any()}

PRIORITY

Master can set this register bit for setting priority of the winner when two masters request the downstream bus at the same time.

Link to this function

priority?(conn)

@spec priority?(t()) :: boolean()

PRIORITY

Master can set this register bit for setting priority of the winner when two masters request the downstream bus at the same time.

Link to this function

read_mailbox(dev)

@spec read_mailbox(t()) :: {:ok, mailbox_data(), t()} | {:error, term()}

Read shared mailbox.

Link to this function

request_downstream_bus(dev)

@spec request_downstream_bus(t()) :: {:ok, t()} | {:error, reason :: any()}

Attempt to acquire the downstream I2C bus.

Link to this function

reserve_time(dev)

@spec reserve_time(t()) :: {:ok, non_neg_integer(), t()} | {:error, term()}
@spec reserve_time(t()) :: {:ok, t()} | {:error, term()}

RES_TIME

Reserve timer. Changes during LOCK_GRANT = 1 will have no effect.

Returns {:ok, n} where n is the number if milliseconds remaining in the reservation.

Link to this function

reserve_time(dev, ms)

RES_TIME

Reserve timer. Changes during LOCK_GRANT = 1 will have no effect.

ms is the number of milliseconds remaining in the reservation.

Link to this function

scl_becomes_io(dev, value)

@spec scl_becomes_io(t(), boolean()) :: {:ok, t()} | {:error, term()}

SCL_IO

SCL becomes I/O pin; master can read or write to this register bit. If master reads this bit, the value is the state of the downstream SCL pin. Zero value means SCL is LOW, and one means SCL pin is HIGH. When master writes ‘0’ to this register bit, the downstream SCL pin will assert LOW. If master writes‘1’ to this register bit, the downstream SCL pin will be pulled HIGH. Remark: SCL becomes I/O pin only when BUS_CONNECT = 0 and LOCK_GRANT = 1.

  • false -> When read, shows the SCL pin of the downstream bus is LOW. When written, PCA9641 drives SCL pin of downstream bus LOW.
  • true -> When read, shows the SCL pin of the downstream bus is HIGH. When written, PCA9641 drives SCL pin of the downstream bus HIGH.
Link to this function

scl_becomes_io?(pca9641)

@spec scl_becomes_io?(t()) :: boolean()

SCL_IO

SCL becomes I/O pin; master can read or write to this register bit. If master reads this bit, the value is the state of the downstream SCL pin. Zero value means SCL is LOW, and one means SCL pin is HIGH. When master writes ‘0’ to this register bit, the downstream SCL pin will assert LOW. If master writes‘1’ to this register bit, the downstream SCL pin will be pulled HIGH. Remark: SCL becomes I/O pin only when BUS_CONNECT = 0 and LOCK_GRANT = 1.

  • false -> When read, shows the SCL pin of the downstream bus is LOW. When written, PCA9641 drives SCL pin of downstream bus LOW.
  • true -> When read, shows the SCL pin of the downstream bus is HIGH. When written, PCA9641 drives SCL pin of the downstream bus HIGH.
Link to this function

sda_becomes_io(dev, value)

@spec sda_becomes_io(t(), boolean()) :: {:ok, t()} | {:error, term()}

SDA_IO

SDA becomes I/O pin; master can read or write to this register bit. If master reads this bit, the value is the state of the downstream SDA pin. Zero value means SDA is LOW, and one means SDA pin is HIGH. When master writes ‘0’ to this register bit, the downstream SDA pin will assert LOW. If master writes‘1’ to this register bit, the downstream SDA pin will be pulled HIGH. Remark: SDA becomes I/O pin only when BUS_CONNECT = 0 and LOCK_GRANT = 1.

  • false -> When read, indicates the SDA pin of the downstream bus is LOW. When written, PCA9641 drives SDA pin of downstream bus LOW.
  • true -> When read, indicates the SDA pin of the downstream bus is HIGH. When written, PCA9641 drives SDA pin of the downstream bus HIGH.
Link to this function

sda_becomes_io?(pca9641)

@spec sda_becomes_io?(t()) :: boolean()

SDA_IO

SDA becomes I/O pin; master can read or write to this register bit. If master reads this bit, the value is the state of the downstream SDA pin. Zero value means SDA is LOW, and one means SDA pin is HIGH. When master writes ‘0’ to this register bit, the downstream SDA pin will assert LOW. If master writes‘1’ to this register bit, the downstream SDA pin will be pulled HIGH. Remark: SDA becomes I/O pin only when BUS_CONNECT = 0 and LOCK_GRANT = 1.

  • false -> When read, indicates the SDA pin of the downstream bus is LOW. When written, PCA9641 drives SDA pin of downstream bus LOW.
  • true -> When read, indicates the SDA pin of the downstream bus is HIGH. When written, PCA9641 drives SDA pin of the downstream bus HIGH.
Link to this function

smbus_software_reset(dev, value)

@spec smbus_software_reset(t(), boolean()) :: {:ok, t()} | {:error, term()}

SMBUS_SWRST

Non-granted or granted master sends a soft reset, if this bit is set, PCA9641 sets clock LOW for 35 ms following reset of all register values to defaults.

  • false -> Normal operation.
  • true -> Enable sending SMBus time-out to downstream bus, after receiving a general call soft reset from master.
Link to this function

smbus_software_reset?(pca9641)

@spec smbus_software_reset?(t()) :: boolean()

SMBUS_SWRST

Non-granted or granted master sends a soft reset, if this bit is set, PCA9641 sets clock LOW for 35 ms following reset of all register values to defaults.

  • false -> Normal operation.
  • true -> Enable sending SMBus time-out to downstream bus, after receiving a general call soft reset from master.
Link to this function

test_interrupt_interrupt_enabled?(pca9641)

@spec test_interrupt_interrupt_enabled?(t()) :: boolean()

TEST_INT_MSK

  • false -> Enable output interrupt when TEST_INT function is set.
  • true -> Disable output interrupt when TEST_INT function is set.
Link to this function

test_interrupt_pin(dev, value)

@spec test_interrupt_pin(t(), boolean()) :: {:ok, t()} | {:error, term()}

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 Routine to handle housekeeping tasks.
Link to this function

test_interrupt_pin?(pca9641)

@spec test_interrupt_pin?(t()) :: boolean()

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 Routine to handle housekeeping tasks.
Link to this function

test_interrupt_pin_interrupt?(pca9641)

@spec test_interrupt_pin_interrupt?(t()) :: boolean()

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.
Link to this function

verify_identity(dev)

@spec verify_identity(t()) :: {:ok, t()} | {:error, reason :: any()}

Verify the contents of the identity register.

It should match the expected value of 56.

Link to this function

wait_for_bus_init(conn)

@spec wait_for_bus_init(t()) :: {:ok, t()} | {:error, :bus_init_fail}

Waits for downstream bus initialisation to succeed.

Waits for a maximum of 1 second.

Link to this function

write_mailbox(dev, arg)

@spec write_mailbox(pid(), mailbox_data()) :: :ok | {:error, term()}

Write shared mailbox.