แนะนำการใช้งาน TinyGo สำหรับบอร์ด Raspberry Pico (ตอนที่ 1)#

Keywords: Go / TinyGo, Microcontroller Programming, Raspberry Pico, RP2040


TinyGo#

TinyGo เป็นคอมไพเลอร์สำหรับภาษา Go ที่สามารถนำไปใช้กับบอร์ดไมโครคอนโทรลเลอร์ได้ โดยปรกติแล้วในการเขียนโปรแกรมไมโครคอนโทรลเลอร์ ก็นิยมใช้ภาษา C/C++ หรือ Framework อย่างเช่น Arduino เป็นต้น แต่บทความนี้ขอนำเสนอแนวทางและตัวอย่างการเขียนโค้ดด้วยภาษา Go และคอมไพล์โค้ดด้วย TinyGo Compiler โดยเลือกใช้บอร์ด Raspberry Pi Pico (RP2040 Soc) เป็นตัวอย่าง

แนะนำให้ผู้อ่านได้ติดตั้งซอฟต์แวร์ต่อไปนี้ก่อน

บอร์ดที่ได้นำมาทดลองใช้งาน ได้แก่

  • Raspberry Pi Pico / Pico-W
  • VCC GND Studio YD-RP2040
  • WaveShare Zero-RP2040

รูป: แผนผังแสดงตำแหน่งขาของบอร์ด YD-RP2040

การเขียนโค้ดด้วยภาษา Go และใช้งานวงจรต่าง ๆ ของไมโครคอนโทรลเลอร์ รวมถึงการใช้งานร่วมกับโมดูลอิเล็กทรอนิกส์ประเภทต่าง ๆ ก็มีตัวช่วย กล่าวคือ มีไลบรารีให้เลือกใช้งาน และผู้ใช้สามารถดูได้จาก

รูป: github.com/tinygo-org/drivers/examples

 


ตัวอย่างโค้ดแรก (ใช้ไฟล์ชื่อ main.go) คือ การทำให้ LED บนบอร์ด Pico กระพริบได้ โดยเลือกใช้ขา GP25 ซึ่งตรงกับวงจร LED บนบอร์ด Pico

package main

import (
    "machine"
    "time"
)

func main() {
    println("TinyGo on Pico-RP2040 Board")
    led := machine.LED // or machine.GP25
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})
    for { // an endless loop
        state := !led.Get() // Toggle the state
        println( "LED:", state ) // Send output string to serial
        led.Set( state )  // Update the LED output
        time.Sleep( 100*time.Millisecond ) // Wait for 0.1 sec
    }
}

ในโค้ดตัวอย่างภายในฟังก์ชัน main คำสั่ง println() ส่งข้อความผ่าน USB-CDC ไปยังคอมพิวเตอร์ของผู้ใช้ ถัดไปมีการใช้ตัวแปร led เพื่ออ้างอิง machine.LED ซึ่งมีชนิดข้อมูลเป็น machine.Pin และมีการใช้คำสั่ง Configure() ของ machine.Pin เพื่อตั้งค่าโหมดการใช้งาน โดยให้เป็นขาเอาต์พุต (machine.PinOutput)

  led := machine.LED // or machine.GP25
  led.Configure(machine.PinConfig{Mode: machine.PinOutput})

การใช้คำสั่ง Get() และ Set() ของ machine.Pin เป็นการอ่านค่าปัจจุบันและเขียนค่าใหม่ให้ขา GPIO ที่ใช้งาน และคำสั่ง time.Sleep(100*time.Millisecond) เป็นการหน่วงเวลาไว้ 0.1 วินาที ก่อนทำคำสั่งถัดไป

การตั้งค่าบอร์ด RP2040 เพื่อเข้าสู่โหมด Bootloader

  1. กดปุ่ม BOOTSEL บนบอร์ด Raspberry Pi Pico
  2. เชื่อมต่อบอร์ดเข้ากับคอมพิวเตอร์ผ่านสาย USB
  3. เมื่อเข้าสู่โหมด Bootloader สำเร็จ บอร์ดจะปรากฏเป็นไดรฟ์ USB ชื่อ RPI-RP2

