Fast Refresh on the Pervasive 2.66" Display

September 26, 2023

Introduction

I have a Pervasive Displays 2.66" E-ink development kit, because a family member bought one but didn't have a project for it. This display is cool because it supports fast refresh, which many hobbyist-class e-ink displays don't have. I decided that I wanted to connect it (eventually) to my Quartz64 and use it as a small terminal emulator, but to do this, I had to figure out how to talk to it and make it refresh fast.

Available Documentation

My (and most people's) normal process of finding documentation on any electronic part is to go to the manufacturer's website and see what documentation they provide. Pervasive Displays provides a demo UF2 file that runs on the Pi Pico (with fast refresh), and several Arduino libraries to interact with the screen, but are clearly more interested in taking you on as a paid partner. This screen is available in several different types at https://www.pervasivedisplays.com/product/2-66-e-ink-displays. This takes you to this application note, which describes the SPI interface, specific commands for normal and fast refresh, and how to read OTP memory in the display driver. Seems useful, right?

No! Throw this application note away! Do not use this document! It's full of typos and outright incorrect information, and most of the information appears to be for other screens or outdated or wrong in a hundred ways. Run from this document.

It took a while for me to figure this out, but that's why I'm writing this blog post. We're going to look at how you should actually interface with this screen. I took information from the normal Arduino library, the the fast update (except it isn't really) Arduino library, and the demo UF2 file.

The ultimate goal of my reverse engineering here is to drive this display with my Quartz64 board, probably to make a very small terminal emulator. I'll describe that project in a future post, and I'll link to the code that shows how to drive the screen from Rust using spidev and gpio_cdev.

SPI Interface

