2

I got a 5.8" eink display from Pervasive Displays and cannot get it to work, I feel that only a little bit is missing. After a lot of struggle I managed to run it via their sample C code, but the ported code to Python doesn't work. First the hardware details:

I connected it as described here: https://embeddedcomputing.weebly.com/connecting-the-ext3-to-the-raspberry-pi.html then simplified the example code to a bare minimum here this works fine, it displays the sample image. Its a bit tricky stuff, since it instructs to run the code in a Arduino emulator (RasPiArduino).

Here is my code for the bare minimum code that should work (it should trigger a display refresh), but nothing happens:

import spidev  # SPI library
import time
import RPi.GPIO as GPIO 
from images import BW_0x00Buffer, BW_monoBuffer # just arrays with hex numbers, see repo

class board_EXT3: # EXT3 pin 1 Black -> +3.3V # EXT3 pin 2 Brown -> GPIO 11 SPI0 SCLK panelBusy = 7 # EXT3 pin 3 Red -> GPIO7 pin 26 (SPI chip select 1 / CE1) panelDC = 8 # EXT3 pin 4 Orange -> GPIO8 pin 24 (SPI chip select 0 / CE0) panelReset = 25 # EXT3 pin 5 Yellow -> GPIO25 pin 22 panelCS = 27 # EXT3 pin 9 Grey -> GPIO27 pin 13 # EXT3 pin 6 Green -> GPIO9 SPI0 MISO # EXT3 pin 7 Blue -> GPIO10 SPI0 MOSI flashCS = 22 # EXT3 pin 8 Violet -> GPIO22 pin 15 # EXT3 pin 10 White -> GROUND

def COG_initial(): GPIO.setup(board_EXT3.panelBusy, GPIO.IN) # all pins 0

GPIO.setup(board_EXT3.panelDC, GPIO.OUT)
GPIO.output(board_EXT3.panelDC, GPIO.HIGH)
GPIO.setup(board_EXT3.panelReset, GPIO.OUT)
GPIO.output(board_EXT3.panelReset, GPIO.HIGH)
GPIO.setup(board_EXT3.panelCS, GPIO.OUT)
GPIO.output(board_EXT3.panelCS,GPIO.HIGH)

global spi
spi = spidev.SpiDev()
spi.close()
spi.open(0, 0)  # device 0 (SPI_CE0_N)
spi.max_speed_hz = 122000  # bit rate, was 8000000 should be OK too
spi.lsbfirst = False
spi.mode = 0b00  # polarity CPOL=0 CPHA=0, bits
spi.bits_per_word = 8 # read only on the Pi

def globalUpdate(data1s, data2s): print("reset") _reset() # needed for min print("soft start") _DCDC_softStart_Mid() # needed for min

print("refresh")
_displayRefresh() # needed for min
print("shutdown")
_DCDC_softShutdown_Mid()

def _reset(): time.sleep(0.2) GPIO.output(board_EXT3.panelReset, GPIO.HIGH) # RES = 1 time.sleep(0.02) GPIO.output(board_EXT3.panelReset, GPIO.LOW) time.sleep(0.2) GPIO.output(board_EXT3.panelReset, GPIO.HIGH) time.sleep(0.05) GPIO.output(board_EXT3.panelCS, GPIO.HIGH) time.sleep(0.005)