การคอมไพล์โค้ดเพื่อสร้างไฟล์เอาต์พุตเป็น .elf และ .uf2 และการอัปโหลดไปยังบอร์ด Pico ด้วยคำสั่งต่อไปนี้

  • tinygo clean
  • tinygo build -target=pico -size=short -o firmware.elf main.go
  • tinygo build -target=pico -size=short -o firmware.uf2 main.go
  • tinygo flash -target=pico main.go

รูป: ตัวอย่างการคอมไพล์โค้ดและอัปโหลดโปรแกรมไปยังบอร์ด Pico โดยใช้ซอฟต์แวร์ VS Code IDE

แต่ถ้าจะไม่ใช้คำสั่ง time.Sleep() และเปลี่ยนเป็นวิธีตรวจสอบช่วงเวลาระหว่างเวลาปัจจุบันกับเวลาครั้งก่อนที่ได้มีการสลับสถานะของเอาต์พุต ก็มีแนวทางการเขียนโค้ดดังนี้

package main

import (
    "machine"
    "time"
)

func main() {
    println("TinyGo LED Blink")
    // Initialize the onboard LED
    led := machine.LED
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})

    // Set the toggle interval
    intervalMsec := time.Millisecond * 100
    lastTime := time.Now()

    for {
        now := time.Now() // Get the current time
        // Check if the time difference exceeds the toggle interval
        if now.Sub(lastTime) >= intervalMsec {
            led.Set(!led.Get()) // Toggle the LED
            lastTime = now      // Update the last time
        }
    }
}

 


โค้ดตัวอย่างนี้สาธิตการใช้ machine.USBCDC เป็นช่องทางการส่งข้อความที่จะต้องมีการแปลงเป็นข้อมูลไบต์ก่อนส่งไปยังคอมพิวเตอร์ของผู้ใช้ โดยใช้คำสั่ง machine.USBCDC.Write() แต่จะให้ผลเหมือนการใช้คำสั่ง println()

package main

import (
    "fmt"
    "machine"
    "time"
)

var (
    usbcdc = machine.USBCDC
)

func main() {
    led := machine.LED
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})
    count := 0
    for {
        value := (count%2 == 0) // Compute logic value
        led.Set(value)          // Update the LED
        textStr := fmt.Sprintf("Count: %v\n", count)
        usbcdc.Write([]byte(textStr)) // Write bytes to USB-CDC
        count++                       // Increment counter
        time.Sleep(500 * time.Millisecond)
    }
}

รูป: ตัวอย่างการรับข้อความจากบอร์ด Pico

 


▷ ตัวอย่างที่ 3: LED Toggle Upon Button Click (Polling Loop)#

ตัวอย่างสำหรับการอ่านค่าจากปุ่มกด และเมื่อมีการกดปุ่มแล้วปล่อยแต่ละครั้ง จะสลับสถานะลอจิกของ LED บนบอร์ด บอร์ดทดลองที่ได้นำมาใช้คือ YD-RP2040 ซึ่งมีวงจร LED ต่ออยู่ที่ขา GPIO25 และมีวงจรปุ่มกดอยู่ที่ขา GPIO24 (ต่อใช้งานแบบ Active-Low)

package main

import (
    "machine"
    "time"
)

func main() {
    led := machine.LED // Use GPIO25 pin for LED output
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})

    btn := machine.GPIO24 // Use GPIO24 pin for active-low button
    btn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})

    lastState := btn.Get() // Get the current button state
    ledState := !lastState
    led.Set(ledState)
    for {
        currentState := btn.Get()       // Read the button input
        if currentState && !lastState { // Detect state change
            ledState = !ledState // Toggle LED state
            led.Set(ledState)    // Write the LED output
            println("LED State:", ledState)
        }
        lastState = currentState
        time.Sleep(10 * time.Millisecond)
    }
}

