Skip to content

Quickstart

Show text on an ISF15ACP4 from your MCU: wire seven signals and two supplies, write a small port class, call four driver functions.

The 15 V drive supply comes last

Switch VCC (14 V to 16 V) with a GPIO-controlled supply or charge pump that is off at power-up. The datasheet turns it on only after VDD is up and the reset pulse is done, and off before VDD. A VCC that is always on skips that sequence.

1. Wire it

MCU ISF15ACP4 pin Notes
SPI MOSI 8 SDI Write only: the display has no data output
SPI SCK 7 SCK SPI mode 0, up to 6.6 MHz; start at 1 MHz
GPIO out 4 SS Chip select, active low; the port drives it
GPIO out 6 D/C Low: command bytes; high: pixel data
GPIO out 5 RES Reset, active low
GPIO out VCC supply enable Turns on the 15 V supply that feeds pin 9
GPIO in, pull-up 1 SW Pin 2 SW to GND: pressed reads low
2.8 V or 3.3 V 3 VDD 2.4 V to 3.5 V; MCU I/O at the same level
GND 10 GND Common ground for both supplies

The Hardware page has the timing, the power sequence and the electrical limits.

2. Add the driver

Add the repository as a submodule and link the hf_isf15acp4 CMake target (or include inc/ and compile src/isf15acp4_font_data.cpp with C++20). Installation covers CMake and ESP-IDF.

git submodule add https://github.com/hardfoc/hf-isf15acp4-driver.git external/hf-isf15acp4-driver

3. Write the port

The port is the only platform code. It writes SPI bytes inside an SS-low transaction with D/C set, drives the control pins and waits. Replace the platform_* calls with your SDK's.

my_spi.hpp
class MySpi : public isf15acp4::SpiInterface<MySpi> {
public:
  // Your board's GPIO numbers.
  static constexpr int kCs = 10, kDc = 13, kRes = 14, kVccEn = 9, kSwitch = 8;

  bool EnsureInitialized() noexcept {
    platform_gpio_write(kCs, true);       // SS idle high
    platform_gpio_write(kVccEn, false);   // 15 V drive supply off until after reset
    return platform_spi_init();
  }

  // D/C low = command, high = data; SS low for the whole transaction.
  bool BeginCommand() noexcept { return begin(false); }
  bool BeginData() noexcept { return begin(true); }
  void EndTransaction() noexcept { platform_gpio_write(kCs, true); }

  // Must not return before the last bit is on the wire: SS rises right after.
  bool WriteBytes(const uint8_t* data, std::size_t len) noexcept {
    return platform_spi_write(data, len);
  }

  void GpioSet(isf15acp4::CtrlPin pin, isf15acp4::GpioSignal signal) noexcept {
    const bool active = signal == isf15acp4::GpioSignal::Active;
    switch (pin) {
      case isf15acp4::CtrlPin::ChipSelect:  platform_gpio_write(kCs, !active); break;   // SS active low
      case isf15acp4::CtrlPin::DataCommand: platform_gpio_write(kDc, active); break;    // high = data
      case isf15acp4::CtrlPin::Reset:       platform_gpio_write(kRes, !active); break;  // RES# active low
      case isf15acp4::CtrlPin::VccEnable:   platform_gpio_write(kVccEn, active); break;
      case isf15acp4::CtrlPin::Switch:      break;                                      // input only
    }
  }

  // The switch closes pins 1-2; wired to GND with a pull-up, pressed reads low.
  bool GpioRead(isf15acp4::CtrlPin pin) noexcept {
    return pin == isf15acp4::CtrlPin::Switch && !platform_gpio_read(kSwitch);
  }

  void DelayUs(uint32_t us) noexcept { platform_delay_us(us); }

private:
  bool begin(bool data) noexcept {
    platform_gpio_write(kDc, data);
    platform_gpio_write(kCs, false);
    return true;
  }
};

CtrlPin::Reset is active low like SS: Active holds the controller in reset, Inactive lets it run. Porting explains each method and the pin levels.

4. Show something

main.cpp
#include "isf15acp4.hpp"

int show_ready(MySpi& spi) {
  isf15acp4::SmartDisplay<MySpi> display(&spi);       // ISF15ACP4, software canvas

  if (!display.Initialize()) {                        // 1. reset, 15 V on, SSD1331 init table
    return -1;                                        // wiring, VCC or SPI: see GetLastError()
  }

  auto gfx = display.CreateGraphicsContext();         // 2. draw into a 96 x 64 RGB565 frame
  gfx.Clear(isf15acp4::colors::Black);
  gfx.DrawString(4, 4, "READY", isf15acp4::colors::Green);
  gfx.DrawRect({0, 0, 95, 63}, isf15acp4::colors::White);

  if (!display.Present(gfx)) {                        // 3. send the frame (12,288 bytes)
    return -2;
  }
  return display.IsPressed() ? 1 : 0;                 // 4. the switch, through your port
}

What each step does:

  1. Initialize() calls your EnsureInitialized(), pulses RES for at least 3 µs, enables VCC, waits 1 ms, sends the ISF15ACP4 initialisation table (which ends with display on) and sets the full 96 x 64 window. Nothing else works until it returns true.
  2. CreateGraphicsContext() returns a canvas the size of the panel. Drawing only changes RAM on your MCU.
  3. Present() streams the frame to the display: 12,288 bytes with D/C high.
  4. IsPressed() calls your GpioRead(CtrlPin::Switch).

This example is built by the host CI, so it compiles against the current headers.

If it does not work

Symptom First thing to check
Initialize() returns false GetLastError(): your EnsureInitialized() or WriteBytes() failed
Returns true, screen stays dark RES ends low (Reset mapping inverted), VCC not at 14 V to 16 V, SS or D/C swapped
Shapes in the right place, colours wrong A leftover byte swap or raw WritePixels() bytes (Troubleshooting)
Switch always pressed or never Pull-up on the SW pin, active level in GpioRead()

Next

  • API guide: brightness, hardware drawing, raw pixel writes, the switch, errors.
  • Graphics: the three backends, text, images.
  • Examples: complete ESP32 applications.