ตัวอย่าง: LEDs — Animation บน LED Matrix

โปรแกรม assembly วาด animation สีรุ้งบน LED matrix ผ่าน memory-mapped I/O — ตัวอย่างคลาสสิกของการเขียน firmware ระดับ assembly

เป้าหมายของบทเรียน

การเตรียมอุปกรณ์

  1. เปิด Ripes
  2. ไปที่แท็บ I/O
  3. Double-click LED Matrix เพื่อสร้าง instance
  4. ปรับ Width และ Height ตามต้องการ (เช่น 8x8 หรือ 16x16)
  5. เพิ่ม 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 รวมกัน:

การ Scan Matrix

โปรแกรมใช้ nested loop:

การคำนวณ Address ของแต่ละ Pixel

t2 คือ pointer ไปยัง pixel ปัจจุบัน:

วิธีรันและสังเกต

  1. เลือก Single Cycle processor (จาก toolbar → Select Processor) — เพราะ I/O ต้องการ throughput สูง
  2. กด Reset
  3. เปิดแท็บ I/O ค้างไว้เพื่อเห็น LED matrix
  4. กด Auto-clock หรือ Run
  5. สังเกต LED matrix จะแสดงสีไล่ตามตำแหน่ง row/column และเปลี่ยนสีตามเวลา (frame counter t6)
เคล็ดลับการ Debug ถ้าหน้าจอ LED ไม่เปลี่ยน ลอง:
  • ตรวจสอบว่ามี LED Matrix device ในแท็บ I/O หรือไม่
  • ใช้ Pop-out ดึง LED Matrix ออกมาเป็นหน้าต่างลอย เพื่อดูพร้อมกับแท็บ Processor
  • กด Step ทีละครั้ง สังเกตค่าใน a5 (pointer) และ s0 (สีที่กำลังเขียน)

แบบฝึกหัด

  1. เปลี่ยนสีของ LED ทั้งหมดให้เป็นสีแดงล้วน (แก้ที่ตรงไหน?)
  2. สร้าง animation ให้ทุก LED กระพริบขาว/ดำสลับกัน
  3. เขียน loop ที่ทำให้ LED แต่ละแถวมีสีต่างกัน (rainbow แนวตั้ง)
  4. ลองสร้าง Switches device เพิ่มและทำให้สวิตช์ตัวที่ N ควบคุม LED ตัวที่ N (อ้างอิงตัวอย่าง switchesAndLeds.c)