MicroPython(ESP32)でモノクロ液晶(LS027B4DH01)の全ライン書き換えを高速化してみる(SPI通信)

Share on:

MicroPython(ESP32)でモノクロ液晶(LS027B4DH01)の全ライン書き換えを行ったときに意外と処理時間が掛かったので、SPIでデータを送信するためのプログラム回りで高速化できないか検討してみました。

全ライン送信の処理

送信の処理を2パターン作成して、処理時間を比較してみました。

処理1

最初に試した処理は、書き換えるラインの数×50バイトのbytearray( =bytearray(50)[240] )を渡して、ダミー+ラインアドレス+bytearray(50)のデータ送信を240回(全ライン分)繰り返すという内容です。

 1def update_multi_line(self, start_line, line_count, byte_array):
 2    
 3    self.scs.on() # chip select on
 4    utime.sleep_us(4)
 5    self.spi.write(b'\x01') # send mode
 6
 7    # first line
 8    self.spi.write(b'\x00') # gate line address
 9    self.spi.write(byte_array[0]) # one line data
10    
11    # second line ~ last line
12    for y, fifty_byte in enumerate(byte_array):
13        self.spi.write(b'\x00') # dummy data
14        self.spi.write(bytearray([y + start_line])) # gate line address
15        self.spi.write(fifty_byte) # one line data
16
17    self.spi.write(b'\x00\x00') # dummy data
18    utime.sleep_us(4)
19    self.scs.off()
20    utime.sleep_us(4)

処理2

次に試した処理は、あらかじめ送信する全データ分の配列、bytearray(12482)を作成して、それをspi.write()に渡して一気に書き込むという内容です。

1self.data = bytearray(12482) # (1+1+50) * 240 + 2
2
3def update_all_line(self):
4    self.scs.on()
5    utime.sleep_us(4)
6    self.spi.write(self.data)
7    utime.sleep_us(4)
8    self.scs.off()
9    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の内容だけを渡すようにしています。

 1def update2(a, b):
 2    lcd.update_multi_line(  0, 120, a)
 3    lcd.update_multi_line(120, 120, b)
 4
 5def timed_function(f, *args, **kwargs):
 6    def new_func(*args, **kwargs):
 7        t = utime.ticks_us()
 8        result = f(*args, **kwargs)
 9        delta = utime.ticks_diff(utime.ticks_us(), t)
10        print( 'Time = {:6.3f}ms'.format(delta/1000) )
11        return result
12    return new_func
13
14timed_update2 = timed_function(update2)
15
16count = 0
17while count < 3:
18    a = [ bytearray([0x00 for x in range(50)]) for y in range(120) ]
19    b = [ bytearray([0xFF for x in range(50)]) for y in range(120) ]
20
21    timed_update2(a, b)

SPIの送信処理だけで88msecかかるという結果でした。

1Time = 87.880ms
2Time = 87.699ms
3Time = 87.670ms

処理2の実行時間

 1def update():
 2    lcd.update_all_line()
 3
 4def timed_function(f, *args, **kwargs):
 5    def new_func(*args, **kwargs):
 6        t = utime.ticks_us()
 7        result = f(*args, **kwargs)
 8        delta = utime.ticks_diff(utime.ticks_us(), t)
 9        print( 'Time = {:6.3f}ms'.format(delta/1000) )
10        return result
11    return new_func
12
13timed_update  = timed_function(update)
14
15count = 0
16while count < 3:
17
18    for y in range(240):
19        if y > 120:
20            d = 0xFF
21        else:
22            d = 0x00
23        for x in range(50):
24            lcd.data[2 + y*52 + x] = d
25    
26    timed_update()

SPIの送信処理だけで11msecかかるという結果で、処理1よりも8倍速くなりました。

1Time = 10.675ms
2Time = 10.876ms
3Time = 10.876ms

MicroPythonで処理時間の計測

MicroPythonで処理時間を計測する場合は、MicroPythoのリファレンスに下記のような関数で計測すると良い、と書いていたのでそれを使用しました。timed_function()に計測したい関数を渡してあげることで、utimeモジュールで関数の処理時間を計測できます。

 1import utime
 2
 3def timed_function(f, *args, **kwargs):
 4    def new_func(*args, **kwargs):
 5        t = utime.ticks_us()
 6        result = f(*args, **kwargs)
 7        delta = utime.ticks_diff(utime.ticks_us(), t)
 8        print( 'Time = {:6.3f}ms'.format(delta/1000) )
 9        return result
10    return new_func
11
12timed_update  = timed_function(update)

所感