def _DCDC_softStart_Mid(): # Initialize COG Driver data4 = [0x7d] _sendIndexData(0x05, data4, 1) time.sleep(0.2) data5 = [0x00] _sendIndexData(0x05, data5, 1) time.sleep(0.01) data6 = [0x3f] _sendIndexData(0xc2, data6, 1) time.sleep(0.001) data7 = [0x00] _sendIndexData(0xd8, data7, 1) # MS_SYNC mtp_0x1d data8 = [0x00] _sendIndexData(0xd6, data8, 1) # BVSS mtp_0x1e data9 = [0x10] _sendIndexData(0xa7, data9, 1) time.sleep(0.1) _sendIndexData(0xa7, data5, 1) time.sleep(0.1) print("ss1") data10 = [0x00, 0x01] # OSC _sendIndexData(0x03, data10, 2) # OSC mtp_0x12 _sendIndexData(0x44, data5, 1) data11 = [0x80] _sendIndexData(0x45, data11, 1) _sendIndexData(0xa7, data9, 1) time.sleep(0.1) _sendIndexData(0xa7, data7, 1) time.sleep(0.1) data12 = [0x06] _sendIndexData(0x44, data12, 1) data13 = [0x82] _sendIndexData(0x45, data13, 1) # Temperature 0x82@25C _sendIndexData(0xa7, data9, 1) time.sleep(0.1) _sendIndexData(0xa7, data7, 1) time.sleep(0.1) data14 = [0x25] _sendIndexData(0x60, data14, 1) # TCON mtp_0x0b data15 = [0x00] # STV_DIR _sendIndexData(0x61, data15, 1) # STV_DIR mtp_0x1c data16 = [0x00] _sendIndexData(0x01, data16, 1) # DCTL mtp_0x10 ?? this is not in the flowchart data17 = [0x00] _sendIndexData(0x02, data17, 1) # VCOM mtp_0x11 print("ss2") # DC-DC soft start index51 = [0x50, 0x01, 0x0a, 0x01] _sendIndexData(0x51, index51, 2) index09 = [0x1f, 0x9f, 0x7f, 0xff]

for value in range(1, 5): # 1,2,3,4
    _sendIndexData(0x09, index09, 1)
    index51[1] = value
    _sendIndexData(0x51, index51, 2)
    _sendIndexData(0x09, index09[1:], 1)
    time.sleep(0.002)
for value in range(1, 11):
    _sendIndexData(0x09, index09, 1)
    index51[3] = value
    _sendIndexData(0x51, index51[2:], 2)
    _sendIndexData(0x09, index09[1:], 1)
    time.sleep(0.002)
for value in range(3, 11):
    _sendIndexData(0x09, index09[2:], 1)
    index51[3] = value
    _sendIndexData(0x51, index51[2:], 2)
    _sendIndexData(0x09, index09[3:], 1)
    time.sleep(0.002)
for value in range(9, 1, -1): # 9,8,7,...2
    _sendIndexData(0x09, index09[2:], 1)
    index51[2] = value
    _sendIndexData(0x51, index51[2:], 2)
    _sendIndexData(0x09, index09[3:], 1)
    time.sleep(0.002)
_sendIndexData(0x09, index09[3:], 1)
time.sleep(0.01)

def _displayRefresh(): while GPIO.input(board_EXT3.panelBusy) != GPIO.HIGH: time.sleep(0.1) _sendIndexData(0x15, [0x3c], 1) # Display Refresh time.sleep(0.005)

def _DCDC_softShutdown_Mid(): while GPIO.input(board_EXT3.panelBusy) != GPIO.HIGH: time.sleep(0.1) data19 = [0x7f] _sendIndexData(0x09, data19, 1) data20 = [0x7d] _sendIndexData(0x05, data20, 1) data55 = [0x00] _sendIndexData(0x09, data55, 1) time.sleep(0.2)

while GPIO.input(board_EXT3.panelBusy) != GPIO.HIGH:
    time.sleep(0.1)
GPIO.output(board_EXT3.panelDC, GPIO.LOW)
GPIO.output(board_EXT3.panelCS, GPIO.LOW)
GPIO.output(board_EXT3.panelReset, GPIO.LOW)

GPIO.output(board_EXT3.panelCS, GPIO.HIGH) # CS# = 1

def _sendIndexData(index, data, size): GPIO.output(board_EXT3.panelDC, GPIO.LOW) # DC Low GPIO.output(board_EXT3.panelCS, GPIO.LOW) # (CS == CSB) Low time.sleep(0.05) spi.writebytes([index]) # SPI.transfer(index) time.sleep(0.05) GPIO.output(board_EXT3.panelCS, GPIO.HIGH) # CS High GPIO.output(board_EXT3.panelDC, GPIO.HIGH) # DC High GPIO.output(board_EXT3.panelCS, GPIO.LOW) # CS Low time.sleep(0.05) #for i in range(size): # spi.writebytes([data[i]]) spi.writebytes(data[:size]) time.sleep(0.05) GPIO.output(board_EXT3.panelCS, GPIO.HIGH) # CS High

