[TH] ESP32 : Display of rotation squares with application ulab.

บทความนี้เป็นการใช้ ESP32 เพิ่อทำการหมุนสี่เหลี่ยมด้วยการใช้ไลบรารี ulab และนำผลจากการคำนวณไปแสดงผลที่โมดูลแอลซีดีกราฟิกที่ใช้หน่วยควบคุมเป็น ST7735s ดังที่เคยได้เขียนบทความไปแล้ว ซึ่งตัวอย่างของโปรแกรมประกอบด้วยการหมุนสี่เหลี่ยม 1 ชิ้นตามเข็มนาฬิกา และการหมุนสี่เหลี่ยมหลายชิ้นในทิศตรงกันข้ามกัน

คลิป 1 ตัวอย่างผลการทำงานที่ปรับแต่งความเร็วในการแสดงผลแล้ว

อุปกรณ์

อุปกรณ์ประกอบการทดลอง คือ

  1. ESP32
  2. ST7735S

ตัวอย่างโปรแกรม

ตัวอย่างโปรแกรม code18-9 เป็นการหมุนสี่เหลี่ยมจากมุม 0 ถึง 359 ด้วยการวาดเส้นจากจุดยอด 4 จุด และคำนวณการหมุนด้วยไลบรารีของ ulab (อ่านบทความที่นี่) โดยผลลัพธ์ที่ได้เป็นดังภาพที่ 1

# code18-9
from st7735 import TFT
from machine import SPI,Pin
import machine as mc
import time
import math
import ulab as np

minX = -10.0
maxX = 10.0
minY = -5.0
maxY = 5.0
scrWidth = const(160)
scrHeight = const(80)
ratioX = float(scrWidth)/(math.fabs(minX)+math.fabs(maxX)+1)
ratioY = float(scrHeight)/(math.fabs(minY)+math.fabs(maxY)+1)
centerX = const(scrWidth >> 1)
centerY = const(scrHeight >> 1)

spi = SPI(1, baudrate=20000000,
          sck=Pin(14), mosi=Pin(13),
          polarity=0, phase=0)
# dc, rst, cs
tft=TFT(spi,2,None,15)
tft.init_7735(tft.GREENTAB80x160)


def rotate(pX,pY,angle):
    rad = math.radians(angle)
    xCos = pX*np.vector.cos(rad)
    ySin = pY*np.vector.sin(rad)
    xSin = pX*np.vector.sin(rad)
    yCos = pY*np.vector.cos(rad)
    newX = xCos - ySin
    newY = xSin + yCos
    return (newX, newY)

def draw(pX, pY,aColor=tft.WHITE):
    newPx = np.array(pX*ratioX+centerX,dtype=np.uint16)
    newPy = np.array(pY*ratioY+centerY,dtype=np.uint16)
    tft.line((newPx[0],newPy[0]),(newPx[1],newPy[1]),aColor)
    tft.line((newPx[1],newPy[1]),(newPx[2],newPy[2]),aColor)
    tft.line((newPx[2],newPy[2]),(newPx[3],newPy[3]),aColor)
    tft.line((newPx[3],newPy[3]),(newPx[0],newPy[0]),aColor)

# main program
tft.rotation(1)
tft.fill(tft.BLACK)

t0 = time.ticks_us()
pX = np.array([-2,2,2,-2],dtype=np.float)
pY = np.array([2,2,-2,-2],dtype=np.float)
for degree in range(360):
    newP = rotate(pX,pY,degree)
    draw(newP[0],newP[1],tft.WHITE)
    time.sleep_ms(100)
    tft.fill(0)
for degree in range(360):
    newP = rotate(pX,pY,-degree)
    draw(newP[0],newP[1],tft.CYAN)
    time.sleep_ms(100)
    tft.fill(0)
print("ulab: Delta = {} usec".format(time.ticks_us()-t0))

# endof program
time.sleep_ms(2000)
tft.on(False)
spi.deinit()

ภาพที่ 1 ผลลัพธ์จาก code18-9

ตัวอย่างโปรแกรม code18-10 เป็นการเพิ่มสี่เหลี่ยมอีก 3 ชิ้น โดยมาจากการคำนวณด้วยการขยายวัตถุจากสี่เหลี่ยมรูปแรก และสั่งวาดให้หมุนกลับทิศกัน พร้อมแต่ละชิ้นหมุนตรงข้ามสลับกันไป ตัวอย่างผลลัพธ์เป็นดังภาพที่ 2

# code18-10
from st7735 import TFT
from machine import SPI,Pin
import machine as mc
import time
import math
import ulab as np