まだまだ改良の余地はあると思いますが、処理2にすることで処理時間を短縮できました。とりあえず処理2で一気に表示を書き換えるといった使い方をしようかなと思います。

ソースコード

main.py

 1import utime
 2from machine import Pin, SPI
 3from LS027B4DH01 import LS027B4DH01
 4
 5def main():
 6    
 7    print('hello')
 8
 9    lcd = LS027B4DH01()
10    lcd.spi = SPI(
11        2, #vspi = id = 2
12        baudrate=10_000_000, #1MHz
13        polarity=0, phase=0, bits=8, firstbit=SPI.LSB,
14        sck=Pin(18), mosi=Pin(23), miso=Pin(19)
15    )
16    lcd.scs      = Pin(32, Pin.OUT)
17    lcd.extcomin = Pin(33, Pin.OUT)
18    lcd.disp     = Pin(25, Pin.OUT)
19    lcd.initialize()
20    lcd.disp.on()
21
22    print('initialized')
23
24    def update():
25        lcd.update_all_line()
26
27    def update2(a, b):
28        lcd.update_multi_line(  0, 120, a)
29        lcd.update_multi_line(120, 120, b)
30
31    def timed_function(f, *args, **kwargs):
32        def new_func(*args, **kwargs):
33            t = utime.ticks_us()
34            result = f(*args, **kwargs)
35            delta = utime.ticks_diff(utime.ticks_us(), t)
36            print( 'Time = {:6.3f}ms'.format(delta/1000) )
37            return result
38        return new_func
39    
40    timed_update  = timed_function(update)
41    timed_update2 = timed_function(update2)
42
43    count = 0
44    while count < 3:
45
46        for y in range(240):
47            if y > 120:
48                d = 0xFF
49            else:
50                d = 0x00
51            for x in range(50):
52                lcd.data[2 + y*52 + x] = d
53        
54        print('send 1 time')
55        timed_update()
56        
57        a = [ bytearray([0x00 for x in range(50)]) for y in range(120) ]
58        b = [ bytearray([0xFF for x in range(50)]) for y in range(120) ]
59
60        print('send 240 times')
61        timed_update2(a, b)
62
63        count += 1
64
65if __name__ == "__main__":
66    main()

LS027B4DH01.py

 1import utime
 2
 3class LS027B4DH01():
 4    def __init__(self):
 5        self.scs = None
 6        self.extcomin = None
 7        self.disp = None
 8        self.spi = None
 9        self.data = bytearray(12482) # (1+1+50) * 240 + 2
10
11        self.data_reset()
12
13    def update_one_line(self, line, data_array):
14        self.scs.on()
15        # send mode
16        self.spi.write(b'\x01')
17        # send gate line address
18        self.spi.write( bytearray([line]) )
19        # send data
20        self.spi.write(data_array)
21        # dummy data
22        self.spi.write(b'\x00\x00')
23        self.scs.off()
24
25    def data_reset(self):
26        self.data[0] = 0x01
27        self.data[1] = 0x00
28        for i in range(1, 240):
29            self.data[i*52+1] = i
30        
31    def update_all_line(self):
32        self.scs.on()
33        utime.sleep_us(4)
34        self.spi.write(self.data)
35        utime.sleep_us(4)
36        self.scs.off()
37        utime.sleep_us(4)
38
39    def update_multi_line(self, start_line, line_count, byte_array):
40        
41        self.scs.on() # chip select on
42        utime.sleep_us(4)
43        self.spi.write(b'\x01') # send mode
44
45        # first line
46        self.spi.write(b'\x00') # gate line address
47        self.spi.write(byte_array[0]) # one line data
48        
49        # second line ~ last line
50        for y, fifty_byte in enumerate(byte_array):
51            self.spi.write(b'\x00') # dummy data
52            self.spi.write(bytearray([y + start_line])) # gate line address
53            self.spi.write(fifty_byte) # one line data
54
55        self.spi.write(b'\x00\x00') # dummy data
56        utime.sleep_us(4)
57        self.scs.off()
58        utime.sleep_us(4)
59
60    def clear_all(self):
61        self.disp.off()
62        self.scs.on()
63        utime.sleep_us(3)
64        self.spi.write(b'\x04\x00')
65        utime.sleep_us(3)
66        self.scs.off()
67        utime.sleep_us(5)
68        self.disp.on()
69
70    def initialize(self):
71        self.disp.off()
72        utime.sleep_us(500)
73        self.scs.off()
74        self.clear_all()
75        utime.sleep_ms(5)
76        self.disp.on()

関連記事