IBM Developer Day 2018์—์„œ ๋ฐฐํฌํ•œ IoT ๋ฑƒ์ง€๋Š” ESP32 Devkit์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ ๋‹น์‹œ ์„ค์น˜๋œ ํ–‰์‚ฌ์šฉ ์†Œํ”„ํŠธ์›จ์–ด ๋Œ€์‹  ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ์ด ํฌํŒ…๋œ ํŽŒ์›จ์–ด๋ฅผ ์„ค์น˜ํ•˜๋ฉด IoT ๋ฑƒ์ง€๋ฅผ ๊ฐœ๋ฐœ ๋ณด๋“œ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์„ ์˜ SPI ๋ชจ๋“ˆ์„ ์ด์šฉํ•˜์—ฌ SPI ํ†ต์‹  ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋ฉฐ, IoT ๋ฑƒ์ง€์— ํƒ‘์žฌ๋œ LCD์— SPI๋กœ ๋ช…๋ น์„ ์ „๋‹ฌํ•˜์—ฌ ๋‹จ์ˆœ ํ™”๋ฉด ์ œ์–ด์— ๋Œ€ํ•ด ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.

ํ•™์Šต ๋ชฉํ‘œ

์ด ํŠœํ† ๋ฆฌ์–ผ์„ ๋งˆ์น˜๊ฒŒ ๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • REPL์„ ์ด์šฉํ•˜์—ฌ IoT Badge์— ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ ์‹คํ–‰
  • ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ SPI ํ†ต์‹  ๋ฐ LCD ์ œ์–ด

์‚ฌ์ „ ์ค€๋น„ ์‚ฌํ•ญ

  1. Developer Day 2018 IoT Badge ํŽŒ์›จ์–ด ์„ค์น˜ํ•˜๊ธฐ
  2. Developer Day 2018 IoT Badge์— ๋‚˜๋งŒ์˜ ํŒŒ์ด์ฌ ์ฝ”๋“œ ์‹คํ–‰ํ•˜๊ธฐ
  3. IBM Developer Day 2018 IoT Badge
  4. ๋ฐ์ดํ„ฐ ํ†ต์‹ ์šฉ USB 2.0 Micro B Type ์ผ€์ด๋ธ” (๋งˆ์ดํฌ๋กœ 5ํ•€)

์†Œ์š” ์‹œ๊ฐ„

์ด ํŠœํ† ๋ฆฌ์–ผ์„ ์™„๋ฃŒํ•˜๊ธฐ๊นŒ์ง€ ๋Œ€๋žต 30๋ถ„ ์ •๋„๊ฐ€ ์†Œ์š”๋ฉ๋‹ˆ๋‹ค.

๋‹จ๊ณ„

ํŽŒ์›จ์–ด ๋ฒ„์ ผ ํ™•์ธ

IoT Badge๋Š” ๋ฆฌ์…‹ ํ›„ ๋ฑƒ์ง€๊ฐ€ ์‹œ์ž‘ํ•˜๋ฉด์„œ ์ž๋™์œผ๋กœ uGFX ๋ชจ๋“ˆ์„ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ VSPI ๋ผ์ธ์„ ์ ์œ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, SPI๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ugfx.deinit()๋ฅผ ์‹คํ–‰ํ•˜์—ฌ uGFX ๋ชจ๋“ˆ์„ ํ•ด์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, release-20190330 V3.0 ๋ฒ„์ ผ๊นŒ์ง€๋Š” ugfx.deinit() ๋ฅผ ์‹คํ–‰ํ•ด๋„ SPI bus๋ฅผ ๊ณ„์† ์ ์œ ํ•˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ๊ธฐ์— ๋งˆ์ดํฌ๋กœํŒŒ์ด์ฌ ํŽŒ์›จ์–ด ๋ฒ„์ ผ release-20190411 ์ดํ›„ ๋ฒ„์ ผ์„ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