รูป: ตัวอย่างการรับข้อความจากบอร์ด Pico

 


▷ ตัวอย่างที่ 4: LED Toggle Upon Button Click (Interrupt-Driven)#

โค้ดตัวอย่างนี้สาธิตการใช้งานคำสั่ง SetInterrupt() ของ machine.Pin เพื่อเปิดใช้งานอินเทอร์รัพท์ที่ขา GPIO และต่อใช้งานกับปุ่มกด เมื่อมีการเปลี่ยนสถานะลอจิกจาก High เป็น Low (เกิดเหตุการณ์ขอบขาลง หรือ Falling Edge ที่ขาอินพุตสำหรับปุ่มกด) จะมีการทำคำสั่งของฟังก์ชัน Callback (หรือเรียกฟังก์ชันนี้ว่า ISR: Interrupt Service Routine) เช่น สลับสถานะลอจิกของ LED หนึ่งครั้ง

package main

import (
    "machine"
    "time"
)

var (
    led      = machine.GPIO25 // for LED output
    btn      = machine.GPIO24 // for active-low button
    ledState = false
)

func main() {
    // Configure the onboard LED as output
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})
    // Configure button as input with pull-up
    btn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})

    // Set up an interrupt on the button pin
    btn.SetInterrupt(machine.PinFalling, func(machine.Pin){ // Callback function
        ledState = !ledState // Toggle LED state
        led.Set(ledState)
    })
    for { // endless loop
        time.Sleep(time.Second)
    }
}

 


▷ ตัวอย่างที่ 5: LED Toggle Upon Button Click (Interrupt + Go Channel)#

โค้ดตัวอย่างนี้มีการทำงานคล้ายกับตัวอย่างที่แล้ว แต่มีการใช้งาน Go Channel เพื่อใช้สื่อสารจากฟังก์ชัน Callback เมื่อเกิดอินเทอร์รัพท์แต่ละครั้ง ไปยังฟังก์ชัน main เมื่อฟังก์ชัน main ได้รับข้อมูลตัวเลขจาก Go Channel ที่อ้างอิงด้วยตัวแปรชื่อ buttonPressed ก็จะสลับสถานะลอจิกของ LED หนึ่งครั้ง

package main

import (
    "machine"
    "time"
)

var (
    led = machine.GPIO25 // for LED output
    btn = machine.GPIO24 // for active-low button
)

func main() {
    // Configure the onboard LED as output
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})

    // Configure button as input with pull-up
    btn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
    // Create a channel for button press events
    buttonPressed := make(chan int, 1)
    // Set up an interrupt on the button pin
    btn.SetInterrupt(machine.PinFalling, func(machine.Pin){ // Callback function
        select {
        case buttonPressed <- 1: // Send signal to channel
        default: // Avoid blocking if channel is full
        }
    })

    ledState := false
    for {
        select {
        case <-buttonPressed: // Wait for signal from ISR
            ledState = !ledState // Toggle the LED output
            led.Set(ledState)
            println("LED:", ledState)
        default: // Avoid blocking if channel is empty
            time.Sleep(10 * time.Millisecond)
        }
    }
}

หรืออาจปรับเปลี่ยนโครงสร้างของโค้ดใหม่ เช่น มีการสร้างฟังก์ชัน Goroutine เพื่อสื่อสารกับฟังก์ชัน Callback

package main

import (
    "machine"
)

var (
    led           = machine.GPIO25    // for LED output
    btn           = machine.GPIO24    // for active-low button
    buttonPressed = make(chan int, 1) // Go chnanel of int with capacity=1
)

func callback(pin machine.Pin) {
    select {
    case buttonPressed <- 1: // Send signal to channel
    default: // Avoid blocking if channel is full
    }
}

