diff --git a/lib/eink/driver/ws2in13v4.ex b/lib/eink/driver/ws2in13v4.ex new file mode 100644 index 0000000..d993a94 --- /dev/null +++ b/lib/eink/driver/ws2in13v4.ex @@ -0,0 +1,137 @@ +defmodule EInk.Driver.WaveshareV4 do + # 2.13" e-Paper HAT (B) V4, 250x122, Black/White, full refresh only currently + use EInk.Driver, width: 250, height: 122, palette: :bw, partial_refresh: false + + alias EInk.Driver.SpiDriver + alias Circuits.GPIO + + require Logger + + # Full refresh LUT + @lut_vcom << + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + >> + + @lut_ww << + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + >> + + @lut_bw << + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + >> + + @lut_wb << + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + >> + + @lut_bb << + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + >> + + @lut_full <<@lut_vcom, @lut_ww, @lut_bw, @lut_wb, @lut_bb>> + + @impl EInk.Driver + def new(opts \\ []) do + spi_driver = SpiDriver.open(opts) + {:ok, %{driver: spi_driver}} + end + + @impl EInk.Driver + def reset(state) do + :ok = GPIO.write(state.driver.reset, 0) + Process.sleep(1) + :ok = GPIO.write(state.driver.reset, 1) + Process.sleep(1) + + :ok = SpiDriver.wait_for_busy(state.driver) + {:ok, state} + end + + @impl EInk.Driver + def init(state, _opts \\ []) do + # Software-Reset + SpiDriver.write(state.driver, 0x12, <<>>) + :ok = SpiDriver.wait_for_busy(state.driver) + + # Display size & driver output control + SpiDriver.write(state.driver, 0x01, <<0xF9, 0x00, 0x00>>) + # RAM data entry mode + SpiDriver.write(state.driver, 0x11, <<0x01>>) + # RAM X address + SpiDriver.write(state.driver, 0x44, <<0x01, 0x10>>) + # RAM Y address range + SpiDriver.write(state.driver, 0x45, <<0x00, 0x00, 0x00, 0x00>>) + # Border + SpiDriver.write(state.driver, 0x3C, <<0x05>>) + + # Voltages + SpiDriver.write(state.driver, 0x2C, <<0x36>>) # VCOM + SpiDriver.write(state.driver, 0x03, <<0x17>>) # Gate voltage + SpiDriver.write(state.driver, 0x04, <<0x41, 0x00, 0x32>>) # Source voltage + + # load LUT (Fulll Refresh) + SpiDriver.write(state.driver, 0x32, @lut_full) + + {:ok, state} + end + + @impl EInk.Driver + def draw(state, image, _opts \\ []) do + # Set RAM-Address Counter + SpiDriver.write(state.driver, 0x4E, <<0x01>>) + SpiDriver.write(state.driver, 0x4F, <<0xF9, 0x00>>) + + # Picture data (B/W channel only) + SpiDriver.write(state.driver, 0x24, image) + + # Display-Update + SpiDriver.write(state.driver, 0x22, <<0xC7>>) + SpiDriver.write(state.driver, 0x20, <<>>) + :ok = SpiDriver.wait_for_busy(state.driver) + + {:ok, state} + end + + @impl EInk.Driver + def sleep(state) do + SpiDriver.write(state.driver, 0x10, <<0x01>>) + {:ok, state} + end + + @impl EInk.Driver + def wake(state) do + {:ok, state} = reset(state) + init(state) + end +end