IoT ๋ฑƒ์ง€์— ์„ค์น˜๋œ Firmware ์ •๋ณด๋Š” ๋ฑƒ์ง€ ๋ถ€ํŒ… ํ›„ REPL ํ™˜๊ฒฝ์—์„œ uos.uname() ๋ช…๋ น์œผ๋กœ ํ˜„์žฌ ์„ค์น˜๋œ firmware์˜ build ์ •๋ณด๋„ ๊ฐ™์ด ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

>>> uos.uname()
(sysname='esp32', nodename='esp32', release='1.9.4', version='v1.9.4-690-gc9da0f0c5-dirty on 2019-04-11', machine='ESP32 module with ESP32')
>>> 

ํŽŒ์›จ์–ด ์—…๋ฐ์ดํŠธ๊ฐ€ ์™„๋ฃŒ ๋˜์—ˆ๋‹ค๋ฉด ์•„๋ž˜ SPI ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ๋กœ ์ด๋™ํ•˜์—ฌ ๊ณ„์† ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

SPI ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ

๋งˆ์ดํฌ๋กœํŒŒ์ด์ฌ์—์„œ ์ œ๊ณตํ•˜๋Š” SPI ๋ชจ๋“ˆ์€ machine.SPI์ž…๋‹ˆ๋‹ค. ์•ž์„œ ์ž ๊น ์ด์•ผ๊ธฐํ•œ๋ฐ”์™€ ๊ฐ™์ด uGFX์—์„œ ๋จผ์ € SPI HOST๋ฅผ ์„ ์ ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ด๋ฅผ ํ•ด์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. REPL์—์„œ ugfx.deinit() ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

>>> ugfx.deinit()
I (577623) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
I (577623) gpio: GPIO[23]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
I (577633) gpio: GPIO[19]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
I (577643) gpio: GPIO[18]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
>>> 

๊ทธ๋ฆฌ๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋กœ machine.SPI ๋ชจ๋“ˆ์„ ์ด์šฉํ•ด SPI ๋ชจ๋“ˆ์„ ์ดˆ๊ธฐํ™” ํ•ฉ๋‹ˆ๋‹ค.

from machine import SPI, Pin
import time

TFT_MOSI = Pin(23) # SPI MOSI
TFT_CLK  = Pin(18) # SPI Clock
TFT_MISO = Pin(19) # SPI MISO  (optional)
vspi = SPI(2, baudrate=80000000, sck=TFT_CLK, miso=TFT_MISO, mosi=TFT_MOSI)

์ฐธ๊ณ ๋กœ, ugfx.deinit()์„ ํ•˜์ง€ ์•Š๊ณ  ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด HOST๊ฐ€ ์‚ฌ์šฉ ์ค‘ ์ด๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

>>> vspi = SPI(2, baudrate=80000000, sck=TFT_CLK, miso=TFT_MISO, mosi=TFT_MOSI)
E (5833) spi_master: spi_bus_initialize(146): host already in use
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: SPI device already in use
>>>

SPI๋กœ LCD ๋“œ๋ผ์ด๋ฒ„ ๊ตฌ์„ฑํ•˜๊ธฐ

Iot Badge์— ์—ฐ๊ฒฐ๋œ LCD๋Š” Adafruit ILI9341๋ผ๋Š” ๊ตฌ๋™์นฉ์„ ์‚ฌ์šฉํ•˜๋ฉฐ SPI๋กœ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. IoT Badge์™€ LCD๊ฐ€ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๊ฒฐ์„ ๋œ ์ •๋ณด๋Š” iot badge ํšŒ๋กœ๋„์— ๋‚˜์™€ ์žˆ์œผ๋ฉฐ ํ‘œ๋กœ ์ •๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ESP32 LCD I/F ILI9341 Description
GPIO23 VSPI_MOSI SDI/SDA Serial Data Input or Input/Output (Master Out to Slave In)
GPIO19 VSPI_MISO SDO Serial Data Output
GPIO18 VSPI_SCK SCL Serial Clock Input
GPIO5 VSPI_CS CSX Chip Enable (Chip Select or Slave Select)