COG_initial() print("update") globalUpdate(BW_monoBuffer, BW_0x00Buffer) print("power off") COG_powerOff() print("program end")

What could I be doing wrong? I have SPI enabled in raspi-config, tried to run it as root, the cabling is correct (since the C code linked above works fine), triple checked everything for typos. My guess it is something trivial, e.g. RasPiArduino's SPI.transfer does something different than spidev's spi.writebytes. The only thing I see that the display does nothing and that _DCDC_softStart_Mid takes longer to execute than the C++ code.

sydd
  • 83
  • 5

2 Answers2

1

After some debugging with a logic analyzer I could fix the code, it had 2 issues:

  1. _sendIndexData had wrong sleep values, the original code had microseconds.
  2. And more importantly spidev sets the cs line to low on each transfer, Pervasive displays EXT kit needs it on low for transferring the index and on high when transferring the data. This is already in the code, just one needs to disable this functionality in spidev: spi.no_cs = True

So here is the working code:

import spidev  # SPI library
import time
import RPi.GPIO as GPIO 
from images import BW_0x00Buffer, BW_monoBuffer

GPIO.setmode(GPIO.BCM) # use standard rpi pin numbering print('starting in GPIO mode ' + str(GPIO.getmode()))

Raspberry Pi Zero, 2B, 3B, 4B configuration

class board_EXT3: # EXT3 pin 1 Black -> +3.3V # EXT3 pin 2 Brown -> GPIO 11 SPI0 SCLK panelBusy = 7 # EXT3 pin 3 Red -> GPIO7 pin 26 (SPI chip select 1 / CE1) panelDC = 8 # EXT3 pin 4 Orange -> GPIO8 pin 24 (SPI chip select 0 / CE0) panelReset = 25 # EXT3 pin 5 Yellow -> GPIO25 pin 22 # EXT3 pin 6 Green -> SPI MISO # EXT3 pin 7 Blue -> SPI MOSI panelCS = 27 # EXT3 pin 9 Grey -> GPIO27 pin 13 # EXT3 pin 6 Green -> GPIO9 SPI0 MISO # EXT3 pin 7 Blue -> GPIO10 SPI0 MOSI # flashCS = 22 # EXT3 pin 8 Violet -> GPIO22 pin 15 # EXT3 pin 10 White -> GROUND

def COG_initial(): GPIO.setup(board_EXT3.panelBusy, GPIO.IN) # all pins 0

GPIO.setup(board_EXT3.panelDC, GPIO.OUT)
GPIO.output(board_EXT3.panelDC, GPIO.HIGH)
GPIO.setup(board_EXT3.panelReset, GPIO.OUT)
GPIO.output(board_EXT3.panelReset, GPIO.HIGH)
GPIO.setup(board_EXT3.panelCS, GPIO.OUT)
GPIO.output(board_EXT3.panelCS,GPIO.HIGH)

global spi
spi = spidev.SpiDev()
spi.close()
spi.open(0, 0)  # device 0 (SPI_CE0_N)
spi.max_speed_hz = 122000  # bit rate, was 8000000 should be OK too
spi.lsbfirst = False
spi.mode = 0b00  # polarity CPOL=0 CPHA=0, bits
spi.bits_per_word = 8 # read only on the Pi
spi.no_cs = True

def globalUpdate(data1s, data2s): print("reset") _reset() # needed for min

dtcl = [0x08] # 0=IST7232, 8=IST7236
_sendIndexData(0x01, dtcl, 1) #DCTL 0x10 of MTP

# Send image data
print("send image data")
data1 = [0x00, 0x1f, 0x50, 0x00, 0x1f, 0x03] # DUW
_sendIndexData(0x13, data1, 6) # DUW
data2 = [0x00, 0x1f, 0x00, 0xc9] # DRFW
_sendIndexData(0x90, data2, 4) # DRFW
data3 = [0x1f, 0x50, 0x14] # RAM_RW
_sendIndexData(0x12, data3, 3) # RAM_RW