func main() {
    // Configure the onboard LED as output
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})

    // Configure button as input with pull-up
    btn.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
    // Set up an interrupt on the button pin
    btn.SetInterrupt(machine.PinFalling, callback)

    go func() { // Start a goroutine
        ledState := false
        for {
            <-buttonPressed // Wait for signal from ISR (blocking)
            ledState = !ledState // Toggle the LED output
            led.Set(ledState)
            println("LED:", ledState)
        }
    }()

    select {} // Block the main function
}

 


▷ ตัวอย่างที่ 6: LED Toggle Using Time Ticker#

ตัวอย่างโค้ดถัดไปสาธิตการใช้งาน time.Ticker ที่จะสร้างสัญญาณและส่งผ่าน Channel ที่มีชื่อว่า ticker.C ตามระยะเวลาที่กำหนดไว้ เช่น 50 msec ดังนั้นเมื่อได้รับสัญญาณจาก ticker.C ก็จะมีการทำคำสั่งของฟังก์ชันตามระยะเวลาที่กำหนด เช่น สลับสถานะลอจิกของ LED

package main

import (
    "machine"
    "time"
)

var (
    led = machine.GPIO25 // for LED output
)

func main() {
    // Configure the onboard LED
    led := machine.LED
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})

    // Create a ticker with 50ms interval
    ticker := time.NewTicker(50 * time.Millisecond)
    for range ticker.C { // Wait for a signal from the Ticker
        led.Set(!led.Get()) // Toggle the LED state
    }
}

หรือจะแก้ไขโค้ด โดยสร้างฟังก์ชัน Goroutine เพื่อรอสัญญาณจาก time.Ticker และสลับสถานะลอจิกของ LED ตามตัวอย่างดังนี้

package main

import (
    "machine"
    "time"
)

var (
    led = machine.GPIO25 // for LED output
)

func ledToggle(ticker *time.Ticker) {
    for range ticker.C { // Wait for a signal from the time ticker.
        led.Set(!led.Get()) // Toggle the LED state
    }
}

func main() {
    // Configure the onboard LED
    led.Configure(machine.PinConfig{Mode: machine.PinOutput})
    // Create a ticker with 50ms interval
    ticker := time.NewTicker(50 * time.Millisecond)
    go ledToggle(ticker) // Start a goroutine
    select {}            // Block the main function
}

ถ้าลองลดระยะเวลาในการสลับสถานะลอจิกแต่ละครั้งให้น้อยลง เช่น 500 ไมโครวินาที แล้ววัดสัญญาณเอาต์พุตด้วยออสซิลโลสโคปแบบดิจิทัล

รูป: การวัดสัญญาณ เมื่อกำหนดให้มีระยะเวลา 500us ซึ่งจะได้ความถี่ 1kHz ช่วงเวลาที่สัญญาณมีลอจิก High และ Low กว้างเท่ากัน (หนึ่งคาบกว้าง 1000us หรือ 1ms)

แต่ถ้าลดลงระยะเวลาให้เป็น 100 ไมโครวินาที จะได้รูปสัญญาณดังนี้

รูป: การวัดสัญญาณ เมื่อกำหนดให้มีระยะเวลา 100us ซึ่งจะได้ความถี่ 5kHz แต่ช่วงเวลาที่สัญญาณมีลอจิก High และ Low กว้างไม่เท่ากัน

 


▷ ตัวอย่างที่ 7: PWM-based LED Dimming#

ถัดไปเป็นตัวอย่างการสร้างสัญญาณ PWM (Pulse Width Modulation) โดยเลือกใช้ขา GPIO25 สำหรับสัญญาณเอาต์พุต และต่อกับวงจร LED บนบอร์ด Pico

สัญญาณ PWM ที่ได้เป็นเอาต์พุตแบบดิจิทัลนั้น มีความถี่คงที่ 500Hz และมีการปรับค่าความกว้างของพัลส์ช่วงที่เป็นลอจิก High ในหนึ่งคาบเวลา (หรือเรียกว่า Duty Cycle) ให้เพิ่มขึ้นหรือลดลงได้ ค่าที่ใช้สำหรับ Duty Cycle จะเป็นตัวเลขจำนวนเต็ม 16 บิต