IoT Badge์— ์‚ฌ์šฉ๋œ LCD๋Š” ILI9341์„ 4-wire 8-bit data serial interface II ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์‚ฌ์šฉํ•˜๋Š” Pin์€ SCL,SDI,D/CX,SDO, CS์ž…๋‹ˆ๋‹ค.

ESP32 LCD I/F ILI9341 Description
GPIO21 LCD_RST RESX Reset
GPIO22 LCD_DC D/CX Data or Command Selection Input

์ถ”๊ฐ€๋กœ ์‚ฌ์šฉํ•˜๋Š” Pin์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ดˆ๊ธฐํ™” ํ•ฉ๋‹ˆ๋‹ค. Control ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฏ€๋กœ OUTPUT ๋ชจ๋“œ๋กœ ์ดˆ๊ธฐํ™” ํ•ฉ๋‹ˆ๋‹ค.

TFT_CS   = Pin(5, Pin.OUT, value=1)  # Chip Select
TFT_RST  = Pin(21, Pin.OUT, value=1) # SPI Reset (optional)
TFT_DC   = Pin(22, Pin.OUT, value=1) # Data/Command 

ILI9341์€ LCD ์ œ์–ด๋ฅผ ์œ„ํ•œ ๊ฐ€๋กœ, ์„ธ๋กœ ๋ณ€ํ™˜์ด๋‚˜ Display์— ๋Œ€์‘ํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋งต ๋“ฑ์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋ช…๋ น ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜์—ฌ ์„ค์ •์„ ํ•ด ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

SPI ๋ฐ์ดํ„ฐ ์ „์†ก

ILI9341์˜ SPI ๋ฐฉ์‹์€ Command ์ „์†กํ•˜๊ณ  ๊ทธ Command์—์„œ ํ•„์š”๋กœ ํ•˜๋Š” Data๋ฅผ ์ด์–ด์„œ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ Command์™€ Data๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

  1. Chip Select๋ฅผ Low๋กœ ์„ค์ •
  2. Data/Command๋ฅผ Low๋กœ ์„ค์ •ํ•˜์—ฌ Command ์ธ์‹
  3. ๋ช…๋ น ์ฝ”๋“œ ์ „์†ก
  4. Data/Command๋ฅผ High๋กœ ์„ค์ •ํ•˜์—ฌ Data ์ธ์‹
  5. ๋ช…๋ น ์ฝ”๋“œ์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก
  6. Chip Select๋ฅผ High๋กœ ์„ค์ •

์œ„์˜ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•„์š”ํ•œ ๋ช…๋ น๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ILI9341๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ ๊ตฌ์„ฑํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

def writeCommand(cmd, data=None):
    TFT_CS(0) # startWrite
    TFT_DC(0) # Command
    vspi.write(bytearray([cmd]))
    TFT_DC(1) # Data or Ready

    if data is not None:
        vspi.write(data)
    else:
        time.sleep_ms(120)
  
    TFT_CS(1) # endWrite

Command๋Š” ์ •์ˆ˜, data๋Š” bytearray ํ˜•์‹์œผ๋กœ ์ „๋‹ฌ์ด ๋˜์–ด์•ผ ํ•˜๋ฉฐ command์— ๋”ฐ๋ผ data์˜ ๊ธธ์ด๋Š” ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Display ON(29h) ๋ช…๋ น์˜ ๊ฒฝ์šฐ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์ง€๋งŒ, Display Function Control (B6h)๋Š” 4๊ฐœ์˜ parameter๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์‹ฌ์ง€์–ด Memory Write (2Ch)์™€ ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ ๊ฐ€๋ณ€์ž…๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์•ž์„œ ๋งŒ๋“  writeCommand()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

writeCommand(0x36, b'\x28')
writeCommand(0x36, bytearray([0x28]))

SPI ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 

