MicroPython(ESP32)でモノクロ液晶(LS027B4DH01)の全ライン書き換えを高速化してみる(SPI通信)
2020/08/13 categories:ESP32| tags:ESP32|MicroPython|LS027B4DH01|SPI|
MicroPython(ESP32)でモノクロ液晶(LS027B4DH01)の全ライン書き換えを行ったときに意外と処理時間が掛かったので、SPIでデータを送信するためのプログラム回りで高速化できないか検討してみました。
全ライン送信の処理
送信の処理を2パターン作成して、処理時間を比較してみました。
処理1
最初に試した処理は、書き換えるラインの数×50バイトのbytearray( =bytearray(50)[240] )を渡して、ダミー+ラインアドレス+bytearray(50)のデータ送信を240回(全ライン分)繰り返すという内容です。
def update_multi_line(self, start_line, line_count, byte_array):
self.scs.on() # chip select on
utime.sleep_us(4)
self.spi.write(b'\x01') # send mode
# first line
self.spi.write(b'\x00') # gate line address
self.spi.write(byte_array[0]) # one line data
# second line ~ last line
for y, fifty_byte in enumerate(byte_array):
self.spi.write(b'\x00') # dummy data
self.spi.write(bytearray([y + start_line])) # gate line address
self.spi.write(fifty_byte) # one line data
self.spi.write(b'\x00\x00') # dummy data
utime.sleep_us(4)
self.scs.off()
utime.sleep_us(4)
処理2
次に試した処理は、あらかじめ送信する全データ分の配列、bytearray(12482)を作成して、それをspi.write()に渡して一気に書き込むという内容です。
self.data = bytearray(12482) # (1+1+50) * 240 + 2
def update_all_line(self):
self.scs.on()
utime.sleep_us(4)
self.spi.write(self.data)
utime.sleep_us(4)
self.scs.off()
utime.sleep_us(4)
描画用データの配列
bytearray(12482)の中身は液晶のデータシートに書いている送信内容を参考に、初期値を下記のようにしています。下記表ではArray indexの0~12481がself.dataのインデックスに当たり、そのインデックスの初期値がValue、内容がContentsとなっています。表示を書き換えるためにはData1~Data50の値を書き換えるということになります。
処理1の実行時間
処理1を実行するためのコードは下記の通りです。SPIで送信する部分だけ時間を測定したかったので、timed_function()にはlcd.update_all_line()という処理1の内容だけを渡すようにしています。
def update2(a, b):
lcd.update_multi_line( 0, 120, a)
lcd.update_multi_line(120, 120, b)
def timed_function(f, *args, **kwargs):
def new_func(*args, **kwargs):
t = utime.ticks_us()
result = f(*args, **kwargs)
delta = utime.ticks_diff(utime.ticks_us(), t)
print( 'Time = {:6.3f}ms'.format(delta/1000) )
return result
return new_func
timed_update2 = timed_function(update2)
count = 0
while count < 3:
a = [ bytearray([0x00 for x in range(50)]) for y in range(120) ]
b = [ bytearray([0xFF for x in range(50)]) for y in range(120) ]
timed_update2(a, b)
SPIの送信処理だけで88msecかかるという結果でした。
Time = 87.880ms
Time = 87.699ms
Time = 87.670ms
処理2の実行時間
def update():
lcd.update_all_line()
def timed_function(f, *args, **kwargs):
def new_func(*args, **kwargs):
t = utime.ticks_us()
result = f(*args, **kwargs)
delta = utime.ticks_diff(utime.ticks_us(), t)
print( 'Time = {:6.3f}ms'.format(delta/1000) )
return result
return new_func
timed_update = timed_function(update)
count = 0
while count < 3:
for y in range(240):
if y > 120:
d = 0xFF
else:
d = 0x00
for x in range(50):
lcd.data[2 + y*52 + x] = d
timed_update()
SPIの送信処理だけで11msecかかるという結果で、処理1よりも8倍速くなりました。
Time = 10.675ms
Time = 10.876ms
Time = 10.876ms
MicroPythonで処理時間の計測
MicroPythonで処理時間を計測する場合は、MicroPythoのリファレンスに下記のような関数で計測すると良い、と書いていたのでそれを使用しました。timed_function()に計測したい関数を渡してあげることで、utimeモジュールで関数の処理時間を計測できます。
import utime
def timed_function(f, *args, **kwargs):
def new_func(*args, **kwargs):
t = utime.ticks_us()
result = f(*args, **kwargs)
delta = utime.ticks_diff(utime.ticks_us(), t)
print( 'Time = {:6.3f}ms'.format(delta/1000) )
return result
return new_func
timed_update = timed_function(update)
所感
まだまだ改良の余地はあると思いますが、処理2にすることで処理時間を短縮できました。とりあえず処理2で一気に表示を書き換えるといった使い方をしようかなと思います。
ソースコード
main.py
import utime
from machine import Pin, SPI
from LS027B4DH01 import LS027B4DH01
def main():
print('hello')
lcd = LS027B4DH01()
lcd.spi = SPI(
2, #vspi = id = 2
baudrate=10_000_000, #1MHz
polarity=0, phase=0, bits=8, firstbit=SPI.LSB,
sck=Pin(18), mosi=Pin(23), miso=Pin(19)
)
lcd.scs = Pin(32, Pin.OUT)
lcd.extcomin = Pin(33, Pin.OUT)
lcd.disp = Pin(25, Pin.OUT)
lcd.initialize()
lcd.disp.on()
print('initialized')
def update():
lcd.update_all_line()
def update2(a, b):
lcd.update_multi_line( 0, 120, a)
lcd.update_multi_line(120, 120, b)
def timed_function(f, *args, **kwargs):
def new_func(*args, **kwargs):
t = utime.ticks_us()
result = f(*args, **kwargs)
delta = utime.ticks_diff(utime.ticks_us(), t)
print( 'Time = {:6.3f}ms'.format(delta/1000) )
return result
return new_func
timed_update = timed_function(update)
timed_update2 = timed_function(update2)
count = 0
while count < 3:
for y in range(240):
if y > 120:
d = 0xFF
else:
d = 0x00
for x in range(50):
lcd.data[2 + y*52 + x] = d
print('send 1 time')
timed_update()
a = [ bytearray([0x00 for x in range(50)]) for y in range(120) ]
b = [ bytearray([0xFF for x in range(50)]) for y in range(120) ]
print('send 240 times')
timed_update2(a, b)
count += 1
if __name__ == "__main__":
main()
LS027B4DH01.py
import utime
class LS027B4DH01():
def __init__(self):
self.scs = None
self.extcomin = None
self.disp = None
self.spi = None
self.data = bytearray(12482) # (1+1+50) * 240 + 2
self.data_reset()
def update_one_line(self, line, data_array):
self.scs.on()
# send mode
self.spi.write(b'\x01')
# send gate line address
self.spi.write( bytearray([line]) )
# send data
self.spi.write(data_array)
# dummy data
self.spi.write(b'\x00\x00')
self.scs.off()
def data_reset(self):
self.data[0] = 0x01
self.data[1] = 0x00
for i in range(1, 240):
self.data[i*52+1] = i
def update_all_line(self):
self.scs.on()
utime.sleep_us(4)
self.spi.write(self.data)
utime.sleep_us(4)
self.scs.off()
utime.sleep_us(4)
def update_multi_line(self, start_line, line_count, byte_array):
self.scs.on() # chip select on
utime.sleep_us(4)
self.spi.write(b'\x01') # send mode
# first line
self.spi.write(b'\x00') # gate line address
self.spi.write(byte_array[0]) # one line data
# second line ~ last line
for y, fifty_byte in enumerate(byte_array):
self.spi.write(b'\x00') # dummy data
self.spi.write(bytearray([y + start_line])) # gate line address
self.spi.write(fifty_byte) # one line data
self.spi.write(b'\x00\x00') # dummy data
utime.sleep_us(4)
self.scs.off()
utime.sleep_us(4)
def clear_all(self):
self.disp.off()
self.scs.on()
utime.sleep_us(3)
self.spi.write(b'\x04\x00')
utime.sleep_us(3)
self.scs.off()
utime.sleep_us(5)
self.disp.on()
def initialize(self):
self.disp.off()
utime.sleep_us(500)
self.scs.off()
self.clear_all()
utime.sleep_ms(5)
self.disp.on()