# send first frame
print("send 1st frame")
_sendIndexData(0x10, data1s, 23040) # First frame

data33 = [0x1f, 0x50, 0x14] # RAM_RW
_sendIndexData(0x12, data33, 3) # RAM_RW

# send second frame
print("send 2nd frame")
_sendIndexData(0x11, data2s, 23040) # Second frame

print("soft start")
_DCDC_softStart_Mid() # needed for min

print("refresh")
_displayRefresh() # needed for min
print("shutdown")
_DCDC_softShutdown_Mid() # if left out it'll just darken

def _reset(): time.sleep(0.2) GPIO.output(board_EXT3.panelReset, GPIO.HIGH) # RES = 1 time.sleep(0.02) GPIO.output(board_EXT3.panelReset, GPIO.LOW) time.sleep(0.2) GPIO.output(board_EXT3.panelReset, GPIO.HIGH) time.sleep(0.05) GPIO.output(board_EXT3.panelCS, GPIO.HIGH) # CS# = 1 -- not in the manual time.sleep(0.005)

DC-DC soft-start command

- Implemented after image data are uploaded to CoG

- Specific to mid-sized EPDs only

def _DCDC_softStart_Mid(): # Initialize COG Driver data4 = [0x7d] _sendIndexData(0x05, data4, 1) time.sleep(0.2) data5 = [0x00] _sendIndexData(0x05, data5, 1) time.sleep(0.01) data6 = [0x3f] _sendIndexData(0xc2, data6, 1) time.sleep(0.001) data7 = [0x00] _sendIndexData(0xd8, data7, 1) # MS_SYNC mtp_0x1d data8 = [0x00] _sendIndexData(0xd6, data8, 1) # BVSS mtp_0x1e data9 = [0x10] _sendIndexData(0xa7, data9, 1) time.sleep(0.1) _sendIndexData(0xa7, data5, 1) time.sleep(0.1) print("ss1") data10 = [0x00, 0x01] # OSC _sendIndexData(0x03, data10, 2) # OSC mtp_0x12 _sendIndexData(0x44, data5, 1) data11 = [0x80] _sendIndexData(0x45, data11, 1) _sendIndexData(0xa7, data9, 1) time.sleep(0.1) _sendIndexData(0xa7, data7, 1) time.sleep(0.1) data12 = [0x06] _sendIndexData(0x44, data12, 1) data13 = [0x82] _sendIndexData(0x45, data13, 1) # Temperature 0x82@25C _sendIndexData(0xa7, data9, 1) time.sleep(0.1) _sendIndexData(0xa7, data7, 1) time.sleep(0.1) data14 = [0x25] _sendIndexData(0x60, data14, 1) # TCON mtp_0x0b data15 = [0x00] # STV_DIR _sendIndexData(0x61, data15, 1) # STV_DIR mtp_0x1c data16 = [0x00] _sendIndexData(0x01, data16, 1) # DCTL mtp_0x10 ?? this is not in the flowchart data17 = [0x00] _sendIndexData(0x02, data17, 1) # VCOM mtp_0x11 print("ss2") # DC-DC soft start index51 = [0x50, 0x01, 0x0a, 0x01] _sendIndexData(0x51, index51, 2) index09 = [0x1f, 0x9f, 0x7f, 0xff]

for value in range(1, 5): # 1,2,3,4
    _sendIndexData(0x09, index09, 1)
    index51[1] = value
    _sendIndexData(0x51, index51, 2)
    _sendIndexData(0x09, index09[1:], 1)
    time.sleep(0.002)
for value in range(1, 11):
    _sendIndexData(0x09, index09, 1)
    index51[3] = value
    _sendIndexData(0x51, index51[2:], 2)
    _sendIndexData(0x09, index09[1:], 1)
    time.sleep(0.002)
for value in range(3, 11):
    _sendIndexData(0x09, index09[2:], 1)
    index51[3] = value
    _sendIndexData(0x51, index51[2:], 2)
    _sendIndexData(0x09, index09[3:], 1)
    time.sleep(0.002)