Most small e-ink screens use an internal display driver and a serial interface, and this one is no exception. The panel requires a high voltage charge pump, provided by the PCB that comes with the kit, which also breaks out 2 (not 3! Check the pinout in the datasheet) SPI pins, a chip select (/CS) pin, a D/C pin, a RESET pin, and a BUSY status pin. The panel is rated for 3.3V logic and power (the datasheet is seems mostly correct, unlike the appnote). The EXT3 board that comes with the kit also includes a SPI flash chip (what's on it). Pervasive calls the display driver the "chip on glass", or just "CoG".

Use SPI mode 0 (CPOL = 0, CPHA = 0) and MSB first. I don't know the maximum SPI speed, but it's probably around 10 MHz. The Arduino libraries use 4 MHz, and I've had success driving it at 8 MHz.

The display uses SPI but differentiates between data and command writes by the D/C pin: low for command, high for data. A command is an 8-bit register to write to, and data can be any length.

To write to a register:

  1. Activate the chip.
  2. Pull D/C and /CS low to select the CoG and put it in command mode.

  3. Write the register.
  4. With your platform's SPI system, write one byte (the register you want to write to) to the SPI port.

  5. Put the chip into data mode.
  6. Pull /CS high briefly (1 microsecond is enough) and pull D/C high to start sending data. Then, pull /CS low again and send all the data.

If needed, deselect the chip afterward. Example Pico SDK code for this (the microsecond sleeps are probably optional):

void pervasive_266_write_register(uint8_t reg, uint8_t* data, uint32_t data_len) {
    // pull D/C and display panel CS low
    gpio_put(PIN_DC, 0);
    gpio_put(PIN_PANEL_CS, 0);
	
    sleep_us(50);
    
    spi_write_blocking(SPI_PORT, &reg, 1); // write register ID
	
    sleep_us(50);

    // pull high/low to deselect and reselect the panel and to write data
    gpio_put(PIN_PANEL_CS, 1);
    gpio_put(PIN_DC, 1); // pull D/C high to send data
    sleep_us(1);
    gpio_put(PIN_PANEL_CS, 0);
    
    sleep_us(50);

    // write all data
    spi_write_blocking(SPI_PORT, data, data_len);
    
    sleep_us(50);

    // deselect panel
    gpio_put(PIN_PANEL_CS, 1);
}

Image data format

Image data is stored as 1bpp color, formatted in rows. The most significant bit corresponds to the leftmost pixel in a row.

The screen is oriented such that when the flex cable "tail" is pointing downward, the origin is in the upper left corner of the display. This means that, on the 2.66" screen, each row is 152 pixels, and there are 296 rows. One frame of data on this screen is 5624 bytes.

Normal Display Updates

A normal update of the display refreshes the whole screen several times (to erase the afterimage), then draws the desired image on the screen. All the work is done by the chip in the display, called the "CoG", or "chip on glass".

The list below describes how to drive both the special pins and what data to write on the SPI bus. Most operations are done through SPI. To issue a normal refresh to the screen:

  1. Hard reset the CoG.
  2. SPI transaction: none

    Assert RESET high for 5 ms, then low for 10 ms, then high for 5 ms again, then put /CS high and wait 5 ms.

  3. Soft reset the CoG.
  4. SPI transaction: Write 0x0e to register 0x00

    The soft reset is always required, regardless of display mode, while the hard reset is only required for a complete update and before the first fast update.

  5. Set the temperature.
  6. SPI transaction: Write the surrounding temperature in Celsius (one byte) to register 0xe5.

    The CoG needs to know the surrounding air temperature in Celsius. This adjusts the refresh speed, presumably to compensate for changes the e-ink material's properties at various temperatures.

  7. Activate this temperature value.
  8. SPI transaction: Write 0x02 to register 0xe0.

    I don't know the exact purpose of this command, but I suspect that it tells the CoG that the temperature you just supplied is what it should actually use.

  9. Set PSR.
  10. SPI transaction: Write 0xcf, 0x8d to register 0x00.

    I don't know what PSR actually stands for (maybe "panel settings register"?), but it's referenced in both the evil application note and the Arduino library and you have to set it.

  11. Write image data.
  12. SPI transaction: Write the new image to register 0x10 and 5642 bytes of anything to register 0x13.

    For a normal update, the CoG only needs one image but two register writes. You must write to both registers! The CoG will simply ignore whatever data you write to 0x13.

  13. Refresh the panel.
  14. SPI transaction: Select register 0x04, wait, select register 0x12, wait, select register 0x02, wait.

    You don't have to write any actual data to the registers in this section, you just have to "select" the register by writing it over SPI while D/C is low. The CoG doesn't care if you write data, so I just write 0x00 to these registers.

    0x04 turns on the DC/DC converter, 0x12 refreshes the display (this is by far the longest step), and 0x02 turns off the DC/DC converter. Between each write, poll the BUSY pin and wait for it to go high.

That's all you have to do for a normal update! The display will flash to clear the old image and then display the new image you provided.

Fast Refreshes

Technically, there are two kinds of fast refreshes. The first one is fairly slow and is described (inaccurately, of course) in the application note. It's only available with the Arduino library, and has this odd behavior where it will flash the new pixels like a full refresh, but only the new pixels. Each fast refresh in this mode takes about 3 seconds, and it has problems with ghosting or even making pixels visible if the background is dark.

The faster kind of fast refresh is what we're accustomed to with e-ink panels like a Kindle, Boox, PineNote, and all the rest. It simply moves the new pixels with one charge cycle, so you get ghosting, but it's fast. I haven't timed these refreshes yet, but I think they take about 300-500 ms. Pervasive claims 300 ms fast refreshes.

The slow fast refresh is the only kind available with the free Arduino library. The demo UF2, which uses Pervasive's full library suite that you have to pay for, uses fast refresh. As such, I'm not here to nab any features of the full suite other than fast refresh. GUI rendering, shape primitives, text, and everything else you need for a screen like this is still up to you.

I uploaded the demo UF2 to the Pi Pico that came in the kit, hooked it up to my logic analyzer, and captured the command sequence that makes fast refresh work.

Fast Refresh Updates

Real fast refreshes are similar to normal updates, but with more initial configuration before the image data. The list below shows the SPI transaction again but doesn't describe operations shared with the normal refresh cycle.

  1. Hard reset the CoG.
  2. SPI transaction: none.

    You only have to do this once, but it must be done before all fast refreshes.

  3. Soft reset the CoG.
  4. SPI transaction: Write 0x0e to register 0x00

  5. Set the temperature.
  6. SPI transaction: Write the surrounding temperature in Celsius (one byte) to register 0xe5.

  7. Activate the temperature.
  8. SPI transaction: Write 0x02 to register 0xe0.

  9. Set the PSR.
  10. SPI transaction: Write 0xcf, 0x8d to register 0x00.

  11. Set the PSR again.
  12. SPI transaction: Write 0xff, 0x8f to register 0x00.

    You have to send more data to the same register. I assume this is part of PSR, but I don't really know.

  13. Set "Vcom and data interval setting".
  14. SPI transaction: Write 0x07 to register 0x50.

    This is a number described in the application note and the Arduino library, so I'm willing to believe that this is the correct name.

  15. Write to some other registers.
  16. SPI transaction: Write 0x0c to register 0x30, then write 0x11 to register 0x82.

    I don't know what these do.

  17. Write strings of data to some more registers.
  18. SPI transaction:

    These are long strings of data that have to be written to certain registers. Again, I have no idea what they do, but they enable true fast refresh. Each string is 16 bytes long.

  19. Write the old and new images.
  20. SPI transaction: Write the old image to register 0x10 and the new image of anything to register 0x13.

    The CoG uses both images to figure out which pixels need their state changed, through some kind of image operation (subtraction, maybe?).

  21. Refresh the panel.
  22. SPI transaction: Select register 0x04, wait, select register 0x12, wait, select register 0x02, wait.

    Enable the DC/DC converter, wait for BUSY to go high, refresh the panel, wait for BUSY, then optionally turn the DC/DC converter back off, and wait for BUSY. The demo UF2 doesn't turn it off---maybe to reduce wear?

That's it! The display will run a fast refresh cycle and change only the pixels that have been changed between the two images.

Future Usage

I'm working on a simple terminal emulator (not a modesetting driver, though I might do that later) in Rust that will display on the e-ink screen. I'll make a post about this soon, and I'll probably add images to this post at the same time.