ตัวอย่าง: LEDs — Animation บน LED Matrix
โปรแกรม assembly วาด animation สีรุ้งบน LED matrix ผ่าน memory-mapped I/O — ตัวอย่างคลาสสิกของการเขียน firmware ระดับ assembly
เป้าหมายของบทเรียน
- เข้าใจ memory-mapped I/O ในการเขียนโปรแกรม
- ใช้ symbol ที่ Ripes export มา (เช่น
LED_MATRIX_0_BASE) - เห็น nested loops สำหรับการ scan ทุก pixel ของ matrix
- ฝึกใช้คำสั่งจัดการ bit (
slli,and,or) สำหรับ pack สี RGB
การเตรียมอุปกรณ์
- เปิด Ripes
- ไปที่แท็บ I/O
- Double-click LED Matrix เพื่อสร้าง instance
- ปรับ Width และ Height ตามต้องการ (เช่น 8x8 หรือ 16x16)
- เพิ่ม LED size ให้ดูง่าย
สำคัญ
ต้องสร้าง LED Matrix device ก่อน โหลดโปรแกรม เพราะโปรแกรมจะอ้างอิง symbol
LED_MATRIX_0_BASE, LED_MATRIX_0_WIDTH, LED_MATRIX_0_HEIGHT ที่ออกมาจาก device
การโหลดโปรแกรม
เลือกเมนู File → Load Example → Assembly → LEDs
โค้ดทั้งหมด
# LEDs animation
# ต้องสร้าง LED Matrix ในแท็บ I/O ก่อน
li a0 LED_MATRIX_0_BASE # a0 = base address ของ LED matrix
li a1 LED_MATRIX_0_WIDTH # a1 = ความกว้าง
li a2 LED_MATRIX_0_HEIGHT # a2 = ความสูง
loop:
addi sp, sp, -16
sw s0, 12(sp)
sw s1, 8(sp)
mv t6, zero # t6 = ตัวนับ frame
add t3, a2, a1
slli a6, a1, 2 # a6 = width * 4 (byte offset per row)
lui a3, 16
addi a7, a3, -256
lui t0, 4080
init:
mv t4, zero
mv t1, zero # t1 = row counter
mv t2, a0 # t2 = pointer ไปยัง LED ปัจจุบัน
nextRow:
mv a4, zero
slli a3, t1, 8
sub a3, a3, t1
divu a3, a3, a2
add a3, a3, t6
slli a3, a3, 8
and t5, a3, a7 # t5 = green channel
mv a5, t2
mv a3, a1
nextPixel:
divu s0, a4, a1 # คำนวณ red channel
add s0, s0, t6
add s1, t4, a4 # คำนวณ blue channel
divu s1, s1, t3
add s1, s1, t6
slli s0, s0, 16
and s0, s0, t0
or s0, t5, s0 # รวม green + red
andi s1, s1, 255
or s0, s0, s1 # รวมเข้ากับ blue
sw s0, 0(a5) # เขียนสีลง LED ที่ตำแหน่งนี้
addi a3, a3, -1
addi a5, a5, 4 # ขยับ pointer ไป pixel ถัดไป
addi a4, a4, 255
bnez a3, nextPixel
addi t6, t6, 1 # frame counter++
addi t1, t1, 1
add t2, t2, a6 # ขยับ pointer ไปแถวถัดไป
addi t4, t4, 255
bne t1, a2, nextRow
j init # วนใหม่
วิเคราะห์โครงสร้าง
การ Pack สี RGB ลงใน 32-bit word
LED Matrix แต่ละ pixel ใช้ 32 bits ในการเก็บสี:
┌──────────┬──────────┬──────────┬──────────┐
│ unused │ RED │ GREEN │ BLUE │
│ 31..24 │ 23..16 │ 15..8 │ 7..0 │
└──────────┴──────────┴──────────┴──────────┘
โปรแกรมจึงใช้ slli shift bits และ or รวมกัน:
slli s0, s0, 16— เอา red ไป bit 16-23slli a3, a3, 8— เอา green ไป bit 8-15andi s1, s1, 255— เก็บ blue ไว้ bit 0-7
การ Scan Matrix
โปรแกรมใช้ nested loop:
- Outer loop (
init → nextRow): วนt1ผ่านทุก row - Inner loop (
nextPixel): วนa3ผ่านทุก pixel ใน row - Outermost loop (
loop → init): วนt6เป็น frame counter สำหรับ animation
การคำนวณ Address ของแต่ละ Pixel
t2 คือ pointer ไปยัง pixel ปัจจุบัน:
- เริ่มต้นที่
LED_MATRIX_0_BASE - หลังจบ row หนึ่ง:
t2 = t2 + a6(โดยa6 = width × 4) - หลังจบ pixel หนึ่ง:
a5 = a5 + 4(ขยับไปอีก 4 byte)
วิธีรันและสังเกต
- เลือก Single Cycle processor (จาก toolbar → Select Processor) — เพราะ I/O ต้องการ throughput สูง
- กด Reset
- เปิดแท็บ I/O ค้างไว้เพื่อเห็น LED matrix
- กด Auto-clock หรือ Run
- สังเกต LED matrix จะแสดงสีไล่ตามตำแหน่ง row/column และเปลี่ยนสีตามเวลา (frame counter
t6)
เคล็ดลับการ Debug
ถ้าหน้าจอ LED ไม่เปลี่ยน ลอง:
- ตรวจสอบว่ามี LED Matrix device ในแท็บ I/O หรือไม่
- ใช้ Pop-out ดึง LED Matrix ออกมาเป็นหน้าต่างลอย เพื่อดูพร้อมกับแท็บ Processor
- กด Step ทีละครั้ง สังเกตค่าใน
a5(pointer) และs0(สีที่กำลังเขียน)
แบบฝึกหัด
- เปลี่ยนสีของ LED ทั้งหมดให้เป็นสีแดงล้วน (แก้ที่ตรงไหน?)
- สร้าง animation ให้ทุก LED กระพริบขาว/ดำสลับกัน
- เขียน loop ที่ทำให้ LED แต่ละแถวมีสีต่างกัน (rainbow แนวตั้ง)
- ลองสร้าง Switches device เพิ่มและทำให้สวิตช์ตัวที่ N ควบคุม LED ตัวที่ N (อ้างอิงตัวอย่าง
switchesAndLeds.c)