mirror of
https://github.com/antirez/uc8151_micropython.git
synced 2025-12-06 06:12:49 +08:00
3x faster greyscale images using all 4 channels.
This commit is contained in:
94
uc8151.py
94
uc8151.py
@@ -560,21 +560,34 @@ class UC8151:
|
|||||||
# Before calling this function, the framebuffer must be already with
|
# Before calling this function, the framebuffer must be already with
|
||||||
# all the bits set to 0. Just call fb.fill(0) to do this quickly.
|
# all the bits set to 0. Just call fb.fill(0) to do this quickly.
|
||||||
@micropython.viper
|
@micropython.viper
|
||||||
def set_pixels_for_greyscale(self, grey:ptr8, fb:ptr8, width:int, height:int) -> int:
|
def set_pixels_for_greyscale(self, grey:ptr8, fb1:ptr8, fb2:ptr8, width:int, height:int) -> int:
|
||||||
count = int(width*height)
|
count = int(width*height)
|
||||||
anypixel = int(0)
|
anypixel = int(0)
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
if grey[i] > 0:
|
# Pixel that reached level "1" are the only ones at the
|
||||||
# Pixel that reached level "1" are the only ones at the
|
# current grey level we want to set.
|
||||||
# current grey level we want to set.
|
byte = i >> 3
|
||||||
if grey[i] == 1:
|
bit = 1 << (7-(i&7))
|
||||||
byte = i >> 3
|
|
||||||
bit = 1 << (7-(i&7))
|
if grey[i] == 1: # WW condition
|
||||||
fb[byte] |= bit
|
anypixel = 1
|
||||||
anypixel = 1
|
pass # Both bits at 0
|
||||||
# We decrement all the pixels not yet at 0, so successive
|
elif grey[i] == 2: # BB condition
|
||||||
# level of greys will appear at value "1".
|
anypixel = 1
|
||||||
grey[i] -= 1
|
fb1[byte] |= bit
|
||||||
|
fb2[byte] |= bit
|
||||||
|
elif grey[i] == 3: # WB condition
|
||||||
|
anypixel = 1
|
||||||
|
fb1[byte] |= bit
|
||||||
|
else: # BW condition, pixels not touched.
|
||||||
|
fb2[byte] |= bit
|
||||||
|
|
||||||
|
# We decrement all the pixels not yet at 0, so successive
|
||||||
|
# level of greys will appear at values 1, 2, 3.
|
||||||
|
if grey[i] > 3:
|
||||||
|
grey[i] -= 3
|
||||||
|
else:
|
||||||
|
grey[i] = 0
|
||||||
return anypixel
|
return anypixel
|
||||||
|
|
||||||
def load_greyscale_image(self,filename):
|
def load_greyscale_image(self,filename):
|
||||||
@@ -582,9 +595,9 @@ class UC8151:
|
|||||||
# 1. How many frames it takes for a pixel to reach full black?
|
# 1. How many frames it takes for a pixel to reach full black?
|
||||||
# 2. How many greys we want to generate?
|
# 2. How many greys we want to generate?
|
||||||
|
|
||||||
greyscale = 32 # Can't be more than 32. Try 32, 16, 8, 4.
|
greyscale = 32 # Can't be more than 32. Try 32, 16, 8, 4.
|
||||||
frames_to_black = 32
|
frames_to_black = 32
|
||||||
|
|
||||||
# Read image data.
|
# Read image data.
|
||||||
f = open(filename,"rb")
|
f = open(filename,"rb")
|
||||||
f.read(4)
|
f.read(4)
|
||||||
@@ -599,39 +612,69 @@ class UC8151:
|
|||||||
# Nothing to do for white pixels or already black pixels.
|
# Nothing to do for white pixels or already black pixels.
|
||||||
# Set an empty LUT.
|
# Set an empty LUT.
|
||||||
LUT = bytearray(42)
|
LUT = bytearray(42)
|
||||||
self.write(CMD_LUT_BW,LUT)
|
VCOM = bytearray(44)
|
||||||
self.write(CMD_LUT_WW,LUT)
|
|
||||||
self.write(CMD_LUT_BB,LUT)
|
|
||||||
|
|
||||||
# Now for each level of grey in the image, create a bitmap composed
|
# Now for each level of grey in the image, create a bitmap composed
|
||||||
# only of pixels of that level of grey, and create an ad-hoc LUT
|
# only of pixels of that level of grey, and create an ad-hoc LUT
|
||||||
# that polarizes pixels towards black for an amount of time (frames)
|
# that polarizes pixels towards black for an amount of time (frames)
|
||||||
# proportional to the grey level.
|
# proportional to the grey level.
|
||||||
for g in range(greyscale):
|
for g in range(0,greyscale,3):
|
||||||
self.fb.fill(0)
|
self.fb.fill(0)
|
||||||
|
fb2 = bytearray(self.width*self.height//8)
|
||||||
# Resort to a faster method in Viper to set the pixels for the
|
# Resort to a faster method in Viper to set the pixels for the
|
||||||
# current greyscale level.
|
# current greyscale level.
|
||||||
anypixel = self.set_pixels_for_greyscale(imgdata,self.raw_fb,self.width,self.height)
|
anypixel = self.set_pixels_for_greyscale(imgdata,self.raw_fb,fb2,self.width,self.height)
|
||||||
if anypixel:
|
if anypixel:
|
||||||
|
# Transfer the "old" image, so that for difference
|
||||||
|
# with the new we transfer via .update() we create
|
||||||
|
# the four set of conditions (WW, BB, WB, BW) based
|
||||||
|
# on the difference between the bits in the two
|
||||||
|
# images.
|
||||||
|
#
|
||||||
|
# This is a "fake" update that should not do nothing
|
||||||
|
# if not to refresh the display status of what was
|
||||||
|
# previously on the screen, so we set a repeat of 0.
|
||||||
|
LUT[5] = 1 # Repeat 1 for all
|
||||||
|
LUT[0] = 0x55 # Go black
|
||||||
|
LUT[1] = 0 # Zero frames, fake update.
|
||||||
|
VCOM[1] = LUT[1]
|
||||||
|
|
||||||
|
self.write(CMD_LUT_VCOM,VCOM)
|
||||||
|
self.write(CMD_LUT_WW,LUT)
|
||||||
|
self.write(CMD_LUT_BB,LUT)
|
||||||
|
self.write(CMD_LUT_WB,LUT)
|
||||||
|
self.write(CMD_LUT_BW,LUT)
|
||||||
|
|
||||||
|
self.write(CMD_PON) # Power on
|
||||||
|
self.write(CMD_PTOU) # Partial mode off
|
||||||
|
self.write(CMD_DTM2,fb2) # Start data transfer
|
||||||
|
self.write(CMD_DSP) # End of data
|
||||||
|
self.write(CMD_DRF) # Start refresh cycle.
|
||||||
|
self.wait_and_switch_off()
|
||||||
|
|
||||||
# We set the framebuffer with just the pixels of the level
|
# We set the framebuffer with just the pixels of the level
|
||||||
# of grey we are handling in this cycle, so now we apply
|
# of grey we are handling in this cycle, so now we apply
|
||||||
# the voltage for a time proportional to this level (see
|
# the voltage for a time proportional to this level (see
|
||||||
# the setting of LUT[1], that is the number of frames).
|
# the setting of LUT[1], that is the number of frames).
|
||||||
LUT[5] = 1 # Repeat 1 for all
|
|
||||||
LUT[0] = 0x55 # Go black
|
|
||||||
LUT[1] = int(frames_to_black/greyscale*(g+1))
|
LUT[1] = int(frames_to_black/greyscale*(g+1))
|
||||||
|
self.write(CMD_LUT_WW,LUT)
|
||||||
|
LUT[1] = int(frames_to_black/greyscale*(g+2))
|
||||||
|
self.write(CMD_LUT_BB,LUT)
|
||||||
|
LUT[1] = int(frames_to_black/greyscale*(g+3))
|
||||||
self.write(CMD_LUT_WB,LUT)
|
self.write(CMD_LUT_WB,LUT)
|
||||||
|
LUT[1] = 0 # These pixels will be unaffected, none of them
|
||||||
|
# is of the three colors handled in this cycle.
|
||||||
|
LUT[5] = 0
|
||||||
|
self.write(CMD_LUT_BW,LUT)
|
||||||
|
|
||||||
# Minimal VCOM LUT to avoid any unneeded wait.
|
# Minimal VCOM LUT to avoid any unneeded wait.
|
||||||
VCOM = bytearray(44)
|
|
||||||
VCOM[0] = 0 # Already zero, just to make it obvious.
|
VCOM[0] = 0 # Already zero, just to make it obvious.
|
||||||
VCOM[1] = LUT[1]
|
VCOM[1] = int(frames_to_black/greyscale*(g+3))
|
||||||
VCOM[5] = 1
|
VCOM[5] = 1
|
||||||
self.write(CMD_LUT_VCOM,VCOM)
|
self.write(CMD_LUT_VCOM,VCOM)
|
||||||
|
|
||||||
print("start update grey level",g)
|
# Finally update.
|
||||||
self.update(blocking=True)
|
self.update(blocking=True)
|
||||||
print("end update")
|
|
||||||
|
|
||||||
# Restore a normal LUT based on configured speed.
|
# Restore a normal LUT based on configured speed.
|
||||||
self.set_waveform_lut()
|
self.set_waveform_lut()
|
||||||
@@ -643,6 +686,7 @@ if __name__ == "__main__":
|
|||||||
spi = SPI(0, baudrate=12000000, phase=0, polarity=0, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
|
spi = SPI(0, baudrate=12000000, phase=0, polarity=0, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
|
||||||
eink = UC8151(spi,cs=17,dc=20,rst=21,busy=26,speed=2,no_flickering=False)
|
eink = UC8151(spi,cs=17,dc=20,rst=21,busy=26,speed=2,no_flickering=False)
|
||||||
|
|
||||||
|
eink.load_greyscale_image("dama.grey")
|
||||||
eink.load_greyscale_image("hopper.grey")
|
eink.load_greyscale_image("hopper.grey")
|
||||||
STOP
|
STOP
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user