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.
-
1I can see no relationship between the C code and the Python code. If you want help please present working C code in the question so we can compare it line by line with the Python code.joan– joan2022年03月14日 22:50:57 +00:00Commented Mar 14, 2022 at 22:50
-
@joan both are in the repo I linked above. The C code is here: github.com/matyasf/pi-radio/blob/main/workingCode/… (and in the other files in the same folder), the Python code is abovesydd– sydd2022年03月14日 23:21:00 +00:00Commented Mar 14, 2022 at 23:21
2 Answers 2
After some debugging with a logic analyzer I could fix the code, it had 2 issues:
_sendIndexData
had wrong sleep values, the original code had microseconds.- And more importantly
spidev
sets thecs
line tolow
on each transfer, Pervasive displays EXT kit needs it onlow
for transferring the index and onhigh
when transferring the data. This is already in the code, just one needs to disable this functionality inspidev
: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
.
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.
-
1Yep, thats the plan B, I've already ordered a cheap logic analyzer. As I wrote above the connections are all right, the C code executes fine.sydd– sydd2022年03月18日 11:13:14 +00:00Commented Mar 18, 2022 at 11:13