minX = -10.0
maxX = 10.0
minY = -5.0
maxY = 5.0
scrWidth = const(160)
scrHeight = const(80)
ratioX = float(scrWidth)/(math.fabs(minX)+math.fabs(maxX)+1)
ratioY = float(scrHeight)/(math.fabs(minY)+math.fabs(maxY)+1)
centerX = const(scrWidth >> 1)
centerY = const(scrHeight >> 1)

spi = SPI(1, baudrate=20000000,
          sck=Pin(14), mosi=Pin(13),
          polarity=0, phase=0)
# dc, rst, cs
tft=TFT(spi,2,None,15)
tft.init_7735(tft.GREENTAB80x160)

def rotate(pX,pY,angle):
    rad = math.radians(angle)
    xCos = pX*np.vector.cos(rad)
    ySin = pY*np.vector.sin(rad)
    xSin = pX*np.vector.sin(rad)
    yCos = pY*np.vector.cos(rad)
    newX = xCos - ySin
    newY = xSin + yCos
    return (newX, newY)

def scale(pX,pY,Sx=1.0,Sy=1.0):
    return ((pX*Sx, pY*Sy))

def draw(pX, pY,aColor=tft.WHITE):
    newPx = np.array(pX*ratioX+centerX,dtype=np.uint16)
    newPy = np.array(pY*ratioY+centerY,dtype=np.uint16)
    tft.line((newPx[0],newPy[0]),(newPx[1],newPy[1]),aColor)
    tft.line((newPx[1],newPy[1]),(newPx[2],newPy[2]),aColor)
    tft.line((newPx[2],newPy[2]),(newPx[3],newPy[3]),aColor)
    tft.line((newPx[3],newPy[3]),(newPx[0],newPy[0]),aColor)

# main program
tft.rotation(1)
tft.fill(tft.BLACK)

t0 = time.ticks_us()
pX = np.array([-0.5,0.5,0.5,-0.5],dtype=np.float)
pY = np.array([0.5,0.5,-0.5,-0.5],dtype=np.float)
p2 = scale(pX,pY,2.2,2.2)
p3 = scale(pX,pY,3.8,3.8)
p4 = scale(pX,pY,5.4,5.4)
for degree in range(0,361,4):
    tft.fill(0)
    newP = rotate(pX,pY,degree)
    newP2 = rotate(p2[0],p2[1],-degree)
    newP3 = rotate(p3[0],p3[1], degree)
    newP4 = rotate(p4[0],p4[1], -degree)
    draw(newP[0],newP[1],tft.WHITE)
    draw(newP2[0],newP2[1],tft.GREEN)
    draw(newP3[0],newP3[1],tft.CYAN)
    draw(newP4[0],newP4[1],tft.YELLOW)
for degree in range(0,361,8):
    tft.fill(0)
    newP = rotate(pX,pY,-degree)
    newP2 = rotate(p2[0],p2[1],degree)
    newP3 = rotate(p3[0],p3[1],-degree)
    newP4 = rotate(p4[0],p4[1],degree)
    draw(newP[0],newP[1],tft.WHITE)
    draw(newP2[0],newP2[1],tft.GREEN)
    draw(newP3[0],newP3[1],tft.CYAN)
    draw(newP4[0],newP4[1],tft.YELLOW)
print("ulab: Delta = {} usec".format(time.ticks_us()-t0))

# endof program
time.sleep_ms(2000)
tft.on(False)
spi.deinit()


ภาพที่ 2 ผลลัพธ์จาก code18-10

สรุป

จากตัวอย่างในบทนี้จะพบว่า การทำงานของ ulab ทำให้สามารถประมวลผลเวกเตอร์หรือแถวลำดับของพิกัดในแกน x และ y ได้หลายชุดในคำสั่งเดียว แต่ความเร็วที่สูญเสียไปกลับไปตกในเรื่องของความเร็วในการแสดงผล แต่อย่างไรก็ดี ผู้เขียนหวังว่าบทความนี้คงเป็นตัวอย่างและแนวทางสำหรับผู้ที่สนใจใช้ภาษาไพธอนในการคำนวณค่าทางคณิตศาสตร์เพื่อนำไปประยุกต์ใช้ในการแสดงผล หรือด้าน AI ต่อไป

สุดท้ายนี้ขอให้สนุกกับการเขียนโปรแกรมครับ

(C) 2020, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2020-11-10
ปรับปรุงเมื่อ 2021-08-14
ปรับปรุงเมื่อ 2021-08-17