Current section
Files
Jump to
Current section
Files
lib/commands.ex
defmodule Inky.Commands do
@moduledoc """
`Commands` is responsible for sending commands to the Inky screen. It delegates
to whatever IO module its user provides at init
"""
defmodule State do
@moduledoc false
@state_fields [:io_mod, :io_state]
@enforce_keys @state_fields
defstruct @state_fields
end
@default_io_mod Inky.RpiIO
# API
def init_io(io_mod \\ @default_io_mod, io_args \\ []) do
%State{io_mod: io_mod, io_state: io_mod.init(io_args)}
end
def update(state = %State{}, display, buffer_black, buffer_accent) do
d_pd = display.packed_dimensions
state
|> reset()
|> soft_reset()
|> await_device()
|> set_analog_block_control()
|> set_digital_block_control()
|> set_gate(d_pd.height)
|> set_gate_driving_voltage()
|> dummy_line_period()
|> set_gate_line_width()
|> set_data_entry_mode()
|> power_on()
|> vcom_register()
|> set_border_color()
|> configure_if_yellow(display.accent)
|> set_luts(display.luts)
|> set_dimensions(d_pd.width, d_pd.height)
|> push_pixel_data(buffer_black, buffer_accent)
|> display_update_sequence()
|> trigger_display_update()
|> wait_before_sleep()
|> deep_sleep()
:ok
end
defp reset(state) do
io_call(state, :handle_reset, [0])
io_call(state, :handle_sleep, [100])
io_call(state, :handle_reset, [1])
io_call(state, :handle_sleep, [100])
state
end
# SPI commands
defp wait_before_sleep(state) do
io_call(state, :handle_sleep, [50])
await_device(state)
end
defp await_device(state) do
should_wait = io_call(state, :handle_read_busy) == 1
if should_wait do
io_call(state, :handle_sleep, [10])
await_device(state)
else
state
end
end
defp soft_reset(state), do: write_command(state, 0x12)
defp set_analog_block_control(state), do: write_command(state, 0x74, 0x54)
defp set_digital_block_control(state), do: write_command(state, 0x7E, 0x3B)
defp set_gate(state, packed_height), do: write_command(state, 0x01, packed_height <> <<0x00>>)
defp set_gate_driving_voltage(state), do: write_command(state, 0x03, [0b10000, 0b0001])
defp dummy_line_period(state), do: write_command(state, 0x3A, 0x07)
defp set_gate_line_width(state), do: write_command(state, 0x3B, 0x04)
# Data entry mode setting 0x03 = X/Y increment
defp set_data_entry_mode(state), do: write_command(state, 0x11, 0x03)
defp power_on(state), do: write_command(state, 0x04)
defp vcom_register(state) do
# VCOM Register, 0x3c = -1.5v?
write_command(state, 0x2C, 0x3C)
write_command(state, 0x3C, 0x00)
end
defp set_border_color(state), do: write_command(state, 0x3C, 0x00)
defp configure_if_yellow(state, accent) do
# Set voltage of VSH and VSL on Yellow device
if accent == :yellow do
write_command(state, 0x04, 0x07)
else
state
end
end
defp set_luts(state, luts), do: write_command(state, 0x32, luts)
defp set_dimensions(state, width_data, packed_height) do
height_data = <<0, 0>> <> packed_height
width_data = <<0>> <> width_data
state
# Set RAM X Start/End
|> write_command(0x44, width_data)
# Set RAM Y Start/End
|> write_command(0x45, height_data)
end
defp push_pixel_data(state, buffer_black, buffer_accent) do
# 0x24 == RAM B/W
do_push_pixel_data(state, 0x24, buffer_black)
# 0x26 == RAM Red/Yellow/etc
do_push_pixel_data(state, 0x26, buffer_accent)
state
end
defp do_push_pixel_data(state, pixel_cmd, pixel_buffer) do
# Set RAM X Pointer start
write_command(state, 0x4E, 0x00)
# Set RAM Y Pointer start
write_command(state, 0x4F, <<0x00, 0x00>>)
write_command(state, pixel_cmd, pixel_buffer)
end
defp display_update_sequence(state), do: write_command(state, 0x22, 0xC7)
defp trigger_display_update(state), do: write_command(state, 0x20)
defp deep_sleep(state), do: write_command(state, 0x10, 0x01)
# pipe-able wrappers
defp write_command(state, command) do
io_call(state, :handle_command, [command])
state
end
defp write_command(state, command, data) do
io_call(state, :handle_command, [command, data])
state
end
# Dispatch to the IO callback module that's held in state, using the previously obtained state
defp io_call(state, op, args \\ []) do
apply(state.io_mod, op, [state.io_state | args])
end
end