ในโค้ดตัวอย่างมีฟังก์ชัน pwmInit() ทำหน้าที่ตั้งค่าเริ่มต้นและเปิดใช้งานวงจร PWM สำหรับขา machine.GPIO25 (ชนิดข้อมูล machine.Pin) ซึ่งตรงกับ machine.PWM4 (ชนิดข้อมูล *machine.pmwGroup) ฟังก์ชันนี้ให้ค่ากลับคืนมาเป็นหมายเลขของ PWM Channel และค่า PWM Top (ขนาด 16 บิต) ซึ่งเป็นค่าสูงสุดสำหรับคาบเวลาของสัญญาณ PWM

package main

import (
    "machine"
    "time"
)

const FREQ_HZ = 500                       // PWM Frequency
const PWM_PERIOD uint64 = (1e9 / FREQ_HZ) // in nanoseconds

var ( 
    pwmPin = machine.GPIO25 // Use GPIO25 pin for PWM output.
    pwm = machine.PWM4 // Pin 25 corresponds to PWM Group 4.
)

func pwmInit() (channel uint8, top uint32) {
    // Configure the PWM with the given period.
    pwm.Configure(machine.PWMConfig{
        Period: PWM_PERIOD,
    })
    // Create a PWM channel
    channel, err := pwm.Channel(pwmPin)
    if err != nil {
        return 0, 0 // error
    }
    top = pwm.Top() // Get the PWM top value
    return channel, top
}

func main() {
    println("TinyGo RP2040 PWM Demo...")
    time.Sleep(1 * time.Second)
    pwmChannel, pwmTop := pwmInit() // Initialize PWM output
    println("PWM Channel   :", pwmChannel)
    println("PWM top value :", pwmTop)
    if pwmChannel < 1 {
        panic("PWM creation failed!")
    }

    for {
        value := 0
        for i := 0; i < 200; i++ {
            if i > 100 {
                value = 200 - i
            } else {
                value = i
            }
            // Set PWM duty cycle (0% … 100%)
            pwm.Set(pwmChannel, uint32(value)*pwmTop/100)
            time.Sleep(time.Millisecond * 10)
        }
    }
}

ในการเลือกใช้ขา GPIO สำหรับการสร้างสัญญาณ PWM จะต้องเลือกหมายเลขของ PWM Group สำหรับชิป RP2040 ให้ถูกต้องด้วย ซึ่งสามารถดูได้จากเอกสารออนไลน์

รูป: ข้อมูลเกี่ยวกับการเลือกใช้ขา PWM ของบอร์ด Pico

 

หากจะลองปรับเปลี่ยนโค้ดตัวอย่าง โดยประยุกต์ใช้ Timer Ticker และ Goroutine และให้ผลการทำงานเหมือนเดิม ก็มีแนวทางด้งนี้

package main

import (
    "machine"
    "time"
)

const FREQ_HZ = 500                       // PWM Frequency
const PWM_PERIOD uint64 = (1e9 / FREQ_HZ) // in nanoseconds

var (
    pwmPin = machine.GPIO25 // Use GPIO25 pin for PWM output.
    pwm    = machine.PWM4   // Pin 25 corresponds to PWM Group 4.
    // pwmPin = machine.GPIO15 // Use GPIO15 pin for PWM output.
    // pwm    = machine.PWM7   // Pin 15 corresponds to PWM Group 7.
)

func pwmInit() (channel uint8, top uint32) {
    // Configure the PWM with the given period.
    pwm.Configure(machine.PWMConfig{
        Period: PWM_PERIOD,
    })
    // Create a PWM channel
    channel, err := pwm.Channel(pwmPin)
    if err != nil {
        return 0, 0 // error
    }
    top = pwm.Top() // Get the PWM top value
    return channel, top
}

