Improve the documentation of the defregister
macro.
This commit is contained in:
parent
4b27f5441f
commit
a1b5513c11
9 changed files with 150 additions and 24 deletions
|
@ -6,6 +6,8 @@ defmodule Wafer.Registers do
|
|||
|
||||
This can be a massive time saver, and means you can basically just copy them
|
||||
straight out of the datasheet.
|
||||
|
||||
See the documentation for `defregister/4` for more information.
|
||||
"""
|
||||
alias Wafer.Chip
|
||||
alias Wafer.Conn
|
||||
|
@ -20,8 +22,8 @@ defmodule Wafer.Registers do
|
|||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Define a registers.
|
||||
@doc ~S"""
|
||||
Define functions for interacting with a device register.
|
||||
|
||||
## Parameters
|
||||
- `name` - name of the register.
|
||||
|
@ -31,17 +33,84 @@ defmodule Wafer.Registers do
|
|||
|
||||
## Examples
|
||||
|
||||
### Read-only registers
|
||||
|
||||
Define a read-only register named `status` at address `0x03` which is a single
|
||||
byte wide:
|
||||
|
||||
iex> defregister(:status, 0x03, :ro, 1)
|
||||
|
||||
iex> defregister(:config, 0x01, :rw, 2)
|
||||
This will define the following function along with documentation and
|
||||
typespecs:
|
||||
|
||||
iex> defregister(:int_en, 0x02, :wo, 1)
|
||||
```elixir
|
||||
def read_status(conn), do: Chip.read_register(conn, 0x03, 1)
|
||||
```
|
||||
|
||||
### Write-only registers
|
||||
|
||||
Define a write-only register named `int_en` at address `0x02` which is 2 bytes
|
||||
wide:
|
||||
|
||||
iex> defregister(:int_en, 0x02, :wo, 2)
|
||||
|
||||
This will define the following functions along with documentation and
|
||||
typespecs:
|
||||
|
||||
```elixir
|
||||
def write_int_en(conn, data)
|
||||
when is_binary(data) and byte_size(data) == 2,
|
||||
do: Chip.write_register(conn, 0x2, data)
|
||||
|
||||
def write_int_en(_conn, data),
|
||||
do: {:error, "Argument error: #{inspect(data)}"}
|
||||
```
|
||||
|
||||
### Read-write registers.
|
||||
|
||||
Define a read-write register named `config` at address `0x01`.
|
||||
|
||||
iex> defregister(:config, 0x01, :rw, 1)
|
||||
|
||||
In addition to defining `read_config/1` and `write_config/2` as per the
|
||||
examples above it will also generate the following functions along with
|
||||
documentation and typespecs:
|
||||
|
||||
```elixir
|
||||
def swap_config(conn, data)
|
||||
when is_binary(data) and byte_size(data) == 1,
|
||||
do: Chip.swap_register(conn, 0x01, data)
|
||||
|
||||
def swap_config(_conn, data),
|
||||
do: {:error, "Argument error: #{inspect(data)}"}
|
||||
|
||||
def update_config(conn, callback)
|
||||
when is_function(callback, 1) do
|
||||
with {:ok, data} <- Chip.read_register(conn, 0x01, 1),
|
||||
new_data when is_binary(new_data) and byte_size(new_data) == 1 <- callback.(data),
|
||||
{:ok, conn} <- Chip.write_regsiter(conn, 0x01, new_data),
|
||||
do: {:ok, conn}
|
||||
end
|
||||
|
||||
def update_config(_conn, _callback),
|
||||
do: {:error, "Argument error: callback should be an arity 1 function"}
|
||||
```
|
||||
|
||||
"""
|
||||
defmacro defregister(name, register_address, :ro, bytes)
|
||||
when is_atom(name) and is_integer(register_address) and register_address >= 0 and
|
||||
is_integer(bytes) and bytes >= 0 do
|
||||
empty_bytes = 1..bytes |> Enum.map(fn _ -> 0 end) |> Enum.join(", ")
|
||||
|
||||
quote do
|
||||
@doc """
|
||||
Read the contents of the `#{unquote(name)}` register.
|
||||
|
||||
## Example
|
||||
|
||||
iex> read_#{unquote(name)}(conn)
|
||||
{:ok, <<#{unquote(empty_bytes)}>>}
|
||||
"""
|
||||
@spec unquote(:"read_#{name}")(Conn.t()) :: {:ok, binary} | {:error, reason :: any}
|
||||
def unquote(:"read_#{name}")(conn),
|
||||
do: Chip.read_register(conn, unquote(register_address), unquote(bytes))
|
||||
|
@ -51,8 +120,19 @@ defmodule Wafer.Registers do
|
|||
defmacro defregister(name, register_address, :wo, bytes)
|
||||
when is_atom(name) and is_integer(register_address) and register_address >= 0 and
|
||||
is_integer(bytes) and bytes >= 0 do
|
||||
empty_bytes = 1..bytes |> Enum.map(fn _ -> 0 end) |> Enum.join(", ")
|
||||
|
||||
quote do
|
||||
@spec unquote(:"write_#{name}")(Conn.t(), data :: binary) :: :ok | {:error, reason :: any}
|
||||
@doc """
|
||||
Write new contents to the `#{unquote(name)}` register.
|
||||
|
||||
## Example
|
||||
|
||||
iex> write_#{unquote(name)}(conn, <<#{unquote(empty_bytes)}>>)
|
||||
{:ok, _conn}
|
||||
"""
|
||||
@spec unquote(:"write_#{name}")(Conn.t(), data :: binary) ::
|
||||
{:ok, Conn.t()} | {:error, reason :: any}
|
||||
def unquote(:"write_#{name}")(conn, data)
|
||||
when is_binary(data) and byte_size(data) == unquote(bytes),
|
||||
do: Chip.write_register(conn, unquote(register_address), data)
|
||||
|
@ -64,44 +144,89 @@ defmodule Wafer.Registers do
|
|||
defmacro defregister(name, register_address, :rw, bytes)
|
||||
when is_atom(name) and is_integer(register_address) and register_address >= 0 and
|
||||
is_integer(bytes) and bytes >= 0 do
|
||||
empty_bytes = 1..bytes |> Enum.map(fn _ -> 0 end) |> Enum.join(", ")
|
||||
bits = bytes * 8
|
||||
|
||||
quote do
|
||||
@doc """
|
||||
Read the contents of the `#{unquote(name)}` register.
|
||||
|
||||
## Example
|
||||
|
||||
iex> read_#{unquote(name)}(conn)
|
||||
{:ok, <<#{unquote(empty_bytes)}>>}
|
||||
"""
|
||||
@spec unquote(:"read_#{name}")(Conn.t()) :: {:ok, binary} | {:error, reason :: any}
|
||||
def unquote(:"read_#{name}")(conn),
|
||||
do: Chip.read_register(conn, unquote(register_address), unquote(bytes))
|
||||
|
||||
@spec unquote(:"write_#{name}")(Conn.t(), data :: binary) :: :ok | {:error, reason :: any}
|
||||
@doc """
|
||||
Write new contents to the `#{unquote(name)}` register.
|
||||
|
||||
## Example
|
||||
|
||||
iex> write_#{unquote(name)}(conn, <<#{unquote(empty_bytes)}>>)
|
||||
{:ok, _conn}
|
||||
"""
|
||||
@spec unquote(:"write_#{name}")(Conn.t(), data :: binary) ::
|
||||
{:ok, Conn.t()} | {:error, reason :: any}
|
||||
def unquote(:"write_#{name}")(conn, data)
|
||||
when is_binary(data) and byte_size(data) == unquote(bytes),
|
||||
do: Chip.write_register(conn, unquote(register_address), data)
|
||||
|
||||
def unquote(:"write_#{name}")(_conn, data), do: {:error, "Argument error: #{inspect(data)}"}
|
||||
|
||||
@doc """
|
||||
Swap the contents of the `#{unquote(name)}` register.
|
||||
|
||||
Reads the contents of the register, then replaces it, returning the
|
||||
previous contents. Some drivers may implement this atomically.
|
||||
|
||||
## Example
|
||||
|
||||
iex> swap_#{unquote(name)}(conn, <<#{unquote(empty_bytes)}>>)
|
||||
{:ok, <<#{unquote(empty_bytes)}>>, _conn}
|
||||
"""
|
||||
@spec unquote(:"swap_#{name}")(Conn.t(), data :: binary) ::
|
||||
:ok | {:error, reason :: any}
|
||||
{:ok, Conn.t()} | {:error, reason :: any}
|
||||
def unquote(:"swap_#{name}")(conn, data)
|
||||
when is_binary(data) and byte_size(data) == unquote(bytes),
|
||||
do: Chip.swap_register(conn, unquote(register_address), data)
|
||||
|
||||
def unquote(:"swap_#{name}")(_conn, data), do: {:error, "Argument error: #{inspect(data)}"}
|
||||
|
||||
@doc """
|
||||
Update the contents of the `#{unquote(name)}` register using a
|
||||
transformation function.
|
||||
|
||||
## Example
|
||||
|
||||
iex> transform = fn <<data::size(#{unquote(bits)})>> -> <<(data * 2)::size(#{
|
||||
unquote(bits)
|
||||
})>> end
|
||||
...> update_#{unquote(name)}(conn, transform)
|
||||
{:ok, _conn}
|
||||
"""
|
||||
@spec unquote(:"update_#{name}")(
|
||||
Conn.t(),
|
||||
(<<_::_*unquote(bytes * 8)>> -> <<_::_*unquote(bytes * 8)>>)
|
||||
) :: :ok | {:error, reason :: any}
|
||||
(<<_::_*unquote(bits)>> -> <<_::_*unquote(bits)>>)
|
||||
) :: {:ok, Conn.t()} | {:error, reason :: any}
|
||||
def unquote(:"update_#{name}")(conn, callback) when is_function(callback, 1) do
|
||||
with {:ok, old_data} <-
|
||||
Chip.read_register(conn, unquote(register_address), unquote(bytes)),
|
||||
new_data when is_binary(new_data) and byte_size(new_data) == unquote(bytes) <-
|
||||
callback.(old_data),
|
||||
:ok <- Chip.write_register(conn, unquote(register_address), new_data),
|
||||
do: :ok
|
||||
{:ok, conn} <- Chip.write_register(conn, unquote(register_address), new_data),
|
||||
do: {:ok, conn}
|
||||
end
|
||||
|
||||
def unquote(:"update_#{name}")(_conn, callback),
|
||||
def unquote(:"update_#{name}")(_conn, _callback),
|
||||
do: {:error, "Argument error: callback should be an arity 1 function"}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Define a register with common defaults:
|
||||
Define functions for interacting with a device register with common defaults.
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule WaferDriverCircuits.GPIO.DispatcherTest do
|
||||
use ExUnit.Case, async: true
|
||||
alias Wafer.Driver.Circuits.GPIO.Wrapper
|
||||
alias Wafer.Driver.Circuits.GPIO.Dispatcher, as: Dispatcher
|
||||
alias Wafer.Driver.Circuits.GPIO.Wrapper
|
||||
alias Wafer.InterruptRegistry, as: IR
|
||||
import Mimic
|
||||
@moduledoc false
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
defmodule WaferDriverCircuits.GPIOTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Mimic
|
||||
alias Wafer.Driver.Circuits.GPIO.Wrapper
|
||||
alias Wafer.Driver.Circuits.GPIO, as: Subject
|
||||
alias Wafer.Driver.Circuits.GPIO.Dispatcher, as: Dispatcher
|
||||
alias Wafer.Driver.Circuits.GPIO.Wrapper
|
||||
alias Wafer.GPIO, as: GPIO
|
||||
@moduledoc false
|
||||
|
||||
|
@ -74,7 +74,7 @@ defmodule WaferDriverCircuits.GPIOTest do
|
|||
|> reject(:set_direction, 2)
|
||||
|
||||
assert {:ok, %Subject{} = conn} = Subject.acquire(pin: 1, direction: :out)
|
||||
assert {:ok, %Subject{}} = GPIO.direction(conn, :out)
|
||||
assert {:ok, ^conn} = GPIO.direction(conn, :out)
|
||||
end
|
||||
|
||||
test "when the direction is changing" do
|
||||
|
@ -87,7 +87,8 @@ defmodule WaferDriverCircuits.GPIOTest do
|
|||
:ok
|
||||
end)
|
||||
|
||||
assert {:ok, %Subject{}} = GPIO.direction(conn, :in)
|
||||
assert {:ok, conn} = GPIO.direction(conn, :in)
|
||||
assert conn.direction == :in
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -132,7 +133,7 @@ defmodule WaferDriverCircuits.GPIOTest do
|
|||
:ok
|
||||
end)
|
||||
|
||||
assert {:ok, %Subject{}} = GPIO.pull_mode(conn, :pull_up)
|
||||
assert {:ok, ^conn} = GPIO.pull_mode(conn, :pull_up)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
defmodule WaferCircuits.I2CTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Mimic
|
||||
alias Wafer.Driver.Circuits.I2C.Wrapper
|
||||
alias Wafer.Chip
|
||||
alias Wafer.Driver.Circuits.I2C, as: Subject
|
||||
alias Wafer.Driver.Circuits.I2C.Wrapper
|
||||
alias Wafer.I2C
|
||||
@moduledoc false
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
defmodule WaferCircuits.SPITest do
|
||||
use ExUnit.Case, async: true
|
||||
use Mimic
|
||||
alias Wafer.Driver.Circuits.SPI.Wrapper
|
||||
alias Wafer.Driver.Circuits.SPI, as: Subject
|
||||
alias Wafer.Driver.Circuits.SPI.Wrapper
|
||||
alias Wafer.SPI
|
||||
@moduledoc false
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule WaferDriverElixirALE.GPIO.DispatcherTest do
|
||||
use ExUnit.Case, async: true
|
||||
alias Wafer.Driver.ElixirALE.GPIO.Wrapper
|
||||
alias Wafer.Driver.ElixirALE.GPIO.Dispatcher, as: Dispatcher
|
||||
alias Wafer.Driver.ElixirALE.GPIO.Wrapper
|
||||
alias Wafer.InterruptRegistry, as: IR
|
||||
import Mimic
|
||||
@moduledoc false
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
defmodule WaferDriverElixirALE.GPIOTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Mimic
|
||||
alias Wafer.Driver.ElixirALE.GPIO.Wrapper
|
||||
alias Wafer.Driver.ElixirALE.GPIO, as: Subject
|
||||
alias Wafer.Driver.ElixirALE.GPIO.Dispatcher, as: Dispatcher
|
||||
alias Wafer.Driver.ElixirALE.GPIO.Wrapper
|
||||
alias Wafer.GPIO, as: GPIO
|
||||
@moduledoc false
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
defmodule WaferElixirALE.I2CTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Mimic
|
||||
alias Wafer.Driver.ElixirALE.I2C.Wrapper
|
||||
alias Wafer.Chip
|
||||
alias Wafer.Driver.ElixirALE.I2C, as: Subject
|
||||
alias Wafer.Driver.ElixirALE.I2C.Wrapper
|
||||
alias Wafer.I2C
|
||||
@moduledoc false
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
defmodule WaferElixirALE.SPITest do
|
||||
use ExUnit.Case, async: true
|
||||
use Mimic
|
||||
alias Wafer.Driver.ElixirALE.SPI.Wrapper
|
||||
alias Wafer.Driver.ElixirALE.SPI, as: Subject
|
||||
alias Wafer.Driver.ElixirALE.SPI.Wrapper
|
||||
alias Wafer.SPI
|
||||
@moduledoc false
|
||||
|
||||
|
|
Loading…
Reference in a new issue