ILI9341์˜ SPI๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๊ฒƒ๋„ ๋ฐ์ดํ„ฐ ์ „์†ก๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Command์™€ Data๋กœ ์กฐํ•ฉ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ Command๋ฅผ ๋ณด๋‚ด๊ณ  Data๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

  1. Chip Select๋ฅผ Low๋กœ ์„ค์ •
  2. Data/Command๋ฅผ Low๋กœ ์„ค์ •ํ•˜์—ฌ Command ์ธ์‹
  3. ๋ช…๋ น ์ฝ”๋“œ ์ „์†ก
  4. Data/Command๋ฅผ High๋กœ ์„ค์ •ํ•˜์—ฌ Data ์ธ์‹
  5. ๋ช…๋ น ์ฝ”๋“œ์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 
  6. Chip Select๋ฅผ High๋กœ ์„ค์ •

์ด๋ฅผ python์œผ๋กœ ์ž‘์„ฑํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

def readCommand(cmd, nread=1):
    TFT_CS(0) # startWrite
    TFT_DC(0) # Command
    vspi.write(bytearray([cmd]))
    TFT_DC(1) # Data or Ready
    data = vspi.read(nread)
    TFT_CS(1) # endWrite
    return data

writeCommand์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Command์— ๋”ฐ๋ผ์„œ ์ฝ๋Š” ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ ๋‹ค๋ฅด๋ฏ€๋กœ ์ฝ์–ด์•ผ ํ•˜๋Š” Data ํฌ๊ธฐ๋ฅผ nread ๊ฐ’์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์•ž์„œ ๋งŒ๋“  readCommand()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

display_madctl = readCommand(0x0b, 2)

ILI9341 LCD ์ดˆ๊ธฐํ™” ํ•˜๊ธฐ

ILI9341์— SPI ๋ช…๋ น์„ ์‚ฌ์šฉํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์œผ๋ฏ€๋กœ ILI9341 LCD๋ฅผ ์ดˆ๊ธฐํ™” ํ•ฉ๋‹ˆ๋‹ค. ์ดˆ๊ธฐํ™”์— ํ•„์š”ํ•œ ๋ช…๋ น์–ด๋Š” ILI9341 ๊ทœ๊ฒฉ๋ฌธ์„œ์— ์žˆ์ง€๋งŒ ์šฐ๋ฆฌ๋Š” IoT Badge firmware์˜ LCD driver ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ช…๋ น๊ณผ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์€ ILI9341 ๊ทœ๊ฒฉ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์œ„ ์ฝ”๋“œ์—์„œ LCD model์ด ILI9341์ผ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ดˆ๊ธฐํ™” ๋ช…๋ น ์„ธํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