func main() {
    println("TinyGo RP2040 PWM Demo...")
    pwmChannel, pwmTop := pwmInit() // Initialize PWM output
    println("PWM Channel   :", pwmChannel)
    println("PWM top value :", pwmTop)
    if pwmChannel < 1 {
        panic("PWM creation failed!")
    }
    // Create a ticker with 50ms interval
    ticker := time.NewTicker(10 * time.Millisecond)
    go func() { // Start a goroutine for PWM update
        value := 0
        i := 0
        for range ticker.C {
            if i > 100 {
                value = 200 - i
            } else {
                value = i
            }
            // Set PWM duty cycle (0% … 100%)
            pwm.Set(pwmChannel, uint32(value)*pwmTop/100)
            i = (i + 1) % 200
        }
    }()

    select {} // Blocking the main function
}

รูป: ตัวอย่างรูปคลื่นสัญญาณ PWM ที่มีความถี่ 500Hz แต่มีค่า Duty Cycle แตกต่างกันในแต่ละช่วงเวลา

 


▷ ตัวอย่างที่ 8: RGB LED (Single-Pixel WS2812)#

ตัวอย่างโค้ดถัดไปสาธิตการใช้งานไลบารี tinygo.org/x/drivers/ws2812 เพื่อใช้ในการกำหนดค่าสีให้กับไอซี WS2812 จำนวน 1 พิกเซล โดยนำมาต่อเข้ากับขา GPIO23 และเปลี่ยนค่าสีไปตามลำดับในอาร์เรย์ COLORS เว้นระยะเวลา 500 มิลลิวินาที

package main

import (
    "image/color"
    "machine"
    "time"

    "tinygo.org/x/drivers/ws2812"
)

// Create a color pattern (RGB values)
var COLORS = []color.RGBA{
    {R: 255, G: 0, B: 0},     // Red
    {R: 0, G: 255, B: 0},     // Green
    {R: 0, G: 0, B: 255},     // Blue
    {R: 255, G: 255, B: 0},   // Yellow
    {R: 0, G: 255, B: 255},   // Cyan
    {R: 255, G: 0, B: 255},   // Magenta
    {R: 255, G: 255, B: 255}, // White
    {R: 0, G: 0, B: 0},       // Off
}
var neopixel ws2812.Device

func main() {
    // Define the data pin for WS2812
    dataPin := machine.GPIO23
    dataPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    // Initialize the WS2812 RGB LED
    neopixel = ws2812.NewWS2812(dataPin)
    numColors := len(COLORS)
    for {
        for _, c := range COLORS { // For each color
            neopixel.WriteColors([]color.RGBA{c}) // Update Color
            time.Sleep(500 * time.Millisecond)
        }
        for i := 0; i < numColors; i++ { // For each color
            neopixel.WriteColors(COLORS[i : i+1]) // Update color
            time.Sleep(500 * time.Millisecond)
        }
    }
}

 

ชิป RP2040 มีวงจรภายในประเภทหนึ่งที่เรียกว่า PIO (Programmable Input/Output) และสามารถนำมาใช้ในการส่งข้อมูลเพื่อกำหนดค่าสีให้กับ WS2812B ได้ และช่วยลดภาระการทำงานของซีพียู การเขียนโค้ดก็ทำได้สะดวกเนื่องจากมีไลบารีต่อไปนี้ให้ใช้งาน

  • github.com/tinygo-org/pio/rp2-pio/piolib (อ้างอิงโดยใช้ชื่อ pio) สำหรับการใช้งานวงจร PIO
  • github.com/tinygo-org/pio/rp2-pio/piolib สำหรับการใช้ PIO เพื่อควบคุมการทำงานของ WS2812B

ตัวอย่างโค้ดมีดังนี้

package main

import (
    "image/color"
    "machine"
    "time"

    pio "github.com/tinygo-org/pio/rp2-pio"
    "github.com/tinygo-org/pio/rp2-pio/piolib"
)