for value in range(9, 1, -1): # 9,8,7,...2
    _sendIndexData(0x09, index09[2:], 1)
    index51[2] = value
    _sendIndexData(0x51, index51[2:], 2)
    _sendIndexData(0x09, index09[3:], 1)
    time.sleep(0.002)
_sendIndexData(0x09, index09[3:], 1)
time.sleep(0.01)

EPD Screen refresh function

INPUT: None but requires global variables on SPI pinout and config register data

def _displayRefresh(): while GPIO.input(board_EXT3.panelBusy) != GPIO.HIGH: time.sleep(0.1) _sendIndexData(0x15, [0x3c], 1) # Display Refresh time.sleep(0.005)

DC-DC soft-shutdown command

- Implemented after image data are uploaded to CoG

- Specific to mid-sized EPDs only

def _DCDC_softShutdown_Mid(): while GPIO.input(board_EXT3.panelBusy) != GPIO.HIGH: time.sleep(0.1) data19 = [0x7f] _sendIndexData(0x09, data19, 1) data20 = [0x7d] _sendIndexData(0x05, data20, 1) data55 = [0x00] _sendIndexData(0x09, data55, 1) time.sleep(0.2)

while GPIO.input(board_EXT3.panelBusy) != GPIO.HIGH:
    time.sleep(0.1)
GPIO.output(board_EXT3.panelDC, GPIO.LOW)
GPIO.output(board_EXT3.panelCS, GPIO.LOW)
GPIO.output(board_EXT3.panelReset, GPIO.LOW)

GPIO.output(board_EXT3.panelCS, GPIO.HIGH) # CS# = 1

CoG shutdown function

- Shuts down the CoG and DC/DC circuit after all update functions

INPUT:

- none but requires global variables on SPI pinout and config register data

def COG_powerOff(): register_turnOff = [0x7f, 0x7d, 0x00] _sendIndexData(0x09, register_turnOff, 3) time.sleep(0.2) while GPIO.input(board_EXT3.panelBusy) != GPIO.HIGH: time.sleep(0.05) GPIO.output(board_EXT3.panelDC, GPIO.LOW) GPIO.output(board_EXT3.panelCS, GPIO.LOW) GPIO.output(board_EXT3.panelBusy, GPIO.LOW) time.sleep(0.15) GPIO.output(board_EXT3.panelReset, GPIO.LOW)

def _sendIndexData(index, data, size): GPIO.output(board_EXT3.panelDC, GPIO.LOW) # DC Low GPIO.output(board_EXT3.panelCS, GPIO.LOW) # (CS == CSB) Low time.sleep(0.00005) spi.writebytes2([index]) time.sleep(0.00005) GPIO.output(board_EXT3.panelCS, GPIO.HIGH) # CS High GPIO.output(board_EXT3.panelDC, GPIO.HIGH) # DC High GPIO.output(board_EXT3.panelCS, GPIO.LOW) # CS Low time.sleep(0.00005) spi.writebytes2(data[:size]) time.sleep(0.00005) GPIO.output(board_EXT3.panelCS, GPIO.HIGH) # CS High

COG_initial() print("globalUpdate") globalUpdate(BW_monoBuffer, BW_0x00Buffer) print("power off") COG_powerOff() print("program end")

not that its still not perfect, the power off function gets stuck waiting for busy to be low.

sydd
  • 83
  • 5
0

I think the most effective way to debug this is to hook up a logic analyzer (a Pi running piscope or an actual analyzer/scope) on the SPI lines and look at the signals being transmitted. Remember to reduce the SPI clock frequency to a lower value (e.g. 100 kHz) if your logic analyzer fails to capture 8MHz signals properly.

The problem could be anything from the signal timing to unexpected extra clock transitions or wrong data being transmitted. Of course, you could always find the reason for wrong data by carefully analyzing your own code, the Python library and the SPI kernel driver, but it will be so much faster to simply look at what you transmit and check if those are the same bytes that Arduino is sending.

Dmitry Grigoryev
  • 28,277
  • 6
  • 54
  • 147