DRAM_ATTR static const lcd_init_cmd_t ili_init_cmds[]={
    {0xCF, {0x00, 0x83, 0x30}, 3},
    {0xED, {0x64, 0x03, 0x12, 0x81}, 4},
    {0xE8, {0x85, 0x01, 0x79}, 3},
    {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
    {0xF7, {0x20}, 1},
    {0xEA, {0x00, 0x00}, 2},
    {0xC0, {0x26}, 1},
    {0xC1, {0x11}, 1},
    {0xC5, {0x35, 0x3E}, 2},
    {0xC7, {0xBE}, 1},
    {0x36, {0x28}, 1},
    {0x3A, {0x55}, 1},
    {0xB1, {0x00, 0x1B}, 2},
    {0xF2, {0x08}, 1},
    {0x26, {0x01}, 1},
    {0xE0, {0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00}, 15},
    {0XE1, {0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F}, 15},
    {0x2A, {0x00, 0x00, 0x00, 0xEF}, 4},
    {0x2B, {0x00, 0x00, 0x01, 0x3f}, 4}, 
    {0x2C, {0}, 0},
    {0xB7, {0x07}, 1},
    {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
    {0x11, {0}, 0x80},
    {0x29, {0}, 0x80},
    {0, {0}, 0xff},
};

์ž๋ฃŒ ๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•˜๋ฉด ์ฒ˜์Œ์€ cmd, data ๊ทธ๋ฆฌ๊ณ  data์˜ ํฌ๊ธฐ๋กœ ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

typedef struct {
    uint8_t cmd;
    uint8_t data[16];
    uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;

๊ทธ๋ฆฌ๊ณ , ์ด ๋ช…๋ น์„ธํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ถ€๋ถ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

//Send all the commands
while (lcd_init_cmds[cmd].databytes!=0xff) {
    lcd_cmd(*spi_wr_dev, lcd_init_cmds[cmd].cmd, dc);
    lcd_data(*spi_wr_dev, lcd_init_cmds[cmd].data, lcd_init_cmds[cmd].databytes&0x1F, dc);
    if (lcd_init_cmds[cmd].databytes&0x80) {
        vTaskDelay(100 / portTICK_RATE_MS);
    }
    cmd++;
}

์ด ์ฝ”๋“œ๋Š” C ์–ธ์–ด๋กœ ๋˜์–ด ์žˆ๊ณ  ์ด๊ฒƒ์„ ๊ทธ๋Œ€๋กœ ํŒŒ์ด์ฌ์œผ๋กœ ํฌํŒ…ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋‹จ์ˆœ ์ดˆ๊ธฐํ™” ์ฝ”๋“œ์ด๋ฏ€๋กœ ์ดˆ๊ธฐํ™” ๋ช…๋ น๋งŒ ์ฐธ๊ณ ํ•˜๊ณ  ํŒŒ์ด์ฌ์— ๋งž์ถฐ ๋‹ค์‹œ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์Šต๋‹ˆ๋‹ค. ์œ„์˜ ๋ช…๋ น ์„ธํŠธ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ํŒŒ์ด์ฌ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

_init_cmds = (
    (0xCF, b'\x00\x83\x30'),
    (0xED, b'\x64\x03\x12\x81'),
    (0xE8, b'\x85\x01\x79'),
    (0xCB, b'\x39\x2C\x00\x34\x02'),
    (0xF7, b'\x20'),
    (0xEA, b'\x00\x00'),
    (0xC0, b'\x26'),
    (0xC1, b'\x11'),
    (0xC5, b'\x35\x3E'),
    (0xC7, b'\xBE'),
    (0x36, b'\x28'),
    (0x3A, b'\x55'),
    (0xB1, b'\x00\x1B'),
    (0xF2, b'\x08'),
    (0x26, b'\x01'),
    (0xE0, b'\x1F\x1A\x18\x0A\x0F\x06\x45\x87\x32\x0A\x07\x02\x07\x05\x00'),
    (0xE1, b'\x00\x25\x27\x05\x10\x09\x3A\x78\x4D\x05\x18\x0D\x38\x3A\x1F'),
    (0x2A, b'\x00\x00\x00\xEF'),
    (0x2B, b'\x00\x00\x01\x3F'),
    (0x2C, None),
    (0xB7, b'\x07'),
    (0xB6, b'\x0A\x82\x27\x00'),
    (0x11, None),
    (0x29, None),
  )

# Reset
TFT_RST(1)
time.sleep_ms(100)
TFT_RST(0)
time.sleep_ms(100)
TFT_RST(1)
time.sleep_ms(200)

# Send all the commands
for cmd, data in _init_cmds:
    writeCommand(cmd, data)

์ดˆ๊ธฐํ™”๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™”๋ฉด์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์›ํ•˜๋Š” ์˜์—ญ์— ์  ๊ทธ๋ฆฌ๊ธฐ

ILI9341์—์„œ๋Š” LCD์— ์ ์„ ๊ทธ๋ฆด ๋•Œ RAMWR(2Ch) Command๋ฅผ ์ด์šฉํ•˜์—ฌ ์ƒ‰์ƒ ๊ฐ’์„ ๋ฐ์ดํ„ฐ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด Command์—๋Š” LCD์˜ ์–ด๋Š ์œ„์น˜์— ์ƒ‰์ƒ์„ ์ „๋‹ฌํ•˜๋ผ๋Š” ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ ๋Œ€์‹  ๊ฐ€๋กœ(column), ์„ธ๋กœ(page)์˜ ์˜์—ญ์„ ์ง€์ •ํ•˜๋Š” Command๊ฐ€ ๋”ฐ๋กœ ์ค€๋น„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์„ค์ •ํ•ด ์ค๋‹ˆ๋‹ค.

  1. CASET(2Ah)์œผ๋กœ ๊ฐ€๋กœ ์˜์—ญ ์ง€์ • : ์‹œ์ž‘(SC), ์ข…๋ฃŒ(EC)
  2. PASET(2Bh)์œผ๋กœ ์„ธ๋กœ ์˜์—ญ ์ง€์ • : ์‹œ์ž‘(SP), ์ข…๋ฃŒ(EP)
  3. RAMWR(2Ch)์œผ๋กœ column/page๋กœ ์ง€์ •ํ•œ Address Window์— ํ•„์š”ํ•œ ์ƒ‰์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ

๋”ฐ๋ผ์„œ, x๊ฐ’์„ SC์™€ EC์— ๋˜‘๊ฐ™์ด ์ง€์ •ํ•˜๊ณ , y๊ฐ’์„ SP์™€ EP์— ์ง€์ •ํ•˜๋ฉด x, y ์œ„์น˜์— ์ ์„ ์ฐ์„ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด (10, 10) ์œ„์น˜์— ๋Œ€ํ•œ ๊ฒƒ์„ ์ž…๋ ฅํ•œ๋‹ค๋ฉด SC=0x000A, EC=0x000A, SP=000x0A, EP=0x000A ๊ฐ’์ด ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

writeCommand(0x2A, b'\x00\x0A\x00\x0A')
writeCommand(0x2B, b'\x00\x0A\x00\x0A')
writeCommand(0x2C, b'\xF8\x00')

์›ํ•˜๋Š” ์˜์—ญ์— ์‚ฌ๊ฐํ˜• ๊ทธ๋ฆฌ๊ธฐ

์ข€ ๋” ๋ˆˆ์— ๋„๋„๋ก 2×2 ํฌ๊ธฐ๋กœ ์ •ํ•œ๋‹ค๋ฉด SC=0x000A, EC=0x000B, SP=000x0A, EP=0x000B ๊ฐ’์ด ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

writeCommand(0x2A, b'\x00\x0A\x00\x0B')
writeCommand(0x2B, b'\x00\x0A\x00\x0B')
writeCommand(0x2C, b'\xF8\x00\xF8\x00\xF8\x00\xF8\x00')

๋งŒ์•ฝ ํ™”๋ฉด ์ „์ฒด๋ฅผ ํฐ์ƒ‰์œผ๋กœ ๋ฎ์–ด์“ด๋‹ค๋ฉด (0,0)์—์„œ (320,240)๊นŒ์ง€ 0xFFFF ๊ฐ’์„ ์ถœ๋ ฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ 320x240x2 = 153,600 ๋ฐ”์ดํŠธ์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ๋ฐ, ๋ถ€ํŒ…ํ•˜๋ฉด์„œ 90Kbytes ์ •๋„ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ IoT ๋ฑƒ์ง€์—์„œ๋Š” Out of memory ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋Š” REPL์—์„œ gc.mem_free()๋ฅผ ์‹คํ–‰ํ•ด์„œ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ ๋ผ์„œ, ์ผ์ •๋Ÿ‰ ๋ณ„๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๊ณ  ์ด๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ, ํ•œ row ์”ฉ ์ „์†กํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ ์˜ˆ์ƒ๋˜๋Š” 320×2=640 ๋ฐ”์ดํŠธ ์ •๋„๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์ด์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

import ustruct

LCD_WIDTH = 320
LCD_HEIGHT = 240
rowbuf = bytearray([0xff]*2*LCD_WIDTH)
writeCommand(0x2A, ustruct.pack('>HH', 0, LCD_WIDTH-1))
for i in range(LCD_HEIGHT):
    writeCommand(0x2B, ustruct.pack('>HH', i, i))
    writeCommand(0x2C, rowbuf)

์›ํ•˜๋Š” ์˜์—ญ์— ์›ํ•˜๋Š” ์ƒ‰๊น”์˜ ์‚ฌ๊ฐํ˜• ๊ทธ๋ฆฌ๊ธฐ

์•ž์„œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋Š” ๋ฌด์กฐ๊ฑด ํฐ์ƒ‰์œผ๋กœ LCD ํ™”๋ฉด์„ ์ฑ„์šฐ๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. IoT Badge์˜ LCD๋Š” RGB565๋กœ ํ˜•์‹์„ ๋”ฐ๋ฅด๊ฒŒ ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ํฐ์ƒ‰์€ 0xFFFF๊ฐ€ ๋˜๋ฉฐ, ์ด ๋•Œ๋ฌธ์— rowbuf์˜ ํฌ๊ธฐ๋Š” LCD_WIDTH์˜ ๋‘ ๋ฐฐ์˜ ํฌ๊ธฐ๋งŒํผ ํ• ๋‹นํ›„ 0xFF๋กœ ์ดˆ๊ธฐํ™” ํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋นจ๊ฐ„์ƒ‰์ธ ๊ฒฝ์šฐ๋Š” 0xF800๊ฐ€ ๋˜๋ฉฐ row_buf๋ฅผ ์ดˆ๊ธฐํ™” ํ•  ๋•Œ ์ด๋ฅผ ์ด ๊ฐ’์„ ์ด์šฉํ•˜๋ฉด ๋นจ๊ฐ„์ƒ‰์ด ํ™”๋ฉด์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

RGB565_RED = 0xF800
RGB565_GREEN = 0x07E0
RGB565_BLUE = 0x001F

color = RGB565_RED
color_high = color >> 8
color_low = color & 0xff 

rowbuf = bytearray(2*LCD_WIDTH)
for i in range(0, len(rowbuf), 2):
    rowbuf[i] = color_high
    rowbuf[i+1] = color_low
writeCommand(0x2A, ustruct.pack('>HH', 0, LCD_WIDTH-1))
for i in range(LCD_HEIGHT):
    writeCommand(0x2B, ustruct.pack('>HH', i, i))
    writeCommand(0x2C, rowbuf)

์ด๋ฅผ ์ •๋ฆฌํ•ด์„œ fillrect(x, y, width, height, color) ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฉ๋‹ˆ๋‹ค.

def fillrect(x, y, width, height, color=0xFFFF):
    x1 = 0 if x < 0 else x
    y1 = 0 if y < 0 else y
    x2 = x + width
    x2 = (LCD_WIDTH if x2 >= LCD_WIDTH else x2) - 1
    y2 = y + height
    y2 = (LCD_HEIGHT if y2 >= LCD_HEIGHT else y2) - 1
    
    width = x2 - x1 + 1
    height = y2 - y1 + 1
    
    if width <= 0 or height <= 0 :
        raise Exception('Invalid arguments')
    
    color_high = color >> 8
    color_low = color & 0xff

    rowbuf = bytearray(2*width)

    for i in range(0, len(rowbuf), 2):
        rowbuf[i] = color_high
        rowbuf[i+1] = color_low
    writeCommand(0x2A, ustruct.pack('>HH', x1, x2))
    for i in range(y1, y1+height):
        writeCommand(0x2B, ustruct.pack('>HH', i, i))
        writeCommand(0x2C, rowbuf)

์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ˜ธ์ถœํ•˜๋ฉด 50,50 ์œ„์น˜ ๋ถ€ํ„ฐ 100×50 ํ”ฝ์…€ ํฌ๊ธฐ์˜ ์ดˆ๋ก์ƒ‰ ์ง์‚ฌ๊ฐํ˜•์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

fillrect(50,50, 100, 50, RGB565_GREEN)

๋งบ์Œ๋ง

์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” IBM Developer Day 2018 IoT ๋ฑƒ์ง€์˜ LCD๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋งˆ์ดํฌ๋กœ ํŒŒ์ด์ฌ์˜ SPI(Serial Peripheral Interface)์˜ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.