// Create a color pattern (RGB values)
var COLORS = []color.RGBA{
    {R: 255, G: 0, B: 0},     // Red
    {R: 0, G: 255, B: 0},     // Green
    {R: 0, G: 0, B: 255},     // Blue
    {R: 255, G: 255, B: 0},   // Yellow
    {R: 0, G: 255, B: 255},   // Cyan
    {R: 255, G: 0, B: 255},   // Magenta
    {R: 255, G: 255, B: 255}, // White
    {R: 0, G: 0, B: 0},       // Off
}

type NeoPixel struct {
    Pin    machine.Pin
    ws2812 *piolib.WS2812B
}

func NewNeoPixel(pin machine.Pin) *NeoPixel {
    // Use the state machine (SM) of the PIO unit 0
    sm, _ := pio.PIO0.ClaimStateMachine()
    // Create piolib.WS2812B that uses the SM of PIO0
    ws, _ := piolib.NewWS2812B(sm, pin)
    ws.EnableDMA(true)
    return &NeoPixel{
        ws2812: ws,
    }
}

func (neopixel *NeoPixel) PutColor(c color.Color) {
    neopixel.ws2812.PutColor(c)
}

func (neopixel *NeoPixel) WriteRaw(rawGRB []uint32) error {
    return neopixel.ws2812.WriteRaw(rawGRB)
}

func main() {
    println("Tinygo RP2040-PIO WS2812 Demo..")
    // Define the data pin for WS2812
    dataPin := machine.GPIO23
    dataPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
    // Initialize the WS2812 RGB LED
    neopixel := NewNeoPixel(dataPin)
    for {
        for _, c := range COLORS {
            neopixel.PutColor(c)
            time.Sleep(1000 * time.Millisecond)
        }
    }
}

 


▷ ตัวอย่างที่ 9: Device ID & CPU Temperature Reading#

โค้ดตัวอย่างถัดไป สาธิตการอ่านหมายเลขของชิป (Device ID) โดยใช้คำสั่ง machine.DeviceID() ซึ่งจะได้ข้อมูล 8 ไบต์ และอ่านค่าอุณหภูมิภายในชิป RP2040 โดยใช้คำสั่ง machine.ReadTemperature() ซึ่งจะได้ค่าตัวเลขจำนวนเต็มแบบ int32 ที่จะต้องนำไปหารด้วย 1000 จึงจะได้ค่าอุณหภูมิที่เป็นเลขทศนิยม และมีหน่วยเป็นองศาเซลเซียล แล้วส่งเป็นข้อความออกมาพอร์ต USB-CDC

package main

import (
    "encoding/hex"
    "machine"
    "strconv"
    "time"
)

func main() {
    time.Sleep(time.Second)
    // Retrieve the device ID as a byte slice
    deviceID := machine.DeviceID()
    println("#Number of bytes: ", len(deviceID))
    println("#DeviceID: " + hex.EncodeToString(deviceID))

    for {
        // Read the built-in temperature sensor.
        value := float64(machine.ReadTemperature()) / 1000.0
        // Convert a float (64-bit) to a string
        valueString := strconv.FormatFloat(value, 'f', 1, 64)
        println("CPUTemp:" + valueString)
        time.Sleep(100 * time.Millisecond)
    }
}

รูป: ตัวอย่างการรับข้อความและแสดงผลเป็นรูปกราฟสัญญาณด้วย Arduino Serial Plotter

 


กล่าวสรุป#

บทความนี้ได้นำเสนอตัวอย่างการเขียนโค้ดด้วยภาษา Go และคอมไพล์โค้ดด้วย TinyGo Compiler สำหรับบอร์ดไมโครคอนโทรลเลอร์ RP2040 SoC เช่น Raspberry Pi Pico และ VCC-GND Studio YD-RP2040

 


This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Created: 2024-12-20 | Last Updated: 2024-12-23